summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/cmake/WebKitLibPlugin.cmake1
-rw-r--r--indra/llcharacter/llheadrotmotion.cpp11
-rw-r--r--indra/llcommon/llapr.cpp161
-rw-r--r--indra/llcommon/llapr.h34
-rw-r--r--indra/llcommon/llqueuedthread.cpp73
-rw-r--r--indra/llcommon/llqueuedthread.h4
-rw-r--r--indra/llcommon/llthread.h2
-rw-r--r--indra/llcommon/llversionserver.h2
-rw-r--r--indra/llcommon/llversionviewer.h2
-rw-r--r--indra/llcommon/llworkerthread.cpp1
-rw-r--r--indra/llcommon/llworkerthread.h6
-rw-r--r--indra/llimage/CMakeLists.txt4
-rw-r--r--indra/llimage/llimage.cpp67
-rw-r--r--indra/llimage/llimage.h13
-rw-r--r--indra/llimage/llimagedxt.cpp19
-rw-r--r--indra/llimage/llimagej2c.cpp5
-rw-r--r--indra/llimage/llimagejpeg.cpp9
-rw-r--r--indra/llimage/llimageworker.cpp202
-rw-r--r--indra/llimage/llimageworker.h95
-rw-r--r--indra/llimage/tests/llimageworker_test.cpp260
-rw-r--r--indra/llimagej2coj/llimagej2coj.cpp6
-rw-r--r--indra/llmath/llvolume.cpp2
-rw-r--r--indra/llmessage/llassetstorage.cpp3
-rw-r--r--indra/llmessage/llassetstorage.h11
-rw-r--r--indra/llmessage/llcurl.cpp36
-rw-r--r--indra/llmessage/llcurl.h7
-rw-r--r--indra/llmessage/tests/llcurl_stub.cpp1
-rw-r--r--indra/llmessage/tests/llhttpclientadapter_test.cpp2
-rw-r--r--indra/llrender/llimagegl.cpp395
-rw-r--r--indra/llrender/llimagegl.h85
-rw-r--r--indra/llrender/llrender.cpp15
-rw-r--r--indra/llrender/llrender.h4
-rw-r--r--indra/llrender/lltexture.h2
-rw-r--r--indra/llui/llmenugl.cpp23
-rw-r--r--indra/llui/llmenugl.h21
-rw-r--r--indra/llvfs/lllfsthread.cpp4
-rw-r--r--indra/newview/CMakeLists.txt28
-rw-r--r--indra/newview/English.lproj/InfoPlist.strings4
-rw-r--r--indra/newview/Info-SecondLife.plist2
-rw-r--r--indra/newview/app_settings/settings.xml87
-rw-r--r--indra/newview/llagent.cpp16
-rw-r--r--indra/newview/llappviewer.cpp28
-rw-r--r--indra/newview/llappviewer.h13
-rw-r--r--indra/newview/llassetuploadresponders.cpp10
-rw-r--r--indra/newview/llassetuploadresponders.h1
-rw-r--r--indra/newview/llcolorswatch.cpp2
-rw-r--r--indra/newview/lldebugview.cpp35
-rw-r--r--indra/newview/lldrawable.cpp2
-rw-r--r--indra/newview/lldrawpool.cpp8
-rw-r--r--indra/newview/lldrawpoolalpha.cpp2
-rw-r--r--indra/newview/lldrawpooltree.cpp2
-rw-r--r--indra/newview/lldynamictexture.cpp2
-rw-r--r--indra/newview/llface.cpp157
-rw-r--r--indra/newview/llface.h18
-rw-r--r--indra/newview/llfloaterinspect.cpp25
-rw-r--r--indra/newview/llfloatermap.cpp48
-rw-r--r--indra/newview/llfloatermap.h2
-rw-r--r--indra/newview/llfloaterreporter.cpp2
-rw-r--r--indra/newview/llfloateruipreview.cpp9
-rw-r--r--indra/newview/llfloatervoicedevicesettings.cpp3
-rw-r--r--indra/newview/llfloaterworldmap.cpp184
-rw-r--r--indra/newview/llfloaterworldmap.h10
-rw-r--r--indra/newview/llinspectavatar.cpp9
-rw-r--r--indra/newview/llinventorybridge.cpp10365
-rw-r--r--indra/newview/llinventorybridge.h2
-rw-r--r--indra/newview/llinventorypanel.cpp1814
-rw-r--r--indra/newview/lllandmarkactions.cpp15
-rw-r--r--indra/newview/llnavigationbar.cpp7
-rw-r--r--indra/newview/llnetmap.cpp69
-rw-r--r--indra/newview/llnetmap.h12
-rw-r--r--indra/newview/llpanellandmarks.cpp2
-rw-r--r--indra/newview/llpanellandmarks.h2
-rw-r--r--indra/newview/llpanelmaininventory.cpp1843
-rw-r--r--indra/newview/llpanelmaininventory.h270
-rw-r--r--indra/newview/llpanelmediasettingsgeneral.cpp101
-rw-r--r--indra/newview/llpanelmediasettingsgeneral.h5
-rw-r--r--indra/newview/llpanelprimmediacontrols.cpp7
-rw-r--r--indra/newview/llpanelteleporthistory.cpp2
-rw-r--r--indra/newview/llpreviewtexture.cpp25
-rw-r--r--indra/newview/llpreviewtexture.h12
-rw-r--r--indra/newview/llselectmgr.cpp20
-rw-r--r--indra/newview/llsidepanelinventory.cpp518
-rw-r--r--indra/newview/llsidepanelinventory.h170
-rw-r--r--indra/newview/llsidepanelinventorysubpanel.cpp155
-rw-r--r--indra/newview/llsidepanelinventorysubpanel.h82
-rw-r--r--indra/newview/llsidepaneliteminfo.cpp882
-rw-r--r--indra/newview/llsidepaneliteminfo.h91
-rw-r--r--indra/newview/llsidepaneltaskinfo.cpp1124
-rw-r--r--indra/newview/llsidepaneltaskinfo.h115
-rw-r--r--indra/newview/llspatialpartition.cpp3
-rw-r--r--indra/newview/llstartup.cpp9
-rw-r--r--indra/newview/llsurface.cpp7
-rw-r--r--indra/newview/lltexlayer.cpp121
-rw-r--r--indra/newview/lltexlayer.h19
-rw-r--r--indra/newview/lltexturecache.cpp1286
-rw-r--r--indra/newview/lltexturecache.h65
-rw-r--r--indra/newview/lltexturefetch.cpp1056
-rw-r--r--indra/newview/lltexturefetch.h37
-rw-r--r--indra/newview/lltextureinfo.cpp290
-rw-r--r--indra/newview/lltextureinfo.h80
-rw-r--r--indra/newview/lltextureinfodetails.cpp40
-rw-r--r--indra/newview/lltextureinfodetails.h58
-rw-r--r--indra/newview/lltexturestats.cpp61
-rw-r--r--indra/newview/lltexturestats.h41
-rw-r--r--indra/newview/lltexturestatsuploader.cpp59
-rw-r--r--indra/newview/lltexturestatsuploader.h48
-rw-r--r--indra/newview/lltextureview.cpp332
-rw-r--r--indra/newview/lltextureview.h27
-rw-r--r--indra/newview/lltoolpie.cpp2
-rw-r--r--indra/newview/llurldispatcher.cpp8
-rw-r--r--indra/newview/llviewercamera.cpp16
-rw-r--r--indra/newview/llviewercamera.h14
-rw-r--r--indra/newview/llviewercontrol.cpp9
-rw-r--r--indra/newview/llviewerdisplay.cpp3
-rw-r--r--indra/newview/llviewermenu.cpp85
-rw-r--r--indra/newview/llviewermenu.h1
-rw-r--r--indra/newview/llviewermessage.cpp55
-rw-r--r--indra/newview/llviewermessage.h1
-rw-r--r--indra/newview/llviewerobject.cpp2
-rw-r--r--indra/newview/llviewerobject.h2
-rw-r--r--indra/newview/llviewerobjectlist.cpp8
-rw-r--r--indra/newview/llviewerregion.cpp4
-rw-r--r--indra/newview/llviewerstats.cpp28
-rw-r--r--indra/newview/llviewerstats.h4
-rw-r--r--indra/newview/llviewertexture.cpp877
-rw-r--r--indra/newview/llviewertexture.h137
-rw-r--r--indra/newview/llviewertexturelist.cpp173
-rw-r--r--indra/newview/llviewertexturelist.h23
-rw-r--r--indra/newview/llviewerwindow.cpp15
-rw-r--r--indra/newview/llviewerwindow.h2
-rw-r--r--indra/newview/llvlcomposition.cpp13
-rw-r--r--indra/newview/llvoavatar.cpp44
-rw-r--r--indra/newview/llvoavatar.h5
-rw-r--r--indra/newview/llvoavatarself.cpp85
-rw-r--r--indra/newview/llvoavatarself.h2
-rw-r--r--indra/newview/llvoclouds.cpp2
-rw-r--r--indra/newview/llvoclouds.h2
-rw-r--r--indra/newview/llvograss.cpp2
-rw-r--r--indra/newview/llvograss.h2
-rw-r--r--indra/newview/llvoground.cpp2
-rw-r--r--indra/newview/llvoground.h2
-rw-r--r--indra/newview/llvopartgroup.cpp2
-rw-r--r--indra/newview/llvopartgroup.h2
-rw-r--r--indra/newview/llvosky.cpp4
-rw-r--r--indra/newview/llvosky.h2
-rw-r--r--indra/newview/llvosurfacepatch.cpp2
-rw-r--r--indra/newview/llvosurfacepatch.h2
-rw-r--r--indra/newview/llvotextbubble.cpp2
-rw-r--r--indra/newview/llvotextbubble.h2
-rw-r--r--indra/newview/llvotree.cpp2
-rw-r--r--indra/newview/llvotree.h2
-rw-r--r--indra/newview/llvotreenew.h2
-rw-r--r--indra/newview/llvovolume.cpp193
-rw-r--r--indra/newview/llvovolume.h7
-rw-r--r--indra/newview/llvowater.cpp2
-rw-r--r--indra/newview/llvowater.h2
-rw-r--r--indra/newview/llwearablelist.cpp5
-rw-r--r--indra/newview/llwearablelist.h1
-rw-r--r--indra/newview/llworldmap.cpp1174
-rw-r--r--indra/newview/llworldmap.h332
-rw-r--r--indra/newview/llworldmapmessage.cpp261
-rw-r--r--indra/newview/llworldmapmessage.h83
-rw-r--r--indra/newview/llworldmapview.cpp1244
-rw-r--r--indra/newview/llworldmapview.h51
-rw-r--r--indra/newview/llworldmipmap.cpp275
-rw-r--r--indra/newview/llworldmipmap.h100
-rw-r--r--indra/newview/pipeline.cpp10
-rw-r--r--indra/newview/res/viewerRes.rc8
-rw-r--r--indra/newview/skins/default/textures/Blank.pngbin0 -> 110 bytes
-rw-r--r--indra/newview/skins/default/textures/default_profile_picture.j2cbin6128 -> 1512 bytes
-rw-r--r--indra/newview/skins/default/textures/map_avatar_32.tgabin0 -> 2894 bytes
-rw-r--r--indra/newview/skins/default/textures/map_avatar_above_32.tgabin0 -> 2909 bytes
-rw-r--r--indra/newview/skins/default/textures/map_avatar_below_32.tgabin0 -> 3037 bytes
-rw-r--r--indra/newview/skins/default/textures/map_avatar_you_32.tgabin0 -> 3395 bytes
-rw-r--r--indra/newview/skins/default/textures/textures.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_about.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_media_settings.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_test_textbox.xml77
-rw-r--r--indra/newview/skins/default/xui/en/floater_test_widgets.xml44
-rw-r--r--indra/newview/skins/default/xui/en/floater_world_map.xml33
-rw-r--r--indra/newview/skins/default/xui/en/fonts.xml4
-rw-r--r--indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml2
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory.xml10
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory_add.xml229
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml90
-rw-r--r--indra/newview/skins/default/xui/en/menu_login.xml17
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml92
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_main_inventory.xml882
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_sound.xml13
-rw-r--r--indra/newview/skins/default/xui/en/panel_prim_media_controls.xml40
-rw-r--r--indra/newview/skins/default/xui/en/panel_profile.xml4
-rw-r--r--indra/newview/skins/default/xui/en/sidepanel_inventory.xml249
-rw-r--r--indra/newview/skins/default/xui/en/sidepanel_item_info.xml511
-rw-r--r--indra/newview/skins/default/xui/en/widgets/menu_item.xml6
-rw-r--r--indra/newview/tests/lltextureinfo_test.cpp284
-rw-r--r--indra/newview/tests/lltextureinfodetails_test.cpp98
-rw-r--r--indra/newview/tests/lltexturestatsuploader_test.cpp156
-rw-r--r--indra/newview/tests/llworldmap_test.cpp523
-rw-r--r--indra/newview/tests/llworldmipmap_test.cpp176
200 files changed, 20331 insertions, 12265 deletions
diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake
index 9ec23e80ca..1c572ab27f 100644
--- a/indra/cmake/WebKitLibPlugin.cmake
+++ b/indra/cmake/WebKitLibPlugin.cmake
@@ -51,6 +51,7 @@ elseif (LINUX)
QtGui
QtCore
+ jpeg
fontconfig
X11
Xrender
diff --git a/indra/llcharacter/llheadrotmotion.cpp b/indra/llcharacter/llheadrotmotion.cpp
index 88cd77f7af..0ee378f3b8 100644
--- a/indra/llcharacter/llheadrotmotion.cpp
+++ b/indra/llcharacter/llheadrotmotion.cpp
@@ -251,10 +251,13 @@ BOOL LLHeadRotMotion::onUpdate(F32 time, U8* joint_mask)
mLastHeadRot = head_rot_local;
// Set the head rotation.
- LLQuaternion torsoRotLocal = mNeckState->getJoint()->getParent()->getWorldRotation() * currentInvRootRotWorld;
- head_rot_local = head_rot_local * ~torsoRotLocal;
- mNeckState->setRotation( nlerp(NECK_LAG, LLQuaternion::DEFAULT, head_rot_local) );
- mHeadState->setRotation( nlerp(1.f - NECK_LAG, LLQuaternion::DEFAULT, head_rot_local));
+ if(mNeckState->getJoint() && mNeckState->getJoint()->getParent())
+ {
+ LLQuaternion torsoRotLocal = mNeckState->getJoint()->getParent()->getWorldRotation() * currentInvRootRotWorld;
+ head_rot_local = head_rot_local * ~torsoRotLocal;
+ mNeckState->setRotation( nlerp(NECK_LAG, LLQuaternion::DEFAULT, head_rot_local) );
+ mHeadState->setRotation( nlerp(1.f - NECK_LAG, LLQuaternion::DEFAULT, head_rot_local));
+ }
return TRUE;
}
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index 669afc5330..ed70b1d9f2 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -57,7 +57,7 @@ void ll_init_apr()
if(!LLAPRFile::sAPRFilePoolp)
{
- LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool() ;
+ LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool(FALSE) ;
}
}
@@ -99,13 +99,12 @@ void ll_cleanup_apr()
//
//LLAPRPool
//
-LLAPRPool::LLAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
-{
- mParent = parent ;
- mReleasePoolFlag = releasePoolFlag ;
- mMaxSize = size ;
- mPool = NULL ;
-
+LLAPRPool::LLAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
+ : mParent(parent),
+ mReleasePoolFlag(releasePoolFlag),
+ mMaxSize(size),
+ mPool(NULL)
+{
createAPRPool() ;
}
@@ -148,31 +147,65 @@ void LLAPRPool::releaseAPRPool()
}
}
+//virtual
apr_pool_t* LLAPRPool::getAPRPool()
+{
+ return mPool ;
+}
+
+LLVolatileAPRPool::LLVolatileAPRPool(BOOL is_local, apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
+ : LLAPRPool(parent, size, releasePoolFlag),
+ mNumActiveRef(0),
+ mNumTotalRef(0),
+ mMutexPool(NULL),
+ mMutexp(NULL)
{
- if(!mPool)
+ //create mutex
+ if(!is_local) //not a local apr_pool, that is: shared by multiple threads.
{
- createAPRPool() ;
+ apr_pool_create(&mMutexPool, NULL); // Create a pool for mutex
+ apr_thread_mutex_create(&mMutexp, APR_THREAD_MUTEX_UNNESTED, mMutexPool);
}
-
- return mPool ;
}
-LLVolatileAPRPool::LLVolatileAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
- : LLAPRPool(parent, size, releasePoolFlag)
+
+LLVolatileAPRPool::~LLVolatileAPRPool()
{
- mNumActiveRef = 0 ;
- mNumTotalRef = 0 ;
+ //delete mutex
+ if(mMutexp)
+ {
+ apr_thread_mutex_destroy(mMutexp);
+ apr_pool_destroy(mMutexPool);
+ }
}
-apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool()
+//
+//define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool().
+//
+//virtual
+apr_pool_t* LLVolatileAPRPool::getAPRPool()
{
+ return LLVolatileAPRPool::getVolatileAPRPool() ;
+}
+
+apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool()
+{
+ LLScopedLock lock(mMutexp) ;
+
mNumTotalRef++ ;
mNumActiveRef++ ;
- return getAPRPool() ;
+
+ if(!mPool)
+ {
+ createAPRPool() ;
+ }
+
+ return mPool ;
}
void LLVolatileAPRPool::clearVolatileAPRPool()
{
+ LLScopedLock lock(mMutexp) ;
+
if(mNumActiveRef > 0)
{
mNumActiveRef--;
@@ -251,10 +284,9 @@ void LLScopedLock::unlock()
bool ll_apr_warn_status(apr_status_t status)
{
if(APR_SUCCESS == status) return false;
-#ifndef LL_WINDOWS
char buf[MAX_STRING]; /* Flawfinder: ignore */
- LL_WARNS_ONCE("APR") << "APR: " << apr_strerror(status, buf, MAX_STRING) << LL_ENDL;
-#endif
+ apr_strerror(status, buf, MAX_STRING);
+ LL_WARNS("APR") << "APR: " << buf << LL_ENDL;
return true;
}
@@ -268,10 +300,18 @@ void ll_apr_assert_status(apr_status_t status)
// LLAPRFile functions
//
LLAPRFile::LLAPRFile()
+ : mFile(NULL),
+ mCurrentFilePoolp(NULL)
+{
+}
+
+LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool)
+ : mFile(NULL),
+ mCurrentFilePoolp(NULL)
{
- mFile = NULL ;
- mCurrentFilePoolp = NULL ;
+ open(filename, flags, pool);
}
+
LLAPRFile::~LLAPRFile()
{
close() ;
@@ -295,11 +335,40 @@ apr_status_t LLAPRFile::close()
return ret ;
}
-apr_status_t LLAPRFile::open(LLVolatileAPRPool* pool, const std::string& filename, apr_int32_t flags, S32* sizep)
+apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool, S32* sizep)
{
apr_status_t s ;
- s = open(filename, flags, pool ? pool->getVolatileAPRPool() : NULL, sizep) ;
+
+ //check if already open some file
+ llassert_always(!mFile) ;
+ llassert_always(!mCurrentFilePoolp) ;
+ apr_pool_t* apr_pool = pool ? pool->getVolatileAPRPool() : NULL ;
+ s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, getAPRFilePool(apr_pool));
+
+ if (s != APR_SUCCESS || !mFile)
+ {
+ mFile = NULL ;
+
+ if (sizep)
+ {
+ *sizep = 0;
+ }
+ }
+ else if (sizep)
+ {
+ S32 file_size = 0;
+ apr_off_t offset = 0;
+ if (apr_file_seek(mFile, APR_END, &offset) == APR_SUCCESS)
+ {
+ llassert_always(offset <= 0x7fffffff);
+ file_size = (S32)offset;
+ offset = 0;
+ apr_file_seek(mFile, APR_SET, &offset);
+ }
+ *sizep = file_size;
+ }
+
if(!mCurrentFilePoolp)
{
mCurrentFilePoolp = pool ;
@@ -312,40 +381,25 @@ apr_status_t LLAPRFile::open(LLVolatileAPRPool* pool, const std::string& filenam
return s ;
}
-apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, apr_pool_t* pool, S32* sizep)
+
+//use gAPRPoolp.
+apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, BOOL use_global_pool)
{
apr_status_t s;
//check if already open some file
llassert_always(!mFile) ;
llassert_always(!mCurrentFilePoolp) ;
+ llassert_always(use_global_pool) ; //be aware of using gAPRPoolp.
- s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, getAPRFilePool(pool));
+ s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp);
if (s != APR_SUCCESS || !mFile)
{
mFile = NULL ;
close() ;
- if (sizep)
- {
- *sizep = 0;
- }
return s;
}
- if (sizep)
- {
- S32 file_size = 0;
- apr_off_t offset = 0;
- if (apr_file_seek(mFile, APR_END, &offset) == APR_SUCCESS)
- {
- llassert_always(offset <= 0x7fffffff);
- file_size = (S32)offset;
- offset = 0;
- apr_file_seek(mFile, APR_SET, &offset);
- }
- *sizep = file_size;
- }
-
return s;
}
@@ -369,6 +423,7 @@ S32 LLAPRFile::read(void *buf, S32 nbytes)
apr_status_t s = apr_file_read(mFile, buf, &sz);
if (s != APR_SUCCESS)
{
+ ll_apr_warn_status(s);
return 0;
}
else
@@ -386,6 +441,7 @@ S32 LLAPRFile::write(const void *buf, S32 nbytes)
apr_status_t s = apr_file_write(mFile, buf, &sz);
if (s != APR_SUCCESS)
{
+ ll_apr_warn_status(s);
return 0;
}
else
@@ -434,6 +490,8 @@ apr_file_t* LLAPRFile::open(const std::string& filename, LLVolatileAPRPool* pool
s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool->getVolatileAPRPool());
if (s != APR_SUCCESS || !file_handle)
{
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to open filename: " << filename << LL_ENDL;
file_handle = NULL ;
close(file_handle, pool) ;
return NULL;
@@ -464,6 +522,7 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
}
if (s != APR_SUCCESS)
{
+ ll_apr_warn_status(s);
return -1;
}
else
@@ -501,6 +560,8 @@ S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nb
apr_status_t s = apr_file_read(file_handle, buf, &bytes_read);
if (s != APR_SUCCESS)
{
+ LL_WARNS("APR") << " Attempting to read filename: " << filename << LL_ENDL;
+ ll_apr_warn_status(s);
bytes_read = 0;
}
else
@@ -549,6 +610,8 @@ S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 n
apr_status_t s = apr_file_write(file_handle, buf, &bytes_written);
if (s != APR_SUCCESS)
{
+ LL_WARNS("APR") << " Attempting to write filename: " << filename << LL_ENDL;
+ ll_apr_warn_status(s);
bytes_written = 0;
}
else
@@ -575,8 +638,8 @@ bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool)
if (s != APR_SUCCESS)
{
- LL_DEBUGS("APR") << "LLAPRFile::remove failed on file: " << filename << LL_ENDL;
ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to remove filename: " << filename << LL_ENDL;
return false;
}
return true;
@@ -593,8 +656,8 @@ bool LLAPRFile::rename(const std::string& filename, const std::string& newname,
if (s != APR_SUCCESS)
{
- LL_DEBUGS("APR") << "LLAPRFile::rename failed on file: " << filename << LL_ENDL;
ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to rename filename: " << filename << LL_ENDL;
return false;
}
return true;
@@ -667,8 +730,8 @@ bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool)
if (s != APR_SUCCESS)
{
- LL_DEBUGS("APR") << "LLAPRFile::makeDir failed on file: " << dirname << LL_ENDL;
ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL;
return false;
}
return true;
@@ -685,8 +748,8 @@ bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool)
if (s != APR_SUCCESS)
{
- LL_DEBUGS("APR") << "LLAPRFile::removeDir failed on file: " << dirname << LL_ENDL;
ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to remove directory: " << dirname << LL_ENDL;
return false;
}
return true;
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index a1fcd2cf8d..b08bb617c5 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -70,9 +70,9 @@ class LL_COMMON_API LLAPRPool
{
public:
LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ;
- ~LLAPRPool() ;
+ virtual ~LLAPRPool() ;
- apr_pool_t* getAPRPool() ;
+ virtual apr_pool_t* getAPRPool() ;
apr_status_t getStatus() {return mStatus ; }
protected:
@@ -95,18 +95,21 @@ protected:
class LL_COMMON_API LLVolatileAPRPool : public LLAPRPool
{
public:
- LLVolatileAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE);
- ~LLVolatileAPRPool(){}
+ LLVolatileAPRPool(BOOL is_local = TRUE, apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE);
+ virtual ~LLVolatileAPRPool();
- apr_pool_t* getVolatileAPRPool() ;
-
+ /*virtual*/ apr_pool_t* getAPRPool() ; //define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool().
+ apr_pool_t* getVolatileAPRPool() ;
void clearVolatileAPRPool() ;
BOOL isFull() ;
- BOOL isEmpty() {return !mNumActiveRef ;}
+
private:
S32 mNumActiveRef ; //number of active pointers pointing to the apr_pool.
- S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating.
+ S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating.
+
+ apr_thread_mutex_t *mMutexp;
+ apr_pool_t *mMutexPool;
} ;
/**
@@ -192,18 +195,21 @@ typedef LLAtomic32<S32> LLAtomicS32;
// 1, a temperary pool passed to an APRFile function, which is used within this function and only once.
// 2, a global pool.
//
-class LL_COMMON_API LLAPRFile
+
+class LL_COMMON_API LLAPRFile : boost::noncopyable
{
+ // make this non copyable since a copy closes the file
private:
apr_file_t* mFile ;
LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool.
public:
LLAPRFile() ;
+ LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL);
~LLAPRFile() ;
-
- apr_status_t open(LLVolatileAPRPool* pool, const std::string& filename, apr_int32_t flags, S32* sizep = NULL);
- apr_status_t open(const std::string& filename, apr_int32_t flags, apr_pool_t* pool = NULL, S32* sizep = NULL);
+
+ apr_status_t open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL, S32* sizep = NULL);
+ apr_status_t open(const std::string& filename, apr_int32_t flags, BOOL use_global_pool); //use gAPRPoolp.
apr_status_t close() ;
// Returns actual offset, -1 if seek fails
@@ -217,8 +223,8 @@ public:
apr_file_t* getFileHandle() {return mFile;}
private:
- apr_pool_t* getAPRFilePool(apr_pool_t* pool) ;
-
+ apr_pool_t* getAPRFilePool(apr_pool_t* pool) ;
+
//
//*******************************************************************************************************************************
//static components
diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp
index 3db5c36545..e7ad571a90 100644
--- a/indra/llcommon/llqueuedthread.cpp
+++ b/indra/llcommon/llqueuedthread.cpp
@@ -42,7 +42,8 @@ LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) :
LLThread(name),
mThreaded(threaded),
mIdleThread(TRUE),
- mNextHandle(0)
+ mNextHandle(0),
+ mStarted(FALSE)
{
if (mThreaded)
{
@@ -53,6 +54,10 @@ LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) :
// MAIN THREAD
LLQueuedThread::~LLQueuedThread()
{
+ if (!mThreaded)
+ {
+ endThread();
+ }
shutdown();
// ~LLThread() will be called here
}
@@ -106,6 +111,14 @@ void LLQueuedThread::shutdown()
// virtual
S32 LLQueuedThread::update(U32 max_time_ms)
{
+ if (!mStarted)
+ {
+ if (!mThreaded)
+ {
+ startThread();
+ mStarted = TRUE;
+ }
+ }
return updateQueue(max_time_ms);
}
@@ -452,26 +465,12 @@ S32 LLQueuedThread::processNextRequest()
}
}
- S32 res;
S32 pending = getPending();
- if (pending == 0)
- {
- if (isQuitting())
- {
- res = -1; // exit thread
- }
- else
- {
- res = 0;
- }
- }
- else
- {
- res = pending;
- }
- return res;
+
+ return pending;
}
+// virtual
bool LLQueuedThread::runCondition()
{
// mRunCondition must be locked here
@@ -481,35 +480,53 @@ bool LLQueuedThread::runCondition()
return true;
}
+// virtual
void LLQueuedThread::run()
{
+ // call checPause() immediately so we don't try to do anything before the class is fully constructed
+ checkPause();
+ startThread();
+ mStarted = TRUE;
+
while (1)
{
// this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
checkPause();
- if(isQuitting())
+ if (isQuitting())
+ {
+ endThread();
break;
-
- //llinfos << "QUEUED THREAD RUNNING, queue size = " << mRequestQueue.size() << llendl;
+ }
mIdleThread = FALSE;
+
+ threadedUpdate();
int res = processNextRequest();
if (res == 0)
{
mIdleThread = TRUE;
+ ms_sleep(1);
}
-
- if (res < 0) // finished working and want to exit
- {
- break;
- }
-
//LLThread::yield(); // thread should yield after each request
}
+ llinfos << "LLQueuedThread " << mName << " EXITING." << llendl;
+}
+
+// virtual
+void LLQueuedThread::startThread()
+{
+}
- llinfos << "QUEUED THREAD " << mName << " EXITING." << llendl;
+// virtual
+void LLQueuedThread::endThread()
+{
+}
+
+// virtual
+void LLQueuedThread::threadedUpdate()
+{
}
//============================================================================
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
index 8bfa5632a1..9a9dbb18cc 100644
--- a/indra/llcommon/llqueuedthread.h
+++ b/indra/llcommon/llqueuedthread.h
@@ -166,6 +166,9 @@ private:
virtual bool runCondition(void);
virtual void run(void);
+ virtual void startThread(void);
+ virtual void endThread(void);
+ virtual void threadedUpdate(void);
protected:
handle_t generateHandle();
@@ -200,6 +203,7 @@ public:
protected:
BOOL mThreaded; // if false, run on main thread and do updates during update()
+ BOOL mStarted; // required when mThreaded is false to call startThread() from update()
LLAtomic32<BOOL> mIdleThread; // request queue is empty (or we are quitting) and the thread is idle
typedef std::set<QueuedRequest*, queued_request_less> request_queue_t;
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index c3d7650bd9..932d96d940 100644
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -33,9 +33,7 @@
#ifndef LL_LLTHREAD_H
#define LL_LLTHREAD_H
-#include "llapr.h"
#include "llapp.h"
-
#include "apr_thread_cond.h"
class LLThread;
diff --git a/indra/llcommon/llversionserver.h b/indra/llcommon/llversionserver.h
index 71c6fc0591..0f1e59a18c 100644
--- a/indra/llcommon/llversionserver.h
+++ b/indra/llcommon/llversionserver.h
@@ -36,7 +36,7 @@
const S32 LL_VERSION_MAJOR = 1;
const S32 LL_VERSION_MINOR = 31;
const S32 LL_VERSION_PATCH = 0;
-const S32 LL_VERSION_BUILD = 3256;
+const S32 LL_VERSION_BUILD = 200030;
const char * const LL_CHANNEL = "Second Life Server";
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 082d054ba2..540aea4252 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -36,7 +36,7 @@
const S32 LL_VERSION_MAJOR = 2;
const S32 LL_VERSION_MINOR = 0;
const S32 LL_VERSION_PATCH = 0;
-const S32 LL_VERSION_BUILD = 3256;
+const S32 LL_VERSION_BUILD = 200030;
const char * const LL_CHANNEL = "Second Life Developer";
diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp
index 5dda600755..82c736266d 100644
--- a/indra/llcommon/llworkerthread.cpp
+++ b/indra/llcommon/llworkerthread.cpp
@@ -201,6 +201,7 @@ LLWorkerClass::~LLWorkerClass()
{
llassert_always(!(mWorkFlags & WCF_WORKING));
llassert_always(mWorkFlags & WCF_DELETE_REQUESTED);
+ llassert_always(!mMutex.isLocked());
if (mRequestHandle != LLWorkerThread::nullHandle())
{
LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle);
diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h
index a12bd52a64..a1e85d2ecc 100644
--- a/indra/llcommon/llworkerthread.h
+++ b/indra/llcommon/llworkerthread.h
@@ -52,6 +52,7 @@ class LLWorkerClass;
class LL_COMMON_API LLWorkerThread : public LLQueuedThread
{
+ friend class LLWorkerClass;
public:
class WorkRequest : public LLQueuedThread::QueuedRequest
{
@@ -92,8 +93,11 @@ public:
handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL);
- void deleteWorker(LLWorkerClass* workerclass); // schedule for deletion
S32 getNumDeletes() { return (S32)mDeleteList.size(); } // debug
+
+private:
+ void deleteWorker(LLWorkerClass* workerclass); // schedule for deletion
+
};
//============================================================================
diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt
index 0635ddd5f5..22be4078a1 100644
--- a/indra/llimage/CMakeLists.txt
+++ b/indra/llimage/CMakeLists.txt
@@ -3,6 +3,7 @@
project(llimage)
include(00-Common)
+include(LLAddBuildTest)
include(LLCommon)
include(LLImage)
include(LLMath)
@@ -59,3 +60,6 @@ target_link_libraries(llimage
${PNG_LIBRARIES}
${ZLIB_LIBRARIES}
)
+
+# Add tests
+#ADD_BUILD_TEST(llimageworker llimage)
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 73c23fa8d8..575ad5363d 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -55,13 +55,9 @@ std::string LLImage::sLastErrorMessage;
LLMutex* LLImage::sMutex = NULL;
//static
-void LLImage::initClass(LLWorkerThread* workerthread)
+void LLImage::initClass()
{
sMutex = new LLMutex(NULL);
- if (workerthread)
- {
- LLImageWorker::initImageWorker(workerthread);
- }
LLImageJ2C::openDSO();
}
@@ -69,7 +65,6 @@ void LLImage::initClass(LLWorkerThread* workerthread)
void LLImage::cleanupClass()
{
LLImageJ2C::closeDSO();
- LLImageWorker::cleanupImageWorker();
delete sMutex;
sMutex = NULL;
}
@@ -316,6 +311,21 @@ void LLImageRaw::deleteData()
LLImageBase::deleteData();
}
+void LLImageRaw::setDataAndSize(U8 *data, S32 width, S32 height, S8 components)
+{
+ if(data == getData())
+ {
+ return ;
+ }
+
+ deleteData();
+
+ LLImageBase::setSize(width, height, components) ;
+ LLImageBase::setDataAndSize(data, width * height * components) ;
+
+ sGlobalRawMemory += getDataSize();
+}
+
BOOL LLImageRaw::resize(U16 width, U16 height, S8 components)
{
if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components))
@@ -816,6 +826,51 @@ void LLImageRaw::copyScaled( LLImageRaw* src )
}
}
+//scale down image by not blending a pixel with its neighbors.
+BOOL LLImageRaw::scaleDownWithoutBlending( S32 new_width, S32 new_height)
+{
+ LLMemType mt1(mMemType);
+
+ S8 c = getComponents() ;
+ llassert((1 == c) || (3 == c) || (4 == c) );
+
+ S32 old_width = getWidth();
+ S32 old_height = getHeight();
+
+ S32 new_data_size = old_width * new_height * c ;
+ llassert_always(new_data_size > 0);
+
+ F32 ratio_x = (F32)old_width / new_width ;
+ F32 ratio_y = (F32)old_height / new_height ;
+ if( ratio_x < 1.0f || ratio_y < 1.0f )
+ {
+ return TRUE; // Nothing to do.
+ }
+ ratio_x -= 1.0f ;
+ ratio_y -= 1.0f ;
+
+ U8* new_data = new U8[new_data_size] ;
+ llassert_always(new_data != NULL) ;
+
+ U8* old_data = getData() ;
+ S32 i, j, k, s, t;
+ for(i = 0, s = 0, t = 0 ; i < new_height ; i++)
+ {
+ for(j = 0 ; j < new_width ; j++)
+ {
+ for(k = 0 ; k < c ; k++)
+ {
+ new_data[s++] = old_data[t++] ;
+ }
+ t += (S32)(ratio_x * c + 0.1f) ;
+ }
+ t += (S32)(ratio_y * old_width * c + 0.1f) ;
+ }
+
+ setDataAndSize(new_data, new_width, new_height, c) ;
+
+ return TRUE ;
+}
BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data )
{
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 8108553107..686f583886 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -50,7 +50,8 @@ const S32 MAX_IMAGE_AREA = MAX_IMAGE_SIZE * MAX_IMAGE_SIZE;
const S32 MAX_IMAGE_COMPONENTS = 8;
const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS;
-// Note! These CANNOT be changed without invalidating the viewer VFS files, I think?
+// Note! These CANNOT be changed without modifying simulator code
+// *TODO: change both to 1024 when SIM texture fetching is deprecated
const S32 FIRST_PACKET_SIZE = 600;
const S32 MAX_IMG_PACKET_SIZE = 1000;
@@ -61,7 +62,6 @@ const S32 MAX_IMG_PACKET_SIZE = 1000;
class LLImageFormatted;
class LLImageRaw;
class LLColor4U;
-class LLWorkerThread;
typedef enum e_image_codec
{
@@ -82,7 +82,7 @@ typedef enum e_image_codec
class LLImage
{
public:
- static void initClass(LLWorkerThread* workerthread);
+ static void initClass();
static void cleanupClass();
static const std::string& getLastError();
@@ -131,7 +131,7 @@ public:
protected:
// special accessor to allow direct setting of mData and mDataSize by LLImageFormatted
- void setDataAndSize(U8 *data, S32 size) { mData = data; mDataSize = size; };
+ void setDataAndSize(U8 *data, S32 size) { mData = data; mDataSize = size; }
public:
static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels);
@@ -192,6 +192,7 @@ public:
void contractToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, BOOL scale_image = TRUE);
void biasedScaleToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE);
BOOL scale( S32 new_width, S32 new_height, BOOL scale_image = TRUE );
+ BOOL scaleDownWithoutBlending( S32 new_width, S32 new_height) ;
// Fill the buffer with a constant color
void fill( const LLColor4U& color );
@@ -240,6 +241,8 @@ protected:
U8 fastFractionalMult(U8 a,U8 b);
+ void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ;
+
public:
static S32 sGlobalRawMemory;
static S32 sRawImageCount;
@@ -310,7 +313,7 @@ protected:
protected:
S8 mCodec;
S8 mDecoding;
- S8 mDecoded;
+ S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC
S8 mDiscardLevel;
public:
diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp
index 1ce4517a0d..0aa6840ff6 100644
--- a/indra/llimage/llimagedxt.cpp
+++ b/indra/llimage/llimagedxt.cpp
@@ -264,6 +264,8 @@ void LLImageDXT::setFormat()
// virtual
BOOL LLImageDXT::decode(LLImageRaw* raw_image, F32 time)
{
+ // *TODO: Test! This has been tweaked since its intial inception,
+ // but we don't use it any more!
llassert_always(raw_image);
if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5)
@@ -274,8 +276,17 @@ BOOL LLImageDXT::decode(LLImageRaw* raw_image, F32 time)
S32 width = getWidth(), height = getHeight();
S32 ncomponents = getComponents();
+ U8* data = NULL;
+ if (mDiscardLevel >= 0)
+ {
+ data = getData() + getMipOffset(mDiscardLevel);
+ calcDiscardWidthHeight(mDiscardLevel, mFileFormat, width, height);
+ }
+ else
+ {
+ data = getData() + getMipOffset(0);
+ }
S32 image_size = formatBytes(mFileFormat, width, height);
- U8* data = getData() + getMipOffset(0);
if ((!getData()) || (data + image_size > getData() + getDataSize()))
{
@@ -300,10 +311,8 @@ BOOL LLImageDXT::getMipData(LLPointer<LLImageRaw>& raw, S32 discard)
llerrs << "Request for invalid discard level" << llendl;
}
U8* data = getData() + getMipOffset(discard);
- // I'm not sure these are the correct initial values for height and width,
- // but previously they were being used uninitialized. JC
- S32 width = raw->getWidth();
- S32 height = raw->getHeight();
+ S32 width = 0;
+ S32 height = 0;
calcDiscardWidthHeight(discard, mFileFormat, width, height);
raw = new LLImageRaw(data, width, height, getComponents());
return TRUE;
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index 2352c8edd7..74f08b2d0b 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -283,6 +283,7 @@ BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
}
+// Returns TRUE to mean done, whether successful or not.
BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count )
{
LLMemType mt1(mMemType);
@@ -295,7 +296,7 @@ BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 fir
if (!getData() || (getDataSize() < 16))
{
setLastError("LLImageJ2C uninitialized");
- res = FALSE;
+ res = TRUE; // done
}
else
{
@@ -348,7 +349,7 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text,
//static
S32 LLImageJ2C::calcHeaderSizeJ2C()
{
- return 600; //2048; // ??? hack... just needs to be >= actual header size...
+ return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size...
}
//static
diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp
index fa0dd3ff05..79ea79cc07 100644
--- a/indra/llimage/llimagejpeg.cpp
+++ b/indra/llimage/llimagejpeg.cpp
@@ -188,6 +188,7 @@ void LLImageJPEG::decodeTermSource (j_decompress_ptr cinfo)
}
+// Returns true when done, whether or not decode was successful.
BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time)
{
llassert_always(raw_image);
@@ -198,7 +199,7 @@ BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time)
if (!getData() || (0 == getDataSize()))
{
setLastError("LLImageJPEG trying to decode an image with no data!");
- return FALSE;
+ return TRUE; // done
}
S32 row_stride = 0;
@@ -226,7 +227,7 @@ BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time)
if(setjmp(sSetjmpBuffer))
{
jpeg_destroy_decompress(&cinfo);
- return FALSE;
+ return TRUE; // done
}
try
{
@@ -320,7 +321,7 @@ BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time)
catch (int)
{
jpeg_destroy_decompress(&cinfo);
- return FALSE;
+ return TRUE; // done
}
// Check to see whether any corrupt-data warnings occurred
@@ -328,7 +329,7 @@ BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time)
{
// TODO: extract the warning to find out what went wrong.
setLastError( "Unable to decode JPEG image.");
- return FALSE;
+ return TRUE; // done
}
return TRUE;
diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp
index 532e996188..86d41515e7 100644
--- a/indra/llimage/llimageworker.cpp
+++ b/indra/llimage/llimageworker.cpp
@@ -37,152 +37,138 @@
//----------------------------------------------------------------------------
-//static
-LLWorkerThread* LLImageWorker::sWorkerThread = NULL;
-S32 LLImageWorker::sCount = 0;
+// MAIN THREAD
+LLImageDecodeThread::LLImageDecodeThread(bool threaded)
+ : LLQueuedThread("imagedecode", threaded)
+{
+ mCreationMutex = new LLMutex(getAPRPool());
+}
-//static
-void LLImageWorker::initImageWorker(LLWorkerThread* workerthread)
+// MAIN THREAD
+// virtual
+S32 LLImageDecodeThread::update(U32 max_time_ms)
{
- sWorkerThread = workerthread;
+ LLMutexLock lock(mCreationMutex);
+ for (creation_list_t::iterator iter = mCreationList.begin();
+ iter != mCreationList.end(); ++iter)
+ {
+ creation_info& info = *iter;
+ ImageRequest* req = new ImageRequest(info.handle, info.image,
+ info.priority, info.discard, info.needs_aux,
+ info.responder);
+ addRequest(req);
+ }
+ mCreationList.clear();
+ S32 res = LLQueuedThread::update(max_time_ms);
+ return res;
}
-//static
-void LLImageWorker::cleanupImageWorker()
+LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image,
+ U32 priority, S32 discard, BOOL needs_aux, Responder* responder)
+{
+ LLMutexLock lock(mCreationMutex);
+ handle_t handle = generateHandle();
+ mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder));
+ return handle;
+}
+
+// Used by unit test only
+// Returns the size of the mutex guarded list as an indication of sanity
+S32 LLImageDecodeThread::tut_size()
+{
+ LLMutexLock lock(mCreationMutex);
+ S32 res = mCreationList.size();
+ return res;
+}
+
+LLImageDecodeThread::Responder::~Responder()
{
}
//----------------------------------------------------------------------------
-LLImageWorker::LLImageWorker(LLImageFormatted* image, U32 priority,
- S32 discard,
- LLPointer<LLResponder> responder)
- : LLWorkerClass(sWorkerThread, "Image"),
+LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatted* image,
+ U32 priority, S32 discard, BOOL needs_aux,
+ LLImageDecodeThread::Responder* responder)
+ : LLQueuedThread::QueuedRequest(handle, priority, FLAG_AUTO_COMPLETE),
mFormattedImage(image),
- mDecodedType(-1),
mDiscardLevel(discard),
- mPriority(priority),
+ mNeedsAux(needs_aux),
+ mDecodedRaw(FALSE),
+ mDecodedAux(FALSE),
mResponder(responder)
{
- ++sCount;
}
-LLImageWorker::~LLImageWorker()
+LLImageDecodeThread::ImageRequest::~ImageRequest()
{
- mDecodedImage = NULL;
+ mDecodedImageRaw = NULL;
+ mDecodedImageAux = NULL;
mFormattedImage = NULL;
- --sCount;
}
//----------------------------------------------------------------------------
-//virtual, main thread
-void LLImageWorker::startWork(S32 param)
-{
- llassert_always(mDecodedImage.isNull());
- mDecodedType = -1;
-}
-bool LLImageWorker::doWork(S32 param)
+// Returns true when done, whether or not decode was successful.
+bool LLImageDecodeThread::ImageRequest::processRequest()
{
- bool decoded = false;
- if(mDecodedImage.isNull())
+ const F32 decode_time_slice = .1f;
+ bool done = true;
+ if (!mDecodedRaw && mFormattedImage.notNull())
{
- if (!mFormattedImage->updateData())
- {
- mDecodedType = -2; // failed
- return true;
- }
- if (mDiscardLevel >= 0)
+ // Decode primary channels
+ if (mDecodedImageRaw.isNull())
{
- mFormattedImage->setDiscardLevel(mDiscardLevel);
- }
- if (!(mFormattedImage->getWidth() * mFormattedImage->getHeight() * mFormattedImage->getComponents()))
- {
- decoded = true; // failed
- }
- else
- {
- mDecodedImage = new LLImageRaw(); // allow possibly smaller size set during decoding
+ // parse formatted header
+ if (!mFormattedImage->updateData())
+ {
+ return true; // done (failed)
+ }
+ if (!(mFormattedImage->getWidth() * mFormattedImage->getHeight() * mFormattedImage->getComponents()))
+ {
+ return true; // done (failed)
+ }
+ if (mDiscardLevel >= 0)
+ {
+ mFormattedImage->setDiscardLevel(mDiscardLevel);
+ }
+ mDecodedImageRaw = new LLImageRaw(mFormattedImage->getWidth(),
+ mFormattedImage->getHeight(),
+ mFormattedImage->getComponents());
}
+ done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms
+ mDecodedRaw = done;
}
- if (!decoded)
+ if (done && mNeedsAux && !mDecodedAux && mFormattedImage.notNull())
{
- if (param == 0)
- {
- // Decode primary channels
- decoded = mFormattedImage->decode(mDecodedImage, .1f); // 1ms
- }
- else
+ // Decode aux channel
+ if (!mDecodedImageAux)
{
- // Decode aux channel
- decoded = mFormattedImage->decodeChannels(mDecodedImage, .1f, param, param); // 1ms
+ mDecodedImageAux = new LLImageRaw(mFormattedImage->getWidth(),
+ mFormattedImage->getHeight(),
+ 1);
}
+ done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); // 1ms
+ mDecodedAux = done;
}
- if (decoded)
- {
- // Call the callback immediately; endWork doesn't get called until ckeckWork
- if (mResponder.notNull())
- {
- bool success = (!wasAborted() && mDecodedImage.notNull() && mDecodedImage->getDataSize() != 0);
- mResponder->completed(success);
- }
- }
- return decoded;
-}
-void LLImageWorker::endWork(S32 param, bool aborted)
-{
- if (mDecodedType != -2)
- {
- mDecodedType = aborted ? -2 : param;
- }
+ return done;
}
-//----------------------------------------------------------------------------
-
-
-BOOL LLImageWorker::requestDecodedAuxData(LLPointer<LLImageRaw>& raw, S32 channel, S32 discard)
+void LLImageDecodeThread::ImageRequest::finishRequest(bool completed)
{
- // For most codecs, only mDiscardLevel data is available.
- // (see LLImageDXT for exception)
- if (discard >= 0 && discard != mFormattedImage->getDiscardLevel())
- {
- llerrs << "Request for invalid discard level" << llendl;
- }
- checkWork();
- if (mDecodedType == -2)
+ if (mResponder.notNull())
{
- return TRUE; // aborted, done
- }
- if (mDecodedType != channel)
- {
- if (!haveWork())
- {
- addWork(channel, mPriority);
- }
- return FALSE;
- }
- else
- {
- llassert_always(!haveWork());
- llassert_always(mDecodedType == channel);
- raw = mDecodedImage; // smart pointer acquires ownership of data
- mDecodedImage = NULL;
- return TRUE;
+ bool success = completed && mDecodedRaw && (!mNeedsAux || mDecodedAux);
+ mResponder->completed(success, mDecodedImageRaw, mDecodedImageAux);
}
+ // Will automatically be deleted
}
-BOOL LLImageWorker::requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard)
+// Used by unit test only
+// Checks that a responder exists for this instance so that something can happen when completion is reached
+bool LLImageDecodeThread::ImageRequest::tut_isOK()
{
- if (mFormattedImage->getCodec() == IMG_CODEC_DXT)
- {
- // special case
- LLImageDXT* imagedxt = (LLImageDXT*)((LLImageFormatted*)mFormattedImage);
- return imagedxt->getMipData(raw, discard);
- }
- else
- {
- return requestDecodedAuxData(raw, 0, discard);
- }
+ return mResponder.notNull();
}
diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h
index 0d66695d6e..6a5b86a277 100644
--- a/indra/llimage/llimageworker.h
+++ b/indra/llimage/llimageworker.h
@@ -37,49 +37,72 @@
#include "llpointer.h"
#include "llworkerthread.h"
-class LLImageWorker : public LLWorkerClass
+class LLImageDecodeThread : public LLQueuedThread
{
public:
- static void initImageWorker(LLWorkerThread* workerthread);
- static void cleanupImageWorker();
-
-public:
- static LLWorkerThread* getWorkerThread() { return sWorkerThread; }
-
- // LLWorkerThread
-public:
- LLImageWorker(LLImageFormatted* image, U32 priority, S32 discard,
- LLPointer<LLResponder> responder);
- ~LLImageWorker();
-
- // called from WORKER THREAD, returns TRUE if done
- /*virtual*/ bool doWork(S32 param);
-
- BOOL requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard = -1);
- BOOL requestDecodedAuxData(LLPointer<LLImageRaw>& raw, S32 channel, S32 discard = -1);
- void releaseDecodedData();
- void cancelDecode();
+ class Responder : public LLThreadSafeRefCount
+ {
+ protected:
+ virtual ~Responder();
+ public:
+ virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) = 0;
+ };
-private:
- // called from MAIN THREAD
- /*virtual*/ void startWork(S32 param); // called from addWork()
- /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork()
+ class ImageRequest : public LLQueuedThread::QueuedRequest
+ {
+ protected:
+ virtual ~ImageRequest(); // use deleteRequest()
+
+ public:
+ ImageRequest(handle_t handle, LLImageFormatted* image,
+ U32 priority, S32 discard, BOOL needs_aux,
+ LLImageDecodeThread::Responder* responder);
-protected:
- LLPointer<LLImageFormatted> mFormattedImage;
- LLPointer<LLImageRaw> mDecodedImage;
- S32 mDecodedType;
- S32 mDiscardLevel;
+ /*virtual*/ bool processRequest();
+ /*virtual*/ void finishRequest(bool completed);
-private:
- U32 mPriority;
- LLPointer<LLResponder> mResponder;
+ // Used by unit tests to check the consitency of the request instance
+ bool tut_isOK();
+
+ private:
+ // input
+ LLPointer<LLImageFormatted> mFormattedImage;
+ S32 mDiscardLevel;
+ BOOL mNeedsAux;
+ // output
+ LLPointer<LLImageRaw> mDecodedImageRaw;
+ LLPointer<LLImageRaw> mDecodedImageAux;
+ BOOL mDecodedRaw;
+ BOOL mDecodedAux;
+ LLPointer<LLImageDecodeThread::Responder> mResponder;
+ };
-protected:
- static LLWorkerThread* sWorkerThread;
-
public:
- static S32 sCount;
+ LLImageDecodeThread(bool threaded = true);
+ handle_t decodeImage(LLImageFormatted* image,
+ U32 priority, S32 discard, BOOL needs_aux,
+ Responder* responder);
+ S32 update(U32 max_time_ms);
+
+ // Used by unit tests to check the consistency of the thread instance
+ S32 tut_size();
+
+private:
+ struct creation_info
+ {
+ handle_t handle;
+ LLPointer<LLImageFormatted> image;
+ U32 priority;
+ S32 discard;
+ BOOL needs_aux;
+ LLPointer<Responder> responder;
+ creation_info(handle_t h, LLImageFormatted* i, U32 p, S32 d, BOOL aux, Responder* r)
+ : handle(h), image(i), priority(p), discard(d), needs_aux(aux), responder(r)
+ {}
+ };
+ typedef std::list<creation_info> creation_list_t;
+ creation_list_t mCreationList;
+ LLMutex* mCreationMutex;
};
#endif
diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp
new file mode 100644
index 0000000000..cc44696a45
--- /dev/null
+++ b/indra/llimage/tests/llimageworker_test.cpp
@@ -0,0 +1,260 @@
+/**
+ * @file llimageworker_test.cpp
+ * @author Merov Linden
+ * @date 2009-04-28
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header: almost always required for newview cpp files
+#include <list>
+#include <map>
+#include <algorithm>
+// Class to test
+#include "../llimageworker.h"
+// For timer class
+#include "../llcommon/lltimer.h"
+// Tut header
+#include "../test/lltut.h"
+
+// -------------------------------------------------------------------------------------------
+// Stubbing: Declarations required to link and run the class being tested
+// Notes:
+// * Add here stubbed implementation of the few classes and methods used in the class to be tested
+// * Add as little as possible (let the link errors guide you)
+// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
+// * A simulator for a class can be implemented here. Please comment and document thoroughly.
+
+LLImageBase::LLImageBase() {}
+LLImageBase::~LLImageBase() {}
+void LLImageBase::dump() { }
+void LLImageBase::sanityCheck() { }
+void LLImageBase::deleteData() { }
+U8* LLImageBase::allocateData(S32 size) { return NULL; }
+U8* LLImageBase::reallocateData(S32 size) { return NULL; }
+
+LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components) { }
+LLImageRaw::~LLImageRaw() { }
+void LLImageRaw::deleteData() { }
+U8* LLImageRaw::allocateData(S32 size) { return NULL; }
+U8* LLImageRaw::reallocateData(S32 size) { return NULL; }
+
+// End Stubbing
+// -------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------
+// TUT
+// -------------------------------------------------------------------------------------------
+
+namespace tut
+{
+ // Test wrapper declarations
+
+ // Note: We derive the responder class for 2 reasons:
+ // 1. It's a pure virtual class and we can't compile without completed() being implemented
+ // 2. We actually need a responder to test that the thread work test completed
+ // We implement this making no assumption on what's done in the thread or worker
+ // though, just that the responder's completed() method is called in the end.
+ // Note on responders: responders are ref counted and *will* be deleted by the request they are
+ // attached to when the queued request is deleted. The recommended way of using them is to
+ // create them when creating a request, put a callback method in completed() and not rely on
+ // anything to survive in the responder object once completed() has been called. Let the request
+ // do the deletion and clean up itself.
+ class responder_test : public LLImageDecodeThread::Responder
+ {
+ public:
+ responder_test(bool* res)
+ {
+ done = res;
+ *done = false;
+ }
+ virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
+ {
+ *done = true;
+ }
+ private:
+ // This is what can be thought of as the minimal implementation of a responder
+ // Done will be switched to true when completed() is called and can be tested
+ // outside the responder. A better way of doing this is to store a callback here.
+ bool* done;
+ };
+
+ // Test wrapper declaration : decode thread
+ struct imagedecodethread_test
+ {
+ // Instance to be tested
+ LLImageDecodeThread* mThread;
+
+ // Constructor and destructor of the test wrapper
+ imagedecodethread_test()
+ {
+ mThread = NULL;
+ }
+ ~imagedecodethread_test()
+ {
+ delete mThread;
+ }
+ };
+
+ // Test wrapper declaration : image worker
+ // Note: this class is not meant to be instantiated outside an LLImageDecodeThread instance
+ // but it's not a bad idea to get its public API a good shake as part of a thorough unit test set.
+ // Some gotcha with the destructor though (see below).
+ struct imagerequest_test
+ {
+ // Instance to be tested
+ LLImageDecodeThread::ImageRequest* mRequest;
+ bool done;
+
+ // Constructor and destructor of the test wrapper
+ imagerequest_test()
+ {
+ done = false;
+ mRequest = new LLImageDecodeThread::ImageRequest(0, 0,
+ LLQueuedThread::PRIORITY_NORMAL, 0, FALSE,
+ new responder_test(&done));
+ }
+ ~imagerequest_test()
+ {
+ // We should delete the object *but*, because its destructor is protected, that cannot be
+ // done from outside an LLImageDecodeThread instance... So we leak memory here... It's fine...
+ //delete mRequest;
+ }
+ };
+
+ // Tut templating thingamagic: test group, object and test instance
+ typedef test_group<imagedecodethread_test> imagedecodethread_t;
+ typedef imagedecodethread_t::object imagedecodethread_object_t;
+ tut::imagedecodethread_t tut_imagedecodethread("imagedecodethread");
+
+ typedef test_group<imagerequest_test> imagerequest_t;
+ typedef imagerequest_t::object imagerequest_object_t;
+ tut::imagerequest_t tut_imagerequest("imagerequest");
+
+ // ---------------------------------------------------------------------------------------
+ // Test functions
+ // Notes:
+ // * Test as many as you possibly can without requiring a full blown simulation of everything
+ // * The tests are executed in sequence so the test instance state may change between calls
+ // * Remember that you cannot test private methods with tut
+ // ---------------------------------------------------------------------------------------
+
+ // ---------------------------------------------------------------------------------------
+ // Test the LLImageDecodeThread interface
+ // ---------------------------------------------------------------------------------------
+ //
+ // Note on Unit Testing Queued Thread Classes
+ //
+ // Since methods on such a class are called on a separate loop and that we can't insert tut
+ // ensure() calls in there, we exercise the class with 2 sets of tests:
+ // - 1: Test as a single threaded instance: We declare the class but ask for no thread
+ // to be spawned (easy with LLThreads since there's a boolean argument on the constructor
+ // just for that). We can then unit test each public method like we do on a normal class.
+ // - 2: Test as a threaded instance: We let the thread launch and check that its external
+ // behavior is as expected (i.e. it runs, can accept a work order and processes
+ // it). Typically though there's no guarantee that this exercises all the methods of the
+ // class which is why we also need the previous "non threaded" set of unit tests for
+ // complete coverage.
+ //
+ // ---------------------------------------------------------------------------------------
+
+ template<> template<>
+ void imagedecodethread_object_t::test<1>()
+ {
+ // Test a *non threaded* instance of the class
+ mThread = new LLImageDecodeThread(false);
+ ensure("LLImageDecodeThread: non threaded constructor failed", mThread != NULL);
+ // Test that we start with an empty list right at creation
+ ensure("LLImageDecodeThread: non threaded init state incorrect", mThread->tut_size() == 0);
+ // Insert something in the queue
+ bool done = false;
+ LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
+ // Verifies we got a valid handle
+ ensure("LLImageDecodeThread: non threaded decodeImage(), returned handle is null", decodeHandle != 0);
+ // Verifies that we do now have something in the queued list
+ ensure("LLImageDecodeThread: non threaded decodeImage() insertion in threaded list failed", mThread->tut_size() == 1);
+ // Trigger queue handling "manually" (on a threaded instance, this is done on the thread loop)
+ S32 res = mThread->update(0);
+ // Verifies that we successfully handled the list
+ ensure("LLImageDecodeThread: non threaded update() list handling test failed", res == 0);
+ // Verifies that the list is now empty
+ ensure("LLImageDecodeThread: non threaded update() list emptying test failed", mThread->tut_size() == 0);
+ }
+
+ template<> template<>
+ void imagedecodethread_object_t::test<2>()
+ {
+ // Test a *threaded* instance of the class
+ mThread = new LLImageDecodeThread(true);
+ ensure("LLImageDecodeThread: threaded constructor failed", mThread != NULL);
+ // Test that we start with an empty list right at creation
+ ensure("LLImageDecodeThread: threaded init state incorrect", mThread->tut_size() == 0);
+ // Insert something in the queue
+ bool done = false;
+ LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
+ // Verifies we get back a valid handle
+ ensure("LLImageDecodeThread: threaded decodeImage(), returned handle is null", decodeHandle != 0);
+ // Wait a little so to simulate the main thread doing something on its main loop...
+ ms_sleep(500); // 500 milliseconds
+ // Verifies that the responder has *not* been called yet in the meantime
+ ensure("LLImageDecodeThread: responder creation failed", done == false);
+ // Ask the thread to update: that means tells the queue to check itself and creates work requests
+ mThread->update(1);
+ // Wait till the thread has time to handle the work order (though it doesn't do much per work order...)
+ const U32 INCREMENT_TIME = 500; // 500 milliseconds
+ const U32 MAX_TIME = 20 * INCREMENT_TIME; // Do the loop 20 times max, i.e. wait 10 seconds but no more
+ U32 total_time = 0;
+ while ((done == false) && (total_time < MAX_TIME))
+ {
+ ms_sleep(INCREMENT_TIME);
+ total_time += INCREMENT_TIME;
+ }
+ // Verifies that the responder has now been called
+ ensure("LLImageDecodeThread: threaded work unit not processed", done == true);
+ }
+
+ // ---------------------------------------------------------------------------------------
+ // Test the LLImageDecodeThread::ImageRequest interface
+ // ---------------------------------------------------------------------------------------
+
+ template<> template<>
+ void imagerequest_object_t::test<1>()
+ {
+ // Test that we start with a correct request at creation
+ ensure("LLImageDecodeThread::ImageRequest::ImageRequest() constructor test failed", mRequest->tut_isOK());
+ bool res = mRequest->processRequest();
+ // Verifies that we processed the request successfully
+ ensure("LLImageDecodeThread::ImageRequest::processRequest() processing request test failed", res == true);
+ // Check that we can call the finishing call safely
+ try {
+ mRequest->finishRequest(false);
+ } catch (...) {
+ fail("LLImageDecodeThread::ImageRequest::finishRequest() test failed");
+ }
+ }
+}
diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index ec6264dcf5..e71429b18d 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -168,7 +168,7 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
// dereference the array.
if(!image || !image->numcomps)
{
- llwarns << "ERROR -> decodeImpl: failed to decode image!" << llendl;
+ LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image!" << LL_ENDL;
if (image)
{
opj_image_destroy(image);
@@ -241,7 +241,7 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
}
else // Some rare OpenJPEG versions have this bug.
{
- llwarns << "ERROR -> decodeImpl: failed to decode image! (NULL comp data - OpenJPEG bug)" << llendl;
+ LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image! (NULL comp data - OpenJPEG bug)" << LL_ENDL;
opj_image_destroy(image);
return TRUE; // done
@@ -375,7 +375,7 @@ BOOL LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, con
if (!bSuccess)
{
opj_cio_close(cio);
- llinfos << "Failed to encode image." << llendl;
+ LL_DEBUGS("Texture") << "Failed to encode image." << LL_ENDL;
return FALSE;
}
codestream_length = cio_tell(cio);
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index b8ef92f9a9..df4c618ac1 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -3582,7 +3582,7 @@ S32 LLVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
if (face == -1) // ALL_SIDES
{
start_face = 0;
- end_face = getNumFaces() - 1;
+ end_face = getNumVolumeFaces() - 1;
}
else
{
diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp
index b3087bcc3f..0ab1081200 100644
--- a/indra/llmessage/llassetstorage.cpp
+++ b/indra/llmessage/llassetstorage.cpp
@@ -64,6 +64,9 @@ const LLUUID CATEGORIZE_LOST_AND_FOUND_ID(std::string("00000000-0000-0000-0000-0
const U64 TOXIC_ASSET_LIFETIME = (120 * 1000000); // microseconds
+LLTempAssetStorage::~LLTempAssetStorage()
+{
+}
///----------------------------------------------------------------------------
/// LLAssetInfo
diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h
index 56adbd5ccf..83cfdf6110 100644
--- a/indra/llmessage/llassetstorage.h
+++ b/indra/llmessage/llassetstorage.h
@@ -204,7 +204,16 @@ typedef std::map<LLUUID,U64,lluuid_less> toxic_asset_map_t;
typedef void (*LLGetAssetCallback)(LLVFS *vfs, const LLUUID &asset_id,
LLAssetType::EType asset_type, void *user_data, S32 status, LLExtStat ext_status);
-class LLAssetStorage
+class LLTempAssetStorage
+{
+public:
+ virtual ~LLTempAssetStorage() =0;
+ virtual void addTempAssetData(const LLUUID& asset_id,
+ const LLUUID& agent_id,
+ const std::string& host_name) = 0;
+};
+
+class LLAssetStorage : public LLTempAssetStorage
{
public:
// VFS member is public because static child methods need it :(
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp
index 5ff41322b7..dc02367a62 100644
--- a/indra/llmessage/llcurl.cpp
+++ b/indra/llmessage/llcurl.cpp
@@ -229,7 +229,7 @@ public:
U32 report(CURLcode);
void getTransferInfo(LLCurl::TransferInfo* info);
- void prepRequest(const std::string& url, ResponderPtr, bool post = false);
+ void prepRequest(const std::string& url, const std::vector<std::string>& headers, ResponderPtr, bool post = false);
const char* getErrorBuffer();
@@ -441,7 +441,9 @@ size_t curlHeaderCallback(void* data, size_t size, size_t nmemb, void* user_data
return n;
}
-void LLCurl::Easy::prepRequest(const std::string& url, ResponderPtr responder, bool post)
+void LLCurl::Easy::prepRequest(const std::string& url,
+ const std::vector<std::string>& headers,
+ ResponderPtr responder, bool post)
{
resetState();
@@ -474,8 +476,13 @@ void LLCurl::Easy::prepRequest(const std::string& url, ResponderPtr responder, b
{
slist_append("Connection: keep-alive");
slist_append("Keep-alive: 300");
+ // Accept and other headers
+ for (std::vector<std::string>::const_iterator iter = headers.begin();
+ iter != headers.end(); ++iter)
+ {
+ slist_append((*iter).c_str());
+ }
}
- // *FIX: should have ACCEPT headers
}
////////////////////////////////////////////////////////////////////////////
@@ -685,15 +692,18 @@ LLCurlRequest::LLCurlRequest() :
mActiveMulti(NULL),
mActiveRequestCount(0)
{
+ mThreadID = LLThread::currentID();
}
LLCurlRequest::~LLCurlRequest()
{
+ llassert_always(mThreadID == LLThread::currentID());
for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer());
}
void LLCurlRequest::addMulti()
{
+ llassert_always(mThreadID == LLThread::currentID());
LLCurl::Multi* multi = new LLCurl::Multi();
mMultiSet.insert(multi);
mActiveMulti = multi;
@@ -723,17 +733,20 @@ bool LLCurlRequest::addEasy(LLCurl::Easy* easy)
void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder)
{
- getByteRange(url, 0, -1, responder);
+ getByteRange(url, headers_t(), 0, -1, responder);
}
-bool LLCurlRequest::getByteRange(const std::string& url, S32 offset, S32 length, LLCurl::ResponderPtr responder)
+bool LLCurlRequest::getByteRange(const std::string& url,
+ const headers_t& headers,
+ S32 offset, S32 length,
+ LLCurl::ResponderPtr responder)
{
LLCurl::Easy* easy = allocEasy();
if (!easy)
{
return false;
}
- easy->prepRequest(url, responder);
+ easy->prepRequest(url, headers, responder);
easy->setopt(CURLOPT_HTTPGET, 1);
if (length > 0)
{
@@ -745,14 +758,17 @@ bool LLCurlRequest::getByteRange(const std::string& url, S32 offset, S32 length,
return res;
}
-bool LLCurlRequest::post(const std::string& url, const LLSD& data, LLCurl::ResponderPtr responder)
+bool LLCurlRequest::post(const std::string& url,
+ const headers_t& headers,
+ const LLSD& data,
+ LLCurl::ResponderPtr responder)
{
LLCurl::Easy* easy = allocEasy();
if (!easy)
{
return false;
}
- easy->prepRequest(url, responder);
+ easy->prepRequest(url, headers, responder);
LLSDSerialize::toXML(data, easy->getInput());
S32 bytes = easy->getInput().str().length();
@@ -772,6 +788,7 @@ bool LLCurlRequest::post(const std::string& url, const LLSD& data, LLCurl::Respo
// Note: call once per frame
S32 LLCurlRequest::process()
{
+ llassert_always(mThreadID == LLThread::currentID());
S32 res = 0;
for (curlmulti_set_t::iterator iter = mMultiSet.begin();
iter != mMultiSet.end(); )
@@ -791,6 +808,7 @@ S32 LLCurlRequest::process()
S32 LLCurlRequest::getQueued()
{
+ llassert_always(mThreadID == LLThread::currentID());
S32 queued = 0;
for (curlmulti_set_t::iterator iter = mMultiSet.begin();
iter != mMultiSet.end(); )
@@ -1011,7 +1029,7 @@ void LLCurl::initClass()
S32 mutex_count = CRYPTO_num_locks();
for (S32 i=0; i<mutex_count; i++)
{
- sSSLMutex.push_back(new LLMutex(gAPRPoolp));
+ sSSLMutex.push_back(new LLMutex(NULL));
}
CRYPTO_set_id_callback(&LLCurl::ssl_thread_id);
CRYPTO_set_locking_callback(&LLCurl::ssl_locking_callback);
diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h
index 0b58e7c4a5..1bc1767966 100644
--- a/indra/llmessage/llcurl.h
+++ b/indra/llmessage/llcurl.h
@@ -194,12 +194,14 @@ namespace boost
class LLCurlRequest
{
public:
+ typedef std::vector<std::string> headers_t;
+
LLCurlRequest();
~LLCurlRequest();
void get(const std::string& url, LLCurl::ResponderPtr responder);
- bool getByteRange(const std::string& url, S32 offset, S32 length, LLCurl::ResponderPtr responder);
- bool post(const std::string& url, const LLSD& data, LLCurl::ResponderPtr responder);
+ bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder);
+ bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder);
S32 process();
S32 getQueued();
@@ -213,6 +215,7 @@ private:
curlmulti_set_t mMultiSet;
LLCurl::Multi* mActiveMulti;
S32 mActiveRequestCount;
+ U32 mThreadID; // debug
};
class LLCurlEasyRequest
diff --git a/indra/llmessage/tests/llcurl_stub.cpp b/indra/llmessage/tests/llcurl_stub.cpp
index e6a5ad9946..ffbe7bd202 100644
--- a/indra/llmessage/tests/llcurl_stub.cpp
+++ b/indra/llmessage/tests/llcurl_stub.cpp
@@ -20,6 +20,7 @@
*/
#include "linden_common.h"
+#include "llcurl.h"
LLCurl::Responder::Responder()
: mReferenceCount(0)
diff --git a/indra/llmessage/tests/llhttpclientadapter_test.cpp b/indra/llmessage/tests/llhttpclientadapter_test.cpp
index 250fa100b6..7065c9d7e4 100644
--- a/indra/llmessage/tests/llhttpclientadapter_test.cpp
+++ b/indra/llmessage/tests/llhttpclientadapter_test.cpp
@@ -50,7 +50,7 @@ std::vector<std::string> put_urls;
std::vector<LLSD> put_body;
std::vector<boost::intrusive_ptr<LLCurl::Responder> > put_responders;
-void LLHTTPClient::put(std::string const &url, LLSD const &body, boost::intrusive_ptr<LLCurl::Responder> responder,float)
+void LLHTTPClient::put(const std::string& url, const LLSD& body, boost::intrusive_ptr<LLCurl::Responder> responder, const LLSD& headers, const F32 timeout)
{
put_urls.push_back(url);
put_responders.push_back(responder);
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index e5fea5b995..f8d7ea00e0 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -43,7 +43,6 @@
#include "llmath.h"
#include "llgl.h"
#include "llrender.h"
-
//----------------------------------------------------------------------------
const F32 MIN_TEXTURE_LIFETIME = 10.f;
@@ -60,21 +59,34 @@ std::list<U32> LLImageGL::sDeadTextureList;
BOOL LLImageGL::sGlobalUseAnisotropic = FALSE;
F32 LLImageGL::sLastFrameTime = 0.f;
+BOOL LLImageGL::sAllowReadBackRaw = FALSE ;
LLImageGL* LLImageGL::sDefaultGLTexture = NULL ;
+
std::set<LLImageGL*> LLImageGL::sImageList;
-#if !LL_RELEASE_FOR_DOWNLOAD
+//****************************************************************************************************
+//The below for texture auditing use only
+//****************************************************************************************************
//-----------------------
//debug use
+BOOL gAuditTexture = FALSE ;
#define MAX_TEXTURE_LOG_SIZE 22 //2048 * 2048
std::vector<S32> LLImageGL::sTextureLoadedCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
std::vector<S32> LLImageGL::sTextureBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
std::vector<S32> LLImageGL::sTextureCurBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
S32 LLImageGL::sCurTexSizeBar = -1 ;
S32 LLImageGL::sCurTexPickSize = -1 ;
-LLPointer<LLImageGL> LLImageGL::sDefaultTexturep = NULL;
+LLPointer<LLImageGL> LLImageGL::sHighlightTexturep = NULL;
+S32 LLImageGL::sMaxCatagories = 1 ;
+
+std::vector<S32> LLImageGL::sTextureMemByCategory;
+std::vector<S32> LLImageGL::sTextureMemByCategoryBound ;
+std::vector<S32> LLImageGL::sTextureCurMemByCategoryBound ;
//------------------------
-#endif
+//****************************************************************************************************
+//End for texture auditing use only
+//****************************************************************************************************
+
//**************************************************************************************
//below are functions for debug use
//do not delete them even though they are not currently being used.
@@ -144,6 +156,60 @@ void LLImageGL::checkTexSize() const
//**************************************************************************************
//----------------------------------------------------------------------------
+BOOL is_little_endian()
+{
+ S32 a = 0x12345678;
+ U8 *c = (U8*)(&a);
+
+ return (*c == 0x78) ;
+}
+//static
+void LLImageGL::initClass(S32 num_catagories)
+{
+ sMaxCatagories = num_catagories ;
+
+ sTextureMemByCategory.resize(sMaxCatagories);
+ sTextureMemByCategoryBound.resize(sMaxCatagories) ;
+ sTextureCurMemByCategoryBound.resize(sMaxCatagories) ;
+}
+
+//static
+void LLImageGL::cleanupClass()
+{
+ sTextureMemByCategory.clear() ;
+ sTextureMemByCategoryBound.clear() ;
+ sTextureCurMemByCategoryBound.clear() ;
+}
+
+//static
+void LLImageGL::setHighlightTexture(S32 category)
+{
+ const S32 dim = 128;
+ sHighlightTexturep = new LLImageGL() ;
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3);
+ U8* data = image_raw->getData();
+ for (S32 i = 0; i<dim; i++)
+ {
+ for (S32 j = 0; j<dim; j++)
+ {
+ const S32 border = 2;
+ if (i<border || j<border || i>=(dim-border) || j>=(dim-border))
+ {
+ *data++ = 0xff;
+ *data++ = 0xff;
+ *data++ = 0xff;
+ }
+ else
+ {
+ *data++ = 0xff;
+ *data++ = 0xff;
+ *data++ = 0x00;
+ }
+ }
+ }
+ sHighlightTexturep->createGLTexture(0, image_raw, 0, TRUE, category);
+ image_raw = NULL;
+}
//static
S32 LLImageGL::dataFormatBits(S32 dataformat)
@@ -211,19 +277,31 @@ void LLImageGL::updateStats(F32 current_time)
sBoundTextureMemoryInBytes = sCurBoundTextureMemory;
sCurBoundTextureMemory = 0;
-#if !LL_RELEASE_FOR_DOWNLOAD
- for(U32 i = 0 ; i < sTextureCurBoundCounter.size() ; i++)
+ if(gAuditTexture)
{
- sTextureBoundCounter[i] = sTextureCurBoundCounter[i] ;
- sTextureCurBoundCounter[i] = 0 ;
+ for(U32 i = 0 ; i < sTextureCurBoundCounter.size() ; i++)
+ {
+ sTextureBoundCounter[i] = sTextureCurBoundCounter[i] ;
+ sTextureCurBoundCounter[i] = 0 ;
+ }
+ for(U32 i = 0 ; i < sTextureCurMemByCategoryBound.size() ; i++)
+ {
+ sTextureMemByCategoryBound[i] = sTextureCurMemByCategoryBound[i] ;
+ sTextureCurMemByCategoryBound[i] = 0 ;
+ }
}
-#endif
}
//static
-S32 LLImageGL::updateBoundTexMem(const S32 delta)
+S32 LLImageGL::updateBoundTexMem(const S32 mem, const S32 ncomponents, S32 category)
{
- LLImageGL::sCurBoundTextureMemory += delta;
+ if(gAuditTexture && ncomponents > 0 && category > -1)
+ {
+ sTextureCurBoundCounter[getTextureCounterIndex(mem / ncomponents)]++ ;
+ sTextureCurMemByCategoryBound[category] += mem ;
+ }
+
+ LLImageGL::sCurBoundTextureMemory += mem ;
return LLImageGL::sCurBoundTextureMemory;
}
@@ -237,6 +315,7 @@ void LLImageGL::destroyGL(BOOL save_state)
gGL.getTexUnit(stage)->unbind(LLTexUnit::TT_TEXTURE);
}
+ sAllowReadBackRaw = true ;
for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
iter != sImageList.end(); iter++)
{
@@ -246,7 +325,7 @@ void LLImageGL::destroyGL(BOOL save_state)
if (save_state && glimage->isGLTextureCreated() && glimage->mComponents)
{
glimage->mSaveData = new LLImageRaw;
- if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false))
+ if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) //necessary, keep it.
{
glimage->mSaveData = NULL ;
}
@@ -256,6 +335,7 @@ void LLImageGL::destroyGL(BOOL save_state)
stop_glerror();
}
}
+ sAllowReadBackRaw = false ;
}
//static
@@ -273,7 +353,7 @@ void LLImageGL::restoreGL()
{
if (glimage->getComponents() && glimage->mSaveData->getComponents())
{
- glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData);
+ glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, TRUE, glimage->getCategory());
stop_glerror();
}
glimage->mSaveData = NULL; // deletes data
@@ -355,7 +435,7 @@ void LLImageGL::init(BOOL usemipmaps)
mPickMask = NULL;
mTextureMemory = 0;
mLastBindTime = 0.f;
-
+
mTarget = GL_TEXTURE_2D;
mBindTarget = LLTexUnit::TT_TEXTURE;
mUseMipMaps = usemipmaps;
@@ -381,7 +461,11 @@ void LLImageGL::init(BOOL usemipmaps)
mHasExplicitFormat = FALSE;
mGLTextureCreated = FALSE ;
+
mIsMask = FALSE;
+ mCategory = -1 ;
+ mAlphaStride = 0 ;
+ mAlphaOffset = 0 ;
mNeedsAlphaAndPickMask = TRUE ;
mDiscardLevelInAtlas = -1 ;
@@ -486,6 +570,10 @@ void LLImageGL::dump()
}
//----------------------------------------------------------------------------
+void LLImageGL::forceUpdateBindStats(void) const
+{
+ mLastBindTime = sLastFrameTime;
+}
BOOL LLImageGL::updateBindStats(S32 tex_mem) const
{
@@ -499,7 +587,7 @@ BOOL LLImageGL::updateBindStats(S32 tex_mem) const
{
// we haven't accounted for this texture yet this frame
sUniqueCount++;
- updateBoundTexMem(tex_mem);
+ updateBoundTexMem(tex_mem, mComponents, mCategory);
mLastBindTime = sLastFrameTime;
return TRUE ;
@@ -525,6 +613,8 @@ void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_for
else
mFormatType = type_format;
mFormatSwapBytes = swap_bytes;
+
+ calcAlphaChannelOffsetAndStride() ;
}
//----------------------------------------------------------------------------
@@ -540,7 +630,6 @@ void LLImageGL::setImage(const LLImageRaw* imageraw)
void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
{
- llpushcallstacks ;
bool is_compressed = false;
if (mFormatPrimary >= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && mFormatPrimary <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
{
@@ -749,7 +838,6 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
}
stop_glerror();
mGLTextureCreated = true;
- llpushcallstacks ;
}
BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image)
@@ -840,7 +928,6 @@ void LLImageGL::postAddToAtlas()
BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update)
{
- llpushcallstacks ;
if (!width || !height)
{
return TRUE;
@@ -930,7 +1017,6 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
stop_glerror();
mGLTextureCreated = true;
}
- llpushcallstacks ;
return TRUE;
}
@@ -942,8 +1028,9 @@ BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S3
// Copy sub image from frame buffer
BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height)
{
- if (gGL.getTexUnit(0)->bind(this))
+ if (gGL.getTexUnit(0)->bind(this, false, true))
{
+ //checkTexSize() ;
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height);
mGLTextureCreated = true;
stop_glerror();
@@ -1007,7 +1094,7 @@ BOOL LLImageGL::createGLTexture()
return TRUE ;
}
-BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/)
+BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, BOOL to_create, S32 category)
{
if (gGLManager.mIsDisabled)
{
@@ -1027,8 +1114,10 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
// Actual image width/height = raw image width/height * 2^discard_level
- S32 w = imageraw->getWidth() << discard_level;
- S32 h = imageraw->getHeight() << discard_level;
+ S32 raw_w = imageraw->getWidth() ;
+ S32 raw_h = imageraw->getHeight() ;
+ S32 w = raw_w << discard_level;
+ S32 h = raw_h << discard_level;
// setSize may call destroyGLTexture if the size does not match
setSize(w, h, imageraw->getComponents());
@@ -1062,15 +1151,25 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
default:
llerrs << "Bad number of components for texture: " << (U32)getComponents() << llendl;
}
+
+ calcAlphaChannelOffsetAndStride() ;
}
+ if(!to_create) //not create a gl texture
+ {
+ destroyGLTexture();
+ mCurrentDiscardLevel = discard_level;
+ mLastBindTime = sLastFrameTime;
+ return TRUE ;
+ }
+
+ setCategory(category) ;
const U8* rawdata = imageraw->getData();
return createGLTexture(discard_level, rawdata, FALSE, usename);
}
BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips, S32 usename)
{
- llpushcallstacks ;
llassert(data_in);
if (discard_level < 0)
@@ -1137,11 +1236,14 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
if (old_name != 0)
{
sGlobalTextureMemoryInBytes -= mTextureMemory;
-#if !LL_RELEASE_FOR_DOWNLOAD
- decTextureCounter(mTextureMemory / mComponents) ;
-#endif
+
+ if(gAuditTexture)
+ {
+ decTextureCounter(mTextureMemory, mComponents, mCategory) ;
+ }
LLImageGL::deleteTextures(1, &old_name);
+
stop_glerror();
}
@@ -1149,82 +1251,20 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
sGlobalTextureMemoryInBytes += mTextureMemory;
mTexelsInGLTexture = getWidth() * getHeight() ;
-#if !LL_RELEASE_FOR_DOWNLOAD
- incTextureCounter(mTextureMemory / mComponents) ;
-#endif
-
+ if(gAuditTexture)
+ {
+ incTextureCounter(mTextureMemory, mComponents, mCategory) ;
+ }
// mark this as bound at this point, so we don't throw it out immediately
mLastBindTime = sLastFrameTime;
-
- llpushcallstacks ;
return TRUE;
}
-BOOL LLImageGL::setDiscardLevel(S32 discard_level)
-{
- llassert(discard_level >= 0);
- llassert(mCurrentDiscardLevel >= 0);
-
- discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
-
- if (discard_level == mCurrentDiscardLevel)
- {
- // nothing to do
- return FALSE;
- }
- else if (discard_level < mCurrentDiscardLevel)
- {
- // larger image
- dump();
- llerrs << "LLImageGL::setDiscardLevel() called with larger discard level; use createGLTexture()" << llendl;
- return FALSE;
- }
- else if (mUseMipMaps)
- {
- LLPointer<LLImageRaw> imageraw = new LLImageRaw;
- while(discard_level > mCurrentDiscardLevel)
- {
- if (readBackRaw(discard_level, imageraw, false))
- {
- break;
- }
- discard_level--;
- }
- if (discard_level == mCurrentDiscardLevel)
- {
- // unable to increase the discard level
- return FALSE;
- }
- return createGLTexture(discard_level, imageraw);
- }
- else
- {
-#if !LL_LINUX && !LL_SOLARIS
- // *FIX: This should not be skipped for the linux client.
- llerrs << "LLImageGL::setDiscardLevel() called on image without mipmaps" << llendl;
-#endif
- return FALSE;
- }
-}
-
-BOOL LLImageGL::isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents)
-{
- assert_glerror();
- S32 gl_discard = discard_level - mCurrentDiscardLevel;
- LLGLint glwidth = 0;
- glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_WIDTH, (GLint*)&glwidth);
- LLGLint glheight = 0;
- glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_HEIGHT, (GLint*)&glheight);
- LLGLint glcomponents = 0 ;
- glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_INTERNAL_FORMAT, (GLint*)&glcomponents);
- assert_glerror();
-
- return glwidth >= image_width && glheight >= image_height && (GL_RGB8 == glcomponents || GL_RGBA8 == glcomponents) ;
-}
-
BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const
{
- llpushcallstacks ;
+ llassert_always(sAllowReadBackRaw) ;
+ //llerrs << "should not call this function!" << llendl ;
+
if (discard_level < 0)
{
discard_level = mCurrentDiscardLevel;
@@ -1327,7 +1367,7 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
return FALSE ;
}
//-----------------------------------------------------------------------------------------------
- llpushcallstacks ;
+
return TRUE ;
}
@@ -1345,25 +1385,26 @@ void LLImageGL::deleteDeadTextures()
stop_glerror();
}
}
-
+
glDeleteTextures(1, &tex);
stop_glerror();
}
}
-
+
void LLImageGL::destroyGLTexture()
{
if (mTexName != 0)
{
if(mTextureMemory)
{
-#if !LL_RELEASE_FOR_DOWNLOAD
- decTextureCounter(mTextureMemory / mComponents) ;
-#endif
+ if(gAuditTexture)
+ {
+ decTextureCounter(mTextureMemory, mComponents, mCategory) ;
+ }
sGlobalTextureMemoryInBytes -= mTextureMemory;
mTextureMemory = 0;
}
-
+
LLImageGL::deleteTextures(1, &mTexName);
mTexName = 0;
mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
@@ -1479,6 +1520,11 @@ S32 LLImageGL::getMipBytes(S32 discard_level) const
return res;
}
+BOOL LLImageGL::isJustBound() const
+{
+ return (BOOL)(sLastFrameTime - mLastBindTime < 0.5f);
+}
+
BOOL LLImageGL::getBoundRecently() const
{
return (BOOL)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME);
@@ -1490,44 +1536,104 @@ void LLImageGL::setTarget(const LLGLenum target, const LLTexUnit::eTextureType b
mBindTarget = bind_target;
}
-void LLImageGL::analyzeAlpha(const void* data_in, S32 w, S32 h)
+const S8 INVALID_OFFSET = -99 ;
+void LLImageGL::setNeedsAlphaAndPickMask(BOOL need_mask)
{
- if(!mNeedsAlphaAndPickMask)
+ if(mNeedsAlphaAndPickMask != need_mask)
{
- return ;
+ mNeedsAlphaAndPickMask = need_mask;
+
+ if(mNeedsAlphaAndPickMask)
+ {
+ mAlphaOffset = 0 ;
+ }
+ else //do not need alpha mask
+ {
+ mAlphaOffset = INVALID_OFFSET ;
+ mIsMask = FALSE;
+ }
}
+}
- if (mFormatType != GL_UNSIGNED_BYTE)
+void LLImageGL::calcAlphaChannelOffsetAndStride()
+{
+ if(mAlphaOffset == INVALID_OFFSET)//do not need alpha mask
{
- llwarns << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << llendl;
+ return ;
}
- U32 stride = 0;
+ mAlphaStride = -1 ;
switch (mFormatPrimary)
{
case GL_LUMINANCE:
case GL_ALPHA:
- stride = 1;
+ mAlphaStride = 1;
break;
case GL_LUMINANCE_ALPHA:
- stride = 2;
+ mAlphaStride = 2;
break;
case GL_RGB:
- //no alpha
+ mNeedsAlphaAndPickMask = FALSE ;
mIsMask = FALSE;
- return;
+ return ; //no alpha channel.
case GL_RGBA:
- stride = 4;
+ mAlphaStride = 4;
break;
case GL_BGRA_EXT:
- stride = 4;
+ mAlphaStride = 4;
break;
default:
- return;
+ break;
+ }
+
+ mAlphaOffset = -1 ;
+ if (mFormatType == GL_UNSIGNED_BYTE)
+ {
+ mAlphaOffset = mAlphaStride - 1 ;
+ }
+ else if(is_little_endian())
+ {
+ if (mFormatType == GL_UNSIGNED_INT_8_8_8_8)
+ {
+ mAlphaOffset = 0 ;
+ }
+ else if (mFormatType == GL_UNSIGNED_INT_8_8_8_8_REV)
+ {
+ mAlphaOffset = 3 ;
+ }
+ }
+ else //big endian
+ {
+ if (mFormatType == GL_UNSIGNED_INT_8_8_8_8)
+ {
+ mAlphaOffset = 3 ;
+ }
+ else if (mFormatType == GL_UNSIGNED_INT_8_8_8_8_REV)
+ {
+ mAlphaOffset = 0 ;
+ }
+ }
+
+ if( mAlphaStride < 1 || //unsupported format
+ mAlphaOffset < 0 || //unsupported type
+ (mFormatPrimary == GL_BGRA_EXT && mFormatType != GL_UNSIGNED_BYTE)) //unknown situation
+ {
+ llwarns << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << llendl;
+
+ mNeedsAlphaAndPickMask = FALSE ;
+ mIsMask = FALSE;
+ }
+}
+
+void LLImageGL::analyzeAlpha(const void* data_in, S32 w, S32 h)
+{
+ if(!mNeedsAlphaAndPickMask)
+ {
+ return ;
}
U32 length = w * h;
- const GLubyte* current = ((const GLubyte*) data_in)+stride-1;
+ const GLubyte* current = ((const GLubyte*) data_in) + mAlphaOffset ;
S32 sample[16];
memset(sample, 0, sizeof(S32)*16);
@@ -1535,7 +1641,7 @@ void LLImageGL::analyzeAlpha(const void* data_in, S32 w, S32 h)
for (U32 i = 0; i < length; i++)
{
++sample[*current/16];
- current += stride;
+ current += mAlphaStride ;
}
U32 total = 0;
@@ -1638,8 +1744,30 @@ BOOL LLImageGL::getMask(const LLVector2 &tc)
return res;
}
-//----------------------------------------------------------------------------
-#if !LL_RELEASE_FOR_DOWNLOAD
+void LLImageGL::setCategory(S32 category)
+{
+ if(!gAuditTexture)
+ {
+ return ;
+ }
+ if(mCategory != category)
+ {
+ if(mCategory > -1)
+ {
+ sTextureMemByCategory[mCategory] -= mTextureMemory ;
+ }
+ if(category > -1 && category < sMaxCatagories)
+ {
+ sTextureMemByCategory[category] += mTextureMemory ;
+ mCategory = category;
+ }
+ else
+ {
+ mCategory = -1 ;
+ }
+ }
+}
+
//for debug use
//val is a "power of two" number
S32 LLImageGL::getTextureCounterIndex(U32 val)
@@ -1663,18 +1791,33 @@ S32 LLImageGL::getTextureCounterIndex(U32 val)
return ret ;
}
}
-void LLImageGL::incTextureCounter(U32 val)
+
+//static
+void LLImageGL::incTextureCounter(U32 val, S32 ncomponents, S32 category)
{
sTextureLoadedCounter[getTextureCounterIndex(val)]++ ;
+ sTextureMemByCategory[category] += (S32)val * ncomponents ;
}
-void LLImageGL::decTextureCounter(U32 val)
+
+//static
+void LLImageGL::decTextureCounter(U32 val, S32 ncomponents, S32 category)
{
sTextureLoadedCounter[getTextureCounterIndex(val)]-- ;
+ sTextureMemByCategory[category] += (S32)val * ncomponents ;
}
-void LLImageGL::setCurTexSizebar(S32 index)
+
+void LLImageGL::setCurTexSizebar(S32 index, BOOL set_pick_size)
{
sCurTexSizeBar = index ;
- sCurTexPickSize = (1 << index) ;
+
+ if(set_pick_size)
+ {
+ sCurTexPickSize = (1 << index) ;
+ }
+ else
+ {
+ sCurTexPickSize = -1 ;
+ }
}
void LLImageGL::resetCurTexSizebar()
{
@@ -1682,7 +1825,9 @@ void LLImageGL::resetCurTexSizebar()
sCurTexPickSize = -1 ;
}
//----------------------------------------------------------------------------
-#endif
+
+//----------------------------------------------------------------------------
+
// Manual Mip Generation
/*
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
index a094605607..937065043c 100644
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -47,7 +47,6 @@ class LLTextureAtlas ;
#define MEGA_BYTES_TO_BYTES(x) ((x) << 20)
//============================================================================
-
class LLImageGL : public LLRefCount
{
friend class LLTexUnit;
@@ -63,6 +62,7 @@ public:
BOOL updateBindStats(S32 tex_mem) const ;
F32 getTimePassedSinceLastBound();
+ void forceUpdateBindStats(void) const;
// needs to be called every frame
static void updateStats(F32 current_time);
@@ -71,8 +71,9 @@ public:
static void destroyGL(BOOL save_state = TRUE);
static void restoreGL();
- // Sometimes called externally for textures not using LLImageGL (should go away...)
- static S32 updateBoundTexMem(const S32 delta);
+ // Sometimes called externally for textures not using LLImageGL (should go away...)
+ static S32 updateBoundTexMem(const S32 mem, const S32 ncomponents, S32 category) ;
+
static bool checkSize(S32 width, S32 height);
//for server side use only.
@@ -91,6 +92,7 @@ protected:
virtual ~LLImageGL();
void analyzeAlpha(const void* data_in, S32 w, S32 h);
+ void calcAlphaChannelOffsetAndStride();
public:
virtual void dump(); // debugging info to llinfos
@@ -105,14 +107,15 @@ public:
static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels);
BOOL createGLTexture() ;
- BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0);
+ BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE,
+ S32 category = sMaxCatagories - 1);
BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0);
void setImage(const LLImageRaw* imageraw);
void setImage(const U8* data_in, BOOL data_hasmips = FALSE);
BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE);
BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE);
BOOL setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height);
- BOOL setDiscardLevel(S32 discard_level);
+
// Read back a raw image for this discard level, if it exists
BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const;
void destroyGLTexture();
@@ -131,6 +134,7 @@ public:
S32 getBytes(S32 discard_level = -1) const;
S32 getMipBytes(S32 discard_level = -1) const;
BOOL getBoundRecently() const;
+ BOOL isJustBound() const;
LLGLenum getPrimaryFormat() const { return mFormatPrimary; }
LLGLenum getFormatType() const { return mFormatType; }
@@ -150,8 +154,6 @@ public:
BOOL getUseMipMaps() const { return mUseMipMaps; }
void setUseMipMaps(BOOL usemips) { mUseMipMaps = usemips; }
- BOOL isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents) ;
-
void updatePickMask(S32 width, S32 height, const U8* data_in);
BOOL getMask(const LLVector2 &tc);
@@ -178,7 +180,7 @@ public:
void init(BOOL usemipmaps);
virtual void cleanup(); // Clean up the LLImageGL so it can be reinitialized. Be careful when using this in derived class destructors
- void setNeedsAlphaAndPickMask(BOOL need_mask) {mNeedsAlphaAndPickMask = need_mask;}
+ void setNeedsAlphaAndPickMask(BOOL need_mask);
BOOL preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image);
void postAddToAtlas() ;
@@ -187,7 +189,7 @@ public:
// Various GL/Rendering options
S32 mTextureMemory;
mutable F32 mLastBindTime; // last time this was bound, by discard level
-
+
private:
LLPointer<LLImageRaw> mSaveData; // used for destroyGL/restoreGL
U8* mPickMask; //downsampled bitmap approximation of alpha channel. NULL if no alpha channel
@@ -197,13 +199,15 @@ private:
BOOL mIsMask;
BOOL mNeedsAlphaAndPickMask;
-
+ S8 mAlphaStride ;
+ S8 mAlphaOffset ;
+
bool mGLTextureCreated ;
LLGLuint mTexName;
U16 mWidth;
U16 mHeight;
S8 mCurrentDiscardLevel;
-
+
S8 mDiscardLevelInAtlas;
U32 mTexelsInAtlas ;
U32 mTexelsInGLTexture;
@@ -233,7 +237,7 @@ public:
static S32 sCount;
static F32 sLastFrameTime;
-
+
static LLGLuint sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS]; // Currently bound texture ID
// Global memory statistics
@@ -246,30 +250,61 @@ public:
static LLImageGL* sDefaultGLTexture ;
static BOOL sAutomatedTest;
-#if !LL_RELEASE_FOR_DOWNLOAD
+#if DEBUG_MISS
+ BOOL mMissed; // Missed on last bind?
+ BOOL getMissed() const { return mMissed; };
+#else
+ BOOL getMissed() const { return FALSE; };
+#endif
+
+public:
+ static void initClass(S32 num_catagories) ;
+ static void cleanupClass() ;
+private:
+ static S32 sMaxCatagories ;
+
+ //the flag to allow to call readBackRaw(...).
+ //can be removed if we do not use that function at all.
+ static BOOL sAllowReadBackRaw ;
+//
+//****************************************************************************************************
+//The below for texture auditing use only
+//****************************************************************************************************
+private:
+ S32 mCategory ;
+public:
+ void setCategory(S32 category) ;
+ S32 getCategory()const {return mCategory ;}
+
//for debug use: show texture size distribution
//----------------------------------------
- static LLPointer<LLImageGL> sDefaultTexturep; //default texture to replace normal textures
+ static LLPointer<LLImageGL> sHighlightTexturep; //default texture to replace normal textures
static std::vector<S32> sTextureLoadedCounter ;
static std::vector<S32> sTextureBoundCounter ;
static std::vector<S32> sTextureCurBoundCounter ;
static S32 sCurTexSizeBar ;
static S32 sCurTexPickSize ;
-
+
+ static void setHighlightTexture(S32 category) ;
static S32 getTextureCounterIndex(U32 val) ;
- static void incTextureCounter(U32 val) ;
- static void decTextureCounter(U32 val) ;
- static void setCurTexSizebar(S32 index) ;
+ static void incTextureCounter(U32 val, S32 ncomponents, S32 category) ;
+ static void decTextureCounter(U32 val, S32 ncomponents, S32 category) ;
+ static void setCurTexSizebar(S32 index, BOOL set_pick_size = TRUE) ;
static void resetCurTexSizebar();
//----------------------------------------
-#endif
-#if DEBUG_MISS
- BOOL mMissed; // Missed on last bind?
- BOOL getMissed() const { return mMissed; };
-#else
- BOOL getMissed() const { return FALSE; };
-#endif
+ //for debug use: show texture category distribution
+ //----------------------------------------
+
+ static std::vector<S32> sTextureMemByCategory;
+ static std::vector<S32> sTextureMemByCategoryBound ;
+ static std::vector<S32> sTextureCurMemByCategoryBound ;
+ //----------------------------------------
+//****************************************************************************************************
+//End of definitions for texture auditing use only
+//****************************************************************************************************
+
};
+extern BOOL gAuditTexture;
#endif // LL_LLIMAGEGL_H
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index b74d824c9e..fc45df8153 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -179,7 +179,7 @@ void LLTexUnit::disable(void)
}
}
-bool LLTexUnit::bind(LLTexture* texture, bool forceBind)
+bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
{
stop_glerror();
if (mIndex < 0) return false;
@@ -198,9 +198,19 @@ bool LLTexUnit::bind(LLTexture* texture, bool forceBind)
//if deleted, will re-generate it immediately
texture->forceImmediateUpdate() ;
+ gl_tex->forceUpdateBindStats() ;
return texture->bindDefaultImage(mIndex);
}
+ //in audit, replace the selected texture by the default one.
+ if(gAuditTexture && for_rendering && LLImageGL::sCurTexPickSize > 0)
+ {
+ if(texture->getWidth() * texture->getHeight() == LLImageGL::sCurTexPickSize)
+ {
+ gl_tex->updateBindStats(gl_tex->mTextureMemory);
+ return bind(LLImageGL::sHighlightTexturep.get());
+ }
+ }
if ((mCurrTexture != gl_tex->getTexName()) || forceBind)
{
activate();
@@ -223,7 +233,7 @@ bool LLTexUnit::bind(LLTexture* texture, bool forceBind)
return true;
}
-bool LLTexUnit::bind(LLImageGL* texture, bool forceBind)
+bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind)
{
stop_glerror();
if (mIndex < 0) return false;
@@ -260,6 +270,7 @@ bool LLTexUnit::bind(LLImageGL* texture, bool forceBind)
setTextureFilteringOption(texture->mFilterOption);
}
}
+
return true;
}
diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index cb2a4d4450..0121a190ee 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -149,8 +149,8 @@ public:
// Binds the LLImageGL to this texture unit
// (automatically enables the unit for the LLImageGL's texture type)
- bool bind(LLImageGL* texture, bool forceBind = false);
- bool bind(LLTexture* texture, bool forceBind = false);
+ bool bind(LLImageGL* texture, bool for_rendering = false, bool forceBind = false);
+ bool bind(LLTexture* texture, bool for_rendering = false, bool forceBind = false);
// Binds a cubemap to this texture unit
// (automatically enables the texture unit for cubemaps)
diff --git a/indra/llrender/lltexture.h b/indra/llrender/lltexture.h
index 0cd9667644..7034e9199d 100644
--- a/indra/llrender/lltexture.h
+++ b/indra/llrender/lltexture.h
@@ -63,7 +63,7 @@ public:
//
virtual S8 getType() const = 0 ;
virtual void setKnownDrawSize(S32 width, S32 height) = 0 ;
- virtual bool bindDefaultImage(const S32 stage = 0) const = 0 ;
+ virtual bool bindDefaultImage(const S32 stage = 0) = 0 ;
virtual void forceImmediateUpdate() = 0 ;
virtual void setActive() = 0 ;
virtual S32 getWidth(S32 discard_level = -1) const = 0 ;
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 956e843987..c6a38c7ca7 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -118,6 +118,7 @@ const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bo
const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
+static MenuRegistry::Register<LLMenuItemGL> register_menu_item("menu_item");
static MenuRegistry::Register<LLMenuItemSeparatorGL> register_separator("menu_item_separator");
static MenuRegistry::Register<LLMenuItemCallGL> register_menu_item_call("menu_item_call");
static MenuRegistry::Register<LLMenuItemCheckGL> register_menu_item_check("menu_item_check");
@@ -132,6 +133,28 @@ static LLDefaultChildRegistry::Register<LLMenuGL> register_menu_default("menu");
///============================================================================
/// Class LLMenuItemGL
///============================================================================
+
+LLMenuItemGL::Params::Params()
+: shortcut("shortcut"),
+ jump_key("jump_key", KEY_NONE),
+ use_mac_ctrl("use_mac_ctrl", false),
+ rect("rect"),
+ left("left"),
+ top("top"),
+ right("right"),
+ bottom("bottom"),
+ width("width"),
+ height("height"),
+ bottom_delta("bottom_delta"),
+ left_delta("left_delta"),
+ enabled_color("enabled_color"),
+ disabled_color("disabled_color"),
+ highlight_bg_color("highlight_bg_color"),
+ highlight_fg_color("highlight_fg_color")
+{
+ mouse_opaque = true;
+}
+
// Default constructor
LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p)
: LLUICtrl(p),
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 09d9e407c7..dc8ed3b3fd 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -79,26 +79,7 @@ public:
highlight_fg_color;
- Params()
- : shortcut("shortcut"),
- jump_key("jump_key", KEY_NONE),
- use_mac_ctrl("use_mac_ctrl", false),
- rect("rect"),
- left("left"),
- top("top"),
- right("right"),
- bottom("bottom"),
- width("width"),
- height("height"),
- bottom_delta("bottom_delta"),
- left_delta("left_delta"),
- enabled_color("enabled_color"),
- disabled_color("disabled_color"),
- highlight_bg_color("highlight_bg_color"),
- highlight_fg_color("highlight_fg_color")
- {
- mouse_opaque = true;
- }
+ Params();
};
protected:
diff --git a/indra/llvfs/lllfsthread.cpp b/indra/llvfs/lllfsthread.cpp
index 704e1ab142..e85cc437f4 100644
--- a/indra/llvfs/lllfsthread.cpp
+++ b/indra/llvfs/lllfsthread.cpp
@@ -189,7 +189,7 @@ bool LLLFSThread::Request::processRequest()
{
llassert(mOffset >= 0);
LLAPRFile infile ;
- infile.open(mThread->getLocalAPRFilePool(), mFileName, LL_APR_RB);
+ infile.open(mFileName, LL_APR_RB, mThread->getLocalAPRFilePool());
if (!infile.getFileHandle())
{
llwarns << "LLLFS: Unable to read file: " << mFileName << llendl;
@@ -213,7 +213,7 @@ bool LLLFSThread::Request::processRequest()
if (mOffset < 0)
flags |= APR_APPEND;
LLAPRFile outfile ;
- outfile.open(mThread->getLocalAPRFilePool(), mFileName, flags);
+ outfile.open(mFileName, flags, mThread->getLocalAPRFilePool());
if (!outfile.getFileHandle())
{
llwarns << "LLLFS: Unable to write file: " << mFileName << llendl;
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index cba9b09dc0..08e43da9e4 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -268,7 +268,6 @@ set(viewer_SOURCE_FILES
llmaniprotate.cpp
llmanipscale.cpp
llmaniptranslate.cpp
- llmapresponders.cpp
llmediactrl.cpp
llmediadataclient.cpp
llmediaremotectrl.cpp
@@ -370,7 +369,9 @@ set(viewer_SOURCE_FILES
llsearchhistory.cpp
llselectmgr.cpp
llsidepanelinventory.cpp
- llsidepanelobjectinfo.cpp
+ llsidepanelinventorysubpanel.cpp
+ llsidepaneliteminfo.cpp
+ llsidepaneltaskinfo.cpp
llsidetray.cpp
llsidetraypanelcontainer.cpp
llsky.cpp
@@ -397,6 +398,10 @@ set(viewer_SOURCE_FILES
lltexturecache.cpp
lltexturectrl.cpp
lltexturefetch.cpp
+ lltextureinfo.cpp
+ lltextureinfodetails.cpp
+ lltexturestats.cpp
+ lltexturestatsuploader.cpp
lltextureview.cpp
lltoast.cpp
lltoastalertpanel.cpp
@@ -522,6 +527,8 @@ set(viewer_SOURCE_FILES
llwlparamset.cpp
llworld.cpp
llworldmap.cpp
+ llworldmapmessage.cpp
+ llworldmipmap.cpp
llworldmapview.cpp
llxmlrpclistener.cpp
llxmlrpctransaction.cpp
@@ -757,7 +764,6 @@ set(viewer_HEADER_FILES
llmaniprotate.h
llmanipscale.h
llmaniptranslate.h
- llmapresponders.h
llmediactrl.h
llmediadataclient.h
llmediaremotectrl.h
@@ -857,7 +863,9 @@ set(viewer_HEADER_FILES
llsearchhistory.h
llselectmgr.h
llsidepanelinventory.h
- llsidepanelobjectinfo.h
+ llsidepanelinventorysubpanel.h
+ llsidepaneliteminfo.h
+ llsidepaneltaskinfo.h
llsidetray.h
llsidetraypanelcontainer.h
llsky.h
@@ -885,6 +893,10 @@ set(viewer_HEADER_FILES
lltexturecache.h
lltexturectrl.h
lltexturefetch.h
+ lltextureinfo.h
+ lltextureinfodetails.h
+ lltexturestats.h
+ lltexturestatsuploader.h
lltextureview.h
lltoast.h
lltoastalertpanel.h
@@ -1011,6 +1023,8 @@ set(viewer_HEADER_FILES
llwlparamset.h
llworld.h
llworldmap.h
+ llworldmapmessage.h
+ llworldmipmap.h
llworldmapview.h
llxmlrpclistener.h
llxmlrpctransaction.h
@@ -1663,6 +1677,12 @@ LL_ADD_INTEGRATION_TEST(llcapabilitylistener
)
#ADD_VIEWER_BUILD_TEST(llmemoryview viewer)
+#ADD_VIEWER_BUILD_TEST(llagentaccess viewer)
+#ADD_VIEWER_BUILD_TEST(llworldmap viewer)
+#ADD_VIEWER_BUILD_TEST(llworldmipmap viewer)
+#ADD_VIEWER_BUILD_TEST(lltextureinfo viewer)
+#ADD_VIEWER_BUILD_TEST(lltextureinfodetails viewer)
+#ADD_VIEWER_BUILD_TEST(lltexturestatsuploader viewer)
# Don't do these for DARWIN or LINUX here -- they're taken care of by viewer_manifest.py
diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings
index dceaba9a43..879408d6e4 100644
--- a/indra/newview/English.lproj/InfoPlist.strings
+++ b/indra/newview/English.lproj/InfoPlist.strings
@@ -2,6 +2,6 @@
CFBundleName = "Second Life";
-CFBundleShortVersionString = "Second Life version 2.0.0.3256";
-CFBundleGetInfoString = "Second Life version 2.0.0.3256, Copyright 2004-2009 Linden Research, Inc.";
+CFBundleShortVersionString = "Second Life version 2.0.0.200030";
+CFBundleGetInfoString = "Second Life version 2.0.0.200030, Copyright 2004-2009 Linden Research, Inc.";
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
index 7aec8a343d..38ebb22b84 100644
--- a/indra/newview/Info-SecondLife.plist
+++ b/indra/newview/Info-SecondLife.plist
@@ -32,7 +32,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
- <string>2.0.0.3256</string>
+ <string>2.0.0.200030</string>
<key>CSResourcesFileMapped</key>
<true/>
</dict>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index e8af26c743..6f6d5fb979 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -309,7 +309,18 @@
<key>Value</key>
<integer>0</integer>
</map>
- <key>AutoAcceptNewInventory</key>
+ <key>AuditTexture</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable texture auditting.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>AutoAcceptNewInventory</key>
<map>
<key>Comment</key>
<string>Automatically accept new notecards/textures/landmarks</string>
@@ -3574,7 +3585,7 @@
<key>Type</key>
<string>String</string>
<key>Value</key>
- <string>http://www.google.com/search?q=site%3Awiki.secondlife.com+[TOPIC]&amp;ignore_channel=[CHANNEL]&amp;ignore_version=[VERSION]&amp;ignore_os=[OS]&amp;ignore_language=[LANGUAGE]&amp;ignore_version_major=[VERSION_MAJOR]&amp;ignore_version_minor=[VERSION_MINOR]&amp;ignore_version_patch=[VERSION_PATCH]&amp;ignore_version_build=[VERSION_BUILD]</string>
+ <string>http://docs.lindenlab.com/help/helpfloater.php?topic=[TOPIC]&amp;channel=[CHANNEL]&amp;version=[VERSION]&amp;os=[OS]&amp;language=[LANGUAGE]&amp;version_major=[VERSION_MAJOR]&amp;version_minor=[VERSION_MINOR]&amp;version_patch=[VERSION_PATCH]&amp;version_build=[VERSION_BUILD]</string>
</map>
<key>HighResSnapshot</key>
<map>
@@ -4567,6 +4578,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>MiniMapPrimMaxRadius</key>
+ <map>
+ <key>Comment</key>
+ <string>Radius of the largest prim to show on the MiniMap. Increasing beyond 256 may cause client lag.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>256.0</real>
+ </map>
<key>MiniMapRotate</key>
<map>
<key>Comment</key>
@@ -4581,7 +4603,7 @@
<key>MiniMapScale</key>
<map>
<key>Comment</key>
- <string>Miniature world map zoom levle (pixels per region)</string>
+ <string>Miniature world map zoom level (pixels per region)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -6247,7 +6269,7 @@
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
- <integer>0</integer>
+ <integer>1</integer>
</map>
<key>RenderHighlightFadeTime</key>
@@ -8611,6 +8633,28 @@
<key>Value</key>
<real>20.0</real>
</map>
+ <key>TextureDisable</key>
+ <map>
+ <key>Comment</key>
+ <string>If TRUE, do not load textures for in-world content</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>TextureLoadFullRes</key>
+ <map>
+ <key>Comment</key>
+ <string>If TRUE, always load textures at full resolution (discard = 0)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>TextureMemory</key>
<map>
<key>Comment</key>
@@ -10669,5 +10713,38 @@
<key>Value</key>
<integer>0</integer>
</map>
- </map>
+ <key>LogTextureDownloadsToViewerLog</key>
+ <map>
+ <key>Comment</key>
+ <string>Send texture download details to the viewer log</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>LogTextureDownloadsToSimulator</key>
+ <map>
+ <key>Comment</key>
+ <string>Send a digest of texture info to the sim</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>TextureLoggingThreshold</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies the byte threshold at which texture download data should be sent to the sim.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+</map>
</llsd>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 75a72e5b17..24bdf66c2a 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -75,6 +75,8 @@
#include "llstatusbar.h"
#include "llteleportflags.h"
#include "llteleporthistory.h"
+#include "lltexturestats.h"
+#include "lltexturestats.h"
#include "lltool.h"
#include "lltoolcomp.h"
#include "lltoolmgr.h"
@@ -6085,17 +6087,16 @@ void LLAgent::teleportCancel()
void LLAgent::teleportViaLocation(const LLVector3d& pos_global)
{
LLViewerRegion* regionp = getRegion();
- LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
+ U64 handle = to_region_handle(pos_global);
+ LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle);
if(regionp && info)
{
- U32 x_pos;
- U32 y_pos;
- from_region_handle(info->mHandle, &x_pos, &y_pos);
+ LLVector3d region_origin = info->getGlobalOrigin();
LLVector3 pos_local(
- (F32)(pos_global.mdV[VX] - x_pos),
- (F32)(pos_global.mdV[VY] - y_pos),
+ (F32)(pos_global.mdV[VX] - region_origin.mdV[VX]),
+ (F32)(pos_global.mdV[VY] - region_origin.mdV[VY]),
(F32)(pos_global.mdV[VZ]));
- teleportRequest(info->mHandle, pos_local);
+ teleportRequest(handle, pos_local);
}
else if(regionp &&
teleportCore(regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY])))
@@ -6512,3 +6513,4 @@ LLAgentQueryManager::~LLAgentQueryManager()
}
// EOF
+
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index e95eec4741..8e6d6b885d 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -57,6 +57,8 @@
#include "llallocator.h"
#include "llares.h"
#include "llcurl.h"
+#include "lltexturestats.h"
+#include "lltexturestats.h"
#include "llviewerwindow.h"
#include "llviewerdisplay.h"
#include "llviewermedia.h"
@@ -255,9 +257,6 @@ F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME;
BOOL gDisconnected = FALSE;
-// Map scale in pixels per region
-F32 gMapScale = 128.f;
-
// used to restore texture state after a mode switch
LLFrameTimer gRestoreGLTimer;
BOOL gRestoreGL = FALSE;
@@ -419,7 +418,7 @@ static void settings_to_globals()
gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc");
gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates");
- gMapScale = gSavedSettings.getF32("MapScale");
+ LLWorldMapView::sMapScale = gSavedSettings.getF32("MapScale");
LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap");
}
@@ -432,7 +431,7 @@ static void settings_modify()
LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4]
gDebugGL = gSavedSettings.getBOOL("RenderDebugGL") || gDebugSession;
gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline");
-
+ gAuditTexture = gSavedSettings.getBOOL("AuditTexture");
#if LL_VECTORIZE
if (gSysCPU.hasAltivec())
{
@@ -553,7 +552,7 @@ LLAppViewer* LLAppViewer::sInstance = NULL;
const std::string LLAppViewer::sGlobalSettingsName = "Global";
LLTextureCache* LLAppViewer::sTextureCache = NULL;
-LLWorkerThread* LLAppViewer::sImageDecodeThread = NULL;
+LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL;
LLTextureFetch* LLAppViewer::sTextureFetch = NULL;
LLAppViewer::LLAppViewer() :
@@ -657,6 +656,9 @@ bool LLAppViewer::init()
//////////////////////////////////////////////////////////////////////////////
// *FIX: The following code isn't grouped into functions yet.
+ // Statistics / debug timer initialization
+ init_statistics();
+
//
// Various introspection concerning the libs we're using - particularly
// the libs involved in getting to a full login screen.
@@ -1637,14 +1639,14 @@ bool LLAppViewer::initThreads()
LLWatchdog::getInstance()->init(watchdog_killer_callback);
}
- LLVFSThread::initClass(enable_threads && true);
- LLLFSThread::initClass(enable_threads && true);
+ LLVFSThread::initClass(enable_threads && false);
+ LLLFSThread::initClass(enable_threads && false);
// Image decoding
- LLAppViewer::sImageDecodeThread = new LLWorkerThread("ImageDecode", enable_threads && true);
+ LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true);
LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true);
- LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), enable_threads && false);
- LLImage::initClass(LLAppViewer::getImageDecodeThread());
+ LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), sImageDecodeThread, enable_threads && true);
+ LLImage::initClass();
if (LLFastTimer::sLog || LLFastTimer::sMetricLog)
{
@@ -2437,7 +2439,7 @@ void LLAppViewer::cleanupSavedSettings()
}
}
- gSavedSettings.setF32("MapScale", gMapScale );
+ gSavedSettings.setF32("MapScale", LLWorldMapView::sMapScale );
// Some things are cached in LLAgent.
if (gAgent.mInitialized)
@@ -2763,7 +2765,7 @@ void LLAppViewer::initMarkerFile()
// Create the marker file for this execution & lock it
apr_status_t s;
- s = mMarkerFile.open(mMarkerFileName, LL_APR_W, gAPRPoolp);
+ s = mMarkerFile.open(mMarkerFileName, LL_APR_W, TRUE);
if (s == APR_SUCCESS && mMarkerFile.getFileHandle())
{
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 73256a8fe6..c1bfbca868 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -41,15 +41,13 @@ class LLCommandLineParser;
class LLFrameTimer;
class LLPumpIO;
class LLTextureCache;
+class LLImageDecodeThread;
class LLTextureFetch;
-class LLTimer;
-class LLVFS;
class LLWatchdogTimeout;
-class LLWorkerThread;
+class LLCommandLineParser;
struct apr_dso_handle_t;
-
class LLAppViewer : public LLApp
{
public:
@@ -100,7 +98,7 @@ public:
// Thread accessors
static LLTextureCache* getTextureCache() { return sTextureCache; }
- static LLWorkerThread* getImageDecodeThread() { return sImageDecodeThread; }
+ static LLImageDecodeThread* getImageDecodeThread() { return sImageDecodeThread; }
static LLTextureFetch* getTextureFetch() { return sTextureFetch; }
const std::string& getSerialNumber() { return mSerialNumber; }
@@ -232,7 +230,7 @@ private:
// Thread objects.
static LLTextureCache* sTextureCache;
- static LLWorkerThread* sImageDecodeThread;
+ static LLImageDecodeThread* sImageDecodeThread;
static LLTextureFetch* sTextureFetch;
S32 mNumSessions;
@@ -320,9 +318,6 @@ extern F32 gSimFrames;
extern BOOL gDisconnected;
-// Map scale in pixels per region
-extern F32 gMapScale;
-
extern LLFrameTimer gRestoreGLTimer;
extern BOOL gRestoreGL;
extern BOOL gUseWireframe;
diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp
index d4df6dfbe7..4d85ecb97c 100644
--- a/indra/newview/llassetuploadresponders.cpp
+++ b/indra/newview/llassetuploadresponders.cpp
@@ -369,7 +369,7 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content)
std::string result = content["state"];
LLUUID new_id = content["new_asset"];
- llinfos << "LLSendTexLayerResponder::result from capabilities: " << result << llendl;
+ llinfos << "result: " << result << "new_id:" << new_id << llendl;
if (result == "complete"
&& mBakedUploadData != NULL)
{ // Invoke
@@ -383,6 +383,14 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content)
}
}
+void LLSendTexLayerResponder::error(U32 statusNum, const std::string& reason)
+{
+ llinfos << "status: " << statusNum << " reason: " << reason << llendl;
+
+ // Invoke the original callback with an error result
+ LLTexLayerSetBuffer::onTextureUploadComplete(LLUUID(), (void*) mBakedUploadData, -1, LL_EXSTAT_NONE);
+ mBakedUploadData = NULL; // deleted in onTextureUploadComplete()
+}
LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(const LLSD& post_data,
const LLUUID& vfile_id,
diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h
index a08d70213c..e656351305 100644
--- a/indra/newview/llassetuploadresponders.h
+++ b/indra/newview/llassetuploadresponders.h
@@ -84,6 +84,7 @@ public:
~LLSendTexLayerResponder();
virtual void uploadComplete(const LLSD& content);
+ virtual void error(U32 statusNum, const std::string& reason);
LLBakedUploadData * mBakedUploadData;
};
diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp
index ed304bdd34..442e9ab27b 100644
--- a/indra/newview/llcolorswatch.cpp
+++ b/indra/newview/llcolorswatch.cpp
@@ -228,7 +228,7 @@ void LLColorSwatchCtrl::draw()
{
if (!mFallbackImageName.empty())
{
- LLPointer<LLViewerTexture> fallback_image = LLViewerTextureManager::getFetchedTextureFromFile(mFallbackImageName, TRUE,
+ LLPointer<LLViewerFetchedTexture> fallback_image = LLViewerTextureManager::getFetchedTextureFromFile(mFallbackImageName, TRUE,
LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
if( fallback_image->getComponents() == 4 )
{
diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp
index 24a57cb0c1..9057d84f63 100644
--- a/indra/newview/lldebugview.cpp
+++ b/indra/newview/lldebugview.cpp
@@ -45,7 +45,7 @@
#include "llviewerwindow.h"
#include "llappviewer.h"
#include "llmemoryview.h"
-
+#include "llviewertexture.h"
//
// Globals
//
@@ -102,17 +102,29 @@ LLDebugView::LLDebugView(const LLDebugView::Params& p)
gTextureView = LLUICtrlFactory::create<LLTextureView>(tvp);
addChild(gTextureView);
//gTextureView->reshape(r.getWidth(), r.getHeight(), TRUE);
-#if !LL_RELEASE_FOR_DOWNLOAD
- r.set(150, rect.getHeight() - 50, 900 + LLImageGL::sTextureLoadedCounter.size() * 30, 100);
- LLTextureSizeView::Params tsvp;
- tsvp.name("gTextureSizeView");
- tsvp.rect(r);
- tsvp.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT);
- tsvp.visible(false);
- gTextureSizeView = LLUICtrlFactory::create<LLTextureSizeView>(tsvp);
- addChild(gTextureSizeView);
-#endif
+ if(gAuditTexture)
+ {
+ r.set(150, rect.getHeight() - 50, 900 + LLImageGL::sTextureLoadedCounter.size() * 30, 100);
+ LLTextureSizeView::Params tsv ;
+ tsv.name("gTextureSizeView");
+ tsv.rect(r);
+ tsv.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT);
+ tsv.visible(false);
+ gTextureSizeView = LLUICtrlFactory::create<LLTextureSizeView>(tsv);
+ addChild(gTextureSizeView);
+ gTextureSizeView->setType(LLTextureSizeView::TEXTURE_MEM_OVER_SIZE) ;
+
+ r.set(150, rect.getHeight() - 50, 900 + LLViewerTexture::getTotalNumOfCategories() * 30, 100);
+ LLTextureSizeView::Params tcv ;
+ tcv.name("gTextureCategoryView");
+ tcv.rect(r);
+ tcv.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT);
+ tcv.visible(false);
+ gTextureCategoryView = LLUICtrlFactory::create<LLTextureSizeView>(tcv);
+ gTextureCategoryView->setType(LLTextureSizeView::TEXTURE_MEM_OVER_CATEGORY);
+ addChild(gTextureCategoryView);
+ }
}
@@ -122,5 +134,6 @@ LLDebugView::~LLDebugView()
gDebugView = NULL;
gTextureView = NULL;
gTextureSizeView = NULL;
+ gTextureCategoryView = NULL;
}
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index 069155c255..03a3f2b43d 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -105,7 +105,7 @@ void LLDrawable::init()
mVObjp = NULL;
// mFaces
mSpatialGroupp = NULL;
- mVisible = 0;
+ mVisible = sCurVisible - 2;//invisible for the current frame and the last frame.
mRadius = 0.f;
mGeneration = -1;
diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp
index d8c34581d5..95ddacb722 100644
--- a/indra/newview/lldrawpool.cpp
+++ b/indra/newview/lldrawpool.cpp
@@ -279,7 +279,7 @@ S32 LLFacePool::drawLoopSetTex(face_array_t& face_list, S32 stage)
iter != face_list.end(); iter++)
{
LLFace *facep = *iter;
- gGL.getTexUnit(stage)->bind(facep->getTexture());
+ gGL.getTexUnit(stage)->bind(facep->getTexture(), TRUE) ;
gGL.getTexUnit(0)->activate();
res += facep->renderIndexed();
}
@@ -475,17 +475,13 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture)
{
if (params.mTexture.notNull())
{
- gGL.getTexUnit(0)->bind(params.mTexture.get());
+ gGL.getTexUnit(0)->bind(params.mTexture, TRUE) ;
if (params.mTextureMatrix)
{
glMatrixMode(GL_TEXTURE);
glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix);
gPipeline.mTextureMatrixOps++;
}
- if(params.mTexture.notNull())//will be removed.
- {
- params.mTexture->addTextureStats(params.mVSize);
- }
}
else
{
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
index a5a29dea7b..6d77361414 100644
--- a/indra/newview/lldrawpoolalpha.cpp
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -219,7 +219,7 @@ void LLDrawPoolAlpha::render(S32 pass)
gPipeline.enableLightsFullbright(LLColor4(1,1,1,1));
glColor4f(1,0,0,1);
LLViewerFetchedTexture::sSmokeImagep->addTextureStats(1024.f*1024.f);
- gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sSmokeImagep) ;
+ gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sSmokeImagep, TRUE) ;
renderAlphaHighlight(LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_TEXCOORD0);
}
diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp
index 8d2cbc583c..5521fb05a8 100644
--- a/indra/newview/lldrawpooltree.cpp
+++ b/indra/newview/lldrawpooltree.cpp
@@ -253,7 +253,7 @@ void LLDrawPoolTree::renderTree(BOOL selecting)
LLGLState normalize(GL_NORMALIZE, TRUE);
// Bind the texture for this tree.
- gGL.getTexUnit(sDiffTex)->bind(mTexturep.get());
+ gGL.getTexUnit(sDiffTex)->bind(mTexturep.get(), TRUE);
U32 indices_drawn = 0;
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index 0bb5edf3f9..e41c4104eb 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -104,7 +104,7 @@ void LLViewerDynamicTexture::generateGLTexture(LLGLint internal_format, LLGLenum
{
setExplicitFormat(internal_format, primary_format, type_format, swap_bytes);
}
- createGLTexture(0, raw_image);
+ createGLTexture(0, raw_image, 0, TRUE, LLViewerTexture::DYNAMIC_TEX);
setAddressMode((mClamp) ? LLTexUnit::TAM_CLAMP : LLTexUnit::TAM_WRAP);
mGLTexturep->setGLTextureCreated(false);
}
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 8ec448e281..c54b83d865 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -52,6 +52,7 @@
#include "llvovolume.h"
#include "pipeline.h"
#include "llviewerregion.h"
+#include "llviewerwindow.h"
#define LL_MAX_INDICES_COUNT 1000000
@@ -175,6 +176,9 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
mLastIndicesCount = mIndicesCount;
mLastIndicesIndex = mIndicesIndex;
+ mImportanceToCamera = 0.f ;
+ mBoundingSphereRadius = 0.0f ;
+
mAtlasInfop = NULL ;
mUsingAtlas = FALSE ;
}
@@ -186,6 +190,7 @@ void LLFace::destroy()
{
mTexture->removeFace(this) ;
}
+
if (mDrawPoolp)
{
mDrawPoolp->removeFace(this);
@@ -207,7 +212,7 @@ void LLFace::destroy()
}
}
}
-
+
setDrawInfo(NULL);
removeAtlas();
@@ -256,6 +261,7 @@ void LLFace::setPool(LLFacePool* new_pool, LLViewerTexture *texturep)
}
mDrawPoolp = new_pool;
}
+
setTexture(texturep) ;
}
@@ -750,7 +756,9 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
}
mCenterLocal = (newMin+newMax)*0.5f;
-
+ LLVector3 tmp = (newMin - newMax) ;
+ mBoundingSphereRadius = tmp.length() * 0.5f ;
+
updateCenterAgent();
}
@@ -1314,6 +1322,151 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
return TRUE;
}
+const F32 LEAST_IMPORTANCE = 0.05f ;
+const F32 LEAST_IMPORTANCE_FOR_LARGE_IMAGE = 0.3f ;
+
+F32 LLFace::getTextureVirtualSize()
+{
+ F32 radius;
+ F32 cos_angle_to_view_dir;
+ mPixelArea = calcPixelArea(cos_angle_to_view_dir, radius);
+
+ if (mPixelArea <= 0)
+ {
+ return 0.f;
+ }
+
+ //get area of circle in texture space
+ LLVector2 tdim = mTexExtents[1] - mTexExtents[0];
+ F32 texel_area = (tdim * 0.5f).lengthSquared()*3.14159f;
+ if (texel_area <= 0)
+ {
+ // Probably animated, use default
+ texel_area = 1.f;
+ }
+
+ //apply texel area to face area to get accurate ratio
+ //face_area /= llclamp(texel_area, 1.f/64.f, 16.f);
+ F32 face_area = mPixelArea / llclamp(texel_area, 0.015625f, 128.f);
+
+ if(face_area > LLViewerTexture::sMaxSmallImageSize)
+ {
+ if(mImportanceToCamera < LEAST_IMPORTANCE) //if the face is not important, do not load hi-res.
+ {
+ static const F32 MAX_LEAST_IMPORTANCE_IMAGE_SIZE = 128.0f * 128.0f ;
+ face_area = llmin(face_area * 0.5f, MAX_LEAST_IMPORTANCE_IMAGE_SIZE) ;
+ }
+ else if(face_area > LLViewerTexture::sMinLargeImageSize) //if is large image, shrink face_area by considering the partial overlapping.
+ {
+ if(mImportanceToCamera < LEAST_IMPORTANCE_FOR_LARGE_IMAGE)//if the face is not important, do not load hi-res.
+ {
+ face_area = LLViewerTexture::sMinLargeImageSize ;
+ }
+ else if(mTexture.notNull() && mTexture->isLargeImage())
+ {
+ face_area *= adjustPartialOverlapPixelArea(cos_angle_to_view_dir, radius );
+ }
+ }
+ }
+
+ return face_area;
+}
+
+F32 LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
+{
+ //get area of circle around face
+ LLVector3 center = getPositionAgent();
+ LLVector3 size = (mExtents[1] - mExtents[0]) * 0.5f;
+
+ LLVector3 lookAt = center - LLViewerCamera::getInstance()->getOrigin();
+ F32 dist = lookAt.normVec() ;
+
+ //get area of circle around node
+ F32 app_angle = atanf(size.length()/dist);
+ radius = app_angle*LLDrawable::sCurPixelAngle;
+ F32 face_area = radius*radius * 3.14159f;
+
+ if(dist < mBoundingSphereRadius) //camera is very close
+ {
+ cos_angle_to_view_dir = 1.0f ;
+ mImportanceToCamera = 1.0f ;
+ }
+ else
+ {
+ cos_angle_to_view_dir = lookAt * LLViewerCamera::getInstance()->getXAxis() ;
+ mImportanceToCamera = LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist) ;
+ }
+
+ return face_area ;
+}
+
+//the projection of the face partially overlaps with the screen
+F32 LLFace::adjustPartialOverlapPixelArea(F32 cos_angle_to_view_dir, F32 radius )
+{
+ F32 screen_radius = (F32)llmax(gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight()) ;
+ F32 center_angle = acosf(cos_angle_to_view_dir) ;
+ F32 d = center_angle * LLDrawable::sCurPixelAngle ;
+
+ if(d + radius > screen_radius + 5.f)
+ {
+ //----------------------------------------------
+ //calculate the intersection area of two circles
+ //F32 radius_square = radius * radius ;
+ //F32 d_square = d * d ;
+ //F32 screen_radius_square = screen_radius * screen_radius ;
+ //face_area =
+ // radius_square * acosf((d_square + radius_square - screen_radius_square)/(2 * d * radius)) +
+ // screen_radius_square * acosf((d_square + screen_radius_square - radius_square)/(2 * d * screen_radius)) -
+ // 0.5f * sqrtf((-d + radius + screen_radius) * (d + radius - screen_radius) * (d - radius + screen_radius) * (d + radius + screen_radius)) ;
+ //----------------------------------------------
+
+ //the above calculation is too expensive
+ //the below is a good estimation: bounding box of the bounding sphere:
+ F32 alpha = 0.5f * (radius + screen_radius - d) / radius ;
+ alpha = llclamp(alpha, 0.f, 1.f) ;
+ return alpha * alpha ;
+ }
+ return 1.0f ;
+}
+
+const S8 FACE_IMPORTANCE_LEVEL = 4 ;
+const F32 FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[FACE_IMPORTANCE_LEVEL][2] = //{distance, importance_weight}
+ {{16.1f, 1.0f}, {32.1f, 0.5f}, {48.1f, 0.2f}, {96.1f, 0.05f} } ;
+const F32 FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[FACE_IMPORTANCE_LEVEL][2] = //{cos(angle), importance_weight}
+ {{0.985f /*cos(10 degrees)*/, 1.0f}, {0.94f /*cos(20 degrees)*/, 0.8f}, {0.866f /*cos(30 degrees)*/, 0.64f}, {0.0f, 0.36f}} ;
+
+//static
+F32 LLFace::calcImportanceToCamera(F32 cos_angle_to_view_dir, F32 dist)
+{
+ F32 importance = 0.f ;
+
+ if(cos_angle_to_view_dir > LLViewerCamera::getInstance()->getCosHalfFov() &&
+ dist < FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[FACE_IMPORTANCE_LEVEL - 1][0])
+ {
+ F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ;
+ F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed();
+
+ if(camera_moving_speed > 10.0f || camera_angular_speed > 1.0f)
+ {
+ //if camera moves or rotates too fast, ignore the importance factor
+ return 0.f ;
+ }
+
+ //F32 camera_relative_speed = camera_moving_speed * (lookAt * LLViewerCamera::getInstance()->getVelocityDir()) ;
+
+ S32 i = 0 ;
+ for(i = 0; i < FACE_IMPORTANCE_LEVEL && dist > FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[i][0]; ++i);
+ i = llmin(i, FACE_IMPORTANCE_LEVEL - 1) ;
+ F32 dist_factor = FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[i][1] ;
+
+ for(i = 0; i < FACE_IMPORTANCE_LEVEL && cos_angle_to_view_dir < FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[i][0] ; ++i) ;
+ i = llmin(i, FACE_IMPORTANCE_LEVEL - 1) ;
+ importance = dist_factor * FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[i][1] ;
+ }
+
+ return importance ;
+}
+
BOOL LLFace::verify(const U32* indices_array) const
{
BOOL ok = TRUE;
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 2b134c8c31..68eee061b8 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -188,6 +188,9 @@ public:
void setIndicesIndex(S32 idx) { mIndicesIndex = idx; }
void setDrawInfo(LLDrawInfo* draw_info);
+ F32 getTextureVirtualSize() ;
+ F32 getImportanceToCamera()const {return mImportanceToCamera ;}
+
//for atlas
LLTextureAtlasSlot* getAtlasInfo() ;
void setAtlasInUse(BOOL flag);
@@ -200,6 +203,12 @@ public:
void removeAtlas() ;
BOOL switchTexture() ;
+private:
+ F32 adjustPartialOverlapPixelArea(F32 cos_angle_to_view_dir, F32 radius );
+ F32 calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) ;
+public:
+ static F32 calcImportanceToCamera(F32 to_view_dir, F32 dist);
+
public:
LLVector3 mCenterLocal;
@@ -214,7 +223,7 @@ public:
LLMatrix4* mTextureMatrix;
LLDrawInfo* mDrawInfo;
-protected:
+private:
friend class LLGeometryManager;
friend class LLVolumeGeometryManager;
@@ -244,6 +253,13 @@ protected:
F32 mVSize;
F32 mPixelArea;
+ //importance factor, in the range [0, 1.0].
+ //1.0: the most important.
+ //based on the distance from the face to the view point and the angle from the face center to the view direction.
+ F32 mImportanceToCamera ;
+ F32 mBoundingSphereRadius ;
+
+
//atlas
LLPointer<LLTextureAtlasSlot> mAtlasInfop ;
BOOL mUsingAtlas ;
diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp
index e26937e93f..13ca7638c5 100644
--- a/indra/newview/llfloaterinspect.cpp
+++ b/indra/newview/llfloaterinspect.cpp
@@ -88,32 +88,7 @@ LLFloaterInspect::~LLFloaterInspect(void)
}
//sInstance = NULL;
}
-/*
-BOOL LLFloaterInspect::isVisible()
-{
- return (!!sInstance);
-}*/
-/*
-void LLFloaterInspect::show(void* ignored)
-{
- // setForceSelection ensures that the pie menu does not deselect things when it
- // looses the focus (this can happen with "select own objects only" enabled
- // VWR-1471
- BOOL forcesel = LLSelectMgr::getInstance()->setForceSelection(TRUE);
-
- if (!sInstance) // first use
- {
- sInstance = new LLFloaterInspect;
- }
- sInstance->openFloater();
- LLToolMgr::getInstance()->setTransientTool(LLToolCompInspect::getInstance());
- LLSelectMgr::getInstance()->setForceSelection(forcesel); // restore previouis value
-
- sInstance->mObjectSelection = LLSelectMgr::getInstance()->getSelection();
- sInstance->refresh();
-}
-*/
void LLFloaterInspect::onOpen(const LLSD& key)
{
BOOL forcesel = LLSelectMgr::getInstance()->setForceSelection(TRUE);
diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp
index 0c9a759f32..3fe711a166 100644
--- a/indra/newview/llfloatermap.cpp
+++ b/indra/newview/llfloatermap.cpp
@@ -51,11 +51,26 @@
#include "llviewermenu.h"
//
+// Constants
+//
+const F32 MAP_MINOR_DIR_THRESHOLD = 0.08f;
+
+//
// Member functions
//
LLFloaterMap::LLFloaterMap(const LLSD& key)
- : LLFloater(key)
+ : LLFloater(key),
+ mPopupMenu(NULL),
+ mTextBoxEast(NULL),
+ mTextBoxNorth(NULL),
+ mTextBoxWest(NULL),
+ mTextBoxSouth(NULL),
+ mTextBoxSouthEast(NULL),
+ mTextBoxNorthEast(NULL),
+ mTextBoxNorthWest(NULL),
+ mTextBoxSouthWest(NULL),
+ mMap(NULL)
{
//Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this, "floater_map.xml", FALSE);
}
@@ -92,6 +107,8 @@ BOOL LLFloaterMap::postBuild()
mPopupMenu->setItemEnabled ("Stop Tracking", false);
}
+ updateMinorDirections();
+
// Get the drag handle all the way in back
sendChildToBack(getDragHandle());
@@ -139,6 +156,23 @@ void LLFloaterMap::setDirectionPos( LLTextBox* text_box, F32 rotation )
llround(map_half_height - text_half_height + radius * sin( rotation )) );
}
+void LLFloaterMap::updateMinorDirections()
+{
+ if (mTextBoxNorthEast == NULL)
+ {
+ return;
+ }
+
+ // Hide minor directions if they cover too much of the map
+ bool show_minors = mTextBoxNorthEast->getRect().getHeight() < MAP_MINOR_DIR_THRESHOLD *
+ llmin(getRect().getWidth(), getRect().getHeight());
+
+ mTextBoxNorthEast->setVisible(show_minors);
+ mTextBoxNorthWest->setVisible(show_minors);
+ mTextBoxSouthWest->setVisible(show_minors);
+ mTextBoxSouthEast->setVisible(show_minors);
+}
+
// virtual
void LLFloaterMap::draw()
{
@@ -180,17 +214,23 @@ void LLFloaterMap::draw()
LLFloater::draw();
}
+void LLFloaterMap::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLFloater::reshape(width, height, called_from_parent);
+ updateMinorDirections();
+}
+
void LLFloaterMap::handleZoom(const LLSD& userdata)
{
std::string level = userdata.asString();
F32 scale = 0.0f;
if (level == std::string("close"))
- scale = MAP_SCALE_MAX;
+ scale = LLNetMap::MAP_SCALE_MAX;
else if (level == std::string("medium"))
- scale = MAP_SCALE_MID;
+ scale = LLNetMap::MAP_SCALE_MID;
else if (level == std::string("far"))
- scale = MAP_SCALE_MIN;
+ scale = LLNetMap::MAP_SCALE_MIN;
if (scale != 0.0f)
{
gSavedSettings.setF32("MiniMapScale", scale );
diff --git a/indra/newview/llfloatermap.h b/indra/newview/llfloatermap.h
index 501777ed07..6c9138c6a7 100644
--- a/indra/newview/llfloatermap.h
+++ b/indra/newview/llfloatermap.h
@@ -51,12 +51,14 @@ public:
/*virtual*/ BOOL postBuild();
/*virtual*/ BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
/*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
/*virtual*/ void draw();
private:
void handleZoom(const LLSD& userdata);
void handleStopTracking (const LLSD& userdata);
void setDirectionPos( LLTextBox* text_box, F32 rotation );
+ void updateMinorDirections();
LLMenuGL* mPopupMenu;
diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp
index 0f4d6e33a3..1002697fe5 100644
--- a/indra/newview/llfloaterreporter.cpp
+++ b/indra/newview/llfloaterreporter.cpp
@@ -790,7 +790,7 @@ void LLFloaterReporter::takeScreenshot()
// store in the image list so it doesn't try to fetch from the server
LLPointer<LLViewerFetchedTexture> image_in_list =
LLViewerTextureManager::getFetchedTexture(mResourceDatap->mAssetInfo.mUuid, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::FETCHED_TEXTURE);
- image_in_list->createGLTexture(0, raw);
+ image_in_list->createGLTexture(0, raw, 0, TRUE, LLViewerTexture::OTHER);
// the texture picker then uses that texture
LLTexturePicker* texture = getChild<LLTextureCtrl>("screenshot");
diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp
index 2fe21f28de..663bd232f7 100644
--- a/indra/newview/llfloateruipreview.cpp
+++ b/indra/newview/llfloateruipreview.cpp
@@ -670,6 +670,15 @@ void LLFloaterUIPreview::refreshList()
}
}
+ found = TRUE;
+ while(found) // for every sidepanel file that matches the pattern
+ {
+ if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "sidepanel_*.xml", name, FALSE))) // get next file matching pattern
+ {
+ addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
+ }
+ }
+
if(!mFileList->isEmpty()) // if there were any matching files, just select the first one (so we don't have to worry about disabling buttons when no entry is selected)
{
mFileList->selectFirstItem();
diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp
index bbeb287171..43024a4bd0 100644
--- a/indra/newview/llfloatervoicedevicesettings.cpp
+++ b/indra/newview/llfloatervoicedevicesettings.cpp
@@ -95,6 +95,9 @@ void LLPanelVoiceDeviceSettings::handleVisibilityChange ( BOOL new_visibility )
else
{
cleanup();
+ // when closing this window, turn of visiblity control so that
+ // next time preferences is opened we don't suspend voice
+ gSavedSettings.setBOOL("ShowDeviceSettings", FALSE);
}
}
void LLPanelVoiceDeviceSettings::draw()
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
index 823c8e7911..7d2eb98111 100644
--- a/indra/newview/llfloaterworldmap.cpp
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -56,7 +56,6 @@
#include "llregionhandle.h"
#include "llscrolllistctrl.h"
#include "llslurl.h"
-#include "lltabcontainer.h"
#include "lltextbox.h"
#include "lltracker.h"
#include "lltrans.h"
@@ -64,7 +63,9 @@
#include "llviewermenu.h"
#include "llviewerregion.h"
#include "llviewerstats.h"
+#include "llviewertexture.h"
#include "llworldmap.h"
+#include "llworldmapmessage.h"
#include "llworldmapview.h"
#include "lluictrlfactory.h"
#include "llappviewer.h"
@@ -80,6 +81,12 @@
//---------------------------------------------------------------------------
static const F32 MAP_ZOOM_TIME = 0.2f;
+// Merov: we switched from using the "world size" (which varies depending where the user went) to a fixed
+// width of 512 regions max visible at a time. This makes the zoom slider works in a consistent way across
+// sessions and doesn't prevent the user to pan the world if it was to grow a lot beyond that limit.
+// Currently (01/26/09), this value allows the whole grid to be visible in a 1024x1024 window.
+static const S32 MAX_VISIBLE_REGIONS = 512;
+
enum EPanDirection
{
PAN_UP,
@@ -190,11 +197,11 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
gFloaterWorldMap = this;
mFactoryMap["objects_mapview"] = LLCallbackMap(createWorldMapView, NULL);
- mFactoryMap["terrain_mapview"] = LLCallbackMap(createWorldMapView, NULL);
//Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this, "floater_world_map.xml", FALSE);
mCommitCallbackRegistrar.add("WMap.Location", boost::bind(&LLFloaterWorldMap::onLocationCommit, this));
mCommitCallbackRegistrar.add("WMap.AvatarCombo", boost::bind(&LLFloaterWorldMap::onAvatarComboCommit, this));
+ mCommitCallbackRegistrar.add("WMap.Landmark", boost::bind(&LLFloaterWorldMap::onLandmarkComboCommit, this));
mCommitCallbackRegistrar.add("WMap.SearchResult", boost::bind(&LLFloaterWorldMap::onCommitSearchResult, this));
mCommitCallbackRegistrar.add("WMap.CommitLocation", boost::bind(&LLFloaterWorldMap::onCommitLocation, this));
mCommitCallbackRegistrar.add("WMap.GoHome", boost::bind(&LLFloaterWorldMap::onGoHome, this));
@@ -213,17 +220,7 @@ void* LLFloaterWorldMap::createWorldMapView(void* data)
BOOL LLFloaterWorldMap::postBuild()
{
- mTabs = getChild<LLTabContainer>("maptab");
- if (!mTabs) return FALSE;
-
- mTabs->setCommitCallback(boost::bind(&LLFloaterWorldMap::onCommitBackground, this));
-
- // The following callback syncs the worlmap tabs with the images.
- // Commented out since it was crashing when LLWorldMap became a singleton.
- // We should be fine without it but override the onOpen method and put it
- // there if it turns out to be needed. -MG
- //
- //onCommitBackground();
+ mPanel = getChild<LLPanel>("objects_mapview");
LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo");
if (avatar_combo)
@@ -251,8 +248,8 @@ BOOL LLFloaterWorldMap::postBuild()
landmark_combo->setTextEntryCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) );
}
- mCurZoomVal = log(gMapScale)/log(2.f);
- childSetValue("zoom slider", gMapScale);
+ mCurZoomVal = log(LLWorldMapView::sMapScale)/log(2.f);
+ childSetValue("zoom slider", LLWorldMapView::sMapScale);
setDefaultBtn(NULL);
@@ -265,7 +262,7 @@ BOOL LLFloaterWorldMap::postBuild()
LLFloaterWorldMap::~LLFloaterWorldMap()
{
// All cleaned up by LLView destructor
- mTabs = NULL;
+ mPanel = NULL;
// Inventory deletes all observers on shutdown
mInventory = NULL;
@@ -298,7 +295,7 @@ void LLFloaterWorldMap::onOpen(const LLSD& key)
mIsClosing = FALSE;
LLWorldMapView* map_panel;
- map_panel = (LLWorldMapView*)mTabs->getCurrentPanel();
+ map_panel = (LLWorldMapView*)gFloaterWorldMap->mPanel;
map_panel->clearLastClick();
{
@@ -309,15 +306,8 @@ void LLFloaterWorldMap::onOpen(const LLSD& key)
}
map_panel->updateVisibleBlocks();
- // Reload the agent positions when we show the window
- LLWorldMap::getInstance()->eraseItems();
-
- // Reload any maps that may have changed
- LLWorldMap::getInstance()->clearSimFlags();
-
- const S32 panel_num = mTabs->getCurrentPanelIndex();
- const bool request_from_sim = true;
- LLWorldMap::getInstance()->setCurrentLayer(panel_num, request_from_sim);
+ // Reload items as they may have changed
+ LLWorldMap::getInstance()->reloadItems();
// We may already have a bounding box for the regions of the world,
// so use that to adjust the view.
@@ -351,12 +341,9 @@ void LLFloaterWorldMap::onOpen(const LLSD& key)
// static
void LLFloaterWorldMap::reloadIcons(void*)
{
- LLWorldMap::getInstance()->eraseItems();
-
- LLWorldMap::getInstance()->sendMapLayerRequest();
+ LLWorldMap::getInstance()->reloadItems();
}
-
// virtual
BOOL LLFloaterWorldMap::handleHover(S32 x, S32 y, MASK mask)
{
@@ -388,12 +375,6 @@ BOOL LLFloaterWorldMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
void LLFloaterWorldMap::reshape( S32 width, S32 height, BOOL called_from_parent )
{
LLFloater::reshape( width, height, called_from_parent );
-
- // Might have changed size of world display area
- // JC: Technically, this is correct, but it makes the slider "pop"
- // if you resize the window, then draw the slider. Just leaving it
- // the way it was when you opened the window seems better.
- // adjustZoomSliderBounds();
}
@@ -457,7 +438,7 @@ void LLFloaterWorldMap::draw()
{
F64 seconds = LLTimer::getElapsedSeconds();
double value = fmod(seconds, 2);
- value = 0.5 + 0.5*cos(value * M_PI);
+ value = 0.5 + 0.5*cos(value * F_PI);
LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0);
childSetColor("location_icon", loading_color);
}
@@ -475,7 +456,7 @@ void LLFloaterWorldMap::draw()
childSetEnabled("Teleport", (BOOL)tracking_status);
// childSetEnabled("Clear", (BOOL)tracking_status);
- childSetEnabled("Show Destination", (BOOL)tracking_status || LLWorldMap::getInstance()->mIsTrackingUnknownLocation);
+ childSetEnabled("Show Destination", (BOOL)tracking_status || LLWorldMap::getInstance()->isTracking());
childSetEnabled("copy_slurl", (mSLURL.size() > 0) );
setMouseOpaque(TRUE);
@@ -495,6 +476,18 @@ void LLFloaterWorldMap::draw()
mCurZoomVal = lerp(mCurZoomVal, (F32)childGetValue("zoom slider").asReal(), interp);
F32 map_scale = 256.f*pow(2.f, mCurZoomVal);
LLWorldMapView::setScale( map_scale );
+
+ // Enable/disable checkboxes depending on the zoom level
+ // If above threshold level (i.e. low res) -> Disable all checkboxes
+ // If under threshold level (i.e. high res) -> Enable all checkboxes
+ bool enable = LLWorldMapView::showRegionInfo();
+ childSetEnabled("people_chk", enable);
+ childSetEnabled("infohub_chk", enable);
+ childSetEnabled("telehub_chk", enable);
+ childSetEnabled("land_for_sale_chk", enable);
+ childSetEnabled("event_chk", enable);
+ childSetEnabled("event_mature_chk", enable);
+ childSetEnabled("event_adult_chk", enable);
LLFloater::draw();
}
@@ -583,14 +576,14 @@ void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )
void LLFloaterWorldMap::trackEvent(const LLItemInfo &event_info)
{
mTrackedStatus = LLTracker::TRACKING_LOCATION;
- LLTracker::trackLocation(event_info.mPosGlobal, event_info.mName, event_info.mToolTip, LLTracker::LOCATION_EVENT);
+ LLTracker::trackLocation(event_info.getGlobalPosition(), event_info.getName(), event_info.getToolTip(), LLTracker::LOCATION_EVENT);
setDefaultBtn("Teleport");
}
void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
{
mTrackedStatus = LLTracker::TRACKING_LOCATION;
- LLTracker::trackLocation(item.mPosGlobal, item.mName, item.mToolTip, LLTracker::LOCATION_ITEM);
+ LLTracker::trackLocation(item.getGlobalPosition(), item.getName(), item.getToolTip(), LLTracker::LOCATION_ITEM);
setDefaultBtn("Teleport");
}
@@ -599,29 +592,27 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
if (!sim_info)
{
- LLWorldMap::getInstance()->mIsTrackingUnknownLocation = TRUE;
- LLWorldMap::getInstance()->mInvalidLocation = FALSE;
- LLWorldMap::getInstance()->mUnknownLocation = pos_global;
+ // We haven't found a region for that point yet, leave the tracking to the world map
+ LLWorldMap::getInstance()->setTracking(pos_global);
LLTracker::stopTracking(NULL);
S32 world_x = S32(pos_global.mdV[0] / 256);
S32 world_y = S32(pos_global.mdV[1] / 256);
- LLWorldMap::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true);
+ LLWorldMapMessage::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true);
setDefaultBtn("");
return;
}
- if (sim_info->mAccess == SIM_ACCESS_DOWN)
+ if (sim_info->isDown())
{
- // Down sim. Show the blue circle of death!
- LLWorldMap::getInstance()->mIsTrackingUnknownLocation = TRUE;
- LLWorldMap::getInstance()->mUnknownLocation = pos_global;
- LLWorldMap::getInstance()->mInvalidLocation = TRUE;
+ // Down region. Show the blue circle of death!
+ // i.e. let the world map that this and tell it it's invalid
+ LLWorldMap::getInstance()->setTracking(pos_global);
+ LLWorldMap::getInstance()->setTrackingInvalid();
LLTracker::stopTracking(NULL);
setDefaultBtn("");
return;
}
- std::string sim_name;
- LLWorldMap::getInstance()->simNameFromPosGlobal( pos_global, sim_name );
+ std::string sim_name = sim_info->getName();
F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS );
F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS );
std::string full_name = llformat("%s (%d, %d, %d)",
@@ -633,9 +624,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
std::string tooltip("");
mTrackedStatus = LLTracker::TRACKING_LOCATION;
LLTracker::trackLocation(pos_global, full_name, tooltip);
- LLWorldMap::getInstance()->mIsTrackingUnknownLocation = FALSE;
- LLWorldMap::getInstance()->mIsTrackingDoubleClick = FALSE;
- LLWorldMap::getInstance()->mIsTrackingCommit = FALSE;
+ LLWorldMap::getInstance()->cancelTracking(); // The floater is taking over the tracking
setDefaultBtn("Teleport");
}
@@ -748,9 +737,9 @@ void LLFloaterWorldMap::trackURL(const std::string& region_name, S32 x_coord, S3
// pass sim name to combo box
gFloaterWorldMap->mCompletingRegionName = region_name;
- LLWorldMap::getInstance()->sendNamedRegionRequest(region_name);
+ LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name);
LLStringUtil::toLower(gFloaterWorldMap->mCompletingRegionName);
- LLWorldMap::getInstance()->mIsTrackingCommit = TRUE;
+ LLWorldMap::getInstance()->setTrackingCommit();
}
}
@@ -817,19 +806,12 @@ void LLFloaterWorldMap::buildAvatarIDList()
// Delete all but the "None" entry
S32 list_size = list->getItemCount();
- while (list_size > 1)
+ if (list_size > 1)
{
- list->selectNthItem(1);
+ list->selectItemRange(1, -1);
list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
- --list_size;
}
- LLSD default_column;
- default_column["name"] = "friend name";
- default_column["label"] = "Friend Name";
- default_column["width"] = 500;
- list->addColumn(default_column);
-
// Get all of the calling cards for avatar that are currently online
LLCollectMappableBuddies collector;
LLAvatarTracker::instance().applyFunctor(collector);
@@ -850,10 +832,7 @@ void LLFloaterWorldMap::buildAvatarIDList()
void LLFloaterWorldMap::buildLandmarkIDLists()
{
LLCtrlListInterface *list = childGetListInterface("landmark combo");
- if (!list)
- {
- return;
- }
+ if (!list) return;
// Delete all but the "None" entry
S32 list_size = list->getItemCount();
@@ -894,7 +873,6 @@ void LLFloaterWorldMap::buildLandmarkIDLists()
mLandmarkAssetIDList.put( item->getAssetUUID() );
mLandmarkItemIDList.put( item->getUUID() );
}
- list->sortByColumn(std::string("landmark name"), TRUE);
list->selectFirstItem();
}
@@ -931,7 +909,7 @@ void LLFloaterWorldMap::clearLocationSelection(BOOL clear_ui)
{
childSetValue("spin z", 0);
}
- LLWorldMap::getInstance()->mIsTrackingCommit = FALSE;
+ LLWorldMap::getInstance()->cancelTracking();
mCompletingRegionName = "";
}
@@ -967,18 +945,16 @@ void LLFloaterWorldMap::clearAvatarSelection(BOOL clear_ui)
// can see the whole world, plus a little.
void LLFloaterWorldMap::adjustZoomSliderBounds()
{
- // World size in regions
- S32 world_width_regions = LLWorldMap::getInstance()->getWorldWidth() / REGION_WIDTH_UNITS;
- S32 world_height_regions = LLWorldMap::getInstance()->getWorldHeight() / REGION_WIDTH_UNITS;
-
- // Pad the world size a little bit, so we have a nice border on
- // the edge
- world_width_regions++;
- world_height_regions++;
+ // Merov: we switched from using the "world size" (which varies depending where the user went) to a fixed
+ // width of 512 regions max visible at a time. This makes the zoom slider works in a consistent way across
+ // sessions and doesn't prevent the user to pan the world if it was to grow a lot beyond that limit.
+ // Currently (01/26/09), this value allows the whole grid to be visible in a 1024x1024 window.
+ S32 world_width_regions = MAX_VISIBLE_REGIONS;
+ S32 world_height_regions = MAX_VISIBLE_REGIONS;
// Find how much space we have to display the world
LLWorldMapView* map_panel;
- map_panel = (LLWorldMapView*)mTabs->getCurrentPanel();
+ map_panel = (LLWorldMapView*)mPanel;
LLRect view_rect = map_panel->getRect();
// View size in pixels
@@ -1191,15 +1167,15 @@ void LLFloaterWorldMap::onLocationCommit()
LLStringUtil::toLower(str);
mCompletingRegionName = str;
- LLWorldMap::getInstance()->mIsTrackingCommit = TRUE;
+ LLWorldMap::getInstance()->setTrackingCommit();
if (str.length() >= 3)
{
- LLWorldMap::getInstance()->sendNamedRegionRequest(str);
+ LLWorldMapMessage::getInstance()->sendNamedRegionRequest(str);
}
else
{
str += "#";
- LLWorldMap::getInstance()->sendNamedRegionRequest(str);
+ LLWorldMapMessage::getInstance()->sendNamedRegionRequest(str);
}
}
@@ -1207,8 +1183,8 @@ void LLFloaterWorldMap::onClearBtn()
{
mTrackedStatus = LLTracker::TRACKING_NOTHING;
LLTracker::stopTracking((void *)(intptr_t)TRUE);
- LLWorldMap::getInstance()->mIsTrackingUnknownLocation = FALSE;
- mSLURL = ""; // Clear the SLURL since it's invalid
+ LLWorldMap::getInstance()->cancelTracking();
+ mSLURL = ""; // Clear the SLURL since it's invalid
mSetToUserPosition = TRUE; // Revert back to the current user position
}
@@ -1260,9 +1236,9 @@ void LLFloaterWorldMap::centerOnTarget(BOOL animate)
pos_global = LLTracker::getTrackedPositionGlobal() - gAgent.getCameraPositionGlobal();
}
}
- else if(LLWorldMap::getInstance()->mIsTrackingUnknownLocation)
+ else if(LLWorldMap::getInstance()->isTracking())
{
- pos_global = LLWorldMap::getInstance()->mUnknownLocation - gAgent.getCameraPositionGlobal();;
+ pos_global = LLWorldMap::getInstance()->getTrackedPositionGlobal() - gAgent.getCameraPositionGlobal();;
}
else
{
@@ -1270,8 +1246,8 @@ void LLFloaterWorldMap::centerOnTarget(BOOL animate)
pos_global.clearVec();
}
- LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sPixelsPerMeter)),
- -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sPixelsPerMeter)),
+ LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)),
+ -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)),
!animate);
mWaitingForTracker = FALSE;
}
@@ -1429,13 +1405,6 @@ void LLFloaterWorldMap::flyToAvatar()
}
}
-void LLFloaterWorldMap::onCommitBackground()
-{
- // Find my index
- S32 index = mTabs->getCurrentPanelIndex();
- LLWorldMap::getInstance()->setCurrentLayer(index);
-}
-
void LLFloaterWorldMap::updateSims(bool found_null_sim)
{
if (mCompletingRegionName == "")
@@ -1452,24 +1421,23 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
S32 num_results = 0;
std::map<U64, LLSimInfo*>::const_iterator it;
- for (it = LLWorldMap::getInstance()->mSimInfoMap.begin(); it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it)
+ for (it = LLWorldMap::getInstance()->getRegionMap().begin(); it != LLWorldMap::getInstance()->getRegionMap().end(); ++it)
{
- LLSimInfo* info = (*it).second;
- std::string sim_name = info->mName;
- std::string sim_name_lower = sim_name;
+ LLSimInfo* info = it->second;
+ std::string sim_name_lower = info->getName();
LLStringUtil::toLower(sim_name_lower);
if (sim_name_lower.substr(0, name_length) == mCompletingRegionName)
{
if (sim_name_lower == mCompletingRegionName)
{
- match = sim_name;
+ match = info->getName();
}
LLSD value;
- value["id"] = sim_name;
+ value["id"] = info->getName();
value["columns"][0]["column"] = "sim_name";
- value["columns"][0]["value"] = sim_name;
+ value["columns"][0]["value"] = info->getName();
list->addElement(value);
num_results++;
}
@@ -1526,15 +1494,13 @@ void LLFloaterWorldMap::onCommitSearchResult()
LLStringUtil::toLower(sim_name);
std::map<U64, LLSimInfo*>::const_iterator it;
- for (it = LLWorldMap::getInstance()->mSimInfoMap.begin(); it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it)
+ for (it = LLWorldMap::getInstance()->getRegionMap().begin(); it != LLWorldMap::getInstance()->getRegionMap().end(); ++it)
{
- LLSimInfo* info = (*it).second;
- std::string info_sim_name = info->mName;
- LLStringUtil::toLower(info_sim_name);
+ LLSimInfo* info = it->second;
- if (sim_name == info_sim_name)
+ if (info->isName(sim_name))
{
- LLVector3d pos_global = from_region_handle( info->mHandle );
+ LLVector3d pos_global = info->getGlobalOrigin();
F64 local_x = childGetValue("spin x");
F64 local_y = childGetValue("spin y");
F64 local_z = childGetValue("spin z");
diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h
index 20a8e6d321..7feebb583d 100644
--- a/indra/newview/llfloaterworldmap.h
+++ b/indra/newview/llfloaterworldmap.h
@@ -96,7 +96,7 @@ public:
static const LLUUID& getHomeID() { return sHomeID; }
// A z_attenuation of 0.0f collapses the distance into the X-Y plane
- F32 getDistanceToDestination(const LLVector3d& pos_global, F32 z_attenuation = 0.5f) const;
+ F32 getDistanceToDestination(const LLVector3d& pos_global, F32 z_attenuation = 0.5f) const;
void clearLocationSelection(BOOL clear_ui = FALSE);
void clearAvatarSelection(BOOL clear_ui = FALSE);
@@ -121,8 +121,6 @@ protected:
void onAvatarComboPrearrange();
void onAvatarComboCommit();
- void onCommitBackground();
-
void onComboTextEntry( );
void onSearchTextEntry( LLLineEditor* ctrl );
@@ -155,10 +153,10 @@ protected:
void cacheLandmarkPosition();
-protected:
- LLTabContainer* mTabs;
+private:
+ LLPanel* mPanel; // Panel displaying the map
- // Sets gMapScale, in pixels per region
+ // Ties to LLWorldMapView::sMapScale, in pixels per region
F32 mCurZoomVal;
LLFrameTimer mZoomTimer;
diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp
index 9989a3b473..8dc7833f6a 100644
--- a/indra/newview/llinspectavatar.cpp
+++ b/indra/newview/llinspectavatar.cpp
@@ -47,6 +47,7 @@
#include "llstartup.h"
#include "llviewermenu.h"
#include "llvoiceclient.h"
+#include "llviewerobjectlist.h"
// Linden libraries
#include "llfloater.h"
@@ -113,6 +114,7 @@ private:
void onClickFindOnMap();
bool onVisibleFindOnMap();
bool onVisibleFreezeEject();
+ bool onVisibleZoomIn();
void onClickMuteVolume();
void onVolumeChange(const LLSD& data);
@@ -204,6 +206,8 @@ LLInspectAvatar::LLInspectAvatar(const LLSD& sd)
mVisibleCallbackRegistrar.add("InspectAvatar.VisibleFindOnMap", boost::bind(&LLInspectAvatar::onVisibleFindOnMap, this));
mVisibleCallbackRegistrar.add("InspectAvatar.VisibleFreezeEject",
boost::bind(&LLInspectAvatar::onVisibleFreezeEject, this));
+ mVisibleCallbackRegistrar.add("InspectAvatar.VisibleZoomIn",
+ boost::bind(&LLInspectAvatar::onVisibleZoomIn, this));
// can't make the properties request until the widgets are constructed
// as it might return immediately, so do it in postBuild.
@@ -479,6 +483,11 @@ bool LLInspectAvatar::onVisibleFreezeEject()
return enable_freeze_eject( LLSD(mAvatarID) );
}
+bool LLInspectAvatar::onVisibleZoomIn()
+{
+ return gObjectList.findObject(mAvatarID);
+}
+
void LLInspectAvatar::onClickIM()
{
LLAvatarActions::startIM(mAvatarID);
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index ef61c45d4d..90e48d22ec 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -1,5161 +1,5204 @@
-/**
- * @file llinventorybridge.cpp
- * @brief Implementation of the Inventory-Folder-View-Bridge classes.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include <utility> // for std::pair<>
-
-#include "llfloaterinventory.h"
-#include "llinventorybridge.h"
-
-#include "message.h"
-
-#include "llagent.h"
-#include "llagentwearables.h"
-#include "llcallingcard.h"
-#include "llcheckboxctrl.h" // for radio buttons
-#include "llfloaterreg.h"
-#include "llradiogroup.h"
-#include "llspinctrl.h"
-#include "lltextbox.h"
-#include "llui.h"
-
-#include "llviewercontrol.h"
-#include "llfirstuse.h"
-#include "llfoldertype.h"
-#include "llfloaterchat.h"
-#include "llfloatercustomize.h"
-#include "llfloaterproperties.h"
-#include "llfloaterworldmap.h"
-#include "llfocusmgr.h"
-#include "llfolderview.h"
-#include "llfriendcard.h"
-#include "llavataractions.h"
-#include "llgesturemgr.h"
-#include "lliconctrl.h"
-#include "llinventoryfunctions.h"
-#include "llinventorymodel.h"
-#include "llinventorypanel.h"
-#include "llinventoryclipboard.h"
-#include "lllineeditor.h"
-#include "llmenugl.h"
-#include "llpreviewanim.h"
-#include "llpreviewgesture.h"
-#include "llpreviewnotecard.h"
-#include "llpreviewscript.h"
-#include "llpreviewsound.h"
-#include "llpreviewtexture.h"
-#include "llresmgr.h"
-#include "llscrollcontainer.h"
-#include "llimview.h"
-#include "lltooldraganddrop.h"
-#include "llviewerfoldertype.h"
-#include "llviewertexturelist.h"
-#include "llviewerinventory.h"
-#include "llviewerobjectlist.h"
-#include "llviewerwindow.h"
-#include "llvoavatar.h"
-#include "llwearable.h"
-#include "llwearablelist.h"
-#include "llviewerassettype.h"
-#include "llviewermessage.h"
-#include "llviewerregion.h"
-#include "llvoavatarself.h"
-#include "lltabcontainer.h"
-#include "lluictrlfactory.h"
-#include "llselectmgr.h"
-#include "llsidetray.h"
-#include "llfloateropenobject.h"
-#include "lltrans.h"
-#include "llappearancemgr.h"
-#include "llimfloater.h"
-
-using namespace LLOldEvents;
-
-// Helpers
-// bug in busy count inc/dec right now, logic is complex... do we really need it?
-void inc_busy_count()
-{
-// gViewerWindow->getWindow()->incBusyCount();
-// check balance of these calls if this code is changed to ever actually
-// *do* something!
-}
-void dec_busy_count()
-{
-// gViewerWindow->getWindow()->decBusyCount();
-// check balance of these calls if this code is changed to ever actually
-// *do* something!
-}
-
-// Function declarations
-void wear_add_inventory_item_on_avatar(LLInventoryItem* item);
-void remove_inventory_category_from_avatar(LLInventoryCategory* category);
-void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id);
-bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*);
-bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response);
-
-std::string ICON_NAME[ICON_NAME_COUNT] =
-{
- "Inv_Texture",
- "Inv_Sound",
- "Inv_CallingCard",
- "Inv_CallingCard",
- "Inv_Landmark",
- "Inv_Landmark",
- "Inv_Script",
- "Inv_Clothing",
- "Inv_Object",
- "Inv_Object",
- "Inv_Notecard",
- "Inv_Skin",
- "Inv_Snapshot",
-
- "Inv_BodyShape",
- "Inv_Skin",
- "Inv_Hair",
- "Inv_Eye",
- "Inv_Shirt",
- "Inv_Pants",
- "Inv_Shoe",
- "Inv_Socks",
- "Inv_Jacket",
- "Inv_Gloves",
- "Inv_Undershirt",
- "Inv_Underpants",
- "Inv_Skirt",
- "Inv_Alpha",
- "Inv_Tattoo",
-
- "Inv_Animation",
- "Inv_Gesture",
-
- "inv_item_linkitem.tga",
- "inv_item_linkfolder.tga"
-};
-
-
-// +=================================================+
-// | LLInventoryPanelObserver |
-// +=================================================+
-void LLInventoryPanelObserver::changed(U32 mask)
-{
- mIP->modelChanged(mask);
-}
-
-
-// +=================================================+
-// | LLInvFVBridge |
-// +=================================================+
-
-LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
-mUUID(uuid), mInvType(LLInventoryType::IT_NONE)
-{
- mInventoryPanel = inventory->getHandle();
-}
-
-const std::string& LLInvFVBridge::getName() const
-{
- LLInventoryObject* obj = getInventoryObject();
- if(obj)
- {
- return obj->getName();
- }
- return LLStringUtil::null;
-}
-
-const std::string& LLInvFVBridge::getDisplayName() const
-{
- return getName();
-}
-
-// Folders have full perms
-PermissionMask LLInvFVBridge::getPermissionMask() const
-{
-
- return PERM_ALL;
-}
-
-// virtual
-LLFolderType::EType LLInvFVBridge::getPreferredType() const
-{
- return LLFolderType::FT_NONE;
-}
-
-
-// Folders don't have creation dates.
-time_t LLInvFVBridge::getCreationDate() const
-{
- return 0;
-}
-
-// Can be destoryed (or moved to trash)
-BOOL LLInvFVBridge::isItemRemovable()
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
- if(model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()))
- {
- return TRUE;
- }
- return FALSE;
-}
-
-// Sends an update to all link items that point to the base item.
-void LLInvFVBridge::renameLinkedItems(const LLUUID &item_id, const std::string& new_name)
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
-
- LLInventoryItem* itemp = model->getItem(mUUID);
- if (!itemp) return;
-
- if (itemp->getIsLinkType())
- {
- return;
- }
-
- LLInventoryModel::item_array_t item_array = model->collectLinkedItems(item_id);
- for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
- iter != item_array.end();
- iter++)
- {
- LLViewerInventoryItem *linked_item = (*iter);
- if (linked_item->getUUID() == item_id) continue;
-
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(linked_item);
- new_item->rename(new_name);
- new_item->updateServer(FALSE);
- model->updateItem(new_item);
- // model->addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
- }
- model->notifyObservers();
-}
-
-// Can be moved to another folder
-BOOL LLInvFVBridge::isItemMovable() const
-{
- return TRUE;
-}
-
-/*virtual*/
-/**
- * @brief Adds this item into clipboard storage
- */
-void LLInvFVBridge::cutToClipboard()
-{
- if(isItemMovable())
- {
- LLInventoryClipboard::instance().cut(mUUID);
- }
-}
-// *TODO: make sure this does the right thing
-void LLInvFVBridge::showProperties()
-{
- LLSD key;
- key["id"] = mUUID;
- LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
-
- // LLFloaterReg::showInstance("properties", mUUID);
-}
-
-void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch)
-{
- // Deactivate gestures when moving them into Trash
- LLInvFVBridge* bridge;
- LLInventoryModel* model = getInventoryModel();
- LLViewerInventoryItem* item = NULL;
- LLViewerInventoryCategory* cat = NULL;
- LLInventoryModel::cat_array_t descendent_categories;
- LLInventoryModel::item_array_t descendent_items;
- S32 count = batch.count();
- S32 i,j;
- for(i = 0; i < count; ++i)
- {
- bridge = (LLInvFVBridge*)(batch.get(i));
- if(!bridge || !bridge->isItemRemovable()) continue;
- item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
- if (item)
- {
- if(LLAssetType::AT_GESTURE == item->getType())
- {
- LLGestureManager::instance().deactivateGesture(item->getUUID());
- }
- }
- }
- for(i = 0; i < count; ++i)
- {
- bridge = (LLInvFVBridge*)(batch.get(i));
- if(!bridge || !bridge->isItemRemovable()) continue;
- cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
- if (cat)
- {
- gInventory.collectDescendents( cat->getUUID(), descendent_categories, descendent_items, FALSE );
- for (j=0; j<descendent_items.count(); j++)
- {
- if(LLAssetType::AT_GESTURE == descendent_items[j]->getType())
- {
- LLGestureManager::instance().deactivateGesture(descendent_items[j]->getUUID());
- }
- }
- }
- }
- removeBatchNoCheck(batch);
-}
-
-void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch)
-{
- // this method moves a bunch of items and folders to the trash. As
- // per design guidelines for the inventory model, the message is
- // built and the accounting is performed first. After all of that,
- // we call LLInventoryModel::moveObject() to move everything
- // around.
- LLInvFVBridge* bridge;
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- LLMessageSystem* msg = gMessageSystem;
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- LLViewerInventoryItem* item = NULL;
- LLViewerInventoryCategory* cat = NULL;
- std::vector<LLUUID> move_ids;
- LLInventoryModel::update_map_t update;
- bool start_new_message = true;
- S32 count = batch.count();
- S32 i;
- for(i = 0; i < count; ++i)
- {
- bridge = (LLInvFVBridge*)(batch.get(i));
- if(!bridge || !bridge->isItemRemovable()) continue;
- item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
- if(item)
- {
- if(item->getParentUUID() == trash_id) continue;
- move_ids.push_back(item->getUUID());
- LLPreview::hide(item->getUUID());
- --update[item->getParentUUID()];
- ++update[trash_id];
- if(start_new_message)
- {
- start_new_message = false;
- msg->newMessageFast(_PREHASH_MoveInventoryItem);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->addBOOLFast(_PREHASH_Stamp, TRUE);
- }
- msg->nextBlockFast(_PREHASH_InventoryData);
- msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
- msg->addUUIDFast(_PREHASH_FolderID, trash_id);
- msg->addString("NewName", NULL);
- if(msg->isSendFullFast(_PREHASH_InventoryData))
- {
- start_new_message = true;
- gAgent.sendReliableMessage();
- gInventory.accountForUpdate(update);
- update.clear();
- }
- }
- }
- if(!start_new_message)
- {
- start_new_message = true;
- gAgent.sendReliableMessage();
- gInventory.accountForUpdate(update);
- update.clear();
- }
- for(i = 0; i < count; ++i)
- {
- bridge = (LLInvFVBridge*)(batch.get(i));
- if(!bridge || !bridge->isItemRemovable()) continue;
- cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
- if(cat)
- {
- if(cat->getParentUUID() == trash_id) continue;
- move_ids.push_back(cat->getUUID());
- --update[cat->getParentUUID()];
- ++update[trash_id];
- if(start_new_message)
- {
- start_new_message = false;
- msg->newMessageFast(_PREHASH_MoveInventoryFolder);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->addBOOL("Stamp", TRUE);
- }
- msg->nextBlockFast(_PREHASH_InventoryData);
- msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID());
- msg->addUUIDFast(_PREHASH_ParentID, trash_id);
- if(msg->isSendFullFast(_PREHASH_InventoryData))
- {
- start_new_message = true;
- gAgent.sendReliableMessage();
- gInventory.accountForUpdate(update);
- update.clear();
- }
- }
- }
- if(!start_new_message)
- {
- gAgent.sendReliableMessage();
- gInventory.accountForUpdate(update);
- }
-
- // move everything.
- std::vector<LLUUID>::iterator it = move_ids.begin();
- std::vector<LLUUID>::iterator end = move_ids.end();
- for(; it != end; ++it)
- {
- gInventory.moveObject((*it), trash_id);
- }
-
- // notify inventory observers.
- model->notifyObservers();
-}
-
-BOOL LLInvFVBridge::isClipboardPasteable() const
-{
- if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
- {
- return FALSE;
- }
- LLInventoryModel* model = getInventoryModel();
- if (!model)
- {
- return FALSE;
- }
-
- const LLUUID &agent_id = gAgent.getID();
-
- LLDynamicArray<LLUUID> objects;
- LLInventoryClipboard::instance().retrieve(objects);
- S32 count = objects.count();
- for(S32 i = 0; i < count; i++)
- {
- const LLUUID &item_id = objects.get(i);
-
- // Can't paste folders
- const LLInventoryCategory *cat = model->getCategory(item_id);
- if (cat)
- {
- return FALSE;
- }
-
- const LLInventoryItem *item = model->getItem(item_id);
- if (item)
- {
- if (!item->getPermissions().allowCopyBy(agent_id))
- {
- return FALSE;
- }
- }
- }
- return TRUE;
-}
-
-BOOL LLInvFVBridge::isClipboardPasteableAsLink() const
-{
- if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
- {
- return FALSE;
- }
- const LLInventoryModel* model = getInventoryModel();
- if (!model)
- {
- return FALSE;
- }
-
- LLDynamicArray<LLUUID> objects;
- LLInventoryClipboard::instance().retrieve(objects);
- S32 count = objects.count();
- for(S32 i = 0; i < count; i++)
- {
- const LLInventoryItem *item = model->getItem(objects.get(i));
- if (item)
- {
- if (!LLAssetType::lookupCanLink(item->getActualType()))
- {
- return FALSE;
- }
- }
- const LLViewerInventoryCategory *cat = model->getCategory(objects.get(i));
- if (cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
- {
- return FALSE;
- }
- }
- return TRUE;
-}
-
-void hide_context_entries(LLMenuGL& menu,
- const std::vector<std::string> &entries_to_show,
- const std::vector<std::string> &disabled_entries)
-{
- const LLView::child_list_t *list = menu.getChildList();
-
- LLView::child_list_t::const_iterator itor;
- for (itor = list->begin(); itor != list->end(); ++itor)
- {
- std::string name = (*itor)->getName();
-
- // descend into split menus:
- LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(*itor);
- if ((name == "More") && branchp)
- {
- hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries);
- }
-
-
- bool found = false;
- std::vector<std::string>::const_iterator itor2;
- for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2)
- {
- if (*itor2 == name)
- {
- found = true;
- }
- }
- if (!found)
- {
- (*itor)->setVisible(FALSE);
- }
- else
- {
- for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2)
- {
- if (*itor2 == name)
- {
- (*itor)->setEnabled(FALSE);
- }
- }
- }
- }
-}
-
-// Helper for commonly-used entries
-void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
- std::vector<std::string> &items,
- std::vector<std::string> &disabled_items, U32 flags)
-{
- items.push_back(std::string("Rename"));
- if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Rename"));
- }
-
- if (show_asset_id)
- {
- items.push_back(std::string("Copy Asset UUID"));
- if ( (! ( isItemPermissive() || gAgent.isGodlike() ) )
- || (flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Copy Asset UUID"));
- }
- }
-
- items.push_back(std::string("Copy Separator"));
-
- items.push_back(std::string("Copy"));
- if (!isItemCopyable())
- {
- disabled_items.push_back(std::string("Copy"));
- }
-
- items.push_back(std::string("Paste"));
- if (!isClipboardPasteable() || (flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Paste"));
- }
-
- items.push_back(std::string("Paste As Link"));
- if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Paste As Link"));
- }
- items.push_back(std::string("Paste Separator"));
-
- items.push_back(std::string("Delete"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Delete"));
- }
-
- // If multiple items are selected, disable properties (if it exists).
- if ((flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Properties"));
- }
-}
-
-void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- lldebugs << "LLInvFVBridge::buildContextMenu()" << llendl;
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
- if(isInTrash())
- {
- items.push_back(std::string("PurgeItem"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("PurgeItem"));
- }
- items.push_back(std::string("RestoreItem"));
- }
- else
- {
- items.push_back(std::string("Open"));
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
- }
- hide_context_entries(menu, items, disabled_items);
-}
-
-// *TODO: remove this
-BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const
-{
- BOOL rv = FALSE;
-
- const LLInventoryObject* obj = getInventoryObject();
-
- if(obj)
- {
- *type = LLViewerAssetType::lookupDragAndDropType(obj->getActualType());
- if(*type == DAD_NONE)
- {
- return FALSE;
- }
-
- *id = obj->getUUID();
- //object_ids.put(obj->getUUID());
-
- if (*type == DAD_CATEGORY)
- {
- gInventory.startBackgroundFetch(obj->getUUID());
- }
-
- rv = TRUE;
- }
-
- return rv;
-}
-
-LLInventoryObject* LLInvFVBridge::getInventoryObject() const
-{
- LLInventoryObject* obj = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- obj = (LLInventoryObject*)model->getObject(mUUID);
- }
- return obj;
-}
-
-LLInventoryModel* LLInvFVBridge::getInventoryModel() const
-{
- LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
- return panel ? panel->getModel() : NULL;
-}
-
-BOOL LLInvFVBridge::isInTrash() const
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- return model->isObjectDescendentOf(mUUID, trash_id);
-}
-
-BOOL LLInvFVBridge::isLinkedObjectInTrash() const
-{
- if (isInTrash()) return TRUE;
-
- const LLInventoryObject *obj = getInventoryObject();
- if (obj && obj->getIsLinkType())
- {
- LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id);
- }
- return FALSE;
-}
-
-BOOL LLInvFVBridge::isAgentInventory() const
-{
- const LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
- if(gInventory.getRootFolderID() == mUUID) return TRUE;
- return model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID());
-}
-
-BOOL LLInvFVBridge::isCOFFolder() const
-{
- const LLInventoryModel* model = getInventoryModel();
- if(!model) return TRUE;
- const LLUUID cof_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
- if (mUUID == cof_id || model->isObjectDescendentOf(mUUID, cof_id))
- {
- return TRUE;
- }
- return FALSE;
-}
-
-BOOL LLInvFVBridge::isItemPermissive() const
-{
- return FALSE;
-}
-
-// static
-void LLInvFVBridge::changeItemParent(LLInventoryModel* model,
- LLViewerInventoryItem* item,
- const LLUUID& new_parent,
- BOOL restamp)
-{
- if(item->getParentUUID() != new_parent)
- {
- LLInventoryModel::update_list_t update;
- LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
- update.push_back(old_folder);
- LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
- update.push_back(new_folder);
- gInventory.accountForUpdate(update);
-
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
- new_item->setParent(new_parent);
- new_item->updateParentOnServer(restamp);
- model->updateItem(new_item);
- model->notifyObservers();
- }
-}
-
-// static
-void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model,
- LLViewerInventoryCategory* cat,
- const LLUUID& new_parent,
- BOOL restamp)
-{
- if(cat->getParentUUID() != new_parent)
- {
- LLInventoryModel::update_list_t update;
- LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1);
- update.push_back(old_folder);
- LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
- update.push_back(new_folder);
- gInventory.accountForUpdate(update);
-
- LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
- new_cat->setParent(new_parent);
- new_cat->updateParentOnServer(restamp);
- model->updateCategory(new_cat);
- model->notifyObservers();
- }
-}
-
-
-const std::string safe_inv_type_lookup(LLInventoryType::EType inv_type)
-{
- const std::string rv= LLInventoryType::lookup(inv_type);
- if(rv.empty())
- {
- return std::string("<invalid>");
- }
- return rv;
-}
-
-LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
- LLAssetType::EType actual_asset_type,
- LLInventoryType::EType inv_type,
- LLInventoryPanel* inventory,
- const LLUUID& uuid,
- U32 flags)
-{
- LLInvFVBridge* new_listener = NULL;
- switch(asset_type)
- {
- case LLAssetType::AT_TEXTURE:
- if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLTextureBridge(inventory, uuid, inv_type);
- break;
-
- case LLAssetType::AT_SOUND:
- if(!(inv_type == LLInventoryType::IT_SOUND))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLSoundBridge(inventory, uuid);
- break;
-
- case LLAssetType::AT_LANDMARK:
- if(!(inv_type == LLInventoryType::IT_LANDMARK))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLLandmarkBridge(inventory, uuid, flags);
- break;
-
- case LLAssetType::AT_CALLINGCARD:
- if(!(inv_type == LLInventoryType::IT_CALLINGCARD))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLCallingCardBridge(inventory, uuid);
- break;
-
- case LLAssetType::AT_SCRIPT:
- if(!(inv_type == LLInventoryType::IT_LSL))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLScriptBridge(inventory, uuid);
- break;
-
- case LLAssetType::AT_OBJECT:
- if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags);
- break;
-
- case LLAssetType::AT_NOTECARD:
- if(!(inv_type == LLInventoryType::IT_NOTECARD))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLNotecardBridge(inventory, uuid);
- break;
-
- case LLAssetType::AT_ANIMATION:
- if(!(inv_type == LLInventoryType::IT_ANIMATION))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLAnimationBridge(inventory, uuid);
- break;
-
- case LLAssetType::AT_GESTURE:
- if(!(inv_type == LLInventoryType::IT_GESTURE))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLGestureBridge(inventory, uuid);
- break;
-
- case LLAssetType::AT_LSL_TEXT:
- if(!(inv_type == LLInventoryType::IT_LSL))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLLSLTextBridge(inventory, uuid);
- break;
-
- case LLAssetType::AT_CLOTHING:
- case LLAssetType::AT_BODYPART:
- if(!(inv_type == LLInventoryType::IT_WEARABLE))
- {
- llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
- }
- new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags);
- break;
- case LLAssetType::AT_CATEGORY:
- case LLAssetType::AT_ROOT_CATEGORY:
- if (actual_asset_type == LLAssetType::AT_LINK_FOLDER)
- {
- // Create a link folder handler instead.
- new_listener = new LLLinkFolderBridge(inventory, uuid);
- break;
- }
- new_listener = new LLFolderBridge(inventory, uuid);
- break;
- case LLAssetType::AT_LINK:
- // Only should happen for broken links.
- new_listener = new LLLinkItemBridge(inventory, uuid);
- break;
- case LLAssetType::AT_LINK_FOLDER:
- // Only should happen for broken links.
- new_listener = new LLLinkItemBridge(inventory, uuid);
- break;
- default:
- llinfos << "Unhandled asset type (llassetstorage.h): "
- << (S32)asset_type << llendl;
- break;
- }
-
- if (new_listener)
- {
- new_listener->mInvType = inv_type;
- }
-
- return new_listener;
-}
-
-void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid)
-{
- LLInventoryCategory* cat = model->getCategory(uuid);
- if (cat)
- {
- model->purgeDescendentsOf(uuid);
- model->notifyObservers();
- }
- LLInventoryObject* obj = model->getObject(uuid);
- if (obj)
- {
- model->purgeObject(uuid);
- model->notifyObservers();
- }
-}
-
-// +=================================================+
-// | InventoryFVBridgeBuilder |
-// +=================================================+
-LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset_type,
- LLAssetType::EType actual_asset_type,
- LLInventoryType::EType inv_type,
- LLInventoryPanel* inventory,
- const LLUUID& uuid,
- U32 flags /* = 0x00 */) const
-{
- return LLInvFVBridge::createBridge(asset_type,
- actual_asset_type,
- inv_type,
- inventory,
- uuid,
- flags);
-}
-
-// +=================================================+
-// | LLItemBridge |
-// +=================================================+
-
-void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
-{
- if ("goto" == action)
- {
- gotoItem(folder);
- }
- if ("open" == action)
- {
- openItem();
- return;
- }
- else if ("properties" == action)
- {
- showProperties();
- return;
- }
- else if ("purge" == action)
- {
- purgeItem(model, mUUID);
- return;
- }
- else if ("restoreToWorld" == action)
- {
- restoreToWorld();
- return;
- }
- else if ("restore" == action)
- {
- restoreItem();
- return;
- }
- else if ("copy_uuid" == action)
- {
- // Single item only
- LLInventoryItem* item = model->getItem(mUUID);
- if(!item) return;
- LLUUID asset_id = item->getAssetUUID();
- std::string buffer;
- asset_id.toString(buffer);
-
- gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer));
- return;
- }
- else if ("copy" == action)
- {
- copyToClipboard();
- return;
- }
- else if ("paste" == action)
- {
- // Single item only
- LLInventoryItem* itemp = model->getItem(mUUID);
- if (!itemp) return;
-
- LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID());
- if (!folder_view_itemp) return;
-
- folder_view_itemp->getListener()->pasteFromClipboard();
- return;
- }
- else if ("paste_link" == action)
- {
- // Single item only
- LLInventoryItem* itemp = model->getItem(mUUID);
- if (!itemp) return;
-
- LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID());
- if (!folder_view_itemp) return;
-
- folder_view_itemp->getListener()->pasteLinkFromClipboard();
- return;
- }
-}
-
-void LLItemBridge::selectItem()
-{
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
- if(item && !item->isComplete())
- {
- item->fetchFromServer();
- }
-}
-
-void LLItemBridge::restoreItem()
-{
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
- if(item)
- {
- LLInventoryModel* model = getInventoryModel();
- const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(item->getType()));
- // do not restamp on restore.
- LLInvFVBridge::changeItemParent(model, item, new_parent, FALSE);
- }
-}
-
-void LLItemBridge::restoreToWorld()
-{
- LLViewerInventoryItem* itemp = (LLViewerInventoryItem*)getItem();
- if (itemp)
- {
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessage("RezRestoreToWorld");
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-
- msg->nextBlockFast(_PREHASH_InventoryData);
- itemp->packMessage(msg);
- msg->sendReliable(gAgent.getRegion()->getHost());
- }
-
- //Similar functionality to the drag and drop rez logic
- BOOL remove_from_inventory = FALSE;
-
- //remove local inventory copy, sim will deal with permissions and removing the item
- //from the actual inventory if its a no-copy etc
- if(!itemp->getPermissions().allowCopyBy(gAgent.getID()))
- {
- remove_from_inventory = TRUE;
- }
-
- // Check if it's in the trash. (again similar to the normal rez logic)
- const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
- if(gInventory.isObjectDescendentOf(itemp->getUUID(), trash_id))
- {
- remove_from_inventory = TRUE;
- }
-
- if(remove_from_inventory)
- {
- gInventory.deleteObject(itemp->getUUID());
- gInventory.notifyObservers();
- }
-}
-
-void LLItemBridge::gotoItem(LLFolderView *folder)
-{
- LLInventoryObject *obj = getInventoryObject();
- if (obj && obj->getIsLinkType())
- {
- LLInventoryPanel* active_panel = LLFloaterInventory::getActiveInventory()->getPanel();
- if (active_panel)
- {
- active_panel->setSelection(obj->getLinkedUUID(), TAKE_FOCUS_NO);
- }
- }
-}
-
-LLUIImagePtr LLItemBridge::getIcon() const
-{
- return LLUI::getUIImage(ICON_NAME[OBJECT_ICON_NAME]);
-}
-
-PermissionMask LLItemBridge::getPermissionMask() const
-{
- LLViewerInventoryItem* item = getItem();
- PermissionMask perm_mask = 0;
- if(item)
- {
- BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
- BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
- BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
- gAgent.getID());
-
- if (copy) perm_mask |= PERM_COPY;
- if (mod) perm_mask |= PERM_MODIFY;
- if (xfer) perm_mask |= PERM_TRANSFER;
-
- }
- return perm_mask;
-}
-
-const std::string& LLItemBridge::getDisplayName() const
-{
- if(mDisplayName.empty())
- {
- buildDisplayName(getItem(), mDisplayName);
- }
- return mDisplayName;
-}
-
-void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name)
-{
- if(item)
- {
- name.assign(item->getName());
- }
- else
- {
- name.assign(LLStringUtil::null);
- }
-}
-
-LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const
-{
- U8 font = LLFontGL::NORMAL;
-
- if( gAgentWearables.isWearingItem( mUUID ) )
- {
- // llinfos << "BOLD" << llendl;
- font |= LLFontGL::BOLD;
- }
-
- const LLViewerInventoryItem* item = getItem();
- if (item && item->getIsLinkType())
- {
- font |= LLFontGL::ITALIC;
- }
- return (LLFontGL::StyleFlags)font;
-}
-
-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 LINK = LLTrans::getString("link");
- static std::string BROKEN_LINK = LLTrans::getString("broken_link");
- std::string suffix;
- LLInventoryItem* item = getItem();
- if(item)
- {
- // it's a bit confusing to put nocopy/nomod/etc on calling cards.
- if(LLAssetType::AT_CALLINGCARD != item->getType()
- && item->getPermissions().getOwner() == gAgent.getID())
- {
- BOOL broken_link = LLAssetType::lookupIsLinkType(item->getType());
- if (broken_link) return BROKEN_LINK;
-
- BOOL link = item->getIsLinkType();
- if (link) return LINK;
-
- BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
- if (!copy)
- {
- suffix += NO_COPY;
- }
- BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
- if (!mod)
- {
- suffix += NO_MOD;
- }
- BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
- gAgent.getID());
- if (!xfer)
- {
- suffix += NO_XFER;
- }
- }
- }
- return suffix;
-}
-
-time_t LLItemBridge::getCreationDate() const
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- return item->getCreationDate();
- }
- return 0;
-}
-
-
-BOOL LLItemBridge::isItemRenameable() const
-{
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- // (For now) Don't allow calling card rename since that may confuse users as to
- // what the calling card points to.
- if (item->getInventoryType() == LLInventoryType::IT_CALLINGCARD)
- {
- return FALSE;
- }
- return (item->getPermissions().allowModifyBy(gAgent.getID()));
- }
- return FALSE;
-}
-
-BOOL LLItemBridge::renameItem(const std::string& new_name)
-{
- if(!isItemRenameable())
- return FALSE;
- LLPreview::dirty(mUUID);
- LLInventoryModel* model = getInventoryModel();
- if(!model)
- return FALSE;
- LLViewerInventoryItem* item = getItem();
- if(item && (item->getName() != new_name))
- {
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
- new_item->rename(new_name);
- buildDisplayName(new_item, mDisplayName);
- new_item->updateServer(FALSE);
- model->updateItem(new_item);
-
- model->notifyObservers();
- }
- // return FALSE because we either notified observers (& therefore
- // rebuilt) or we didn't update.
- return FALSE;
-}
-
-
-BOOL LLItemBridge::removeItem()
-{
- if(!isItemRemovable())
- {
- return FALSE;
- }
- // move it to the trash
- LLPreview::hide(mUUID, TRUE);
- LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- LLViewerInventoryItem* item = getItem();
-
- // if item is not already in trash
- if(item && !model->isObjectDescendentOf(mUUID, trash_id))
- {
- // move to trash, and restamp
- LLInvFVBridge::changeItemParent(model, item, trash_id, TRUE);
- // delete was successful
- return TRUE;
- }
- else
- {
- // tried to delete already item in trash (should purge?)
- return FALSE;
- }
-}
-
-BOOL LLItemBridge::isItemCopyable() const
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- // can't copy worn objects. DEV-15183
- LLVOAvatarSelf *avatarp = gAgent.getAvatarObject();
- if( !avatarp )
- {
- return FALSE;
- }
-
- if(avatarp->isWearingAttachment(mUUID))
- {
- return FALSE;
- }
-
- // All items can be copied, not all can be pasted.
- // The only time an item can't be copied is if it's a link
- // return (item->getPermissions().allowCopyBy(gAgent.getID()));
- if (item->getIsLinkType())
- {
- return FALSE;
- }
- return TRUE;
- }
- return FALSE;
-}
-BOOL LLItemBridge::copyToClipboard() const
-{
- if(isItemCopyable())
- {
- LLInventoryClipboard::instance().add(mUUID);
- return TRUE;
- }
- return FALSE;
-}
-
-LLViewerInventoryItem* LLItemBridge::getItem() const
-{
- LLViewerInventoryItem* item = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- item = (LLViewerInventoryItem*)model->getItem(mUUID);
- }
- return item;
-}
-
-BOOL LLItemBridge::isItemPermissive() const
-{
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- U32 mask = item->getPermissions().getMaskBase();
- if((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
- {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-// +=================================================+
-// | LLFolderBridge |
-// +=================================================+
-
-LLFolderBridge* LLFolderBridge::sSelf=NULL;
-
-// Can be moved to another folder
-BOOL LLFolderBridge::isItemMovable() const
-{
- LLInventoryObject* obj = getInventoryObject();
- if(obj)
- {
- return (!LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType()));
- }
- return FALSE;
-}
-
-void LLFolderBridge::selectItem()
-{
-}
-
-
-// Can be destroyed (or moved to trash)
-BOOL LLFolderBridge::isItemRemovable()
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model)
- {
- return FALSE;
- }
-
- if(!model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()))
- {
- return FALSE;
- }
-
- LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
- if( !avatar )
- {
- return FALSE;
- }
-
- LLInventoryCategory* category = model->getCategory(mUUID);
- if( !category )
- {
- return FALSE;
- }
-
- if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
- {
- return FALSE;
- }
-
- LLInventoryModel::cat_array_t descendent_categories;
- LLInventoryModel::item_array_t descendent_items;
- gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
-
- S32 i;
- for( i = 0; i < descendent_categories.count(); i++ )
- {
- LLInventoryCategory* category = descendent_categories[i];
- if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
- {
- return FALSE;
- }
- }
-
- for( i = 0; i < descendent_items.count(); i++ )
- {
- LLInventoryItem* item = descendent_items[i];
- if( (item->getType() == LLAssetType::AT_CLOTHING) ||
- (item->getType() == LLAssetType::AT_BODYPART) )
- {
- if(gAgentWearables.isWearingItem(item->getUUID()))
- {
- return FALSE;
- }
- }
- else
- if( item->getType() == LLAssetType::AT_OBJECT )
- {
- if(avatar->isWearingAttachment(item->getUUID()))
- {
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
-BOOL LLFolderBridge::isUpToDate() const
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
- LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
- if( !category )
- {
- return FALSE;
- }
-
- return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN;
-}
-
-BOOL LLFolderBridge::isItemCopyable() const
-{
- return TRUE;
-}
-
-BOOL LLFolderBridge::copyToClipboard() const
-{
- if(isItemCopyable())
- {
- LLInventoryClipboard::instance().add(mUUID);
- return TRUE;
- }
- return FALSE;
-}
-
-BOOL LLFolderBridge::isClipboardPasteable() const
-{
- if ( ! LLInvFVBridge::isClipboardPasteable() )
- return FALSE;
-
- // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599
- if ( LLFriendCardsManager::instance().isCategoryInFriendFolder( getCategory() ) )
- {
- LLInventoryModel* model = getInventoryModel();
- if ( !model )
- {
- return FALSE;
- }
-
- LLDynamicArray<LLUUID> objects;
- LLInventoryClipboard::instance().retrieve(objects);
- const LLViewerInventoryCategory *current_cat = getCategory();
-
- // Search for the direct descendent of current Friends subfolder among all pasted items,
- // and return false if is found.
- for(S32 i = objects.count() - 1; i >= 0; --i)
- {
- const LLUUID &obj_id = objects.get(i);
- if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) )
- {
- return FALSE;
- }
- }
-
- }
- return TRUE;
-}
-
-BOOL LLFolderBridge::isClipboardPasteableAsLink() const
-{
- // Check normal paste-as-link permissions
- if (!LLInvFVBridge::isClipboardPasteableAsLink())
- {
- return FALSE;
- }
-
- const LLInventoryModel* model = getInventoryModel();
- if (!model)
- {
- return FALSE;
- }
-
- const LLViewerInventoryCategory *current_cat = getCategory();
- if (current_cat)
- {
- const BOOL is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder( current_cat );
- const LLUUID &current_cat_id = current_cat->getUUID();
- LLDynamicArray<LLUUID> objects;
- LLInventoryClipboard::instance().retrieve(objects);
- S32 count = objects.count();
- for(S32 i = 0; i < count; i++)
- {
- const LLUUID &obj_id = objects.get(i);
- const LLInventoryCategory *cat = model->getCategory(obj_id);
- if (cat)
- {
- const LLUUID &cat_id = cat->getUUID();
- // Don't allow recursive pasting
- if ((cat_id == current_cat_id) ||
- model->isObjectDescendentOf(current_cat_id, cat_id))
- {
- return FALSE;
- }
- }
- // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599
- if ( is_in_friend_folder )
- {
- // If object is direct descendent of current Friends subfolder than return false.
- // Note: We can't use 'const LLInventoryCategory *cat', because it may be null
- // in case type of obj_id is LLInventoryItem.
- if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) )
- {
- return FALSE;
- }
- }
- }
- }
- return TRUE;
-
-}
-
-BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
- BOOL drop)
-{
- // This should never happen, but if an inventory item is incorrectly parented,
- // the UI will get confused and pass in a NULL.
- if(!inv_cat) return FALSE;
-
- LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
-
- LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
- if(!avatar) return FALSE;
-
- // cannot drag categories into library
- if(!isAgentInventory())
- {
- return FALSE;
- }
-
- // check to make sure source is agent inventory, and is represented there.
- LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
- BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL)
- && (LLToolDragAndDrop::SOURCE_AGENT == source);
-
- BOOL accept = FALSE;
- S32 i;
- LLInventoryModel::cat_array_t descendent_categories;
- LLInventoryModel::item_array_t descendent_items;
- if(is_agent_inventory)
- {
- const LLUUID& cat_id = inv_cat->getUUID();
-
- // Is the destination the trash?
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- BOOL move_is_into_trash = (mUUID == trash_id)
- || model->isObjectDescendentOf(mUUID, trash_id);
- BOOL is_movable = (!LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType()));
- const LLUUID current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
- BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
- BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
- if (move_is_into_current_outfit || move_is_into_outfit)
- {
- // BAP - restrictions?
- is_movable = true;
- }
-
- if (mUUID == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE))
- {
- is_movable = FALSE; // It's generally movable but not into Favorites folder. EXT-1604
- }
-
- if( is_movable )
- {
- gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE );
-
- for( i = 0; i < descendent_categories.count(); i++ )
- {
- LLInventoryCategory* category = descendent_categories[i];
- if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
- {
- // ...can't move "special folders" like Textures
- is_movable = FALSE;
- break;
- }
- }
-
- if( is_movable )
- {
- if( move_is_into_trash )
- {
- for( i = 0; i < descendent_items.count(); i++ )
- {
- LLInventoryItem* item = descendent_items[i];
- if( (item->getType() == LLAssetType::AT_CLOTHING) ||
- (item->getType() == LLAssetType::AT_BODYPART) )
- {
- if( gAgentWearables.isWearingItem( item->getUUID() ) )
- {
- is_movable = FALSE; // It's generally movable, but not into the trash!
- break;
- }
- }
- else
- if( item->getType() == LLAssetType::AT_OBJECT )
- {
- if( avatar->isWearingAttachment( item->getUUID() ) )
- {
- is_movable = FALSE; // It's generally movable, but not into the trash!
- break;
- }
- }
- }
- }
- }
- }
-
-
- accept = is_movable
- && (mUUID != cat_id) // Can't move a folder into itself
- && (mUUID != inv_cat->getParentUUID()) // Avoid moves that would change nothing
- && !(model->isObjectDescendentOf(mUUID, cat_id)); // Avoid circularity
- if(accept && drop)
- {
- // Look for any gestures and deactivate them
- if (move_is_into_trash)
- {
- for (i = 0; i < descendent_items.count(); i++)
- {
- LLInventoryItem* item = descendent_items[i];
- if (item->getType() == LLAssetType::AT_GESTURE
- && LLGestureManager::instance().isGestureActive(item->getUUID()))
- {
- LLGestureManager::instance().deactivateGesture(item->getUUID());
- }
- }
- }
- // if target is an outfit or current outfit folder we use link
- if (move_is_into_current_outfit || move_is_into_outfit)
- {
-#if SUPPORT_ENSEMBLES
- // BAP - should skip if dup.
- if (move_is_into_current_outfit)
- {
- LLAppearanceManager::wearEnsemble(inv_cat);
- }
- else
- {
- LLPointer<LLInventoryCallback> cb = NULL;
- link_inventory_item(
- gAgent.getID(),
- inv_cat->getUUID(),
- mUUID,
- inv_cat->getName(),
- LLAssetType::AT_LINK_FOLDER,
- cb);
- }
-#endif
- }
- else
- {
-
- // Reparent the folder and restamp children if it's moving
- // into trash.
- LLInvFVBridge::changeCategoryParent(
- model,
- (LLViewerInventoryCategory*)inv_cat,
- mUUID,
- move_is_into_trash);
- }
- }
- }
- else if(LLToolDragAndDrop::SOURCE_WORLD == source)
- {
- // content category has same ID as object itself
- LLUUID object_id = inv_cat->getUUID();
- LLUUID category_id = mUUID;
- accept = move_inv_category_world_to_agent(object_id, category_id, drop);
- }
- return accept;
-}
-
-void warn_move_inventory(LLViewerObject* object, LLMoveInv* move_inv)
-{
- const char* dialog = NULL;
- if (object->flagScripted())
- {
- dialog = "MoveInventoryFromScriptedObject";
- }
- else
- {
- dialog = "MoveInventoryFromObject";
- }
- LLNotifications::instance().add(dialog, LLSD(), LLSD(), boost::bind(move_task_inventory_callback, _1, _2, move_inv));
-}
-
-// Move/copy all inventory items from the Contents folder of an in-world
-// object to the agent's inventory, inside a given category.
-BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
- const LLUUID& category_id,
- BOOL drop,
- void (*callback)(S32, void*),
- void* user_data)
-{
- // Make sure the object exists. If we allowed dragging from
- // anonymous objects, it would be possible to bypass
- // permissions.
- // content category has same ID as object itself
- LLViewerObject* object = gObjectList.findObject(object_id);
- if(!object)
- {
- llinfos << "Object not found for drop." << llendl;
- return FALSE;
- }
-
- // this folder is coming from an object, as there is only one folder in an object, the root,
- // we need to collect the entire contents and handle them as a group
- InventoryObjectList inventory_objects;
- object->getInventoryContents(inventory_objects);
-
- if (inventory_objects.empty())
- {
- llinfos << "Object contents not found for drop." << llendl;
- return FALSE;
- }
-
- BOOL accept = TRUE;
- BOOL is_move = FALSE;
-
- // coming from a task. Need to figure out if the person can
- // move/copy this item.
- InventoryObjectList::iterator it = inventory_objects.begin();
- InventoryObjectList::iterator end = inventory_objects.end();
- for ( ; it != end; ++it)
- {
- // coming from a task. Need to figure out if the person can
- // move/copy this item.
- LLPermissions perm(((LLInventoryItem*)((LLInventoryObject*)(*it)))->getPermissions());
- 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;
- }
- else
- {
- accept = FALSE;
- break;
- }
- }
-
- if(drop && accept)
- {
- it = inventory_objects.begin();
- InventoryObjectList::iterator first_it = inventory_objects.begin();
- LLMoveInv* move_inv = new LLMoveInv;
- move_inv->mObjectID = object_id;
- move_inv->mCategoryID = category_id;
- move_inv->mCallback = callback;
- move_inv->mUserData = user_data;
-
- for ( ; it != end; ++it)
- {
- two_uuids_t two(category_id, (*it)->getUUID());
- move_inv->mMoveList.push_back(two);
- }
-
- if(is_move)
- {
- // Callback called from within here.
- warn_move_inventory(object, move_inv);
- }
- else
- {
- LLNotification::Params params("MoveInventoryFromObject");
- params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv));
- LLNotifications::instance().forceResponse(params, 0);
- }
- }
- return accept;
-}
-
-bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat,
- LLInventoryItem* item)
-{
- // Valid COF items are:
- // - links to wearables (body parts or clothing)
- // - links to attachments
- // - links to gestures
- // - links to ensemble folders
- LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem(); // BAP - safe?
- if (linked_item)
- {
- LLAssetType::EType type = linked_item->getType();
- return (type == LLAssetType::AT_CLOTHING ||
- type == LLAssetType::AT_BODYPART ||
- type == LLAssetType::AT_GESTURE ||
- type == LLAssetType::AT_OBJECT);
- }
- else
- {
- LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory(); // BAP - safe?
- // BAP remove AT_NONE support after ensembles are fully working?
- return (linked_category &&
- ((linked_category->getPreferredType() == LLFolderType::FT_NONE) ||
- (LLFolderType::lookupIsEnsembleType(linked_category->getPreferredType()))));
- }
-}
-
-
-bool LLFindWearables::operator()(LLInventoryCategory* cat,
- LLInventoryItem* item)
-{
- if(item)
- {
- if((item->getType() == LLAssetType::AT_CLOTHING)
- || (item->getType() == LLAssetType::AT_BODYPART))
- {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-
-
-//Used by LLFolderBridge as callback for directory recursion.
-class LLRightClickInventoryFetchObserver : public LLInventoryFetchObserver
-{
-public:
- LLRightClickInventoryFetchObserver() :
- mCopyItems(false)
- { };
- LLRightClickInventoryFetchObserver(const LLUUID& cat_id, bool copy_items) :
- mCatID(cat_id),
- mCopyItems(copy_items)
- { };
- virtual void done()
- {
- // we've downloaded all the items, so repaint the dialog
- LLFolderBridge::staticFolderOptionsMenu();
-
- gInventory.removeObserver(this);
- delete this;
- }
-
-
-protected:
- LLUUID mCatID;
- bool mCopyItems;
-
-};
-
-//Used by LLFolderBridge as callback for directory recursion.
-class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
-{
-public:
- LLRightClickInventoryFetchDescendentsObserver(bool copy_items) : mCopyItems(copy_items) {}
- ~LLRightClickInventoryFetchDescendentsObserver() {}
- virtual void done();
-protected:
- bool mCopyItems;
-};
-
-void LLRightClickInventoryFetchDescendentsObserver::done()
-{
- // Avoid passing a NULL-ref as mCompleteFolders.front() down to
- // gInventory.collectDescendents()
- if( mCompleteFolders.empty() )
- {
- llwarns << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << llendl;
- dec_busy_count();
- gInventory.removeObserver(this);
- delete this;
- return;
- }
-
- // What we do here is get the complete information on the items in
- // the library, 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(mCompleteFolders.front(),
- cat_array,
- item_array,
- LLInventoryModel::EXCLUDE_TRASH);
- S32 count = item_array.count();
-#if 0 // HACK/TODO: Why?
- // This early causes a giant menu to get produced, and doesn't seem to be needed.
- if(!count)
- {
- llwarns << "Nothing fetched in category " << mCompleteFolders.front()
- << llendl;
- dec_busy_count();
- gInventory.removeObserver(this);
- delete this;
- return;
- }
-#endif
-
- LLRightClickInventoryFetchObserver* outfit;
- outfit = new LLRightClickInventoryFetchObserver(mCompleteFolders.front(), mCopyItems);
- LLInventoryFetchObserver::item_ref_t ids;
- for(S32 i = 0; i < count; ++i)
- {
- ids.push_back(item_array.get(i)->getUUID());
- }
-
- // clean up, and remove this as an observer since the call to the
- // outfit could notify observers and throw us into an infinite
- // loop.
- dec_busy_count();
- gInventory.removeObserver(this);
- delete this;
-
- // increment busy count and either tell the inventory to check &
- // call done, or add this object to the inventory for observation.
- inc_busy_count();
-
- // do the fetch
- outfit->fetchItems(ids);
- outfit->done(); //Not interested in waiting and this will be right 99% of the time.
-//Uncomment the following code for laggy Inventory UI.
-/* if(outfit->isEverythingComplete())
- {
- // everything is already here - call done.
- outfit->done();
- }
- else
- {
- // it's all on it's way - add an observer, and the inventory
- // will call done for us when everything is here.
- gInventory.addObserver(outfit);
- }*/
-}
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLInventoryWearObserver
-//
-// Observer for "copy and wear" operation to support knowing
-// when the all of the contents have been added to inventory.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLInventoryCopyAndWearObserver : public LLInventoryObserver
-{
-public:
- LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count) :mCatID(cat_id), mContentsCount(count), mFolderAdded(FALSE) {}
- virtual ~LLInventoryCopyAndWearObserver() {}
- virtual void changed(U32 mask);
-
-protected:
- LLUUID mCatID;
- int mContentsCount;
- BOOL mFolderAdded;
-};
-
-
-
-void LLInventoryCopyAndWearObserver::changed(U32 mask)
-{
- if((mask & (LLInventoryObserver::ADD)) != 0)
- {
- if (!mFolderAdded)
- {
- const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
-
- std::set<LLUUID>::const_iterator id_it = changed_items.begin();
- std::set<LLUUID>::const_iterator id_end = changed_items.end();
- for (;id_it != id_end; ++id_it)
- {
- if ((*id_it) == mCatID)
- {
- mFolderAdded = TRUE;
- break;
- }
- }
- }
-
- if (mFolderAdded)
- {
- LLViewerInventoryCategory* category = gInventory.getCategory(mCatID);
-
- if (NULL == category)
- {
- llwarns << "gInventory.getCategory(" << mCatID
- << ") was NULL" << llendl;
- }
- else
- {
- if (category->getDescendentCount() ==
- mContentsCount)
- {
- gInventory.removeObserver(this);
- LLAppearanceManager::wearInventoryCategory(category, FALSE, TRUE);
- delete this;
- }
- }
- }
-
- }
-}
-
-
-
-void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
-{
- if ("open" == action)
- {
- openItem();
- return;
- }
- else if ("paste" == action)
- {
- pasteFromClipboard();
- return;
- }
- else if ("paste_link" == action)
- {
- pasteLinkFromClipboard();
- return;
- }
- else if ("properties" == action)
- {
- showProperties();
- return;
- }
- else if ("replaceoutfit" == action)
- {
- modifyOutfit(FALSE);
- return;
- }
-#if SUPPORT_ENSEMBLES
- else if ("wearasensemble" == action)
- {
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- LLViewerInventoryCategory* cat = getCategory();
- if(!cat) return;
- LLAppearanceManager::wearEnsemble(cat,true);
- return;
- }
-#endif
- else if ("addtooutfit" == action)
- {
- modifyOutfit(TRUE);
- return;
- }
- else if ("copy" == action)
- {
- copyToClipboard();
- return;
- }
- else if ("removefromoutfit" == action)
- {
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- LLViewerInventoryCategory* cat = getCategory();
- if(!cat) return;
-
- remove_inventory_category_from_avatar ( cat );
- return;
- }
- else if ("purge" == action)
- {
- purgeItem(model, mUUID);
- return;
- }
- else if ("restore" == action)
- {
- restoreItem();
- return;
- }
-}
-
-void LLFolderBridge::openItem()
-{
- lldebugs << "LLFolderBridge::openItem()" << llendl;
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- bool fetching_inventory = model->fetchDescendentsOf(mUUID);
- // Only change folder type if we have the folder contents.
- if (!fetching_inventory)
- {
- // Disabling this for now, it's causing crash when new items are added to folders
- // since folder type may change before new item item has finished processing.
- // determineFolderType();
- }
-}
-
-void LLFolderBridge::closeItem()
-{
- determineFolderType();
-}
-
-void LLFolderBridge::determineFolderType()
-{
- if (isUpToDate())
- {
- LLInventoryModel* model = getInventoryModel();
- LLViewerInventoryCategory* category = model->getCategory(mUUID);
- category->determineFolderType();
- }
-}
-
-BOOL LLFolderBridge::isItemRenameable() const
-{
- LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)getCategory();
- if(cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType())
- && (cat->getOwnerID() == gAgent.getID()))
- {
- return TRUE;
- }
- return FALSE;
-}
-
-void LLFolderBridge::restoreItem()
-{
- LLViewerInventoryCategory* cat;
- cat = (LLViewerInventoryCategory*)getCategory();
- if(cat)
- {
- LLInventoryModel* model = getInventoryModel();
- const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType()));
- // do not restamp children on restore
- LLInvFVBridge::changeCategoryParent(model, cat, new_parent, FALSE);
- }
-}
-
-LLFolderType::EType LLFolderBridge::getPreferredType() const
-{
- LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
- LLViewerInventoryCategory* cat = getCategory();
- if(cat)
- {
- preferred_type = cat->getPreferredType();
- }
-
- return preferred_type;
-}
-
-// Icons for folders are based on the preferred type
-LLUIImagePtr LLFolderBridge::getIcon() const
-{
- LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
- LLViewerInventoryCategory* cat = getCategory();
- if(cat)
- {
- preferred_type = cat->getPreferredType();
- }
- return getIcon(preferred_type);
-}
-
-LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type)
-{
- // we only have one folder image now
- return LLUI::getUIImage("Inv_FolderClosed");
-}
-
-BOOL LLFolderBridge::renameItem(const std::string& new_name)
-{
- if(!isItemRenameable())
- return FALSE;
- LLInventoryModel* model = getInventoryModel();
- if(!model)
- return FALSE;
- LLViewerInventoryCategory* cat = getCategory();
- if(cat && (cat->getName() != new_name))
- {
- LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
- new_cat->rename(new_name);
- new_cat->updateServer(FALSE);
- model->updateCategory(new_cat);
-
- model->notifyObservers();
- }
- // return FALSE because we either notified observers (& therefore
- // rebuilt) or we didn't update.
- return FALSE;
-}
-
-BOOL LLFolderBridge::removeItem()
-{
- if(!isItemRemovable())
- {
- return FALSE;
- }
- // move it to the trash
- LLPreview::hide(mUUID);
- LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
-
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
-
- // Look for any gestures and deactivate them
- LLInventoryModel::cat_array_t descendent_categories;
- LLInventoryModel::item_array_t descendent_items;
- gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
-
- S32 i;
- for (i = 0; i < descendent_items.count(); i++)
- {
- LLInventoryItem* item = descendent_items[i];
- if (item->getType() == LLAssetType::AT_GESTURE
- && LLGestureManager::instance().isGestureActive(item->getUUID()))
- {
- LLGestureManager::instance().deactivateGesture(item->getUUID());
- }
- }
-
- // go ahead and do the normal remove if no 'last calling
- // cards' are being removed.
- LLViewerInventoryCategory* cat = getCategory();
- if(cat)
- {
- LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE);
- }
-
- return TRUE;
-}
-
-void LLFolderBridge::pasteFromClipboard()
-{
- LLInventoryModel* model = getInventoryModel();
- if(model && isClipboardPasteable())
- {
- LLInventoryItem* item = NULL;
- LLDynamicArray<LLUUID> objects;
- LLInventoryClipboard::instance().retrieve(objects);
- S32 count = objects.count();
- const LLUUID parent_id(mUUID);
- for(S32 i = 0; i < count; i++)
- {
- item = model->getItem(objects.get(i));
- if (item)
- {
- if(LLInventoryClipboard::instance().isCutMode())
- {
- // move_inventory_item() is not enough,
- //we have to update inventory locally too
- changeItemParent(model, dynamic_cast<LLViewerInventoryItem*>(item), parent_id, FALSE);
- }
- else
- {
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- parent_id,
- std::string(),
- LLPointer<LLInventoryCallback>(NULL));
- }
- }
- }
- }
-}
-
-void LLFolderBridge::pasteLinkFromClipboard()
-{
- const LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- LLDynamicArray<LLUUID> objects;
- LLInventoryClipboard::instance().retrieve(objects);
- S32 count = objects.count();
- LLUUID parent_id(mUUID);
- for(S32 i = 0; i < count; i++)
- {
- const LLUUID &object_id = objects.get(i);
-#if SUPPORT_ENSEMBLES
- if (LLInventoryCategory *cat = model->getCategory(object_id))
- {
- link_inventory_item(
- gAgent.getID(),
- cat->getUUID(),
- parent_id,
- cat->getName(),
- LLAssetType::AT_LINK_FOLDER,
- LLPointer<LLInventoryCallback>(NULL));
- }
- else
-#endif
- if (LLInventoryItem *item = model->getItem(object_id))
- {
- link_inventory_item(
- gAgent.getID(),
- item->getUUID(),
- parent_id,
- item->getName(),
- LLAssetType::AT_LINK,
- LLPointer<LLInventoryCallback>(NULL));
- }
- }
- }
-}
-
-void LLFolderBridge::staticFolderOptionsMenu()
-{
- if (!sSelf) return;
- sSelf->folderOptionsMenu();
-}
-
-void LLFolderBridge::folderOptionsMenu()
-{
- std::vector<std::string> disabled_items;
-
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
-
- const LLInventoryCategory* category = model->getCategory(mUUID);
- LLFolderType::EType type = category->getPreferredType();
- const bool is_default_folder = category && LLFolderType::lookupIsProtectedType(type);
- // BAP change once we're no longer treating regular categories as ensembles.
- const bool is_ensemble = category && (type == LLFolderType::FT_NONE ||
- LLFolderType::lookupIsEnsembleType(type));
-
- // calling card related functionality for folders.
-
- // Only enable calling-card related options for non-default folders.
- if (!is_default_folder)
- {
- LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
- if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard))
- {
- mItems.push_back(std::string("Calling Card Separator"));
- mItems.push_back(std::string("Conference Chat Folder"));
- mItems.push_back(std::string("IM All Contacts In Folder"));
- }
- }
-
- // wearables related functionality for folders.
- //is_wearable
- LLFindWearables is_wearable;
- LLIsType is_object( LLAssetType::AT_OBJECT );
- LLIsType is_gesture( LLAssetType::AT_GESTURE );
-
- if (mWearables ||
- checkFolderForContentsOfType(model, is_wearable) ||
- checkFolderForContentsOfType(model, is_object) ||
- checkFolderForContentsOfType(model, is_gesture) )
- {
- mItems.push_back(std::string("Folder Wearables Separator"));
-
- // Only enable add/replace outfit for non-default folders.
- if (!is_default_folder)
- {
- mItems.push_back(std::string("Add To Outfit"));
- mItems.push_back(std::string("Replace Outfit"));
- }
- if (is_ensemble)
- {
- mItems.push_back(std::string("Wear As Ensemble"));
- }
- mItems.push_back(std::string("Take Off Items"));
- }
- hide_context_entries(*mMenu, mItems, disabled_items);
-}
-
-BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type)
-{
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- model->collectDescendentsIf(mUUID,
- cat_array,
- item_array,
- LLInventoryModel::EXCLUDE_TRASH,
- is_type);
- return ((item_array.count() > 0) ? TRUE : FALSE );
-}
-
-// Flags unused
-void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- mItems.clear();
- mDisabledItems.clear();
-
- lldebugs << "LLFolderBridge::buildContextMenu()" << llendl;
-// std::vector<std::string> disabled_items;
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
-
- mItems.clear(); //adding code to clear out member Items (which means Items should not have other data here at this point)
- mDisabledItems.clear(); //adding code to clear out disabled members from previous
- if (lost_and_found_id == mUUID)
- {
- // This is the lost+found folder.
- mItems.push_back(std::string("Empty Lost And Found"));
- }
-
- if(trash_id == mUUID)
- {
- // This is the trash.
- mItems.push_back(std::string("Empty Trash"));
- }
- else if(model->isObjectDescendentOf(mUUID, trash_id))
- {
- // This is a folder in the trash.
- mItems.clear(); // clear any items that used to exist
- mItems.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- mDisabledItems.push_back(std::string("Purge Item"));
- }
-
- mItems.push_back(std::string("Restore Item"));
- }
- else if(isAgentInventory()) // do not allow creating in library
- {
- LLViewerInventoryCategory *cat = getCategory();
- // BAP removed protected check to re-enable standard ops in untyped folders.
- // Not sure what the right thing is to do here.
- if (!isCOFFolder() && cat /*&&
- LLAssetType::lookupIsProtectedCategoryType(cat->getPreferredType())*/)
- {
- // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
- if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
- mItems.push_back(std::string("New Folder"));
- mItems.push_back(std::string("New Script"));
- mItems.push_back(std::string("New Note"));
- mItems.push_back(std::string("New Gesture"));
- mItems.push_back(std::string("New Clothes"));
- mItems.push_back(std::string("New Body Parts"));
- mItems.push_back(std::string("Change Type"));
-
- LLViewerInventoryCategory *cat = getCategory();
- if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
- {
- mDisabledItems.push_back(std::string("Change Type"));
- }
-
- getClipboardEntries(false, mItems, mDisabledItems, flags);
- }
- else
- {
- // Want some but not all of the items from getClipboardEntries for outfits.
- if (cat && cat->getPreferredType()==LLFolderType::FT_OUTFIT)
- {
- mItems.push_back(std::string("Rename"));
- mItems.push_back(std::string("Delete"));
- }
- }
-
- //Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06
- mCallingCards = mWearables = FALSE;
-
- LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
- if (checkFolderForContentsOfType(model, is_callingcard))
- {
- mCallingCards=TRUE;
- }
-
- LLFindWearables is_wearable;
- LLIsType is_object( LLAssetType::AT_OBJECT );
- LLIsType is_gesture( LLAssetType::AT_GESTURE );
-
- if (checkFolderForContentsOfType(model, is_wearable) ||
- checkFolderForContentsOfType(model, is_object) ||
- checkFolderForContentsOfType(model, is_gesture) )
- {
- mWearables=TRUE;
- }
-
- mMenu = &menu;
- sSelf = this;
- LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(FALSE);
-
- LLInventoryFetchDescendentsObserver::folder_ref_t folders;
- LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
- if (category)
- {
- folders.push_back(category->getUUID());
- }
- fetch->fetchDescendents(folders);
- inc_busy_count();
- if(fetch->isEverythingComplete())
- {
- // everything is already here - call done.
- fetch->done();
- }
- else
- {
- // it's all on it's way - add an observer, and the inventory
- // will call done for us when everything is here.
- gInventory.addObserver(fetch);
- }
- }
- else
- {
- mItems.push_back(std::string("--no options--"));
- mDisabledItems.push_back(std::string("--no options--"));
- }
- hide_context_entries(menu, mItems, mDisabledItems);
-}
-
-BOOL LLFolderBridge::hasChildren() const
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
- LLInventoryModel::EHasChildren has_children;
- has_children = gInventory.categoryHasChildren(mUUID);
- return has_children != LLInventoryModel::CHILDREN_NO;
-}
-
-BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
- EDragAndDropType cargo_type,
- void* cargo_data)
-{
- //llinfos << "LLFolderBridge::dragOrDrop()" << llendl;
- BOOL accept = FALSE;
- switch(cargo_type)
- {
- case DAD_TEXTURE:
- case DAD_SOUND:
- case DAD_CALLINGCARD:
- case DAD_LANDMARK:
- case DAD_SCRIPT:
- case DAD_OBJECT:
- case DAD_NOTECARD:
- case DAD_CLOTHING:
- case DAD_BODYPART:
- case DAD_ANIMATION:
- case DAD_GESTURE:
- case DAD_LINK:
- accept = dragItemIntoFolder((LLInventoryItem*)cargo_data,
- drop);
- break;
- case DAD_CATEGORY:
- if (LLFriendCardsManager::instance().isAnyFriendCategory(mUUID))
- {
- accept = FALSE;
- }
- else
- {
- accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop);
- }
- break;
- default:
- break;
- }
- return accept;
-}
-
-LLViewerInventoryCategory* LLFolderBridge::getCategory() const
-{
- LLViewerInventoryCategory* cat = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- cat = (LLViewerInventoryCategory*)model->getCategory(mUUID);
- }
- return cat;
-}
-
-
-// static
-void LLFolderBridge::pasteClipboard(void* user_data)
-{
- LLFolderBridge* self = (LLFolderBridge*)user_data;
- if(self) self->pasteFromClipboard();
-}
-
-void LLFolderBridge::createNewCategory(void* user_data)
-{
- LLFolderBridge* bridge = (LLFolderBridge*)user_data;
- if(!bridge) return;
- LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(bridge->mInventoryPanel.get());
- if (!panel) return;
- LLInventoryModel* model = panel->getModel();
- if(!model) return;
- LLUUID id;
- id = model->createNewCategory(bridge->getUUID(),
- LLFolderType::FT_NONE,
- LLStringUtil::null);
- model->notifyObservers();
-
- // At this point, the bridge has probably been deleted, but the
- // view is still there.
- panel->setSelection(id, TAKE_FOCUS_YES);
-}
-
-void LLFolderBridge::createNewShirt(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHIRT);
-}
-
-void LLFolderBridge::createNewPants(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_PANTS);
-}
-
-void LLFolderBridge::createNewShoes(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHOES);
-}
-
-void LLFolderBridge::createNewSocks(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SOCKS);
-}
-
-void LLFolderBridge::createNewJacket(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_JACKET);
-}
-
-void LLFolderBridge::createNewSkirt(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIRT);
-}
-
-void LLFolderBridge::createNewGloves(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_GLOVES);
-}
-
-void LLFolderBridge::createNewUndershirt(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERSHIRT);
-}
-
-void LLFolderBridge::createNewUnderpants(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERPANTS);
-}
-
-void LLFolderBridge::createNewShape(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHAPE);
-}
-
-void LLFolderBridge::createNewSkin(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIN);
-}
-
-void LLFolderBridge::createNewHair(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_HAIR);
-}
-
-void LLFolderBridge::createNewEyes(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_EYES);
-}
-
-// static
-void LLFolderBridge::createWearable(LLFolderBridge* bridge, EWearableType type)
-{
- if(!bridge) return;
- LLUUID parent_id = bridge->getUUID();
- createWearable(parent_id, type);
-}
-
-// Separate function so can be called by global menu as well as right-click
-// menu.
-// static
-void LLFolderBridge::createWearable(const LLUUID &parent_id, EWearableType type)
-{
- LLWearable* wearable = LLWearableList::instance().createNewWearable(type);
- LLAssetType::EType asset_type = wearable->getAssetType();
- LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE;
- create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
- parent_id, wearable->getTransactionID(), wearable->getName(),
- wearable->getDescription(), asset_type, inv_type, wearable->getType(),
- wearable->getPermissions().getMaskNextOwner(),
- LLPointer<LLInventoryCallback>(NULL));
-}
-
-void LLFolderBridge::modifyOutfit(BOOL append)
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- LLViewerInventoryCategory* cat = getCategory();
- if(!cat) return;
-
- // BAP - was:
- // wear_inventory_category_on_avatar( cat, append );
- LLAppearanceManager::wearInventoryCategory( cat, FALSE, append );
-}
-
-// helper stuff
-bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv* move_inv)
-{
- LLFloaterOpenObject::LLCatAndWear* cat_and_wear = (LLFloaterOpenObject::LLCatAndWear* )move_inv->mUserData;
- LLViewerObject* object = gObjectList.findObject(move_inv->mObjectID);
- S32 option = LLNotification::getSelectedOption(notification, response);
-
- if(option == 0 && object)
- {
- if (cat_and_wear && cat_and_wear->mWear)
- {
- InventoryObjectList inventory_objects;
- object->getInventoryContents(inventory_objects);
- int contents_count = inventory_objects.size()-1; //subtract one for containing folder
-
- LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count);
- gInventory.addObserver(inventoryObserver);
- }
-
- two_uuids_list_t::iterator move_it;
- for (move_it = move_inv->mMoveList.begin();
- move_it != move_inv->mMoveList.end();
- ++move_it)
- {
- object->moveInventory(move_it->first, move_it->second);
- }
-
- // update the UI.
- dialog_refresh_all();
- }
-
- if (move_inv->mCallback)
- {
- move_inv->mCallback(option, move_inv->mUserData);
- }
-
- delete move_inv;
- return false;
-}
-
-/*
-Next functions intended to reorder items in the inventory folder and save order on server
-Is now used for Favorites folder.
-
-*TODO: refactoring is needed with Favorites Bar functionality. Probably should be moved in LLInventoryModel
-*/
-void saveItemsOrder(LLInventoryModel::item_array_t& items)
-{
- int sortField = 0;
-
- // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field
- for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i)
- {
- LLViewerInventoryItem* item = *i;
-
- item->setSortField(++sortField);
- item->setComplete(TRUE);
- item->updateServer(FALSE);
-
- gInventory.updateItem(item);
- }
-
- gInventory.notifyObservers();
-}
-
-LLInventoryModel::item_array_t::iterator findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id)
-{
- LLInventoryModel::item_array_t::iterator result = items.end();
-
- for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i)
- {
- if ((*i)->getUUID() == id)
- {
- result = i;
- break;
- }
- }
-
- return result;
-}
-
-void updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& srcItemId, const LLUUID& destItemId)
-{
- LLViewerInventoryItem* srcItem = gInventory.getItem(srcItemId);
- LLViewerInventoryItem* destItem = gInventory.getItem(destItemId);
-
- items.erase(findItemByUUID(items, srcItem->getUUID()));
- items.insert(findItemByUUID(items, destItem->getUUID()), srcItem);
-}
-
-BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
- BOOL drop)
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return FALSE;
-
- // cannot drag into library
- if(!isAgentInventory())
- {
- return FALSE;
- }
-
- LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
- if(!avatar) return FALSE;
-
- LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
- BOOL accept = FALSE;
- LLViewerObject* object = NULL;
- if(LLToolDragAndDrop::SOURCE_AGENT == source)
- {
-
- BOOL is_movable = TRUE;
- switch( inv_item->getActualType() )
- {
- case LLAssetType::AT_ROOT_CATEGORY:
- is_movable = FALSE;
- break;
-
- case LLAssetType::AT_CATEGORY:
- is_movable = !LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)inv_item)->getPreferredType());
- break;
- default:
- break;
- }
-
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
- const LLUUID current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
- BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
- BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
-
- if(is_movable && move_is_into_trash)
- {
- switch(inv_item->getType())
- {
- case LLAssetType::AT_CLOTHING:
- case LLAssetType::AT_BODYPART:
- is_movable = !gAgentWearables.isWearingItem(inv_item->getUUID());
- break;
-
- case LLAssetType::AT_OBJECT:
- is_movable = !avatar->isWearingAttachment(inv_item->getUUID());
- break;
- default:
- break;
- }
- }
-
- 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, getCategory());
- }
-
- const LLUUID& favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
-
- // we can move item inside a folder only if this folder is Favorites. See EXT-719
- accept = is_movable && ((mUUID != inv_item->getParentUUID()) || (mUUID == favorites_id));
- if(accept && drop)
- {
- if (inv_item->getType() == LLAssetType::AT_GESTURE
- && LLGestureManager::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash)
- {
- LLGestureManager::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).
- if (LLFloaterInventory::getActiveInventory())
- {
- LLInventoryPanel* active_panel = LLFloaterInventory::getActiveInventory()->getPanel();
- LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
- if (active_panel && (panel != active_panel))
- {
- active_panel->unSelectAll();
- }
- }
-
- // if dragging from/into favorites folder only reorder items
- if ((mUUID == inv_item->getParentUUID()) && (favorites_id == mUUID))
- {
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLIsType is_type(LLAssetType::AT_LANDMARK);
- model->collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type);
-
- LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
- LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL;
- if (itemp)
- {
- LLUUID srcItemId = inv_item->getUUID();
- LLUUID destItemId = itemp->getListener()->getUUID();
-
- // update order
- updateItemsOrder(items, srcItemId, destItemId);
-
- saveItemsOrder(items);
- }
- }
- else if (favorites_id == mUUID) // if target is the favorites folder we use copy
- {
- copy_inventory_item(
- gAgent.getID(),
- inv_item->getPermissions().getOwner(),
- inv_item->getUUID(),
- mUUID,
- std::string(),
- LLPointer<LLInventoryCallback>(NULL));
- }
- else if (move_is_into_current_outfit || move_is_into_outfit)
- {
- // BAP - should skip if dup.
- if (move_is_into_current_outfit)
- {
- LLAppearanceManager::wearItem(inv_item);
- }
- else
- {
- LLPointer<LLInventoryCallback> cb = NULL;
- link_inventory_item(
- gAgent.getID(),
- inv_item->getUUID(),
- mUUID,
- std::string(),
- LLAssetType::AT_LINK,
- cb);
- }
- }
- else
- {
- // restamp if the move is into the trash.
- LLInvFVBridge::changeItemParent(
- model,
- (LLViewerInventoryItem*)inv_item,
- mUUID,
- move_is_into_trash);
- }
- }
- }
- 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)
- {
- llinfos << "Object not found for drop." << llendl;
- 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;
- }
- if(drop && accept)
- {
- LLMoveInv* move_inv = new LLMoveInv;
- move_inv->mObjectID = inv_item->getParentUUID();
- two_uuids_t item_pair(mUUID, 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
- {
- 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)
- {
- accept = TRUE;
- if(drop)
- {
- copy_inventory_from_notecard(LLToolDragAndDrop::getInstance()->getObjectID(),
- LLToolDragAndDrop::getInstance()->getSourceID(), inv_item);
- }
- }
- else if(LLToolDragAndDrop::SOURCE_LIBRARY == source)
- {
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item;
- if(item && item->isComplete())
- {
- accept = TRUE;
- if(drop)
- {
- copy_inventory_item(
- gAgent.getID(),
- inv_item->getPermissions().getOwner(),
- inv_item->getUUID(),
- mUUID,
- std::string(),
- LLPointer<LLInventoryCallback>(NULL));
- }
- }
- }
- else
- {
- llwarns << "unhandled drag source" << llendl;
- }
- return accept;
-}
-
-// +=================================================+
-// | LLScriptBridge (DEPRECTED) |
-// +=================================================+
-
-LLUIImagePtr LLScriptBridge::getIcon() const
-{
- return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0, FALSE);
-}
-
-// +=================================================+
-// | LLTextureBridge |
-// +=================================================+
-
-LLUIImagePtr LLTextureBridge::getIcon() const
-{
- return get_item_icon(LLAssetType::AT_TEXTURE, mInvType, 0, FALSE);
-}
-
-void LLTextureBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-}
-
-// +=================================================+
-// | LLSoundBridge |
-// +=================================================+
-
-LLUIImagePtr LLSoundBridge::getIcon() const
-{
- return get_item_icon(LLAssetType::AT_SOUND, LLInventoryType::IT_SOUND, 0, FALSE);
-}
-
-void LLSoundBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-/*
-// Changed this back to the way it USED to work:
-// only open the preview dialog through the contextual right-click menu
-// double-click just plays the sound
-
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- openSoundPreview((void*)this);
- //send_uuid_sound_trigger(item->getAssetUUID(), 1.0);
- }
-*/
-}
-
-void LLSoundBridge::previewItem()
-{
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- send_sound_trigger(item->getAssetUUID(), 1.0);
- }
-}
-
-void LLSoundBridge::openSoundPreview(void* which)
-{
- LLSoundBridge *me = (LLSoundBridge *)which;
- LLFloaterReg::showInstance("preview_sound", LLSD(me->mUUID), TAKE_FOCUS_YES);
-}
-
-void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- lldebugs << "LLTextureBridge::buildContextMenu()" << llendl;
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
-
- if(isInTrash())
- {
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
-
- items.push_back(std::string("Restore Item"));
- }
- else
- {
- items.push_back(std::string("Sound Open"));
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
- }
-
- items.push_back(std::string("Sound Separator"));
- items.push_back(std::string("Sound Play"));
-
- hide_context_entries(menu, items, disabled_items);
-}
-
-// +=================================================+
-// | LLLandmarkBridge |
-// +=================================================+
-
-LLLandmarkBridge::LLLandmarkBridge(LLInventoryPanel* inventory, const LLUUID& uuid, U32 flags/* = 0x00*/) :
-LLItemBridge(inventory, uuid)
-{
- mVisited = FALSE;
- if (flags & LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
- {
- mVisited = TRUE;
- }
-}
-
-LLUIImagePtr LLLandmarkBridge::getIcon() const
-{
- return get_item_icon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, mVisited, FALSE);
-}
-
-void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
-
- lldebugs << "LLLandmarkBridge::buildContextMenu()" << llendl;
- if(isInTrash())
- {
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
-
- items.push_back(std::string("Restore Item"));
- }
- else
- {
- items.push_back(std::string("Landmark Open"));
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
- }
-
- items.push_back(std::string("Landmark Separator"));
- items.push_back(std::string("About Landmark"));
-
- // Disable "About Landmark" menu item for
- // multiple landmarks selected. Only one landmark
- // info panel can be shown at a time.
- if ((flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("About Landmark"));
- }
-
- hide_context_entries(menu, items, disabled_items);
-}
-
-// Convenience function for the two functions below.
-void teleport_via_landmark(const LLUUID& asset_id)
-{
- gAgent.teleportViaLandmark( asset_id );
-
- // we now automatically track the landmark you're teleporting to
- // because you'll probably arrive at a telehub instead
- LLFloaterWorldMap* floater_world_map = LLFloaterWorldMap::getInstance();
- if( floater_world_map )
- {
- floater_world_map->trackLandmark( asset_id );
- }
-}
-
-// virtual
-void LLLandmarkBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
-{
- if ("teleport" == action)
- {
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- teleport_via_landmark(item->getAssetUUID());
- }
- }
- else if ("about" == action)
- {
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- LLSD key;
- key["type"] = "landmark";
- key["id"] = item->getUUID();
-
- LLSideTray::getInstance()->showPanel("panel_places", key);
- }
- }
- else
- {
- LLItemBridge::performAction(folder, model, action);
- }
-}
-
-static bool open_landmark_callback(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotification::getSelectedOption(notification, response);
-
- LLUUID asset_id = notification["payload"]["asset_id"].asUUID();
- if (option == 0)
- {
- teleport_via_landmark(asset_id);
- }
-
- return false;
-}
-static LLNotificationFunctorRegistration open_landmark_callback_reg("TeleportFromLandmark", open_landmark_callback);
-
-
-void LLLandmarkBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-/*
- LLViewerInventoryItem* item = getItem();
- if( item )
- {
- // Opening (double-clicking) a landmark immediately teleports,
- // but warns you the first time.
- // open_landmark(item);
- LLSD payload;
- payload["asset_id"] = item->getAssetUUID();
- LLNotifications::instance().add("TeleportFromLandmark", LLSD(), payload);
- }
-*/
-}
-
-
-// +=================================================+
-// | LLCallingCardObserver |
-// +=================================================+
-void LLCallingCardObserver::changed(U32 mask)
-{
- mBridgep->refreshFolderViewItem();
-}
-
-// +=================================================+
-// | LLCallingCardBridge |
-// +=================================================+
-
-LLCallingCardBridge::LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) :
- LLItemBridge(inventory, uuid)
-{
- mObserver = new LLCallingCardObserver(this);
- LLAvatarTracker::instance().addObserver(mObserver);
-}
-
-LLCallingCardBridge::~LLCallingCardBridge()
-{
- LLAvatarTracker::instance().removeObserver(mObserver);
- delete mObserver;
-}
-
-void LLCallingCardBridge::refreshFolderViewItem()
-{
- LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
- LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL;
- if (itemp)
- {
- itemp->refresh();
- }
-}
-
-// virtual
-void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
-{
- if ("begin_im" == action)
- {
- LLViewerInventoryItem *item = getItem();
- if (item && (item->getCreatorUUID() != gAgent.getID()) &&
- (!item->getCreatorUUID().isNull()))
- {
- std::string callingcard_name;
- gCacheName->getFullName(item->getCreatorUUID(), callingcard_name);
- LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID());
- if (session_id != LLUUID::null)
- {
- LLIMFloater::show(session_id);
- }
- }
- }
- else if ("lure" == action)
- {
- LLViewerInventoryItem *item = getItem();
- if (item && (item->getCreatorUUID() != gAgent.getID()) &&
- (!item->getCreatorUUID().isNull()))
- {
- LLAvatarActions::offerTeleport(item->getCreatorUUID());
- }
- }
- else LLItemBridge::performAction(folder, model, action);
-}
-
-LLUIImagePtr LLCallingCardBridge::getIcon() const
-{
- BOOL online = FALSE;
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID());
- }
- return get_item_icon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, online, FALSE);
-}
-
-std::string LLCallingCardBridge::getLabelSuffix() const
-{
- LLViewerInventoryItem* item = getItem();
- if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) )
- {
- return LLItemBridge::getLabelSuffix() + " (online)";
- }
- else
- {
- return LLItemBridge::getLabelSuffix();
- }
-}
-
-void LLCallingCardBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-/*
- LLViewerInventoryItem* item = getItem();
- if(item && !item->getCreatorUUID().isNull())
- {
- LLAvatarActions::showProfile(item->getCreatorUUID());
- }
-*/
-}
-
-void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- lldebugs << "LLCallingCardBridge::buildContextMenu()" << llendl;
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
-
- if(isInTrash())
- {
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
-
- items.push_back(std::string("Restore Item"));
- }
- else
- {
- items.push_back(std::string("Open"));
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- LLInventoryItem* item = getItem();
- BOOL good_card = (item
- && (LLUUID::null != item->getCreatorUUID())
- && (item->getCreatorUUID() != gAgent.getID()));
- BOOL user_online = (LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()));
- items.push_back(std::string("Send Instant Message Separator"));
- items.push_back(std::string("Send Instant Message"));
- items.push_back(std::string("Offer Teleport..."));
- items.push_back(std::string("Conference Chat"));
-
- if (!good_card)
- {
- disabled_items.push_back(std::string("Send Instant Message"));
- }
- if (!good_card || !user_online)
- {
- disabled_items.push_back(std::string("Offer Teleport..."));
- disabled_items.push_back(std::string("Conference Chat"));
- }
- }
- hide_context_entries(menu, items, disabled_items);
-}
-
-BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop,
- EDragAndDropType cargo_type,
- void* cargo_data)
-{
- LLViewerInventoryItem* item = getItem();
- BOOL rv = FALSE;
- if(item)
- {
- // check the type
- switch(cargo_type)
- {
- case DAD_TEXTURE:
- case DAD_SOUND:
- case DAD_LANDMARK:
- case DAD_SCRIPT:
- case DAD_CLOTHING:
- case DAD_OBJECT:
- case DAD_NOTECARD:
- case DAD_BODYPART:
- case DAD_ANIMATION:
- case DAD_GESTURE:
- {
- LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
- const LLPermissions& perm = inv_item->getPermissions();
- if(gInventory.getItem(inv_item->getUUID())
- && perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
- {
- rv = TRUE;
- if(drop)
- {
- LLToolDragAndDrop::giveInventory(item->getCreatorUUID(),
- (LLInventoryItem*)cargo_data);
- }
- }
- else
- {
- // It's not in the user's inventory (it's probably in
- // an object's contents), so disallow dragging it here.
- // You can't give something you don't yet have.
- rv = FALSE;
- }
- break;
- }
- case DAD_CATEGORY:
- {
- LLInventoryCategory* inv_cat = (LLInventoryCategory*)cargo_data;
- if( gInventory.getCategory( inv_cat->getUUID() ) )
- {
- rv = TRUE;
- if(drop)
- {
- LLToolDragAndDrop::giveInventoryCategory(
- item->getCreatorUUID(),
- inv_cat);
- }
- }
- else
- {
- // It's not in the user's inventory (it's probably in
- // an object's contents), so disallow dragging it here.
- // You can't give something you don't yet have.
- rv = FALSE;
- }
- break;
- }
- default:
- break;
- }
- }
- return rv;
-}
-
-BOOL LLCallingCardBridge::removeItem()
-{
- if (LLFriendCardsManager::instance().isItemInAnyFriendsList(getItem()))
- {
- LLAvatarActions::removeFriendDialog(getItem()->getCreatorUUID());
- return FALSE;
- }
- else
- {
- return LLItemBridge::removeItem();
- }
-}
-// +=================================================+
-// | LLNotecardBridge |
-// +=================================================+
-
-LLUIImagePtr LLNotecardBridge::getIcon() const
-{
- return get_item_icon(LLAssetType::AT_NOTECARD, LLInventoryType::IT_NOTECARD, 0, FALSE);
-}
-
-void LLNotecardBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-
-/*
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES);
- }
-*/
-}
-
-
-// +=================================================+
-// | LLGestureBridge |
-// +=================================================+
-
-LLUIImagePtr LLGestureBridge::getIcon() const
-{
- return get_item_icon(LLAssetType::AT_GESTURE, LLInventoryType::IT_GESTURE, 0, FALSE);
-}
-
-LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const
-{
- if( LLGestureManager::instance().isGestureActive(mUUID) )
- {
- return LLFontGL::BOLD;
- }
- else
- {
- return LLFontGL::NORMAL;
- }
-}
-
-std::string LLGestureBridge::getLabelSuffix() const
-{
- if( LLGestureManager::instance().isGestureActive(mUUID) )
- {
- return LLItemBridge::getLabelSuffix() + " (active)";
- }
- else
- {
- return LLItemBridge::getLabelSuffix();
- }
-}
-
-// virtual
-void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
-{
- if ("activate" == action)
- {
- LLGestureManager::instance().activateGesture(mUUID);
-
- LLViewerInventoryItem* item = gInventory.getItem(mUUID);
- if (!item) return;
-
- // Since we just changed the suffix to indicate (active)
- // the server doesn't need to know, just the viewer.
- gInventory.updateItem(item);
- gInventory.notifyObservers();
- }
- else if ("deactivate" == action)
- {
- LLGestureManager::instance().deactivateGesture(mUUID);
-
- LLViewerInventoryItem* item = gInventory.getItem(mUUID);
- if (!item) return;
-
- // Since we just changed the suffix to indicate (active)
- // the server doesn't need to know, just the viewer.
- gInventory.updateItem(item);
- gInventory.notifyObservers();
- }
- else LLItemBridge::performAction(folder, model, action);
-}
-
-void LLGestureBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-/*
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null);
- preview->setFocus(TRUE);
- }
-*/
-}
-
-BOOL LLGestureBridge::removeItem()
-{
- // Force close the preview window, if it exists
- LLGestureManager::instance().deactivateGesture(mUUID);
- return LLItemBridge::removeItem();
-}
-
-void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- lldebugs << "LLGestureBridge::buildContextMenu()" << llendl;
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
- if(isInTrash())
- {
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
-
- items.push_back(std::string("Restore Item"));
- }
- else
- {
- items.push_back(std::string("Open"));
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- items.push_back(std::string("Gesture Separator"));
- items.push_back(std::string("Activate"));
- items.push_back(std::string("Deactivate"));
- }
- hide_context_entries(menu, items, disabled_items);
-}
-
-// +=================================================+
-// | LLAnimationBridge |
-// +=================================================+
-
-LLUIImagePtr LLAnimationBridge::getIcon() const
-{
- return get_item_icon(LLAssetType::AT_ANIMATION, LLInventoryType::IT_ANIMATION, 0, FALSE);
-}
-
-void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
-
- lldebugs << "LLAnimationBridge::buildContextMenu()" << llendl;
- if(isInTrash())
- {
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
-
- items.push_back(std::string("Restore Item"));
- }
- else
- {
- items.push_back(std::string("Animation Open"));
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
- }
-
- items.push_back(std::string("Animation Separator"));
- items.push_back(std::string("Animation Play"));
- items.push_back(std::string("Animation Audition"));
-
- hide_context_entries(menu, items, disabled_items);
-
-}
-
-// virtual
-void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
-{
- if ((action == "playworld") || (action == "playlocal"))
- {
- if (getItem())
- {
- LLPreviewAnim::e_activation_type activate = LLPreviewAnim::NONE;
- if ("playworld" == action) activate = LLPreviewAnim::PLAY;
- if ("playlocal" == action) activate = LLPreviewAnim::AUDITION;
-
- LLPreviewAnim* preview = LLFloaterReg::showTypedInstance<LLPreviewAnim>("preview_anim", LLSD(mUUID));
- if (preview)
- {
- preview->activate(activate);
- }
- }
- }
- else
- {
- LLItemBridge::performAction(folder, model, action);
- }
-}
-
-void LLAnimationBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-/*
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES);
- }
-*/
-}
-
-// +=================================================+
-// | LLObjectBridge |
-// +=================================================+
-
-// static
-LLUUID LLObjectBridge::sContextMenuItemID;
-
-LLObjectBridge::LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) :
-LLItemBridge(inventory, uuid), mInvType(type)
-{
- mAttachPt = (flags & 0xff); // low bye of inventory flags
-
- mIsMultiObject = ( flags & LLInventoryItem::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS ) ? TRUE: FALSE;
-}
-
-BOOL LLObjectBridge::isItemRemovable()
-{
- LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
- if(!avatar) return FALSE;
- if(avatar->isWearingAttachment(mUUID)) return FALSE;
- return LLInvFVBridge::isItemRemovable();
-}
-
-LLUIImagePtr LLObjectBridge::getIcon() const
-{
- return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject );
-}
-
-LLInventoryObject* LLObjectBridge::getObject() const
-{
- LLInventoryObject* object = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- object = (LLInventoryObject*)model->getObject(mUUID);
- }
- return object;
-}
-
-// virtual
-void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
-{
- if ("attach" == action)
- {
- LLUUID object_id = mUUID;
- LLViewerInventoryItem* item;
- item = (LLViewerInventoryItem*)gInventory.getItem(object_id);
- if(item && gInventory.isObjectDescendentOf(object_id, gInventory.getRootFolderID()))
- {
- rez_attachment(item, NULL);
- }
- else if(item && item->isComplete())
- {
- // must be in library. copy it to our inventory and put it on.
- LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0);
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- LLUUID::null,
- std::string(),
- cb);
- }
- gFocusMgr.setKeyboardFocus(NULL);
- }
- else if ("detach" == action)
- {
- LLInventoryItem* item = gInventory.getItem(mUUID);
- if(item)
- {
- gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
- gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
- gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
- gMessageSystem->sendReliable( gAgent.getRegion()->getHost());
- }
- // this object might have been selected, so let the selection manager know it's gone now
- LLViewerObject *found_obj =
- gObjectList.findObject(item->getUUID());
- if (found_obj)
- {
- LLSelectMgr::getInstance()->remove(found_obj);
- }
- else
- {
- llwarns << "object not found - ignoring" << llendl;
- }
- }
- else LLItemBridge::performAction(folder, model, action);
-}
-
-void LLObjectBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-
- LLSD key;
- key["id"] = mUUID;
- LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
-
- /*
- LLFloaterReg::showInstance("properties", mUUID);
- */
-}
-
-LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const
-{
- U8 font = LLFontGL::NORMAL;
-
- LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
- if( avatar && avatar->isWearingAttachment( mUUID ) )
- {
- font |= LLFontGL::BOLD;
- }
-
- LLInventoryItem* item = getItem();
- if (item && item->getIsLinkType())
- {
- font |= LLFontGL::ITALIC;
- }
-
- return (LLFontGL::StyleFlags)font;
-}
-
-std::string LLObjectBridge::getLabelSuffix() const
-{
- LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
- if( avatar && avatar->isWearingAttachment( mUUID ) )
- {
- std::string attachment_point_name = avatar->getAttachedPointName(mUUID);
- LLStringUtil::toLower(attachment_point_name);
-
- LLStringUtil::format_map_t args;
- args["[ATTACHMENT_POINT]"] = attachment_point_name.c_str();
- return LLItemBridge::getLabelSuffix() + LLTrans::getString("WornOnAttachmentPoint", args);
- }
- else
- {
- return LLItemBridge::getLabelSuffix();
- }
-}
-
-void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment)
-{
- LLSD payload;
- payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link.
-
- S32 attach_pt = 0;
- if (gAgent.getAvatarObject() && attachment)
- {
- for (LLVOAvatar::attachment_map_t::iterator iter = gAgent.getAvatarObject()->mAttachmentPoints.begin();
- iter != gAgent.getAvatarObject()->mAttachmentPoints.end(); ++iter)
- {
- if (iter->second == attachment)
- {
- attach_pt = iter->first;
- break;
- }
- }
- }
-
- payload["attachment_point"] = attach_pt;
-
-#if !ENABLE_MULTIATTACHMENTS
- if (attachment && attachment->getNumObjects() > 0)
- {
- LLNotifications::instance().add("ReplaceAttachment", LLSD(), payload, confirm_replace_attachment_rez);
- }
- else
-#endif
- {
- LLNotifications::instance().forceResponse(LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/);
- }
-}
-
-bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response)
-{
- LLVOAvatar *avatarp = gAgent.getAvatarObject();
-
- if (!avatarp->canAttachMoreObjects())
- {
- LLSD args;
- args["MAX_ATTACHMENTS"] = llformat("%d", MAX_AGENT_ATTACHMENTS);
- LLNotifications::instance().add("MaxAttachmentsOnOutfit", args);
- return false;
- }
-
- S32 option = LLNotification::getSelectedOption(notification, response);
- if (option == 0/*YES*/)
- {
- LLViewerInventoryItem* itemp = gInventory.getItem(notification["payload"]["item_id"].asUUID());
-
- if (itemp)
- {
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_ObjectData);
- msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID());
- msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner());
- U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
-#if ENABLE_MULTIATTACHMENTS
- attachment_pt |= ATTACHMENT_ADD;
-#endif
- msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt);
- pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions());
- msg->addStringFast(_PREHASH_Name, itemp->getName());
- msg->addStringFast(_PREHASH_Description, itemp->getDescription());
- msg->sendReliable(gAgent.getRegion()->getHost());
- }
- }
- return false;
-}
-static LLNotificationFunctorRegistration confirm_replace_attachment_rez_reg("ReplaceAttachment", confirm_replace_attachment_rez);
-
-void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
- if(isInTrash())
- {
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
-
- items.push_back(std::string("Restore Item"));
- }
- else
- {
- LLInventoryItem* item = getItem();
- if (item && item->getIsLinkType())
- {
- items.push_back(std::string("Goto Link"));
- }
-
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- LLObjectBridge::sContextMenuItemID = mUUID;
-
- if(item)
- {
- LLVOAvatarSelf* avatarp = gAgent.getAvatarObject();
- if( !avatarp )
- {
- return;
- }
-
- if( avatarp->isWearingAttachment( mUUID ) )
- {
- items.push_back(std::string("Detach From Yourself"));
- }
- else
- if( !isInTrash() && !isLinkedObjectInTrash() )
- {
- items.push_back(std::string("Attach Separator"));
- items.push_back(std::string("Object Wear"));
- items.push_back(std::string("Attach To"));
- items.push_back(std::string("Attach To HUD"));
- // commented out for DEV-32347
- //items.push_back(std::string("Restore to Last Position"));
-
- if (!avatarp->canAttachMoreObjects())
- {
- disabled_items.push_back(std::string("Object Wear"));
- disabled_items.push_back(std::string("Attach To"));
- disabled_items.push_back(std::string("Attach To HUD"));
- }
- LLMenuGL* attach_menu = menu.findChildMenuByName("Attach To", TRUE);
- LLMenuGL* attach_hud_menu = menu.findChildMenuByName("Attach To HUD", TRUE);
- LLVOAvatar *avatarp = gAgent.getAvatarObject();
- if (attach_menu
- && (attach_menu->getChildCount() == 0)
- && attach_hud_menu
- && (attach_hud_menu->getChildCount() == 0)
- && avatarp)
- {
- for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin();
- iter != avatarp->mAttachmentPoints.end(); )
- {
- LLVOAvatar::attachment_map_t::iterator curiter = iter++;
- LLViewerJointAttachment* attachment = curiter->second;
- LLMenuItemCallGL::Params p;
- std::string submenu_name = attachment->getName();
- if (LLTrans::getString(submenu_name) != "")
- {
- p.name = (" ")+LLTrans::getString(submenu_name)+" ";
- }
- else
- {
- p.name = submenu_name;
- }
- LLSD cbparams;
- cbparams["index"] = curiter->first;
- cbparams["label"] = attachment->getName();
- p.on_click.function_name = "Inventory.AttachObject";
- p.on_click.parameter = LLSD(attachment->getName());
- p.on_enable.function_name = "Attachment.Label";
- p.on_enable.parameter = cbparams;
- LLView* parent = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu;
- LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent);
- }
- }
- }
- }
- }
- hide_context_entries(menu, items, disabled_items);
-}
-
-BOOL LLObjectBridge::renameItem(const std::string& new_name)
-{
- if(!isItemRenameable())
- return FALSE;
- LLPreview::dirty(mUUID);
- LLInventoryModel* model = getInventoryModel();
- if(!model)
- return FALSE;
- LLViewerInventoryItem* item = getItem();
- if(item && (item->getName() != new_name))
- {
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
- new_item->rename(new_name);
- buildDisplayName(new_item, mDisplayName);
- new_item->updateServer(FALSE);
- model->updateItem(new_item);
-
- model->notifyObservers();
-
- LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
- if( avatar )
- {
- LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() );
- if( obj )
- {
- LLSelectMgr::getInstance()->deselectAll();
- LLSelectMgr::getInstance()->addAsIndividual( obj, SELECT_ALL_TES, FALSE );
- LLSelectMgr::getInstance()->selectionSetObjectName( new_name );
- LLSelectMgr::getInstance()->deselectAll();
- }
- }
- }
- // return FALSE because we either notified observers (& therefore
- // rebuilt) or we didn't update.
- return FALSE;
-}
-
-// +=================================================+
-// | LLLSLTextBridge |
-// +=================================================+
-
-LLUIImagePtr LLLSLTextBridge::getIcon() const
-{
- return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0, FALSE);
-}
-
-void LLLSLTextBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
- /*
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES);
- }
- */
-}
-
-// +=================================================+
-// | LLWearableBridge |
-// +=================================================+
-
-// *NOTE: hack to get from avatar inventory to avatar
-void wear_inventory_item_on_avatar( LLInventoryItem* item )
-{
- if(item)
- {
- lldebugs << "wear_inventory_item_on_avatar( " << item->getName()
- << " )" << llendl;
-
- LLAppearanceManager::wearItem(item);
- }
-}
-
-void wear_add_inventory_item_on_avatar( LLInventoryItem* item )
-{
- if(item)
- {
- lldebugs << "wear_add_inventory_item_on_avatar( " << item->getName()
- << " )" << llendl;
-
- LLWearableList::instance().getAsset(item->getAssetUUID(),
- item->getName(),
- item->getType(),
- LLWearableBridge::onWearAddOnAvatarArrived,
- new LLUUID(item->getUUID()));
- }
-}
-
-void remove_inventory_category_from_avatar( LLInventoryCategory* category )
-{
- if(!category) return;
- lldebugs << "remove_inventory_category_from_avatar( " << category->getName()
- << " )" << llendl;
-
-
- if( gFloaterCustomize )
- {
- gFloaterCustomize->askToSaveIfDirty(
- boost::bind(remove_inventory_category_from_avatar_step2, _1, category->getUUID()));
- }
- else
- {
- remove_inventory_category_from_avatar_step2(TRUE, category->getUUID() );
- }
-}
-
-struct OnRemoveStruct
-{
- LLUUID mUUID;
- OnRemoveStruct(const LLUUID& uuid):
- mUUID(uuid)
- {
- }
-};
-
-void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id)
-{
-
- // Find all the wearables that are in the category's subtree.
- lldebugs << "remove_inventory_category_from_avatar_step2()" << llendl;
- if(proceed)
- {
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- LLFindWearables is_wearable;
- gInventory.collectDescendentsIf(category_id,
- cat_array,
- item_array,
- LLInventoryModel::EXCLUDE_TRASH,
- is_wearable);
- S32 i;
- S32 wearable_count = item_array.count();
-
- LLInventoryModel::cat_array_t obj_cat_array;
- LLInventoryModel::item_array_t obj_item_array;
- LLIsType is_object( LLAssetType::AT_OBJECT );
- gInventory.collectDescendentsIf(category_id,
- obj_cat_array,
- obj_item_array,
- LLInventoryModel::EXCLUDE_TRASH,
- is_object);
- S32 obj_count = obj_item_array.count();
-
- // Find all gestures in this folder
- LLInventoryModel::cat_array_t gest_cat_array;
- LLInventoryModel::item_array_t gest_item_array;
- LLIsType is_gesture( LLAssetType::AT_GESTURE );
- gInventory.collectDescendentsIf(category_id,
- gest_cat_array,
- gest_item_array,
- LLInventoryModel::EXCLUDE_TRASH,
- is_gesture);
- S32 gest_count = gest_item_array.count();
-
- if (wearable_count > 0) //Loop through wearables. If worn, remove.
- {
- for(i = 0; i < wearable_count; ++i)
- {
- if( gAgentWearables.isWearingItem (item_array.get(i)->getUUID()) )
- {
- LLWearableList::instance().getAsset(item_array.get(i)->getAssetUUID(),
- item_array.get(i)->getName(),
- item_array.get(i)->getType(),
- LLWearableBridge::onRemoveFromAvatarArrived,
- new OnRemoveStruct(item_array.get(i)->getUUID()));
-
- }
- }
- }
-
-
- if (obj_count > 0)
- {
- for(i = 0; i < obj_count; ++i)
- {
- gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
- gMessageSystem->nextBlockFast(_PREHASH_ObjectData );
- gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
- gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item_array.get(i)->getUUID() );
-
- gMessageSystem->sendReliable( gAgent.getRegion()->getHost() );
-
- // this object might have been selected, so let the selection manager know it's gone now
- LLViewerObject *found_obj = gObjectList.findObject( obj_item_array.get(i)->getUUID());
- if (found_obj)
- {
- LLSelectMgr::getInstance()->remove(found_obj);
- }
- else
- {
- llwarns << "object not found, ignoring" << llendl;
- }
- }
- }
-
- if (gest_count > 0)
- {
- for(i = 0; i < gest_count; ++i)
- {
- if ( LLGestureManager::instance().isGestureActive( gest_item_array.get(i)->getUUID()) )
- {
- LLGestureManager::instance().deactivateGesture( gest_item_array.get(i)->getUUID() );
- gInventory.updateItem( gest_item_array.get(i) );
- gInventory.notifyObservers();
- }
-
- }
- }
- }
-}
-
-BOOL LLWearableBridge::renameItem(const std::string& new_name)
-{
- if( gAgentWearables.isWearingItem( mUUID ) )
- {
- gAgentWearables.setWearableName( mUUID, new_name );
- }
- return LLItemBridge::renameItem(new_name);
-}
-
-BOOL LLWearableBridge::isItemRemovable()
-{
- if (gAgentWearables.isWearingItem(mUUID)) return FALSE;
- return LLInvFVBridge::isItemRemovable();
-}
-
-std::string LLWearableBridge::getLabelSuffix() const
-{
- if( gAgentWearables.isWearingItem( mUUID ) )
- {
- return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn");
- }
- else
- {
- return LLItemBridge::getLabelSuffix();
- }
-}
-
-LLUIImagePtr LLWearableBridge::getIcon() const
-{
- return get_item_icon(mAssetType, mInvType, mWearableType, FALSE);
-}
-
-// virtual
-void LLWearableBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
-{
- if ("wear" == action)
- {
- wearOnAvatar();
- }
- else if ("wear_add" == action)
- {
- wearAddOnAvatar();
- }
- else if ("edit" == action)
- {
- editOnAvatar();
- return;
- }
- else if ("take_off" == action)
- {
- if(gAgentWearables.isWearingItem(mUUID))
- {
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLWearableList::instance().getAsset(item->getAssetUUID(),
- item->getName(),
- item->getType(),
- LLWearableBridge::onRemoveFromAvatarArrived,
- new OnRemoveStruct(mUUID));
- }
- }
- }
- else LLItemBridge::performAction(folder, model, action);
-}
-
-void LLWearableBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
- /*
- if( isInTrash() )
- {
- LLNotifications::instance().add("CannotWearTrash");
- }
- else if(isAgentInventory())
- {
- if( !gAgentWearables.isWearingItem( mUUID ) )
- {
- wearOnAvatar();
- }
- }
- else
- {
- // must be in the inventory library. copy it to our inventory
- // and put it on right away.
- LLViewerInventoryItem* item = getItem();
- if(item && item->isComplete())
- {
- LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- LLUUID::null,
- std::string(),
- cb);
- }
- else if(item)
- {
- // *TODO: We should fetch the item details, and then do
- // the operation above.
- LLNotifications::instance().add("CannotWearInfoNotComplete");
- }
- }
- */
-}
-
-void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- lldebugs << "LLWearableBridge::buildContextMenu()" << llendl;
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
- if(isInTrash())
- {
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
-
- items.push_back(std::string("Restore Item"));
- }
- else
- { // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere
- BOOL no_open = ((flags & SUPPRESS_OPEN_ITEM) == SUPPRESS_OPEN_ITEM);
-
- // If we have clothing, don't add "Open" as it's the same action as "Wear" SL-18976
- LLViewerInventoryItem* item = getItem();
- if( !no_open && item )
- {
- no_open = (item->getType() == LLAssetType::AT_CLOTHING) ||
- (item->getType() == LLAssetType::AT_BODYPART);
- }
- if (!no_open)
- {
- items.push_back(std::string("Open"));
- }
-
- if (item && item->getIsLinkType())
- {
- items.push_back(std::string("Goto Link"));
- }
-
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- items.push_back(std::string("Wearable Separator"));
-
- items.push_back(std::string("Wearable Wear"));
- items.push_back(std::string("Wearable Add"));
- items.push_back(std::string("Wearable Edit"));
-
- if ((flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Wearable Edit"));
- }
- // Don't allow items to be worn if their baseobj is in the trash.
- if (isLinkedObjectInTrash())
- {
- disabled_items.push_back(std::string("Wearable Wear"));
- disabled_items.push_back(std::string("Wearable Add"));
- disabled_items.push_back(std::string("Wearable Edit"));
- }
-
- // Disable wear and take off based on whether the item is worn.
- if(item)
- {
- switch (item->getType())
- {
- case LLAssetType::AT_CLOTHING:
- items.push_back(std::string("Take Off"));
- case LLAssetType::AT_BODYPART:
- if (gAgentWearables.isWearingItem(item->getUUID()))
- {
- disabled_items.push_back(std::string("Wearable Wear"));
- disabled_items.push_back(std::string("Wearable Add"));
- }
- else
- {
- disabled_items.push_back(std::string("Take Off"));
- }
- break;
- default:
- break;
- }
- }
- }
- hide_context_entries(menu, items, disabled_items);
-}
-
-// Called from menus
-// static
-BOOL LLWearableBridge::canWearOnAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if(!self) return FALSE;
- if(!self->isAgentInventory())
- {
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem();
- if(!item || !item->isComplete()) return FALSE;
- }
- return (!gAgentWearables.isWearingItem(self->mUUID));
-}
-
-// Called from menus
-// static
-void LLWearableBridge::onWearOnAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if(!self) return;
- self->wearOnAvatar();
-}
-
-void LLWearableBridge::wearOnAvatar()
-{
- // Don't wear anything until initial wearables are loaded, can
- // destroy clothing items.
- if (!gAgentWearables.areWearablesLoaded())
- {
- LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded");
- return;
- }
-
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- if(!isAgentInventory())
- {
- LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- LLUUID::null,
- std::string(),
- cb);
- }
- else
- {
- wear_inventory_item_on_avatar(item);
- }
- }
-}
-
-void LLWearableBridge::wearAddOnAvatar()
-{
- // Don't wear anything until initial wearables are loaded, can
- // destroy clothing items.
- if (!gAgentWearables.areWearablesLoaded())
- {
- LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded");
- return;
- }
-
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- if(!isAgentInventory())
- {
- LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- LLUUID::null,
- std::string(),
- cb);
- }
- else
- {
- wear_add_inventory_item_on_avatar(item);
- }
- }
-}
-
-// static
-void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userdata )
-{
- LLUUID* item_id = (LLUUID*) userdata;
- if(wearable)
- {
- LLViewerInventoryItem* item = NULL;
- item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
- if(item)
- {
- if(item->getAssetUUID() == wearable->getAssetID())
- {
- gAgentWearables.setWearableItem(item, wearable);
- gInventory.notifyObservers();
- //self->getFolderItem()->refreshFromRoot();
- }
- else
- {
- llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl;
- }
- }
- }
- delete item_id;
-}
-
-// static
-// BAP remove the "add" code path once everything is fully COF-ified.
-void LLWearableBridge::onWearAddOnAvatarArrived( LLWearable* wearable, void* userdata )
-{
- LLUUID* item_id = (LLUUID*) userdata;
- if(wearable)
- {
- LLViewerInventoryItem* item = NULL;
- item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
- if(item)
- {
- if(item->getAssetUUID() == wearable->getAssetID())
- {
- bool do_append = true;
- gAgentWearables.setWearableItem(item, wearable, do_append);
- gInventory.notifyObservers();
- //self->getFolderItem()->refreshFromRoot();
- }
- else
- {
- llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl;
- }
- }
- }
- delete item_id;
-}
-
-// static
-BOOL LLWearableBridge::canEditOnAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if(!self) return FALSE;
-
- return (gAgentWearables.isWearingItem(self->mUUID));
-}
-
-// static
-void LLWearableBridge::onEditOnAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if(self)
- {
- self->editOnAvatar();
- }
-}
-
-void LLWearableBridge::editOnAvatar()
-{
- const LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID);
- if( wearable )
- {
- // Set the tab to the right wearable.
- if (gFloaterCustomize)
- gFloaterCustomize->setCurrentWearableType( wearable->getType() );
-
- if( CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode() )
- {
- // Start Avatar Customization
- gAgent.changeCameraToCustomizeAvatar();
- }
- }
-}
-
-// static
-BOOL LLWearableBridge::canRemoveFromAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if( self && (LLAssetType::AT_BODYPART != self->mAssetType) )
- {
- return gAgentWearables.isWearingItem( self->mUUID );
- }
- return FALSE;
-}
-
-// static
-void LLWearableBridge::onRemoveFromAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if(!self) return;
- if(gAgentWearables.isWearingItem(self->mUUID))
- {
- LLViewerInventoryItem* item = self->getItem();
- if (item)
- {
- LLUUID parent_id = item->getParentUUID();
- LLWearableList::instance().getAsset(item->getAssetUUID(),
- item->getName(),
- item->getType(),
- onRemoveFromAvatarArrived,
- new OnRemoveStruct(LLUUID(self->mUUID)));
- }
- }
-}
-
-// static
-void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable,
- void* userdata)
-{
- OnRemoveStruct *on_remove_struct = (OnRemoveStruct*) userdata;
- const LLUUID &item_id = gInventory.getLinkedItemID(on_remove_struct->mUUID);
- if(wearable)
- {
- if( gAgentWearables.isWearingItem( item_id ) )
- {
- EWearableType type = wearable->getType();
-
- if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR || type==WT_EYES ) ) //&&
- //!((!gAgent.isTeen()) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) )
- {
- // MULTI_WEARABLE: FIXME HACK - always remove all
- bool do_remove_all = false;
- gAgentWearables.removeWearable( type, do_remove_all, 0 );
- }
- }
- }
-
- // Find and remove this item from the COF.
- LLInventoryModel::item_array_t items = gInventory.collectLinkedItems(item_id, LLAppearanceManager::getCOF());
- llassert(items.size() == 1); // Should always have one and only one item linked to this in the COF.
- for (LLInventoryModel::item_array_t::const_iterator iter = items.begin();
- iter != items.end();
- ++iter)
- {
- const LLViewerInventoryItem *linked_item = (*iter);
- const LLUUID &item_id = linked_item->getUUID();
- gInventory.purgeObject(item_id);
- }
- gInventory.notifyObservers();
-
- delete on_remove_struct;
-}
-
-LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_type,
- const LLUUID& uuid,LLInventoryModel* model)
-{
- LLInvFVBridgeAction* action = NULL;
- switch(asset_type)
- {
- case LLAssetType::AT_TEXTURE:
- action = new LLTextureBridgeAction(uuid,model);
- break;
-
- case LLAssetType::AT_SOUND:
- action = new LLSoundBridgeAction(uuid,model);
- break;
-
- case LLAssetType::AT_LANDMARK:
- action = new LLLandmarkBridgeAction(uuid,model);
- break;
-
- case LLAssetType::AT_CALLINGCARD:
- action = new LLCallingCardBridgeAction(uuid,model);
- break;
-
- case LLAssetType::AT_OBJECT:
- action = new LLObjectBridgeAction(uuid,model);
- break;
-
- case LLAssetType::AT_NOTECARD:
- action = new LLNotecardBridgeAction(uuid,model);
- break;
-
- case LLAssetType::AT_ANIMATION:
- action = new LLAnimationBridgeAction(uuid,model);
- break;
-
- case LLAssetType::AT_GESTURE:
- action = new LLGestureBridgeAction(uuid,model);
- break;
-
- case LLAssetType::AT_LSL_TEXT:
- action = new LLLSLTextBridgeAction(uuid,model);
- break;
-
- case LLAssetType::AT_CLOTHING:
- case LLAssetType::AT_BODYPART:
- action = new LLWearableBridgeAction(uuid,model);
-
- break;
-
- default:
- break;
- }
- return action;
-}
-
-//static
-void LLInvFVBridgeAction::doAction(LLAssetType::EType asset_type,
- const LLUUID& uuid,LLInventoryModel* model)
-{
- LLInvFVBridgeAction* action = createAction(asset_type,uuid,model);
- if(action)
- {
- action->doIt();
- delete action;
- }
-}
-
-//static
-void LLInvFVBridgeAction::doAction(const LLUUID& uuid, LLInventoryModel* model)
-{
- LLAssetType::EType asset_type = model->getItem(uuid)->getType();
- LLInvFVBridgeAction* action = createAction(asset_type,uuid,model);
- if(action)
- {
- action->doIt();
- delete action;
- }
-}
-
-LLViewerInventoryItem* LLInvFVBridgeAction::getItem() const
-{
- if(mModel)
- return (LLViewerInventoryItem*)mModel->getItem(mUUID);
- return NULL;
-}
-
-//virtual
-void LLTextureBridgeAction::doIt()
-{
- if (getItem())
- {
- LLFloaterReg::showInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES);
- }
-
- LLInvFVBridgeAction::doIt();
-}
-
-//virtual
-void LLSoundBridgeAction::doIt()
-{
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- LLFloaterReg::showInstance("preview_sound", LLSD(mUUID), TAKE_FOCUS_YES);
- }
-
- LLInvFVBridgeAction::doIt();
-}
-
-
-//virtual
-void LLLandmarkBridgeAction::doIt()
-{
- LLViewerInventoryItem* item = getItem();
- if( item )
- {
- // Opening (double-clicking) a landmark immediately teleports,
- // but warns you the first time.
- LLSD payload;
- payload["asset_id"] = item->getAssetUUID();
- LLNotifications::instance().add("TeleportFromLandmark", LLSD(), payload);
- }
-
- LLInvFVBridgeAction::doIt();
-}
-
-
-//virtual
-void LLCallingCardBridgeAction::doIt()
-{
- LLViewerInventoryItem* item = getItem();
- if(item && item->getCreatorUUID().notNull())
- {
- LLAvatarActions::showProfile(item->getCreatorUUID());
- }
-
- LLInvFVBridgeAction::doIt();
-}
-
-//virtual
-void
-LLNotecardBridgeAction::doIt()
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES);
- }
-
- LLInvFVBridgeAction::doIt();
-}
-
-//virtual
-void LLGestureBridgeAction::doIt()
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null);
- preview->setFocus(TRUE);
- }
-
- LLInvFVBridgeAction::doIt();
-}
-
-//virtual
-void LLAnimationBridgeAction::doIt()
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES);
- }
-
- LLInvFVBridgeAction::doIt();
-}
-
-
-//virtual
-void LLObjectBridgeAction::doIt()
-{
- LLFloaterReg::showInstance("properties", mUUID);
-
- LLInvFVBridgeAction::doIt();
-}
-
-
-//virtual
-void LLLSLTextBridgeAction::doIt()
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES);
- }
-
- LLInvFVBridgeAction::doIt();
-}
-
-
-BOOL LLWearableBridgeAction::isInTrash() const
-{
- if(!mModel) return FALSE;
- const LLUUID trash_id = mModel->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- return mModel->isObjectDescendentOf(mUUID, trash_id);
-}
-
-BOOL LLWearableBridgeAction::isAgentInventory() const
-{
- if(!mModel) return FALSE;
- if(gInventory.getRootFolderID() == mUUID) return TRUE;
- return mModel->isObjectDescendentOf(mUUID, gInventory.getRootFolderID());
-}
-
-void LLWearableBridgeAction::wearOnAvatar()
-{
- // Don't wear anything until initial wearables are loaded, can
- // destroy clothing items.
- if (!gAgentWearables.areWearablesLoaded())
- {
- LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded");
- return;
- }
-
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- if(!isAgentInventory())
- {
- LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- LLUUID::null,
- std::string(),
- cb);
- }
- else
- {
- wear_inventory_item_on_avatar(item);
- }
- }
-}
-
-//virtual
-void LLWearableBridgeAction::doIt()
-{
- if(isInTrash())
- {
- LLNotifications::instance().add("CannotWearTrash");
- }
- else if(isAgentInventory())
- {
- if(!gAgentWearables.isWearingItem(mUUID))
- {
- wearOnAvatar();
- }
- }
- else
- {
- // must be in the inventory library. copy it to our inventory
- // and put it on right away.
- LLViewerInventoryItem* item = getItem();
- if(item && item->isComplete())
- {
- LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- LLUUID::null,
- std::string(),
- cb);
- }
- else if(item)
- {
- // *TODO: We should fetch the item details, and then do
- // the operation above.
- LLNotifications::instance().add("CannotWearInfoNotComplete");
- }
- }
-
- LLInvFVBridgeAction::doIt();
-}
-
-// +=================================================+
-// | LLLinkItemBridge |
-// +=================================================+
-// For broken links
-
-std::string LLLinkItemBridge::sPrefix("Link: ");
-
-
-LLUIImagePtr LLLinkItemBridge::getIcon() const
-{
- if (LLViewerInventoryItem *item = getItem())
- {
- return get_item_icon(item->getActualType(), LLInventoryType::IT_NONE, 0, FALSE);
- }
- return get_item_icon(LLAssetType::AT_LINK, LLInventoryType::IT_NONE, 0, FALSE);
-}
-
-void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- // *TODO: Translate
- lldebugs << "LLLink::buildContextMenu()" << llendl;
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
-
- if(isInTrash())
- {
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
-
- items.push_back(std::string("Restore Item"));
- }
- else
- {
- items.push_back(std::string("Delete"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Delete"));
- }
- }
- hide_context_entries(menu, items, disabled_items);
-}
-
-
-// +=================================================+
-// | LLLinkBridge |
-// +=================================================+
-// For broken links.
-
-std::string LLLinkFolderBridge::sPrefix("Link: ");
-
-
-LLUIImagePtr LLLinkFolderBridge::getIcon() const
-{
- LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
- if (LLViewerInventoryItem *item = getItem())
- {
- if (const LLViewerInventoryCategory* cat = item->getLinkedCategory())
- {
- preferred_type = cat->getPreferredType();
- }
- }
- return LLFolderBridge::getIcon(preferred_type);
-}
-
-void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- // *TODO: Translate
- lldebugs << "LLLink::buildContextMenu()" << llendl;
- std::vector<std::string> items;
- std::vector<std::string> disabled_items;
-
- if(isInTrash())
- {
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
-
- items.push_back(std::string("Restore Item"));
- }
- else
- {
- items.push_back(std::string("Goto Link"));
- items.push_back(std::string("Delete"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Delete"));
- }
- }
- hide_context_entries(menu, items, disabled_items);
-}
-
-void LLLinkFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
-{
- if ("goto" == action)
- {
- gotoItem(folder);
- return;
- }
- LLItemBridge::performAction(folder,model,action);
-}
-
-void LLLinkFolderBridge::gotoItem(LLFolderView *folder)
-{
- const LLUUID &cat_uuid = getFolderID();
- if (!cat_uuid.isNull())
- {
- if (LLFolderViewItem *base_folder = folder->getItemByID(cat_uuid))
- {
- if (LLInventoryModel* model = getInventoryModel())
- {
- model->fetchDescendentsOf(cat_uuid);
- }
- base_folder->setOpen(TRUE);
- folder->setSelectionFromRoot(base_folder,TRUE);
- folder->scrollToShowSelection();
- }
- }
-}
-
-const LLUUID &LLLinkFolderBridge::getFolderID() const
-{
- if (LLViewerInventoryItem *link_item = getItem())
- {
- if (const LLViewerInventoryCategory *cat = link_item->getLinkedCategory())
- {
- const LLUUID& cat_uuid = cat->getUUID();
- return cat_uuid;
- }
- }
- return LLUUID::null;
-}
+/**
+ * @file llinventorybridge.cpp
+ * @brief Implementation of the Inventory-Folder-View-Bridge classes.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <utility> // for std::pair<>
+
+#include "llfloaterinventory.h"
+#include "llinventorybridge.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llagentwearables.h"
+#include "llcallingcard.h"
+#include "llcheckboxctrl.h" // for radio buttons
+#include "llfloaterreg.h"
+#include "llradiogroup.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "llui.h"
+
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfoldertype.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterproperties.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llfolderview.h"
+#include "llfriendcard.h"
+#include "llavataractions.h"
+#include "llgesturemgr.h"
+#include "lliconctrl.h"
+#include "llinventoryfunctions.h"
+#include "llinventorymodel.h"
+#include "llinventorypanel.h"
+#include "llinventoryclipboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llpreviewanim.h"
+#include "llpreviewgesture.h"
+#include "llpreviewnotecard.h"
+#include "llpreviewscript.h"
+#include "llpreviewsound.h"
+#include "llpreviewtexture.h"
+#include "llresmgr.h"
+#include "llscrollcontainer.h"
+#include "llimview.h"
+#include "lltooldraganddrop.h"
+#include "llviewerfoldertype.h"
+#include "llviewertexturelist.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llwearable.h"
+#include "llwearablelist.h"
+#include "llviewerassettype.h"
+#include "llviewermessage.h"
+#include "llviewerregion.h"
+#include "llvoavatarself.h"
+#include "lltabcontainer.h"
+#include "lluictrlfactory.h"
+#include "llselectmgr.h"
+#include "llsidetray.h"
+#include "llfloateropenobject.h"
+#include "lltrans.h"
+#include "llappearancemgr.h"
+#include "llimfloater.h"
+
+using namespace LLOldEvents;
+
+// Helpers
+// bug in busy count inc/dec right now, logic is complex... do we really need it?
+void inc_busy_count()
+{
+// gViewerWindow->getWindow()->incBusyCount();
+// check balance of these calls if this code is changed to ever actually
+// *do* something!
+}
+void dec_busy_count()
+{
+// gViewerWindow->getWindow()->decBusyCount();
+// check balance of these calls if this code is changed to ever actually
+// *do* something!
+}
+
+// Function declarations
+void wear_add_inventory_item_on_avatar(LLInventoryItem* item);
+void remove_inventory_category_from_avatar(LLInventoryCategory* category);
+void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id);
+bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*);
+bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response);
+
+std::string ICON_NAME[ICON_NAME_COUNT] =
+{
+ "Inv_Texture",
+ "Inv_Sound",
+ "Inv_CallingCard",
+ "Inv_CallingCard",
+ "Inv_Landmark",
+ "Inv_Landmark",
+ "Inv_Script",
+ "Inv_Clothing",
+ "Inv_Object",
+ "Inv_Object",
+ "Inv_Notecard",
+ "Inv_Skin",
+ "Inv_Snapshot",
+
+ "Inv_BodyShape",
+ "Inv_Skin",
+ "Inv_Hair",
+ "Inv_Eye",
+ "Inv_Shirt",
+ "Inv_Pants",
+ "Inv_Shoe",
+ "Inv_Socks",
+ "Inv_Jacket",
+ "Inv_Gloves",
+ "Inv_Undershirt",
+ "Inv_Underpants",
+ "Inv_Skirt",
+ "Inv_Alpha",
+ "Inv_Tattoo",
+
+ "Inv_Animation",
+ "Inv_Gesture",
+
+ "inv_item_linkitem.tga",
+ "inv_item_linkfolder.tga"
+};
+
+
+// +=================================================+
+// | LLInventoryPanelObserver |
+// +=================================================+
+void LLInventoryPanelObserver::changed(U32 mask)
+{
+ mIP->modelChanged(mask);
+}
+
+
+// +=================================================+
+// | LLInvFVBridge |
+// +=================================================+
+
+LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+mUUID(uuid), mInvType(LLInventoryType::IT_NONE)
+{
+ mInventoryPanel = inventory->getHandle();
+}
+
+const std::string& LLInvFVBridge::getName() const
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if(obj)
+ {
+ return obj->getName();
+ }
+ return LLStringUtil::null;
+}
+
+const std::string& LLInvFVBridge::getDisplayName() const
+{
+ return getName();
+}
+
+// Folders have full perms
+PermissionMask LLInvFVBridge::getPermissionMask() const
+{
+
+ return PERM_ALL;
+}
+
+// virtual
+LLFolderType::EType LLInvFVBridge::getPreferredType() const
+{
+ return LLFolderType::FT_NONE;
+}
+
+
+// Folders don't have creation dates.
+time_t LLInvFVBridge::getCreationDate() const
+{
+ return 0;
+}
+
+// Can be destoryed (or moved to trash)
+BOOL LLInvFVBridge::isItemRemovable()
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ if(model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// Sends an update to all link items that point to the base item.
+void LLInvFVBridge::renameLinkedItems(const LLUUID &item_id, const std::string& new_name)
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+
+ LLInventoryItem* itemp = model->getItem(mUUID);
+ if (!itemp) return;
+
+ if (itemp->getIsLinkType())
+ {
+ return;
+ }
+
+ LLInventoryModel::item_array_t item_array = model->collectLinkedItems(item_id);
+ for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
+ iter != item_array.end();
+ iter++)
+ {
+ LLViewerInventoryItem *linked_item = (*iter);
+ if (linked_item->getUUID() == item_id) continue;
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(linked_item);
+ new_item->rename(new_name);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+ // model->addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
+ }
+ model->notifyObservers();
+}
+
+// Can be moved to another folder
+BOOL LLInvFVBridge::isItemMovable() const
+{
+ return TRUE;
+}
+
+/*virtual*/
+/**
+ * @brief Adds this item into clipboard storage
+ */
+void LLInvFVBridge::cutToClipboard()
+{
+ if(isItemMovable())
+ {
+ LLInventoryClipboard::instance().cut(mUUID);
+ }
+}
+// *TODO: make sure this does the right thing
+void LLInvFVBridge::showProperties()
+{
+ LLSD key;
+ key["id"] = mUUID;
+ LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
+
+ LLFloaterReg::showInstance("properties", mUUID);
+}
+
+void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch)
+{
+ // Deactivate gestures when moving them into Trash
+ LLInvFVBridge* bridge;
+ LLInventoryModel* model = getInventoryModel();
+ LLViewerInventoryItem* item = NULL;
+ LLViewerInventoryCategory* cat = NULL;
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ S32 count = batch.count();
+ S32 i,j;
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
+ if (item)
+ {
+ if(LLAssetType::AT_GESTURE == item->getType())
+ {
+ LLGestureManager::instance().deactivateGesture(item->getUUID());
+ }
+ }
+ }
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
+ if (cat)
+ {
+ gInventory.collectDescendents( cat->getUUID(), descendent_categories, descendent_items, FALSE );
+ for (j=0; j<descendent_items.count(); j++)
+ {
+ if(LLAssetType::AT_GESTURE == descendent_items[j]->getType())
+ {
+ LLGestureManager::instance().deactivateGesture(descendent_items[j]->getUUID());
+ }
+ }
+ }
+ }
+ removeBatchNoCheck(batch);
+}
+
+void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch)
+{
+ // this method moves a bunch of items and folders to the trash. As
+ // per design guidelines for the inventory model, the message is
+ // built and the accounting is performed first. After all of that,
+ // we call LLInventoryModel::moveObject() to move everything
+ // around.
+ LLInvFVBridge* bridge;
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ LLMessageSystem* msg = gMessageSystem;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ LLViewerInventoryItem* item = NULL;
+ LLViewerInventoryCategory* cat = NULL;
+ std::vector<LLUUID> move_ids;
+ LLInventoryModel::update_map_t update;
+ bool start_new_message = true;
+ S32 count = batch.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
+ if(item)
+ {
+ if(item->getParentUUID() == trash_id) continue;
+ move_ids.push_back(item->getUUID());
+ LLPreview::hide(item->getUUID());
+ --update[item->getParentUUID()];
+ ++update[trash_id];
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_Stamp, TRUE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
+ msg->addUUIDFast(_PREHASH_FolderID, trash_id);
+ msg->addString("NewName", NULL);
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ }
+ }
+ if(!start_new_message)
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
+ if(cat)
+ {
+ if(cat->getParentUUID() == trash_id) continue;
+ move_ids.push_back(cat->getUUID());
+ --update[cat->getParentUUID()];
+ ++update[trash_id];
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_MoveInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOL("Stamp", TRUE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID());
+ msg->addUUIDFast(_PREHASH_ParentID, trash_id);
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ }
+
+ // move everything.
+ std::vector<LLUUID>::iterator it = move_ids.begin();
+ std::vector<LLUUID>::iterator end = move_ids.end();
+ for(; it != end; ++it)
+ {
+ gInventory.moveObject((*it), trash_id);
+ }
+
+ // notify inventory observers.
+ model->notifyObservers();
+}
+
+BOOL LLInvFVBridge::isClipboardPasteable() const
+{
+ if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
+ {
+ return FALSE;
+ }
+ LLInventoryModel* model = getInventoryModel();
+ if (!model)
+ {
+ return FALSE;
+ }
+
+ const LLUUID &agent_id = gAgent.getID();
+
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLUUID &item_id = objects.get(i);
+
+ // Can't paste folders
+ const LLInventoryCategory *cat = model->getCategory(item_id);
+ if (cat)
+ {
+ return FALSE;
+ }
+
+ const LLInventoryItem *item = model->getItem(item_id);
+ if (item)
+ {
+ if (!item->getPermissions().allowCopyBy(agent_id))
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLInvFVBridge::isClipboardPasteableAsLink() const
+{
+ if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
+ {
+ return FALSE;
+ }
+ const LLInventoryModel* model = getInventoryModel();
+ if (!model)
+ {
+ return FALSE;
+ }
+
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLInventoryItem *item = model->getItem(objects.get(i));
+ if (item)
+ {
+ if (!LLAssetType::lookupCanLink(item->getActualType()))
+ {
+ return FALSE;
+ }
+ }
+ const LLViewerInventoryCategory *cat = model->getCategory(objects.get(i));
+ if (cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void hide_context_entries(LLMenuGL& menu,
+ const std::vector<std::string> &entries_to_show,
+ const std::vector<std::string> &disabled_entries)
+{
+ const LLView::child_list_t *list = menu.getChildList();
+
+ LLView::child_list_t::const_iterator itor;
+ for (itor = list->begin(); itor != list->end(); ++itor)
+ {
+ std::string name = (*itor)->getName();
+
+ // descend into split menus:
+ LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(*itor);
+ if ((name == "More") && branchp)
+ {
+ hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries);
+ }
+
+
+ bool found = false;
+ std::vector<std::string>::const_iterator itor2;
+ for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2)
+ {
+ if (*itor2 == name)
+ {
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ (*itor)->setVisible(FALSE);
+ }
+ else
+ {
+ for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2)
+ {
+ if (*itor2 == name)
+ {
+ (*itor)->setEnabled(FALSE);
+ }
+ }
+ }
+ }
+}
+
+// Helper for commonly-used entries
+void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
+ std::vector<std::string> &items,
+ std::vector<std::string> &disabled_items, U32 flags)
+{
+ items.push_back(std::string("Rename"));
+ if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Rename"));
+ }
+
+ if (show_asset_id)
+ {
+ items.push_back(std::string("Copy Asset UUID"));
+ if ( (! ( isItemPermissive() || gAgent.isGodlike() ) )
+ || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Copy Asset UUID"));
+ }
+ }
+
+ items.push_back(std::string("Copy Separator"));
+
+ items.push_back(std::string("Copy"));
+ if (!isItemCopyable())
+ {
+ disabled_items.push_back(std::string("Copy"));
+ }
+
+ items.push_back(std::string("Paste"));
+ if (!isClipboardPasteable() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Paste"));
+ }
+
+ items.push_back(std::string("Paste As Link"));
+ if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Paste As Link"));
+ }
+ items.push_back(std::string("Paste Separator"));
+
+ items.push_back(std::string("Delete"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Delete"));
+ }
+
+ // If multiple items are selected, disable properties (if it exists).
+ if ((flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Properties"));
+ }
+}
+
+void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLInvFVBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("PurgeItem"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("PurgeItem"));
+ }
+ items.push_back(std::string("RestoreItem"));
+ }
+ else
+ {
+ items.push_back(std::string("Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// *TODO: remove this
+BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const
+{
+ BOOL rv = FALSE;
+
+ const LLInventoryObject* obj = getInventoryObject();
+
+ if(obj)
+ {
+ *type = LLViewerAssetType::lookupDragAndDropType(obj->getActualType());
+ if(*type == DAD_NONE)
+ {
+ return FALSE;
+ }
+
+ *id = obj->getUUID();
+ //object_ids.put(obj->getUUID());
+
+ if (*type == DAD_CATEGORY)
+ {
+ gInventory.startBackgroundFetch(obj->getUUID());
+ }
+
+ rv = TRUE;
+ }
+
+ return rv;
+}
+
+LLInventoryObject* LLInvFVBridge::getInventoryObject() const
+{
+ LLInventoryObject* obj = NULL;
+ LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ obj = (LLInventoryObject*)model->getObject(mUUID);
+ }
+ return obj;
+}
+
+LLInventoryModel* LLInvFVBridge::getInventoryModel() const
+{
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
+ return panel ? panel->getModel() : NULL;
+}
+
+BOOL LLInvFVBridge::isInTrash() const
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ return model->isObjectDescendentOf(mUUID, trash_id);
+}
+
+BOOL LLInvFVBridge::isLinkedObjectInTrash() const
+{
+ if (isInTrash()) return TRUE;
+
+ const LLInventoryObject *obj = getInventoryObject();
+ if (obj && obj->getIsLinkType())
+ {
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id);
+ }
+ return FALSE;
+}
+
+BOOL LLInvFVBridge::isAgentInventory() const
+{
+ const LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ if(gInventory.getRootFolderID() == mUUID) return TRUE;
+ return model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID());
+}
+
+BOOL LLInvFVBridge::isCOFFolder() const
+{
+ const LLInventoryModel* model = getInventoryModel();
+ if(!model) return TRUE;
+ const LLUUID cof_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ if (mUUID == cof_id || model->isObjectDescendentOf(mUUID, cof_id))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLInvFVBridge::isItemPermissive() const
+{
+ return FALSE;
+}
+
+// static
+void LLInvFVBridge::changeItemParent(LLInventoryModel* model,
+ LLViewerInventoryItem* item,
+ const LLUUID& new_parent,
+ BOOL restamp)
+{
+ if(item->getParentUUID() != new_parent)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setParent(new_parent);
+ new_item->updateParentOnServer(restamp);
+ model->updateItem(new_item);
+ model->notifyObservers();
+ }
+}
+
+// static
+void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model,
+ LLViewerInventoryCategory* cat,
+ const LLUUID& new_parent,
+ BOOL restamp)
+{
+ if(cat->getParentUUID() != new_parent)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+ new_cat->setParent(new_parent);
+ new_cat->updateParentOnServer(restamp);
+ model->updateCategory(new_cat);
+ model->notifyObservers();
+ }
+}
+
+
+const std::string safe_inv_type_lookup(LLInventoryType::EType inv_type)
+{
+ const std::string rv= LLInventoryType::lookup(inv_type);
+ if(rv.empty())
+ {
+ return std::string("<invalid>");
+ }
+ return rv;
+}
+
+LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
+ LLAssetType::EType actual_asset_type,
+ LLInventoryType::EType inv_type,
+ LLInventoryPanel* inventory,
+ const LLUUID& uuid,
+ U32 flags)
+{
+ LLInvFVBridge* new_listener = NULL;
+ switch(asset_type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLTextureBridge(inventory, uuid, inv_type);
+ break;
+
+ case LLAssetType::AT_SOUND:
+ if(!(inv_type == LLInventoryType::IT_SOUND))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLSoundBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_LANDMARK:
+ if(!(inv_type == LLInventoryType::IT_LANDMARK))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLLandmarkBridge(inventory, uuid, flags);
+ break;
+
+ case LLAssetType::AT_CALLINGCARD:
+ if(!(inv_type == LLInventoryType::IT_CALLINGCARD))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLCallingCardBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_SCRIPT:
+ if(!(inv_type == LLInventoryType::IT_LSL))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLScriptBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags);
+ break;
+
+ case LLAssetType::AT_NOTECARD:
+ if(!(inv_type == LLInventoryType::IT_NOTECARD))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLNotecardBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_ANIMATION:
+ if(!(inv_type == LLInventoryType::IT_ANIMATION))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLAnimationBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_GESTURE:
+ if(!(inv_type == LLInventoryType::IT_GESTURE))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLGestureBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_LSL_TEXT:
+ if(!(inv_type == LLInventoryType::IT_LSL))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLLSLTextBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ if(!(inv_type == LLInventoryType::IT_WEARABLE))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags);
+ break;
+ case LLAssetType::AT_CATEGORY:
+ case LLAssetType::AT_ROOT_CATEGORY:
+ if (actual_asset_type == LLAssetType::AT_LINK_FOLDER)
+ {
+ // Create a link folder handler instead.
+ new_listener = new LLLinkFolderBridge(inventory, uuid);
+ break;
+ }
+ new_listener = new LLFolderBridge(inventory, uuid);
+ break;
+ case LLAssetType::AT_LINK:
+ // Only should happen for broken links.
+ new_listener = new LLLinkItemBridge(inventory, uuid);
+ break;
+ case LLAssetType::AT_LINK_FOLDER:
+ // Only should happen for broken links.
+ new_listener = new LLLinkItemBridge(inventory, uuid);
+ break;
+ default:
+ llinfos << "Unhandled asset type (llassetstorage.h): "
+ << (S32)asset_type << llendl;
+ break;
+ }
+
+ if (new_listener)
+ {
+ new_listener->mInvType = inv_type;
+ }
+
+ return new_listener;
+}
+
+void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid)
+{
+ LLInventoryCategory* cat = model->getCategory(uuid);
+ if (cat)
+ {
+ model->purgeDescendentsOf(uuid);
+ model->notifyObservers();
+ }
+ LLInventoryObject* obj = model->getObject(uuid);
+ if (obj)
+ {
+ model->purgeObject(uuid);
+ model->notifyObservers();
+ }
+}
+
+// +=================================================+
+// | InventoryFVBridgeBuilder |
+// +=================================================+
+LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset_type,
+ LLAssetType::EType actual_asset_type,
+ LLInventoryType::EType inv_type,
+ LLInventoryPanel* inventory,
+ const LLUUID& uuid,
+ U32 flags /* = 0x00 */) const
+{
+ return LLInvFVBridge::createBridge(asset_type,
+ actual_asset_type,
+ inv_type,
+ inventory,
+ uuid,
+ flags);
+}
+
+// +=================================================+
+// | LLItemBridge |
+// +=================================================+
+
+void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("goto" == action)
+ {
+ gotoItem(folder);
+ }
+ if ("open" == action)
+ {
+ openItem();
+ return;
+ }
+ else if ("properties" == action)
+ {
+ showProperties();
+ return;
+ }
+ else if ("purge" == action)
+ {
+ purgeItem(model, mUUID);
+ return;
+ }
+ else if ("restoreToWorld" == action)
+ {
+ restoreToWorld();
+ return;
+ }
+ else if ("restore" == action)
+ {
+ restoreItem();
+ return;
+ }
+ else if ("copy_uuid" == action)
+ {
+ // Single item only
+ LLInventoryItem* item = model->getItem(mUUID);
+ if(!item) return;
+ LLUUID asset_id = item->getAssetUUID();
+ std::string buffer;
+ asset_id.toString(buffer);
+
+ gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer));
+ return;
+ }
+ else if ("copy" == action)
+ {
+ copyToClipboard();
+ return;
+ }
+ else if ("paste" == action)
+ {
+ // Single item only
+ LLInventoryItem* itemp = model->getItem(mUUID);
+ if (!itemp) return;
+
+ LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID());
+ if (!folder_view_itemp) return;
+
+ folder_view_itemp->getListener()->pasteFromClipboard();
+ return;
+ }
+ else if ("paste_link" == action)
+ {
+ // Single item only
+ LLInventoryItem* itemp = model->getItem(mUUID);
+ if (!itemp) return;
+
+ LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID());
+ if (!folder_view_itemp) return;
+
+ folder_view_itemp->getListener()->pasteLinkFromClipboard();
+ return;
+ }
+}
+
+void LLItemBridge::selectItem()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ if(item && !item->isComplete())
+ {
+ item->fetchFromServer();
+ }
+}
+
+void LLItemBridge::restoreItem()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ if(item)
+ {
+ LLInventoryModel* model = getInventoryModel();
+ const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(item->getType()));
+ // do not restamp on restore.
+ LLInvFVBridge::changeItemParent(model, item, new_parent, FALSE);
+ }
+}
+
+void LLItemBridge::restoreToWorld()
+{
+ LLViewerInventoryItem* itemp = (LLViewerInventoryItem*)getItem();
+ if (itemp)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("RezRestoreToWorld");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ itemp->packMessage(msg);
+ msg->sendReliable(gAgent.getRegion()->getHost());
+ }
+
+ //Similar functionality to the drag and drop rez logic
+ BOOL remove_from_inventory = FALSE;
+
+ //remove local inventory copy, sim will deal with permissions and removing the item
+ //from the actual inventory if its a no-copy etc
+ if(!itemp->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ remove_from_inventory = TRUE;
+ }
+
+ // Check if it's in the trash. (again similar to the normal rez logic)
+ const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ if(gInventory.isObjectDescendentOf(itemp->getUUID(), trash_id))
+ {
+ remove_from_inventory = TRUE;
+ }
+
+ if(remove_from_inventory)
+ {
+ gInventory.deleteObject(itemp->getUUID());
+ gInventory.notifyObservers();
+ }
+}
+
+void LLItemBridge::gotoItem(LLFolderView *folder)
+{
+ LLInventoryObject *obj = getInventoryObject();
+ if (obj && obj->getIsLinkType())
+ {
+ LLInventoryPanel* active_panel = LLFloaterInventory::getActiveInventory()->getPanel();
+ if (active_panel)
+ {
+ active_panel->setSelection(obj->getLinkedUUID(), TAKE_FOCUS_NO);
+ }
+ }
+}
+
+LLUIImagePtr LLItemBridge::getIcon() const
+{
+ return LLUI::getUIImage(ICON_NAME[OBJECT_ICON_NAME]);
+}
+
+PermissionMask LLItemBridge::getPermissionMask() const
+{
+ LLViewerInventoryItem* item = getItem();
+ PermissionMask perm_mask = 0;
+ if(item)
+ {
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+
+ if (copy) perm_mask |= PERM_COPY;
+ if (mod) perm_mask |= PERM_MODIFY;
+ if (xfer) perm_mask |= PERM_TRANSFER;
+
+ }
+ return perm_mask;
+}
+
+const std::string& LLItemBridge::getDisplayName() const
+{
+ if(mDisplayName.empty())
+ {
+ buildDisplayName(getItem(), mDisplayName);
+ }
+ return mDisplayName;
+}
+
+void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name)
+{
+ if(item)
+ {
+ name.assign(item->getName());
+ }
+ else
+ {
+ name.assign(LLStringUtil::null);
+ }
+}
+
+LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const
+{
+ U8 font = LLFontGL::NORMAL;
+
+ if( gAgentWearables.isWearingItem( mUUID ) )
+ {
+ // llinfos << "BOLD" << llendl;
+ font |= LLFontGL::BOLD;
+ }
+
+ const LLViewerInventoryItem* item = getItem();
+ if (item && item->getIsLinkType())
+ {
+ font |= LLFontGL::ITALIC;
+ }
+ return (LLFontGL::StyleFlags)font;
+}
+
+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 LINK = LLTrans::getString("link");
+ static std::string BROKEN_LINK = LLTrans::getString("broken_link");
+ std::string suffix;
+ LLInventoryItem* item = getItem();
+ if(item)
+ {
+ // it's a bit confusing to put nocopy/nomod/etc on calling cards.
+ if(LLAssetType::AT_CALLINGCARD != item->getType()
+ && item->getPermissions().getOwner() == gAgent.getID())
+ {
+ BOOL broken_link = LLAssetType::lookupIsLinkType(item->getType());
+ if (broken_link) return BROKEN_LINK;
+
+ BOOL link = item->getIsLinkType();
+ if (link) return LINK;
+
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ if (!copy)
+ {
+ suffix += NO_COPY;
+ }
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ if (!mod)
+ {
+ suffix += NO_MOD;
+ }
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+ if (!xfer)
+ {
+ suffix += NO_XFER;
+ }
+ }
+ }
+ return suffix;
+}
+
+time_t LLItemBridge::getCreationDate() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ return item->getCreationDate();
+ }
+ return 0;
+}
+
+
+BOOL LLItemBridge::isItemRenameable() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ // (For now) Don't allow calling card rename since that may confuse users as to
+ // what the calling card points to.
+ if (item->getInventoryType() == LLInventoryType::IT_CALLINGCARD)
+ {
+ return FALSE;
+ }
+ return (item->getPermissions().allowModifyBy(gAgent.getID()));
+ }
+ return FALSE;
+}
+
+BOOL LLItemBridge::renameItem(const std::string& new_name)
+{
+ if(!isItemRenameable())
+ return FALSE;
+ LLPreview::dirty(mUUID);
+ LLInventoryModel* model = getInventoryModel();
+ if(!model)
+ return FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item && (item->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(new_name);
+ buildDisplayName(new_item, mDisplayName);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+
+ model->notifyObservers();
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+
+BOOL LLItemBridge::removeItem()
+{
+ if(!isItemRemovable())
+ {
+ return FALSE;
+ }
+ // move it to the trash
+ LLPreview::hide(mUUID, TRUE);
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ LLViewerInventoryItem* item = getItem();
+
+ // if item is not already in trash
+ if(item && !model->isObjectDescendentOf(mUUID, trash_id))
+ {
+ // move to trash, and restamp
+ LLInvFVBridge::changeItemParent(model, item, trash_id, TRUE);
+ // delete was successful
+ return TRUE;
+ }
+ else
+ {
+ // tried to delete already item in trash (should purge?)
+ return FALSE;
+ }
+}
+
+BOOL LLItemBridge::isItemCopyable() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ // can't copy worn objects. DEV-15183
+ LLVOAvatarSelf *avatarp = gAgent.getAvatarObject();
+ if( !avatarp )
+ {
+ return FALSE;
+ }
+
+ if(avatarp->isWearingAttachment(mUUID))
+ {
+ return FALSE;
+ }
+
+ // All items can be copied, not all can be pasted.
+ // The only time an item can't be copied is if it's a link
+ // return (item->getPermissions().allowCopyBy(gAgent.getID()));
+ if (item->getIsLinkType())
+ {
+ return FALSE;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+BOOL LLItemBridge::copyToClipboard() const
+{
+ if(isItemCopyable())
+ {
+ LLInventoryClipboard::instance().add(mUUID);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+LLViewerInventoryItem* LLItemBridge::getItem() const
+{
+ LLViewerInventoryItem* item = NULL;
+ LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ item = (LLViewerInventoryItem*)model->getItem(mUUID);
+ }
+ return item;
+}
+
+BOOL LLItemBridge::isItemPermissive() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ U32 mask = item->getPermissions().getMaskBase();
+ if((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// +=================================================+
+// | LLFolderBridge |
+// +=================================================+
+
+LLFolderBridge* LLFolderBridge::sSelf=NULL;
+
+// Can be moved to another folder
+BOOL LLFolderBridge::isItemMovable() const
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if(obj)
+ {
+ return (!LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType()));
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::selectItem()
+{
+}
+
+
+// Can be destroyed (or moved to trash)
+BOOL LLFolderBridge::isItemRemovable()
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model)
+ {
+ return FALSE;
+ }
+
+ if(!model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()))
+ {
+ return FALSE;
+ }
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+ LLInventoryCategory* category = model->getCategory(mUUID);
+ if( !category )
+ {
+ return FALSE;
+ }
+
+ if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
+ {
+ return FALSE;
+ }
+
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
+
+ S32 i;
+ for( i = 0; i < descendent_categories.count(); i++ )
+ {
+ LLInventoryCategory* category = descendent_categories[i];
+ if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
+ {
+ return FALSE;
+ }
+ }
+
+ for( i = 0; i < descendent_items.count(); i++ )
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if( (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART) )
+ {
+ if(gAgentWearables.isWearingItem(item->getUUID()))
+ {
+ return FALSE;
+ }
+ }
+ else
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ if(avatar->isWearingAttachment(item->getUUID()))
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLFolderBridge::isUpToDate() const
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ if( !category )
+ {
+ return FALSE;
+ }
+
+ return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN;
+}
+
+BOOL LLFolderBridge::isItemCopyable() const
+{
+ return TRUE;
+}
+
+BOOL LLFolderBridge::copyToClipboard() const
+{
+ if(isItemCopyable())
+ {
+ LLInventoryClipboard::instance().add(mUUID);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLFolderBridge::isClipboardPasteable() const
+{
+ if ( ! LLInvFVBridge::isClipboardPasteable() )
+ return FALSE;
+
+ // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599
+ if ( LLFriendCardsManager::instance().isCategoryInFriendFolder( getCategory() ) )
+ {
+ LLInventoryModel* model = getInventoryModel();
+ if ( !model )
+ {
+ return FALSE;
+ }
+
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ const LLViewerInventoryCategory *current_cat = getCategory();
+
+ // Search for the direct descendent of current Friends subfolder among all pasted items,
+ // and return false if is found.
+ for(S32 i = objects.count() - 1; i >= 0; --i)
+ {
+ const LLUUID &obj_id = objects.get(i);
+ if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) )
+ {
+ return FALSE;
+ }
+ }
+
+ }
+ return TRUE;
+}
+
+BOOL LLFolderBridge::isClipboardPasteableAsLink() const
+{
+ // Check normal paste-as-link permissions
+ if (!LLInvFVBridge::isClipboardPasteableAsLink())
+ {
+ return FALSE;
+ }
+
+ const LLInventoryModel* model = getInventoryModel();
+ if (!model)
+ {
+ return FALSE;
+ }
+
+ const LLViewerInventoryCategory *current_cat = getCategory();
+ if (current_cat)
+ {
+ const BOOL is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder( current_cat );
+ const LLUUID &current_cat_id = current_cat->getUUID();
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLUUID &obj_id = objects.get(i);
+ const LLInventoryCategory *cat = model->getCategory(obj_id);
+ if (cat)
+ {
+ const LLUUID &cat_id = cat->getUUID();
+ // Don't allow recursive pasting
+ if ((cat_id == current_cat_id) ||
+ model->isObjectDescendentOf(current_cat_id, cat_id))
+ {
+ return FALSE;
+ }
+ }
+ // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599
+ if ( is_in_friend_folder )
+ {
+ // If object is direct descendent of current Friends subfolder than return false.
+ // Note: We can't use 'const LLInventoryCategory *cat', because it may be null
+ // in case type of obj_id is LLInventoryItem.
+ if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) )
+ {
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+
+}
+
+BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
+ BOOL drop)
+{
+ // This should never happen, but if an inventory item is incorrectly parented,
+ // the UI will get confused and pass in a NULL.
+ if(!inv_cat) return FALSE;
+
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+
+ // cannot drag categories into library
+ if(!isAgentInventory())
+ {
+ return FALSE;
+ }
+
+ // check to make sure source is agent inventory, and is represented there.
+ LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
+ BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL)
+ && (LLToolDragAndDrop::SOURCE_AGENT == source);
+
+ BOOL accept = FALSE;
+ S32 i;
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ if(is_agent_inventory)
+ {
+ const LLUUID& cat_id = inv_cat->getUUID();
+
+ // Is the destination the trash?
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ BOOL move_is_into_trash = (mUUID == trash_id)
+ || model->isObjectDescendentOf(mUUID, trash_id);
+ BOOL is_movable = (!LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType()));
+ const LLUUID current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
+ BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
+ if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+ // BAP - restrictions?
+ is_movable = true;
+ }
+
+ if (mUUID == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE))
+ {
+ is_movable = FALSE; // It's generally movable but not into Favorites folder. EXT-1604
+ }
+
+ if( is_movable )
+ {
+ gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE );
+
+ for( i = 0; i < descendent_categories.count(); i++ )
+ {
+ LLInventoryCategory* category = descendent_categories[i];
+ if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
+ {
+ // ...can't move "special folders" like Textures
+ is_movable = FALSE;
+ break;
+ }
+ }
+
+ if( is_movable )
+ {
+ if( move_is_into_trash )
+ {
+ for( i = 0; i < descendent_items.count(); i++ )
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if( (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART) )
+ {
+ if( gAgentWearables.isWearingItem( item->getUUID() ) )
+ {
+ is_movable = FALSE; // It's generally movable, but not into the trash!
+ break;
+ }
+ }
+ else
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ if( avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ is_movable = FALSE; // It's generally movable, but not into the trash!
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ accept = is_movable
+ && (mUUID != cat_id) // Can't move a folder into itself
+ && (mUUID != inv_cat->getParentUUID()) // Avoid moves that would change nothing
+ && !(model->isObjectDescendentOf(mUUID, cat_id)); // Avoid circularity
+ if(accept && drop)
+ {
+ // Look for any gestures and deactivate them
+ if (move_is_into_trash)
+ {
+ for (i = 0; i < descendent_items.count(); i++)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && LLGestureManager::instance().isGestureActive(item->getUUID()))
+ {
+ LLGestureManager::instance().deactivateGesture(item->getUUID());
+ }
+ }
+ }
+ // if target is an outfit or current outfit folder we use link
+ if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+#if SUPPORT_ENSEMBLES
+ // BAP - should skip if dup.
+ if (move_is_into_current_outfit)
+ {
+ LLAppearanceManager::wearEnsemble(inv_cat);
+ }
+ else
+ {
+ LLPointer<LLInventoryCallback> cb = NULL;
+ link_inventory_item(
+ gAgent.getID(),
+ inv_cat->getUUID(),
+ mUUID,
+ inv_cat->getName(),
+ LLAssetType::AT_LINK_FOLDER,
+ cb);
+ }
+#endif
+ }
+ else
+ {
+
+ // Reparent the folder and restamp children if it's moving
+ // into trash.
+ LLInvFVBridge::changeCategoryParent(
+ model,
+ (LLViewerInventoryCategory*)inv_cat,
+ mUUID,
+ move_is_into_trash);
+ }
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_WORLD == source)
+ {
+ // content category has same ID as object itself
+ LLUUID object_id = inv_cat->getUUID();
+ LLUUID category_id = mUUID;
+ accept = move_inv_category_world_to_agent(object_id, category_id, drop);
+ }
+ return accept;
+}
+
+void warn_move_inventory(LLViewerObject* object, LLMoveInv* move_inv)
+{
+ const char* dialog = NULL;
+ if (object->flagScripted())
+ {
+ dialog = "MoveInventoryFromScriptedObject";
+ }
+ else
+ {
+ dialog = "MoveInventoryFromObject";
+ }
+ LLNotifications::instance().add(dialog, LLSD(), LLSD(), boost::bind(move_task_inventory_callback, _1, _2, move_inv));
+}
+
+// Move/copy all inventory items from the Contents folder of an in-world
+// object to the agent's inventory, inside a given category.
+BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
+ const LLUUID& category_id,
+ BOOL drop,
+ void (*callback)(S32, void*),
+ void* user_data)
+{
+ // Make sure the object exists. If we allowed dragging from
+ // anonymous objects, it would be possible to bypass
+ // permissions.
+ // content category has same ID as object itself
+ LLViewerObject* object = gObjectList.findObject(object_id);
+ if(!object)
+ {
+ llinfos << "Object not found for drop." << llendl;
+ return FALSE;
+ }
+
+ // this folder is coming from an object, as there is only one folder in an object, the root,
+ // we need to collect the entire contents and handle them as a group
+ InventoryObjectList inventory_objects;
+ object->getInventoryContents(inventory_objects);
+
+ if (inventory_objects.empty())
+ {
+ llinfos << "Object contents not found for drop." << llendl;
+ return FALSE;
+ }
+
+ BOOL accept = TRUE;
+ BOOL is_move = FALSE;
+
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ InventoryObjectList::iterator it = inventory_objects.begin();
+ InventoryObjectList::iterator end = inventory_objects.end();
+ for ( ; it != end; ++it)
+ {
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ LLPermissions perm(((LLInventoryItem*)((LLInventoryObject*)(*it)))->getPermissions());
+ 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;
+ }
+ else
+ {
+ accept = FALSE;
+ break;
+ }
+ }
+
+ if(drop && accept)
+ {
+ it = inventory_objects.begin();
+ InventoryObjectList::iterator first_it = inventory_objects.begin();
+ LLMoveInv* move_inv = new LLMoveInv;
+ move_inv->mObjectID = object_id;
+ move_inv->mCategoryID = category_id;
+ move_inv->mCallback = callback;
+ move_inv->mUserData = user_data;
+
+ for ( ; it != end; ++it)
+ {
+ two_uuids_t two(category_id, (*it)->getUUID());
+ move_inv->mMoveList.push_back(two);
+ }
+
+ if(is_move)
+ {
+ // Callback called from within here.
+ warn_move_inventory(object, move_inv);
+ }
+ else
+ {
+ LLNotification::Params params("MoveInventoryFromObject");
+ params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv));
+ LLNotifications::instance().forceResponse(params, 0);
+ }
+ }
+ return accept;
+}
+
+bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ // Valid COF items are:
+ // - links to wearables (body parts or clothing)
+ // - links to attachments
+ // - links to gestures
+ // - links to ensemble folders
+ LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem(); // BAP - safe?
+ if (linked_item)
+ {
+ LLAssetType::EType type = linked_item->getType();
+ return (type == LLAssetType::AT_CLOTHING ||
+ type == LLAssetType::AT_BODYPART ||
+ type == LLAssetType::AT_GESTURE ||
+ type == LLAssetType::AT_OBJECT);
+ }
+ else
+ {
+ LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory(); // BAP - safe?
+ // BAP remove AT_NONE support after ensembles are fully working?
+ return (linked_category &&
+ ((linked_category->getPreferredType() == LLFolderType::FT_NONE) ||
+ (LLFolderType::lookupIsEnsembleType(linked_category->getPreferredType()))));
+ }
+}
+
+
+bool LLFindWearables::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((item->getType() == LLAssetType::AT_CLOTHING)
+ || (item->getType() == LLAssetType::AT_BODYPART))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+
+//Used by LLFolderBridge as callback for directory recursion.
+class LLRightClickInventoryFetchObserver : public LLInventoryFetchObserver
+{
+public:
+ LLRightClickInventoryFetchObserver() :
+ mCopyItems(false)
+ { };
+ LLRightClickInventoryFetchObserver(const LLUUID& cat_id, bool copy_items) :
+ mCatID(cat_id),
+ mCopyItems(copy_items)
+ { };
+ virtual void done()
+ {
+ // we've downloaded all the items, so repaint the dialog
+ LLFolderBridge::staticFolderOptionsMenu();
+
+ gInventory.removeObserver(this);
+ delete this;
+ }
+
+
+protected:
+ LLUUID mCatID;
+ bool mCopyItems;
+
+};
+
+//Used by LLFolderBridge as callback for directory recursion.
+class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLRightClickInventoryFetchDescendentsObserver(bool copy_items) : mCopyItems(copy_items) {}
+ ~LLRightClickInventoryFetchDescendentsObserver() {}
+ virtual void done();
+protected:
+ bool mCopyItems;
+};
+
+void LLRightClickInventoryFetchDescendentsObserver::done()
+{
+ // Avoid passing a NULL-ref as mCompleteFolders.front() down to
+ // gInventory.collectDescendents()
+ if( mCompleteFolders.empty() )
+ {
+ llwarns << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << llendl;
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+ return;
+ }
+
+ // What we do here is get the complete information on the items in
+ // the library, 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(mCompleteFolders.front(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+ S32 count = item_array.count();
+#if 0 // HACK/TODO: Why?
+ // This early causes a giant menu to get produced, and doesn't seem to be needed.
+ if(!count)
+ {
+ llwarns << "Nothing fetched in category " << mCompleteFolders.front()
+ << llendl;
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+ return;
+ }
+#endif
+
+ LLRightClickInventoryFetchObserver* outfit;
+ outfit = new LLRightClickInventoryFetchObserver(mCompleteFolders.front(), mCopyItems);
+ LLInventoryFetchObserver::item_ref_t ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ ids.push_back(item_array.get(i)->getUUID());
+ }
+
+ // clean up, and remove this as an observer since the call to the
+ // outfit could notify observers and throw us into an infinite
+ // loop.
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+
+ // increment busy count and either tell the inventory to check &
+ // call done, or add this object to the inventory for observation.
+ inc_busy_count();
+
+ // do the fetch
+ outfit->fetchItems(ids);
+ outfit->done(); //Not interested in waiting and this will be right 99% of the time.
+//Uncomment the following code for laggy Inventory UI.
+/* if(outfit->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ outfit->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(outfit);
+ }*/
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryWearObserver
+//
+// Observer for "copy and wear" operation to support knowing
+// when the all of the contents have been added to inventory.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryCopyAndWearObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count) :mCatID(cat_id), mContentsCount(count), mFolderAdded(FALSE) {}
+ virtual ~LLInventoryCopyAndWearObserver() {}
+ virtual void changed(U32 mask);
+
+protected:
+ LLUUID mCatID;
+ int mContentsCount;
+ BOOL mFolderAdded;
+};
+
+
+
+void LLInventoryCopyAndWearObserver::changed(U32 mask)
+{
+ if((mask & (LLInventoryObserver::ADD)) != 0)
+ {
+ if (!mFolderAdded)
+ {
+ const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
+
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ for (;id_it != id_end; ++id_it)
+ {
+ if ((*id_it) == mCatID)
+ {
+ mFolderAdded = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (mFolderAdded)
+ {
+ LLViewerInventoryCategory* category = gInventory.getCategory(mCatID);
+
+ if (NULL == category)
+ {
+ llwarns << "gInventory.getCategory(" << mCatID
+ << ") was NULL" << llendl;
+ }
+ else
+ {
+ if (category->getDescendentCount() ==
+ mContentsCount)
+ {
+ gInventory.removeObserver(this);
+ LLAppearanceManager::wearInventoryCategory(category, FALSE, TRUE);
+ delete this;
+ }
+ }
+ }
+
+ }
+}
+
+
+
+void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("open" == action)
+ {
+ openItem();
+ return;
+ }
+ else if ("paste" == action)
+ {
+ pasteFromClipboard();
+ return;
+ }
+ else if ("paste_link" == action)
+ {
+ pasteLinkFromClipboard();
+ return;
+ }
+ else if ("properties" == action)
+ {
+ showProperties();
+ return;
+ }
+ else if ("replaceoutfit" == action)
+ {
+ modifyOutfit(FALSE);
+ return;
+ }
+#if SUPPORT_ENSEMBLES
+ else if ("wearasensemble" == action)
+ {
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+ LLAppearanceManager::wearEnsemble(cat,true);
+ return;
+ }
+#endif
+ else if ("addtooutfit" == action)
+ {
+ modifyOutfit(TRUE);
+ return;
+ }
+ else if ("copy" == action)
+ {
+ copyToClipboard();
+ return;
+ }
+ else if ("removefromoutfit" == action)
+ {
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+
+ remove_inventory_category_from_avatar ( cat );
+ return;
+ }
+ else if ("purge" == action)
+ {
+ purgeItem(model, mUUID);
+ return;
+ }
+ else if ("restore" == action)
+ {
+ restoreItem();
+ return;
+ }
+}
+
+void LLFolderBridge::openItem()
+{
+ lldebugs << "LLFolderBridge::openItem()" << llendl;
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ bool fetching_inventory = model->fetchDescendentsOf(mUUID);
+ // Only change folder type if we have the folder contents.
+ if (!fetching_inventory)
+ {
+ // Disabling this for now, it's causing crash when new items are added to folders
+ // since folder type may change before new item item has finished processing.
+ // determineFolderType();
+ }
+}
+
+void LLFolderBridge::closeItem()
+{
+ determineFolderType();
+}
+
+void LLFolderBridge::determineFolderType()
+{
+ if (isUpToDate())
+ {
+ LLInventoryModel* model = getInventoryModel();
+ LLViewerInventoryCategory* category = model->getCategory(mUUID);
+ category->determineFolderType();
+ }
+}
+
+BOOL LLFolderBridge::isItemRenameable() const
+{
+ LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)getCategory();
+ if(cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType())
+ && (cat->getOwnerID() == gAgent.getID()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::restoreItem()
+{
+ LLViewerInventoryCategory* cat;
+ cat = (LLViewerInventoryCategory*)getCategory();
+ if(cat)
+ {
+ LLInventoryModel* model = getInventoryModel();
+ const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType()));
+ // do not restamp children on restore
+ LLInvFVBridge::changeCategoryParent(model, cat, new_parent, FALSE);
+ }
+}
+
+LLFolderType::EType LLFolderBridge::getPreferredType() const
+{
+ LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ preferred_type = cat->getPreferredType();
+ }
+
+ return preferred_type;
+}
+
+// Icons for folders are based on the preferred type
+LLUIImagePtr LLFolderBridge::getIcon() const
+{
+ LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ preferred_type = cat->getPreferredType();
+ }
+ return getIcon(preferred_type);
+}
+
+LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type)
+{
+ // we only have one folder image now
+ return LLUI::getUIImage("Inv_FolderClosed");
+}
+
+BOOL LLFolderBridge::renameItem(const std::string& new_name)
+{
+ if(!isItemRenameable())
+ return FALSE;
+ LLInventoryModel* model = getInventoryModel();
+ if(!model)
+ return FALSE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat && (cat->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+ new_cat->rename(new_name);
+ new_cat->updateServer(FALSE);
+ model->updateCategory(new_cat);
+
+ model->notifyObservers();
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+BOOL LLFolderBridge::removeItem()
+{
+ if(!isItemRemovable())
+ {
+ return FALSE;
+ }
+ // move it to the trash
+ LLPreview::hide(mUUID);
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+
+ // Look for any gestures and deactivate them
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
+
+ S32 i;
+ for (i = 0; i < descendent_items.count(); i++)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && LLGestureManager::instance().isGestureActive(item->getUUID()))
+ {
+ LLGestureManager::instance().deactivateGesture(item->getUUID());
+ }
+ }
+
+ // go ahead and do the normal remove if no 'last calling
+ // cards' are being removed.
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE);
+ }
+
+ return TRUE;
+}
+
+void LLFolderBridge::pasteFromClipboard()
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(model && isClipboardPasteable())
+ {
+ LLInventoryItem* item = NULL;
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ const LLUUID parent_id(mUUID);
+ for(S32 i = 0; i < count; i++)
+ {
+ item = model->getItem(objects.get(i));
+ if (item)
+ {
+ if(LLInventoryClipboard::instance().isCutMode())
+ {
+ // move_inventory_item() is not enough,
+ //we have to update inventory locally too
+ changeItemParent(model, dynamic_cast<LLViewerInventoryItem*>(item), parent_id, FALSE);
+ }
+ else
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ parent_id,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+ }
+}
+
+void LLFolderBridge::pasteLinkFromClipboard()
+{
+ const LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ LLUUID parent_id(mUUID);
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLUUID &object_id = objects.get(i);
+#if SUPPORT_ENSEMBLES
+ if (LLInventoryCategory *cat = model->getCategory(object_id))
+ {
+ link_inventory_item(
+ gAgent.getID(),
+ cat->getUUID(),
+ parent_id,
+ cat->getName(),
+ LLAssetType::AT_LINK_FOLDER,
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ else
+#endif
+ if (LLInventoryItem *item = model->getItem(object_id))
+ {
+ link_inventory_item(
+ gAgent.getID(),
+ item->getUUID(),
+ parent_id,
+ item->getName(),
+ LLAssetType::AT_LINK,
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+}
+
+void LLFolderBridge::staticFolderOptionsMenu()
+{
+ if (!sSelf) return;
+ sSelf->folderOptionsMenu();
+}
+
+void LLFolderBridge::folderOptionsMenu()
+{
+ std::vector<std::string> disabled_items;
+
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+
+ const LLInventoryCategory* category = model->getCategory(mUUID);
+ LLFolderType::EType type = category->getPreferredType();
+ const bool is_default_folder = category && LLFolderType::lookupIsProtectedType(type);
+ // BAP change once we're no longer treating regular categories as ensembles.
+ const bool is_ensemble = category && (type == LLFolderType::FT_NONE ||
+ LLFolderType::lookupIsEnsembleType(type));
+
+ // calling card related functionality for folders.
+
+ // Only enable calling-card related options for non-default folders.
+ if (!is_default_folder)
+ {
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard))
+ {
+ mItems.push_back(std::string("Calling Card Separator"));
+ mItems.push_back(std::string("Conference Chat Folder"));
+ mItems.push_back(std::string("IM All Contacts In Folder"));
+ }
+ }
+
+ // wearables related functionality for folders.
+ //is_wearable
+ LLFindWearables is_wearable;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+
+ if (mWearables ||
+ checkFolderForContentsOfType(model, is_wearable) ||
+ checkFolderForContentsOfType(model, is_object) ||
+ checkFolderForContentsOfType(model, is_gesture) )
+ {
+ mItems.push_back(std::string("Folder Wearables Separator"));
+
+ // Only enable add/replace outfit for non-default folders.
+ if (!is_default_folder)
+ {
+ mItems.push_back(std::string("Add To Outfit"));
+ mItems.push_back(std::string("Replace Outfit"));
+ }
+ if (is_ensemble)
+ {
+ mItems.push_back(std::string("Wear As Ensemble"));
+ }
+ mItems.push_back(std::string("Take Off Items"));
+ }
+ hide_context_entries(*mMenu, mItems, disabled_items);
+}
+
+BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type)
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ model->collectDescendentsIf(mUUID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_type);
+ return ((item_array.count() > 0) ? TRUE : FALSE );
+}
+
+// Flags unused
+void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ mItems.clear();
+ mDisabledItems.clear();
+
+ lldebugs << "LLFolderBridge::buildContextMenu()" << llendl;
+// std::vector<std::string> disabled_items;
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
+
+ mItems.clear(); //adding code to clear out member Items (which means Items should not have other data here at this point)
+ mDisabledItems.clear(); //adding code to clear out disabled members from previous
+ if (lost_and_found_id == mUUID)
+ {
+ // This is the lost+found folder.
+ mItems.push_back(std::string("Empty Lost And Found"));
+ }
+
+ if(trash_id == mUUID)
+ {
+ // This is the trash.
+ mItems.push_back(std::string("Empty Trash"));
+ }
+ else if(model->isObjectDescendentOf(mUUID, trash_id))
+ {
+ // This is a folder in the trash.
+ mItems.clear(); // clear any items that used to exist
+ mItems.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ mDisabledItems.push_back(std::string("Purge Item"));
+ }
+
+ mItems.push_back(std::string("Restore Item"));
+ }
+ else if(isAgentInventory()) // do not allow creating in library
+ {
+ LLViewerInventoryCategory *cat = getCategory();
+ // BAP removed protected check to re-enable standard ops in untyped folders.
+ // Not sure what the right thing is to do here.
+ if (!isCOFFolder() && cat /*&&
+ LLAssetType::lookupIsProtectedCategoryType(cat->getPreferredType())*/)
+ {
+ // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
+ if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
+ mItems.push_back(std::string("New Folder"));
+ mItems.push_back(std::string("New Script"));
+ mItems.push_back(std::string("New Note"));
+ mItems.push_back(std::string("New Gesture"));
+ mItems.push_back(std::string("New Clothes"));
+ mItems.push_back(std::string("New Body Parts"));
+ mItems.push_back(std::string("Change Type"));
+
+ LLViewerInventoryCategory *cat = getCategory();
+ if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
+ {
+ mDisabledItems.push_back(std::string("Change Type"));
+ }
+
+ getClipboardEntries(false, mItems, mDisabledItems, flags);
+ }
+ else
+ {
+ // Want some but not all of the items from getClipboardEntries for outfits.
+ if (cat && cat->getPreferredType()==LLFolderType::FT_OUTFIT)
+ {
+ mItems.push_back(std::string("Rename"));
+ mItems.push_back(std::string("Delete"));
+ }
+ }
+
+ //Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06
+ mCallingCards = mWearables = FALSE;
+
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ if (checkFolderForContentsOfType(model, is_callingcard))
+ {
+ mCallingCards=TRUE;
+ }
+
+ LLFindWearables is_wearable;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+
+ if (checkFolderForContentsOfType(model, is_wearable) ||
+ checkFolderForContentsOfType(model, is_object) ||
+ checkFolderForContentsOfType(model, is_gesture) )
+ {
+ mWearables=TRUE;
+ }
+
+ mMenu = &menu;
+ sSelf = this;
+ LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(FALSE);
+
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ if (category)
+ {
+ folders.push_back(category->getUUID());
+ }
+ fetch->fetchDescendents(folders);
+ inc_busy_count();
+ if(fetch->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ fetch->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(fetch);
+ }
+ }
+ else
+ {
+ mItems.push_back(std::string("--no options--"));
+ mDisabledItems.push_back(std::string("--no options--"));
+ }
+ hide_context_entries(menu, mItems, mDisabledItems);
+}
+
+BOOL LLFolderBridge::hasChildren() const
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ LLInventoryModel::EHasChildren has_children;
+ has_children = gInventory.categoryHasChildren(mUUID);
+ return has_children != LLInventoryModel::CHILDREN_NO;
+}
+
+BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data)
+{
+ //llinfos << "LLFolderBridge::dragOrDrop()" << llendl;
+ BOOL accept = FALSE;
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_CALLINGCARD:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_CLOTHING:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ case DAD_LINK:
+ accept = dragItemIntoFolder((LLInventoryItem*)cargo_data,
+ drop);
+ break;
+ case DAD_CATEGORY:
+ if (LLFriendCardsManager::instance().isAnyFriendCategory(mUUID))
+ {
+ accept = FALSE;
+ }
+ else
+ {
+ accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop);
+ }
+ break;
+ default:
+ break;
+ }
+ return accept;
+}
+
+LLViewerInventoryCategory* LLFolderBridge::getCategory() const
+{
+ LLViewerInventoryCategory* cat = NULL;
+ LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ cat = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ }
+ return cat;
+}
+
+
+// static
+void LLFolderBridge::pasteClipboard(void* user_data)
+{
+ LLFolderBridge* self = (LLFolderBridge*)user_data;
+ if(self) self->pasteFromClipboard();
+}
+
+void LLFolderBridge::createNewCategory(void* user_data)
+{
+ LLFolderBridge* bridge = (LLFolderBridge*)user_data;
+ if(!bridge) return;
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(bridge->mInventoryPanel.get());
+ if (!panel) return;
+ LLInventoryModel* model = panel->getModel();
+ if(!model) return;
+ LLUUID id;
+ id = model->createNewCategory(bridge->getUUID(),
+ LLFolderType::FT_NONE,
+ LLStringUtil::null);
+ model->notifyObservers();
+
+ // At this point, the bridge has probably been deleted, but the
+ // view is still there.
+ panel->setSelection(id, TAKE_FOCUS_YES);
+}
+
+void LLFolderBridge::createNewShirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHIRT);
+}
+
+void LLFolderBridge::createNewPants(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_PANTS);
+}
+
+void LLFolderBridge::createNewShoes(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHOES);
+}
+
+void LLFolderBridge::createNewSocks(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SOCKS);
+}
+
+void LLFolderBridge::createNewJacket(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_JACKET);
+}
+
+void LLFolderBridge::createNewSkirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIRT);
+}
+
+void LLFolderBridge::createNewGloves(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_GLOVES);
+}
+
+void LLFolderBridge::createNewUndershirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERSHIRT);
+}
+
+void LLFolderBridge::createNewUnderpants(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERPANTS);
+}
+
+void LLFolderBridge::createNewShape(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHAPE);
+}
+
+void LLFolderBridge::createNewSkin(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIN);
+}
+
+void LLFolderBridge::createNewHair(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_HAIR);
+}
+
+void LLFolderBridge::createNewEyes(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_EYES);
+}
+
+// static
+void LLFolderBridge::createWearable(LLFolderBridge* bridge, EWearableType type)
+{
+ if(!bridge) return;
+ LLUUID parent_id = bridge->getUUID();
+ createWearable(parent_id, type);
+}
+
+// Separate function so can be called by global menu as well as right-click
+// menu.
+// static
+void LLFolderBridge::createWearable(const LLUUID &parent_id, EWearableType type)
+{
+ LLWearable* wearable = LLWearableList::instance().createNewWearable(type);
+ LLAssetType::EType asset_type = wearable->getAssetType();
+ LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE;
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ parent_id, wearable->getTransactionID(), wearable->getName(),
+ wearable->getDescription(), asset_type, inv_type, wearable->getType(),
+ wearable->getPermissions().getMaskNextOwner(),
+ LLPointer<LLInventoryCallback>(NULL));
+}
+
+void LLFolderBridge::modifyOutfit(BOOL append)
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+
+ // BAP - was:
+ // wear_inventory_category_on_avatar( cat, append );
+ LLAppearanceManager::wearInventoryCategory( cat, FALSE, append );
+}
+
+// helper stuff
+bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv* move_inv)
+{
+ LLFloaterOpenObject::LLCatAndWear* cat_and_wear = (LLFloaterOpenObject::LLCatAndWear* )move_inv->mUserData;
+ LLViewerObject* object = gObjectList.findObject(move_inv->mObjectID);
+ S32 option = LLNotification::getSelectedOption(notification, response);
+
+ if(option == 0 && object)
+ {
+ if (cat_and_wear && cat_and_wear->mWear)
+ {
+ InventoryObjectList inventory_objects;
+ object->getInventoryContents(inventory_objects);
+ int contents_count = inventory_objects.size()-1; //subtract one for containing folder
+
+ LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count);
+ gInventory.addObserver(inventoryObserver);
+ }
+
+ two_uuids_list_t::iterator move_it;
+ for (move_it = move_inv->mMoveList.begin();
+ move_it != move_inv->mMoveList.end();
+ ++move_it)
+ {
+ object->moveInventory(move_it->first, move_it->second);
+ }
+
+ // update the UI.
+ dialog_refresh_all();
+ }
+
+ if (move_inv->mCallback)
+ {
+ move_inv->mCallback(option, move_inv->mUserData);
+ }
+
+ delete move_inv;
+ return false;
+}
+
+/*
+Next functions intended to reorder items in the inventory folder and save order on server
+Is now used for Favorites folder.
+
+*TODO: refactoring is needed with Favorites Bar functionality. Probably should be moved in LLInventoryModel
+*/
+void saveItemsOrder(LLInventoryModel::item_array_t& items)
+{
+ int sortField = 0;
+
+ // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field
+ for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i)
+ {
+ LLViewerInventoryItem* item = *i;
+
+ item->setSortField(++sortField);
+ item->setComplete(TRUE);
+ item->updateServer(FALSE);
+
+ gInventory.updateItem(item);
+ }
+
+ gInventory.notifyObservers();
+}
+
+LLInventoryModel::item_array_t::iterator findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id)
+{
+ LLInventoryModel::item_array_t::iterator result = items.end();
+
+ for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i)
+ {
+ if ((*i)->getUUID() == id)
+ {
+ result = i;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& srcItemId, const LLUUID& destItemId)
+{
+ LLViewerInventoryItem* srcItem = gInventory.getItem(srcItemId);
+ LLViewerInventoryItem* destItem = gInventory.getItem(destItemId);
+
+ items.erase(findItemByUUID(items, srcItem->getUUID()));
+ items.insert(findItemByUUID(items, destItem->getUUID()), srcItem);
+}
+
+BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
+ BOOL drop)
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+
+ // cannot drag into library
+ if(!isAgentInventory())
+ {
+ return FALSE;
+ }
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+
+ LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
+ BOOL accept = FALSE;
+ LLViewerObject* object = NULL;
+ if(LLToolDragAndDrop::SOURCE_AGENT == source)
+ {
+
+ BOOL is_movable = TRUE;
+ switch( inv_item->getActualType() )
+ {
+ case LLAssetType::AT_ROOT_CATEGORY:
+ is_movable = FALSE;
+ break;
+
+ case LLAssetType::AT_CATEGORY:
+ is_movable = !LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)inv_item)->getPreferredType());
+ break;
+ default:
+ break;
+ }
+
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
+ const LLUUID current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
+ BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
+
+ if(is_movable && move_is_into_trash)
+ {
+ switch(inv_item->getType())
+ {
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ is_movable = !gAgentWearables.isWearingItem(inv_item->getUUID());
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ is_movable = !avatar->isWearingAttachment(inv_item->getUUID());
+ break;
+ default:
+ break;
+ }
+ }
+
+ 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, getCategory());
+ }
+
+ const LLUUID& favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
+
+ // we can move item inside a folder only if this folder is Favorites. See EXT-719
+ accept = is_movable && ((mUUID != inv_item->getParentUUID()) || (mUUID == favorites_id));
+ if(accept && drop)
+ {
+ if (inv_item->getType() == LLAssetType::AT_GESTURE
+ && LLGestureManager::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash)
+ {
+ LLGestureManager::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).
+ if (LLFloaterInventory::getActiveInventory())
+ {
+ LLInventoryPanel* active_panel = LLFloaterInventory::getActiveInventory()->getPanel();
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
+ if (active_panel && (panel != active_panel))
+ {
+ active_panel->unSelectAll();
+ }
+ }
+
+ // if dragging from/into favorites folder only reorder items
+ if ((mUUID == inv_item->getParentUUID()) && (favorites_id == mUUID))
+ {
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLIsType is_type(LLAssetType::AT_LANDMARK);
+ model->collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type);
+
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
+ LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL;
+ if (itemp)
+ {
+ LLUUID srcItemId = inv_item->getUUID();
+ LLUUID destItemId = itemp->getListener()->getUUID();
+
+ // update order
+ updateItemsOrder(items, srcItemId, destItemId);
+
+ saveItemsOrder(items);
+ }
+ }
+ else if (favorites_id == mUUID) // if target is the favorites folder we use copy
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ inv_item->getPermissions().getOwner(),
+ inv_item->getUUID(),
+ mUUID,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ else if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+ // BAP - should skip if dup.
+ if (move_is_into_current_outfit)
+ {
+ LLAppearanceManager::wearItem(inv_item);
+ }
+ else
+ {
+ LLPointer<LLInventoryCallback> cb = NULL;
+ link_inventory_item(
+ gAgent.getID(),
+ inv_item->getUUID(),
+ mUUID,
+ std::string(),
+ LLAssetType::AT_LINK,
+ cb);
+ }
+ }
+ else
+ {
+ // restamp if the move is into the trash.
+ LLInvFVBridge::changeItemParent(
+ model,
+ (LLViewerInventoryItem*)inv_item,
+ mUUID,
+ move_is_into_trash);
+ }
+ }
+ }
+ 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)
+ {
+ llinfos << "Object not found for drop." << llendl;
+ 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;
+ }
+ if(drop && accept)
+ {
+ LLMoveInv* move_inv = new LLMoveInv;
+ move_inv->mObjectID = inv_item->getParentUUID();
+ two_uuids_t item_pair(mUUID, 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
+ {
+ 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)
+ {
+ accept = TRUE;
+ if(drop)
+ {
+ copy_inventory_from_notecard(LLToolDragAndDrop::getInstance()->getObjectID(),
+ LLToolDragAndDrop::getInstance()->getSourceID(), inv_item);
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_LIBRARY == source)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item;
+ if(item && item->isComplete())
+ {
+ accept = TRUE;
+ if(drop)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ inv_item->getPermissions().getOwner(),
+ inv_item->getUUID(),
+ mUUID,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+ else
+ {
+ llwarns << "unhandled drag source" << llendl;
+ }
+ return accept;
+}
+
+// +=================================================+
+// | LLScriptBridge (DEPRECTED) |
+// +=================================================+
+
+LLUIImagePtr LLScriptBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0, FALSE);
+}
+
+// +=================================================+
+// | LLTextureBridge |
+// +=================================================+
+
+LLUIImagePtr LLTextureBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_TEXTURE, mInvType, 0, FALSE);
+}
+
+void LLTextureBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+}
+
+void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLTextureBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back(std::string("Texture Separator"));
+ items.push_back(std::string("Save As"));
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// virtual
+void LLTextureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("save_as" == action)
+ {
+ LLFloaterReg::showInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES);
+ LLPreviewTexture* preview_texture = LLFloaterReg::findTypedInstance<LLPreviewTexture>("preview_texture", mUUID);
+ if (preview_texture)
+ {
+ preview_texture->openToSave();
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+// +=================================================+
+// | LLSoundBridge |
+// +=================================================+
+
+LLUIImagePtr LLSoundBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SOUND, LLInventoryType::IT_SOUND, 0, FALSE);
+}
+
+void LLSoundBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+// Changed this back to the way it USED to work:
+// only open the preview dialog through the contextual right-click menu
+// double-click just plays the sound
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ openSoundPreview((void*)this);
+ //send_uuid_sound_trigger(item->getAssetUUID(), 1.0);
+ }
+*/
+}
+
+void LLSoundBridge::previewItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ send_sound_trigger(item->getAssetUUID(), 1.0);
+ }
+}
+
+void LLSoundBridge::openSoundPreview(void* which)
+{
+ LLSoundBridge *me = (LLSoundBridge *)which;
+ LLFloaterReg::showInstance("preview_sound", LLSD(me->mUUID), TAKE_FOCUS_YES);
+}
+
+void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLSoundBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Sound Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back(std::string("Sound Separator"));
+ items.push_back(std::string("Sound Play"));
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// +=================================================+
+// | LLLandmarkBridge |
+// +=================================================+
+
+LLLandmarkBridge::LLLandmarkBridge(LLInventoryPanel* inventory, const LLUUID& uuid, U32 flags/* = 0x00*/) :
+LLItemBridge(inventory, uuid)
+{
+ mVisited = FALSE;
+ if (flags & LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
+ {
+ mVisited = TRUE;
+ }
+}
+
+LLUIImagePtr LLLandmarkBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, mVisited, FALSE);
+}
+
+void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ lldebugs << "LLLandmarkBridge::buildContextMenu()" << llendl;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Landmark Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back(std::string("Landmark Separator"));
+ items.push_back(std::string("About Landmark"));
+
+ // Disable "About Landmark" menu item for
+ // multiple landmarks selected. Only one landmark
+ // info panel can be shown at a time.
+ if ((flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("About Landmark"));
+ }
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// Convenience function for the two functions below.
+void teleport_via_landmark(const LLUUID& asset_id)
+{
+ gAgent.teleportViaLandmark( asset_id );
+
+ // we now automatically track the landmark you're teleporting to
+ // because you'll probably arrive at a telehub instead
+ LLFloaterWorldMap* floater_world_map = LLFloaterWorldMap::getInstance();
+ if( floater_world_map )
+ {
+ floater_world_map->trackLandmark( asset_id );
+ }
+}
+
+// virtual
+void LLLandmarkBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("teleport" == action)
+ {
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ teleport_via_landmark(item->getAssetUUID());
+ }
+ }
+ else if ("about" == action)
+ {
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ LLSD key;
+ key["type"] = "landmark";
+ key["id"] = item->getUUID();
+
+ LLSideTray::getInstance()->showPanel("panel_places", key);
+ }
+ }
+ else
+ {
+ LLItemBridge::performAction(folder, model, action);
+ }
+}
+
+static bool open_landmark_callback(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotification::getSelectedOption(notification, response);
+
+ LLUUID asset_id = notification["payload"]["asset_id"].asUUID();
+ if (option == 0)
+ {
+ teleport_via_landmark(asset_id);
+ }
+
+ return false;
+}
+static LLNotificationFunctorRegistration open_landmark_callback_reg("TeleportFromLandmark", open_landmark_callback);
+
+
+void LLLandmarkBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ // Opening (double-clicking) a landmark immediately teleports,
+ // but warns you the first time.
+ // open_landmark(item);
+ LLSD payload;
+ payload["asset_id"] = item->getAssetUUID();
+ LLNotifications::instance().add("TeleportFromLandmark", LLSD(), payload);
+ }
+*/
+}
+
+
+// +=================================================+
+// | LLCallingCardObserver |
+// +=================================================+
+void LLCallingCardObserver::changed(U32 mask)
+{
+ mBridgep->refreshFolderViewItem();
+}
+
+// +=================================================+
+// | LLCallingCardBridge |
+// +=================================================+
+
+LLCallingCardBridge::LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) :
+ LLItemBridge(inventory, uuid)
+{
+ mObserver = new LLCallingCardObserver(this);
+ LLAvatarTracker::instance().addObserver(mObserver);
+}
+
+LLCallingCardBridge::~LLCallingCardBridge()
+{
+ LLAvatarTracker::instance().removeObserver(mObserver);
+ delete mObserver;
+}
+
+void LLCallingCardBridge::refreshFolderViewItem()
+{
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
+ LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL;
+ if (itemp)
+ {
+ itemp->refresh();
+ }
+}
+
+// virtual
+void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("begin_im" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ std::string callingcard_name;
+ gCacheName->getFullName(item->getCreatorUUID(), callingcard_name);
+ LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID());
+ if (session_id != LLUUID::null)
+ {
+ LLIMFloater::show(session_id);
+ }
+ }
+ }
+ else if ("lure" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ LLAvatarActions::offerTeleport(item->getCreatorUUID());
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+LLUIImagePtr LLCallingCardBridge::getIcon() const
+{
+ BOOL online = FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID());
+ }
+ return get_item_icon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, online, FALSE);
+}
+
+std::string LLCallingCardBridge::getLabelSuffix() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (online)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+void LLCallingCardBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+ LLViewerInventoryItem* item = getItem();
+ if(item && !item->getCreatorUUID().isNull())
+ {
+ LLAvatarActions::showProfile(item->getCreatorUUID());
+ }
+*/
+}
+
+void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLCallingCardBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ LLInventoryItem* item = getItem();
+ BOOL good_card = (item
+ && (LLUUID::null != item->getCreatorUUID())
+ && (item->getCreatorUUID() != gAgent.getID()));
+ BOOL user_online = (LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()));
+ items.push_back(std::string("Send Instant Message Separator"));
+ items.push_back(std::string("Send Instant Message"));
+ items.push_back(std::string("Offer Teleport..."));
+ items.push_back(std::string("Conference Chat"));
+
+ if (!good_card)
+ {
+ disabled_items.push_back(std::string("Send Instant Message"));
+ }
+ if (!good_card || !user_online)
+ {
+ disabled_items.push_back(std::string("Offer Teleport..."));
+ disabled_items.push_back(std::string("Conference Chat"));
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data)
+{
+ LLViewerInventoryItem* item = getItem();
+ BOOL rv = FALSE;
+ if(item)
+ {
+ // check the type
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_CLOTHING:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
+ const LLPermissions& perm = inv_item->getPermissions();
+ if(gInventory.getItem(inv_item->getUUID())
+ && perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ rv = TRUE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventory(item->getCreatorUUID(),
+ (LLInventoryItem*)cargo_data);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably in
+ // an object's contents), so disallow dragging it here.
+ // You can't give something you don't yet have.
+ rv = FALSE;
+ }
+ break;
+ }
+ case DAD_CATEGORY:
+ {
+ LLInventoryCategory* inv_cat = (LLInventoryCategory*)cargo_data;
+ if( gInventory.getCategory( inv_cat->getUUID() ) )
+ {
+ rv = TRUE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventoryCategory(
+ item->getCreatorUUID(),
+ inv_cat);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably in
+ // an object's contents), so disallow dragging it here.
+ // You can't give something you don't yet have.
+ rv = FALSE;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return rv;
+}
+
+BOOL LLCallingCardBridge::removeItem()
+{
+ if (LLFriendCardsManager::instance().isItemInAnyFriendsList(getItem()))
+ {
+ LLAvatarActions::removeFriendDialog(getItem()->getCreatorUUID());
+ return FALSE;
+ }
+ else
+ {
+ return LLItemBridge::removeItem();
+ }
+}
+// +=================================================+
+// | LLNotecardBridge |
+// +=================================================+
+
+LLUIImagePtr LLNotecardBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_NOTECARD, LLInventoryType::IT_NOTECARD, 0, FALSE);
+}
+
+void LLNotecardBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+
+/*
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES);
+ }
+*/
+}
+
+
+// +=================================================+
+// | LLGestureBridge |
+// +=================================================+
+
+LLUIImagePtr LLGestureBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_GESTURE, LLInventoryType::IT_GESTURE, 0, FALSE);
+}
+
+LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const
+{
+ if( LLGestureManager::instance().isGestureActive(mUUID) )
+ {
+ return LLFontGL::BOLD;
+ }
+ else
+ {
+ return LLFontGL::NORMAL;
+ }
+}
+
+std::string LLGestureBridge::getLabelSuffix() const
+{
+ if( LLGestureManager::instance().isGestureActive(mUUID) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (active)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+// virtual
+void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("activate" == action)
+ {
+ LLGestureManager::instance().activateGesture(mUUID);
+
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ if (!item) return;
+
+ // Since we just changed the suffix to indicate (active)
+ // the server doesn't need to know, just the viewer.
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ else if ("deactivate" == action)
+ {
+ LLGestureManager::instance().deactivateGesture(mUUID);
+
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ if (!item) return;
+
+ // Since we just changed the suffix to indicate (active)
+ // the server doesn't need to know, just the viewer.
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLGestureBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null);
+ preview->setFocus(TRUE);
+ }
+*/
+}
+
+BOOL LLGestureBridge::removeItem()
+{
+ // Force close the preview window, if it exists
+ LLGestureManager::instance().deactivateGesture(mUUID);
+ return LLItemBridge::removeItem();
+}
+
+void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLGestureBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back(std::string("Gesture Separator"));
+ items.push_back(std::string("Activate"));
+ items.push_back(std::string("Deactivate"));
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// +=================================================+
+// | LLAnimationBridge |
+// +=================================================+
+
+LLUIImagePtr LLAnimationBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_ANIMATION, LLInventoryType::IT_ANIMATION, 0, FALSE);
+}
+
+void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ lldebugs << "LLAnimationBridge::buildContextMenu()" << llendl;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Animation Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back(std::string("Animation Separator"));
+ items.push_back(std::string("Animation Play"));
+ items.push_back(std::string("Animation Audition"));
+
+ hide_context_entries(menu, items, disabled_items);
+
+}
+
+// virtual
+void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ((action == "playworld") || (action == "playlocal"))
+ {
+ if (getItem())
+ {
+ LLPreviewAnim::e_activation_type activate = LLPreviewAnim::NONE;
+ if ("playworld" == action) activate = LLPreviewAnim::PLAY;
+ if ("playlocal" == action) activate = LLPreviewAnim::AUDITION;
+
+ LLPreviewAnim* preview = LLFloaterReg::showTypedInstance<LLPreviewAnim>("preview_anim", LLSD(mUUID));
+ if (preview)
+ {
+ preview->activate(activate);
+ }
+ }
+ }
+ else
+ {
+ LLItemBridge::performAction(folder, model, action);
+ }
+}
+
+void LLAnimationBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+*/
+}
+
+// +=================================================+
+// | LLObjectBridge |
+// +=================================================+
+
+// static
+LLUUID LLObjectBridge::sContextMenuItemID;
+
+LLObjectBridge::LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) :
+LLItemBridge(inventory, uuid), mInvType(type)
+{
+ mAttachPt = (flags & 0xff); // low bye of inventory flags
+
+ mIsMultiObject = ( flags & LLInventoryItem::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS ) ? TRUE: FALSE;
+}
+
+BOOL LLObjectBridge::isItemRemovable()
+{
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+ if(avatar->isWearingAttachment(mUUID)) return FALSE;
+ return LLInvFVBridge::isItemRemovable();
+}
+
+LLUIImagePtr LLObjectBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject );
+}
+
+LLInventoryObject* LLObjectBridge::getObject() const
+{
+ LLInventoryObject* object = NULL;
+ LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ object = (LLInventoryObject*)model->getObject(mUUID);
+ }
+ return object;
+}
+
+// virtual
+void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("attach" == action)
+ {
+ LLUUID object_id = mUUID;
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(object_id);
+ if(item && gInventory.isObjectDescendentOf(object_id, gInventory.getRootFolderID()))
+ {
+ rez_attachment(item, NULL);
+ }
+ else if(item && item->isComplete())
+ {
+ // must be in library. copy it to our inventory and put it on.
+ LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0);
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ gFocusMgr.setKeyboardFocus(NULL);
+ }
+ else if ("detach" == action)
+ {
+ LLInventoryItem* item = gInventory.getItem(mUUID);
+ if(item)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
+ gMessageSystem->sendReliable( gAgent.getRegion()->getHost());
+ }
+ // this object might have been selected, so let the selection manager know it's gone now
+ LLViewerObject *found_obj =
+ gObjectList.findObject(item->getUUID());
+ if (found_obj)
+ {
+ LLSelectMgr::getInstance()->remove(found_obj);
+ }
+ else
+ {
+ llwarns << "object not found - ignoring" << llendl;
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLObjectBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+
+ LLSD key;
+ key["id"] = mUUID;
+ LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
+
+ /*
+ LLFloaterReg::showInstance("properties", mUUID);
+ */
+}
+
+LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const
+{
+ U8 font = LLFontGL::NORMAL;
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if( avatar && avatar->isWearingAttachment( mUUID ) )
+ {
+ font |= LLFontGL::BOLD;
+ }
+
+ LLInventoryItem* item = getItem();
+ if (item && item->getIsLinkType())
+ {
+ font |= LLFontGL::ITALIC;
+ }
+
+ return (LLFontGL::StyleFlags)font;
+}
+
+std::string LLObjectBridge::getLabelSuffix() const
+{
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if( avatar && avatar->isWearingAttachment( mUUID ) )
+ {
+ std::string attachment_point_name = avatar->getAttachedPointName(mUUID);
+ LLStringUtil::toLower(attachment_point_name);
+
+ LLStringUtil::format_map_t args;
+ args["[ATTACHMENT_POINT]"] = attachment_point_name.c_str();
+ return LLItemBridge::getLabelSuffix() + LLTrans::getString("WornOnAttachmentPoint", args);
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment)
+{
+ LLSD payload;
+ payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link.
+
+ S32 attach_pt = 0;
+ if (gAgent.getAvatarObject() && attachment)
+ {
+ for (LLVOAvatar::attachment_map_t::iterator iter = gAgent.getAvatarObject()->mAttachmentPoints.begin();
+ iter != gAgent.getAvatarObject()->mAttachmentPoints.end(); ++iter)
+ {
+ if (iter->second == attachment)
+ {
+ attach_pt = iter->first;
+ break;
+ }
+ }
+ }
+
+ payload["attachment_point"] = attach_pt;
+
+#if !ENABLE_MULTIATTACHMENTS
+ if (attachment && attachment->getNumObjects() > 0)
+ {
+ LLNotifications::instance().add("ReplaceAttachment", LLSD(), payload, confirm_replace_attachment_rez);
+ }
+ else
+#endif
+ {
+ LLNotifications::instance().forceResponse(LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/);
+ }
+}
+
+bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response)
+{
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+
+ if (!avatarp->canAttachMoreObjects())
+ {
+ LLSD args;
+ args["MAX_ATTACHMENTS"] = llformat("%d", MAX_AGENT_ATTACHMENTS);
+ LLNotifications::instance().add("MaxAttachmentsOnOutfit", args);
+ return false;
+ }
+
+ S32 option = LLNotification::getSelectedOption(notification, response);
+ if (option == 0/*YES*/)
+ {
+ LLViewerInventoryItem* itemp = gInventory.getItem(notification["payload"]["item_id"].asUUID());
+
+ if (itemp)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID());
+ msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner());
+ U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
+#if ENABLE_MULTIATTACHMENTS
+ attachment_pt |= ATTACHMENT_ADD;
+#endif
+ msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt);
+ pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions());
+ msg->addStringFast(_PREHASH_Name, itemp->getName());
+ msg->addStringFast(_PREHASH_Description, itemp->getDescription());
+ msg->sendReliable(gAgent.getRegion()->getHost());
+ }
+ }
+ return false;
+}
+static LLNotificationFunctorRegistration confirm_replace_attachment_rez_reg("ReplaceAttachment", confirm_replace_attachment_rez);
+
+void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ LLInventoryItem* item = getItem();
+ if (item && item->getIsLinkType())
+ {
+ items.push_back(std::string("Goto Link"));
+ }
+
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ LLObjectBridge::sContextMenuItemID = mUUID;
+
+ if(item)
+ {
+ LLVOAvatarSelf* avatarp = gAgent.getAvatarObject();
+ if( !avatarp )
+ {
+ return;
+ }
+
+ if( avatarp->isWearingAttachment( mUUID ) )
+ {
+ items.push_back(std::string("Detach From Yourself"));
+ }
+ else
+ if( !isInTrash() && !isLinkedObjectInTrash() )
+ {
+ items.push_back(std::string("Attach Separator"));
+ items.push_back(std::string("Object Wear"));
+ items.push_back(std::string("Attach To"));
+ items.push_back(std::string("Attach To HUD"));
+ // commented out for DEV-32347
+ //items.push_back(std::string("Restore to Last Position"));
+
+ if (!avatarp->canAttachMoreObjects())
+ {
+ disabled_items.push_back(std::string("Object Wear"));
+ disabled_items.push_back(std::string("Attach To"));
+ disabled_items.push_back(std::string("Attach To HUD"));
+ }
+ LLMenuGL* attach_menu = menu.findChildMenuByName("Attach To", TRUE);
+ LLMenuGL* attach_hud_menu = menu.findChildMenuByName("Attach To HUD", TRUE);
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if (attach_menu
+ && (attach_menu->getChildCount() == 0)
+ && attach_hud_menu
+ && (attach_hud_menu->getChildCount() == 0)
+ && avatarp)
+ {
+ for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin();
+ iter != avatarp->mAttachmentPoints.end(); )
+ {
+ LLVOAvatar::attachment_map_t::iterator curiter = iter++;
+ LLViewerJointAttachment* attachment = curiter->second;
+ LLMenuItemCallGL::Params p;
+ std::string submenu_name = attachment->getName();
+ if (LLTrans::getString(submenu_name) != "")
+ {
+ p.name = (" ")+LLTrans::getString(submenu_name)+" ";
+ }
+ else
+ {
+ p.name = submenu_name;
+ }
+ LLSD cbparams;
+ cbparams["index"] = curiter->first;
+ cbparams["label"] = attachment->getName();
+ p.on_click.function_name = "Inventory.AttachObject";
+ p.on_click.parameter = LLSD(attachment->getName());
+ p.on_enable.function_name = "Attachment.Label";
+ p.on_enable.parameter = cbparams;
+ LLView* parent = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu;
+ LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent);
+ }
+ }
+ }
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+BOOL LLObjectBridge::renameItem(const std::string& new_name)
+{
+ if(!isItemRenameable())
+ return FALSE;
+ LLPreview::dirty(mUUID);
+ LLInventoryModel* model = getInventoryModel();
+ if(!model)
+ return FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item && (item->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(new_name);
+ buildDisplayName(new_item, mDisplayName);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+
+ model->notifyObservers();
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() );
+ if( obj )
+ {
+ LLSelectMgr::getInstance()->deselectAll();
+ LLSelectMgr::getInstance()->addAsIndividual( obj, SELECT_ALL_TES, FALSE );
+ LLSelectMgr::getInstance()->selectionSetObjectName( new_name );
+ LLSelectMgr::getInstance()->deselectAll();
+ }
+ }
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+// +=================================================+
+// | LLLSLTextBridge |
+// +=================================================+
+
+LLUIImagePtr LLLSLTextBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0, FALSE);
+}
+
+void LLLSLTextBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+ /*
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+ */
+}
+
+// +=================================================+
+// | LLWearableBridge |
+// +=================================================+
+
+// *NOTE: hack to get from avatar inventory to avatar
+void wear_inventory_item_on_avatar( LLInventoryItem* item )
+{
+ if(item)
+ {
+ lldebugs << "wear_inventory_item_on_avatar( " << item->getName()
+ << " )" << llendl;
+
+ LLAppearanceManager::wearItem(item);
+ }
+}
+
+void wear_add_inventory_item_on_avatar( LLInventoryItem* item )
+{
+ if(item)
+ {
+ lldebugs << "wear_add_inventory_item_on_avatar( " << item->getName()
+ << " )" << llendl;
+
+ LLWearableList::instance().getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ LLWearableBridge::onWearAddOnAvatarArrived,
+ new LLUUID(item->getUUID()));
+ }
+}
+
+void remove_inventory_category_from_avatar( LLInventoryCategory* category )
+{
+ if(!category) return;
+ lldebugs << "remove_inventory_category_from_avatar( " << category->getName()
+ << " )" << llendl;
+
+
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->askToSaveIfDirty(
+ boost::bind(remove_inventory_category_from_avatar_step2, _1, category->getUUID()));
+ }
+ else
+ {
+ remove_inventory_category_from_avatar_step2(TRUE, category->getUUID() );
+ }
+}
+
+struct OnRemoveStruct
+{
+ LLUUID mUUID;
+ OnRemoveStruct(const LLUUID& uuid):
+ mUUID(uuid)
+ {
+ }
+};
+
+void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id)
+{
+
+ // Find all the wearables that are in the category's subtree.
+ lldebugs << "remove_inventory_category_from_avatar_step2()" << llendl;
+ if(proceed)
+ {
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLFindWearables is_wearable;
+ gInventory.collectDescendentsIf(category_id,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_wearable);
+ S32 i;
+ S32 wearable_count = item_array.count();
+
+ LLInventoryModel::cat_array_t obj_cat_array;
+ LLInventoryModel::item_array_t obj_item_array;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ gInventory.collectDescendentsIf(category_id,
+ obj_cat_array,
+ obj_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_object);
+ S32 obj_count = obj_item_array.count();
+
+ // Find all gestures in this folder
+ LLInventoryModel::cat_array_t gest_cat_array;
+ LLInventoryModel::item_array_t gest_item_array;
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+ gInventory.collectDescendentsIf(category_id,
+ gest_cat_array,
+ gest_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_gesture);
+ S32 gest_count = gest_item_array.count();
+
+ if (wearable_count > 0) //Loop through wearables. If worn, remove.
+ {
+ for(i = 0; i < wearable_count; ++i)
+ {
+ if( gAgentWearables.isWearingItem (item_array.get(i)->getUUID()) )
+ {
+ LLWearableList::instance().getAsset(item_array.get(i)->getAssetUUID(),
+ item_array.get(i)->getName(),
+ item_array.get(i)->getType(),
+ LLWearableBridge::onRemoveFromAvatarArrived,
+ new OnRemoveStruct(item_array.get(i)->getUUID()));
+
+ }
+ }
+ }
+
+
+ if (obj_count > 0)
+ {
+ for(i = 0; i < obj_count; ++i)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData );
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item_array.get(i)->getUUID() );
+
+ gMessageSystem->sendReliable( gAgent.getRegion()->getHost() );
+
+ // this object might have been selected, so let the selection manager know it's gone now
+ LLViewerObject *found_obj = gObjectList.findObject( obj_item_array.get(i)->getUUID());
+ if (found_obj)
+ {
+ LLSelectMgr::getInstance()->remove(found_obj);
+ }
+ else
+ {
+ llwarns << "object not found, ignoring" << llendl;
+ }
+ }
+ }
+
+ if (gest_count > 0)
+ {
+ for(i = 0; i < gest_count; ++i)
+ {
+ if ( LLGestureManager::instance().isGestureActive( gest_item_array.get(i)->getUUID()) )
+ {
+ LLGestureManager::instance().deactivateGesture( gest_item_array.get(i)->getUUID() );
+ gInventory.updateItem( gest_item_array.get(i) );
+ gInventory.notifyObservers();
+ }
+
+ }
+ }
+ }
+}
+
+BOOL LLWearableBridge::renameItem(const std::string& new_name)
+{
+ if( gAgentWearables.isWearingItem( mUUID ) )
+ {
+ gAgentWearables.setWearableName( mUUID, new_name );
+ }
+ return LLItemBridge::renameItem(new_name);
+}
+
+BOOL LLWearableBridge::isItemRemovable()
+{
+ if (gAgentWearables.isWearingItem(mUUID)) return FALSE;
+ return LLInvFVBridge::isItemRemovable();
+}
+
+std::string LLWearableBridge::getLabelSuffix() const
+{
+ if( gAgentWearables.isWearingItem( mUUID ) )
+ {
+ return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn");
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+LLUIImagePtr LLWearableBridge::getIcon() const
+{
+ return get_item_icon(mAssetType, mInvType, mWearableType, FALSE);
+}
+
+// virtual
+void LLWearableBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("wear" == action)
+ {
+ wearOnAvatar();
+ }
+ else if ("wear_add" == action)
+ {
+ wearAddOnAvatar();
+ }
+ else if ("edit" == action)
+ {
+ editOnAvatar();
+ return;
+ }
+ else if ("take_off" == action)
+ {
+ if(gAgentWearables.isWearingItem(mUUID))
+ {
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLWearableList::instance().getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ LLWearableBridge::onRemoveFromAvatarArrived,
+ new OnRemoveStruct(mUUID));
+ }
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLWearableBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+ /*
+ if( isInTrash() )
+ {
+ LLNotifications::instance().add("CannotWearTrash");
+ }
+ else if(isAgentInventory())
+ {
+ if( !gAgentWearables.isWearingItem( mUUID ) )
+ {
+ wearOnAvatar();
+ }
+ }
+ else
+ {
+ // must be in the inventory library. copy it to our inventory
+ // and put it on right away.
+ LLViewerInventoryItem* item = getItem();
+ if(item && item->isComplete())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else if(item)
+ {
+ // *TODO: We should fetch the item details, and then do
+ // the operation above.
+ LLNotifications::instance().add("CannotWearInfoNotComplete");
+ }
+ }
+ */
+}
+
+void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLWearableBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ { // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere
+ BOOL no_open = ((flags & SUPPRESS_OPEN_ITEM) == SUPPRESS_OPEN_ITEM);
+
+ // If we have clothing, don't add "Open" as it's the same action as "Wear" SL-18976
+ LLViewerInventoryItem* item = getItem();
+ if( !no_open && item )
+ {
+ no_open = (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART);
+ }
+ if (!no_open)
+ {
+ items.push_back(std::string("Open"));
+ }
+
+ if (item && item->getIsLinkType())
+ {
+ items.push_back(std::string("Goto Link"));
+ }
+
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back(std::string("Wearable Separator"));
+
+ items.push_back(std::string("Wearable Wear"));
+ items.push_back(std::string("Wearable Add"));
+ items.push_back(std::string("Wearable Edit"));
+
+ if ((flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Wearable Edit"));
+ }
+ // Don't allow items to be worn if their baseobj is in the trash.
+ if (isLinkedObjectInTrash())
+ {
+ disabled_items.push_back(std::string("Wearable Wear"));
+ disabled_items.push_back(std::string("Wearable Add"));
+ disabled_items.push_back(std::string("Wearable Edit"));
+ }
+
+ // Disable wear and take off based on whether the item is worn.
+ if(item)
+ {
+ switch (item->getType())
+ {
+ case LLAssetType::AT_CLOTHING:
+ items.push_back(std::string("Take Off"));
+ case LLAssetType::AT_BODYPART:
+ if (gAgentWearables.isWearingItem(item->getUUID()))
+ {
+ disabled_items.push_back(std::string("Wearable Wear"));
+ disabled_items.push_back(std::string("Wearable Add"));
+ }
+ else
+ {
+ disabled_items.push_back(std::string("Take Off"));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// Called from menus
+// static
+BOOL LLWearableBridge::canWearOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return FALSE;
+ if(!self->isAgentInventory())
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem();
+ if(!item || !item->isComplete()) return FALSE;
+ }
+ return (!gAgentWearables.isWearingItem(self->mUUID));
+}
+
+// Called from menus
+// static
+void LLWearableBridge::onWearOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return;
+ self->wearOnAvatar();
+}
+
+void LLWearableBridge::wearOnAvatar()
+{
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgentWearables.areWearablesLoaded())
+ {
+ LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded");
+ return;
+ }
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ if(!isAgentInventory())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_inventory_item_on_avatar(item);
+ }
+ }
+}
+
+void LLWearableBridge::wearAddOnAvatar()
+{
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgentWearables.areWearablesLoaded())
+ {
+ LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded");
+ return;
+ }
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ if(!isAgentInventory())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_add_inventory_item_on_avatar(item);
+ }
+ }
+}
+
+// static
+void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userdata )
+{
+ LLUUID* item_id = (LLUUID*) userdata;
+ if(wearable)
+ {
+ LLViewerInventoryItem* item = NULL;
+ item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
+ if(item)
+ {
+ if(item->getAssetUUID() == wearable->getAssetID())
+ {
+ gAgentWearables.setWearableItem(item, wearable);
+ gInventory.notifyObservers();
+ //self->getFolderItem()->refreshFromRoot();
+ }
+ else
+ {
+ llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl;
+ }
+ }
+ }
+ delete item_id;
+}
+
+// static
+// BAP remove the "add" code path once everything is fully COF-ified.
+void LLWearableBridge::onWearAddOnAvatarArrived( LLWearable* wearable, void* userdata )
+{
+ LLUUID* item_id = (LLUUID*) userdata;
+ if(wearable)
+ {
+ LLViewerInventoryItem* item = NULL;
+ item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
+ if(item)
+ {
+ if(item->getAssetUUID() == wearable->getAssetID())
+ {
+ bool do_append = true;
+ gAgentWearables.setWearableItem(item, wearable, do_append);
+ gInventory.notifyObservers();
+ //self->getFolderItem()->refreshFromRoot();
+ }
+ else
+ {
+ llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl;
+ }
+ }
+ }
+ delete item_id;
+}
+
+// static
+BOOL LLWearableBridge::canEditOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return FALSE;
+
+ return (gAgentWearables.isWearingItem(self->mUUID));
+}
+
+// static
+void LLWearableBridge::onEditOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(self)
+ {
+ self->editOnAvatar();
+ }
+}
+
+void LLWearableBridge::editOnAvatar()
+{
+ const LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID);
+ if( wearable )
+ {
+ // Set the tab to the right wearable.
+ if (gFloaterCustomize)
+ gFloaterCustomize->setCurrentWearableType( wearable->getType() );
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode() )
+ {
+ // Start Avatar Customization
+ gAgent.changeCameraToCustomizeAvatar();
+ }
+ }
+}
+
+// static
+BOOL LLWearableBridge::canRemoveFromAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if( self && (LLAssetType::AT_BODYPART != self->mAssetType) )
+ {
+ return gAgentWearables.isWearingItem( self->mUUID );
+ }
+ return FALSE;
+}
+
+// static
+void LLWearableBridge::onRemoveFromAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return;
+ if(gAgentWearables.isWearingItem(self->mUUID))
+ {
+ LLViewerInventoryItem* item = self->getItem();
+ if (item)
+ {
+ LLUUID parent_id = item->getParentUUID();
+ LLWearableList::instance().getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ onRemoveFromAvatarArrived,
+ new OnRemoveStruct(LLUUID(self->mUUID)));
+ }
+ }
+}
+
+// static
+void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable,
+ void* userdata)
+{
+ OnRemoveStruct *on_remove_struct = (OnRemoveStruct*) userdata;
+ const LLUUID &item_id = gInventory.getLinkedItemID(on_remove_struct->mUUID);
+ if(wearable)
+ {
+ if( gAgentWearables.isWearingItem( item_id ) )
+ {
+ EWearableType type = wearable->getType();
+
+ if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR || type==WT_EYES ) ) //&&
+ //!((!gAgent.isTeen()) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) )
+ {
+ // MULTI_WEARABLE: FIXME HACK - always remove all
+ bool do_remove_all = false;
+ gAgentWearables.removeWearable( type, do_remove_all, 0 );
+ }
+ }
+ }
+
+ // Find and remove this item from the COF.
+ LLInventoryModel::item_array_t items = gInventory.collectLinkedItems(item_id, LLAppearanceManager::getCOF());
+ llassert(items.size() == 1); // Should always have one and only one item linked to this in the COF.
+ for (LLInventoryModel::item_array_t::const_iterator iter = items.begin();
+ iter != items.end();
+ ++iter)
+ {
+ const LLViewerInventoryItem *linked_item = (*iter);
+ const LLUUID &item_id = linked_item->getUUID();
+ gInventory.purgeObject(item_id);
+ }
+ gInventory.notifyObservers();
+
+ delete on_remove_struct;
+}
+
+LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_type,
+ const LLUUID& uuid,LLInventoryModel* model)
+{
+ LLInvFVBridgeAction* action = NULL;
+ switch(asset_type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ action = new LLTextureBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_SOUND:
+ action = new LLSoundBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_LANDMARK:
+ action = new LLLandmarkBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_CALLINGCARD:
+ action = new LLCallingCardBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ action = new LLObjectBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_NOTECARD:
+ action = new LLNotecardBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_ANIMATION:
+ action = new LLAnimationBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_GESTURE:
+ action = new LLGestureBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_LSL_TEXT:
+ action = new LLLSLTextBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ action = new LLWearableBridgeAction(uuid,model);
+
+ break;
+
+ default:
+ break;
+ }
+ return action;
+}
+
+//static
+void LLInvFVBridgeAction::doAction(LLAssetType::EType asset_type,
+ const LLUUID& uuid,LLInventoryModel* model)
+{
+ LLInvFVBridgeAction* action = createAction(asset_type,uuid,model);
+ if(action)
+ {
+ action->doIt();
+ delete action;
+ }
+}
+
+//static
+void LLInvFVBridgeAction::doAction(const LLUUID& uuid, LLInventoryModel* model)
+{
+ LLAssetType::EType asset_type = model->getItem(uuid)->getType();
+ LLInvFVBridgeAction* action = createAction(asset_type,uuid,model);
+ if(action)
+ {
+ action->doIt();
+ delete action;
+ }
+}
+
+LLViewerInventoryItem* LLInvFVBridgeAction::getItem() const
+{
+ if(mModel)
+ return (LLViewerInventoryItem*)mModel->getItem(mUUID);
+ return NULL;
+}
+
+//virtual
+void LLTextureBridgeAction::doIt()
+{
+ if (getItem())
+ {
+ LLFloaterReg::showInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+//virtual
+void LLSoundBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ LLFloaterReg::showInstance("preview_sound", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+//virtual
+void LLLandmarkBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ // Opening (double-clicking) a landmark immediately teleports,
+ // but warns you the first time.
+ LLSD payload;
+ payload["asset_id"] = item->getAssetUUID();
+ LLNotifications::instance().add("TeleportFromLandmark", LLSD(), payload);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+//virtual
+void LLCallingCardBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item && item->getCreatorUUID().notNull())
+ {
+ LLAvatarActions::showProfile(item->getCreatorUUID());
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+//virtual
+void
+LLNotecardBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+//virtual
+void LLGestureBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null);
+ preview->setFocus(TRUE);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+//virtual
+void LLAnimationBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+//virtual
+void LLObjectBridgeAction::doIt()
+{
+ LLFloaterReg::showInstance("properties", mUUID);
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+//virtual
+void LLLSLTextBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+BOOL LLWearableBridgeAction::isInTrash() const
+{
+ if(!mModel) return FALSE;
+ const LLUUID trash_id = mModel->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ return mModel->isObjectDescendentOf(mUUID, trash_id);
+}
+
+BOOL LLWearableBridgeAction::isAgentInventory() const
+{
+ if(!mModel) return FALSE;
+ if(gInventory.getRootFolderID() == mUUID) return TRUE;
+ return mModel->isObjectDescendentOf(mUUID, gInventory.getRootFolderID());
+}
+
+void LLWearableBridgeAction::wearOnAvatar()
+{
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgentWearables.areWearablesLoaded())
+ {
+ LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded");
+ return;
+ }
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ if(!isAgentInventory())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_inventory_item_on_avatar(item);
+ }
+ }
+}
+
+//virtual
+void LLWearableBridgeAction::doIt()
+{
+ if(isInTrash())
+ {
+ LLNotifications::instance().add("CannotWearTrash");
+ }
+ else if(isAgentInventory())
+ {
+ if(!gAgentWearables.isWearingItem(mUUID))
+ {
+ wearOnAvatar();
+ }
+ }
+ else
+ {
+ // must be in the inventory library. copy it to our inventory
+ // and put it on right away.
+ LLViewerInventoryItem* item = getItem();
+ if(item && item->isComplete())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else if(item)
+ {
+ // *TODO: We should fetch the item details, and then do
+ // the operation above.
+ LLNotifications::instance().add("CannotWearInfoNotComplete");
+ }
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+// +=================================================+
+// | LLLinkItemBridge |
+// +=================================================+
+// For broken links
+
+std::string LLLinkItemBridge::sPrefix("Link: ");
+
+
+LLUIImagePtr LLLinkItemBridge::getIcon() const
+{
+ if (LLViewerInventoryItem *item = getItem())
+ {
+ return get_item_icon(item->getActualType(), LLInventoryType::IT_NONE, 0, FALSE);
+ }
+ return get_item_icon(LLAssetType::AT_LINK, LLInventoryType::IT_NONE, 0, FALSE);
+}
+
+void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ // *TODO: Translate
+ lldebugs << "LLLink::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Delete"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Delete"));
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+
+// +=================================================+
+// | LLLinkBridge |
+// +=================================================+
+// For broken links.
+
+std::string LLLinkFolderBridge::sPrefix("Link: ");
+
+
+LLUIImagePtr LLLinkFolderBridge::getIcon() const
+{
+ LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
+ if (LLViewerInventoryItem *item = getItem())
+ {
+ if (const LLViewerInventoryCategory* cat = item->getLinkedCategory())
+ {
+ preferred_type = cat->getPreferredType();
+ }
+ }
+ return LLFolderBridge::getIcon(preferred_type);
+}
+
+void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ // *TODO: Translate
+ lldebugs << "LLLink::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Goto Link"));
+ items.push_back(std::string("Delete"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Delete"));
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+void LLLinkFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("goto" == action)
+ {
+ gotoItem(folder);
+ return;
+ }
+ LLItemBridge::performAction(folder,model,action);
+}
+
+void LLLinkFolderBridge::gotoItem(LLFolderView *folder)
+{
+ const LLUUID &cat_uuid = getFolderID();
+ if (!cat_uuid.isNull())
+ {
+ if (LLFolderViewItem *base_folder = folder->getItemByID(cat_uuid))
+ {
+ if (LLInventoryModel* model = getInventoryModel())
+ {
+ model->fetchDescendentsOf(cat_uuid);
+ }
+ base_folder->setOpen(TRUE);
+ folder->setSelectionFromRoot(base_folder,TRUE);
+ folder->scrollToShowSelection();
+ }
+ }
+}
+
+const LLUUID &LLLinkFolderBridge::getFolderID() const
+{
+ if (LLViewerInventoryItem *link_item = getItem())
+ {
+ if (const LLViewerInventoryCategory *cat = link_item->getLinkedCategory())
+ {
+ const LLUUID& cat_uuid = cat->getUUID();
+ return cat_uuid;
+ }
+ }
+ return LLUUID::null;
+}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index f95e8f9361..3ffeb55d6c 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -383,6 +383,8 @@ class LLTextureBridge : public LLItemBridge
public:
virtual LLUIImagePtr getIcon() const;
virtual void openItem();
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action);
protected:
LLTextureBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type) :
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index 80710610d4..7f69ce9641 100644
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -1,907 +1,907 @@
-/**
- * @file llfloaterinventory.cpp
- * @brief Implementation of the inventory view and associated stuff.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include <utility> // for std::pair<>
-
-#include "llinventorypanel.h"
-
-// Seraph TODO: Remove unnecessary headers
-
-// library includes
-#include "llagent.h"
-#include "llagentwearables.h"
-#include "llcallingcard.h"
-#include "llfloaterreg.h"
-#include "llsdserialize.h"
-#include "llfiltereditor.h"
-#include "llspinctrl.h"
-#include "llui.h"
-#include "message.h"
-
-// newview includes
-#include "llappearancemgr.h"
-#include "llappviewer.h"
-#include "llfirstuse.h"
-#include "llfloaterchat.h"
-#include "llfloatercustomize.h"
-#include "llfocusmgr.h"
-#include "llfolderview.h"
-#include "llgesturemgr.h"
-#include "lliconctrl.h"
-#include "llimview.h"
-#include "llinventorybridge.h"
-#include "llinventoryclipboard.h"
-#include "llinventorymodel.h"
-#include "lllineeditor.h"
-#include "llmenugl.h"
-#include "llpreviewanim.h"
-#include "llpreviewgesture.h"
-#include "llpreviewnotecard.h"
-#include "llpreviewscript.h"
-#include "llpreviewsound.h"
-#include "llpreviewtexture.h"
-#include "llresmgr.h"
-#include "llscrollbar.h"
-#include "llscrollcontainer.h"
-#include "llselectmgr.h"
-#include "lltabcontainer.h"
-#include "lltooldraganddrop.h"
-#include "lluictrlfactory.h"
-#include "llviewerfoldertype.h"
-#include "llviewerinventory.h"
-#include "llviewermessage.h"
-#include "llviewerobjectlist.h"
-#include "llviewerregion.h"
-#include "llviewerwindow.h"
-#include "llvoavatarself.h"
-#include "llwearablelist.h"
-#include "llimfloater.h"
-
-static LLDefaultChildRegistry::Register<LLInventoryPanel> r("inventory_panel");
-
-const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder");
-const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("RecentItemsSortOrder");
-const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string("");
-static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER;
-
-LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :
- LLPanel(p),
- mInventoryObserver(NULL),
- mFolders(NULL),
- mScroller(NULL),
- mSortOrderSetting(p.sort_order_setting),
- mInventory(p.inventory),
- mAllowMultiSelect(p.allow_multi_select),
- mHasInventoryConnection(false),
- mStartFolderString(p.start_folder),
- mBuildDefaultHierarchy(true),
- mInvFVBridgeBuilder(NULL)
-{
- mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER;
-
- // contex menu callbacks
- mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryPanel::doToSelected, this, _2));
- mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH));
- mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND));
- mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&LLInventoryPanel::doCreate, this, _2));
- mCommitCallbackRegistrar.add("Inventory.AttachObject", boost::bind(&LLInventoryPanel::attachObject, this, _2));
- mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this));
-
- setBackgroundColor(LLUIColorTable::instance().getColor("InventoryBackgroundColor"));
- setBackgroundVisible(TRUE);
- setBackgroundOpaque(TRUE);
-}
-
-BOOL LLInventoryPanel::postBuild()
-{
- LLMemType mt(LLMemType::MTYPE_INVENTORY_POST_BUILD);
-
- mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves
-
- // create root folder
- {
- LLRect folder_rect(0,
- 0,
- getRect().getWidth(),
- 0);
- LLFolderView::Params p;
- p.name = getName();
- p.rect = folder_rect;
- p.parent_panel = this;
- mFolders = LLUICtrlFactory::create<LLFolderView>(p);
- mFolders->setAllowMultiSelect(mAllowMultiSelect);
- }
-
- mCommitCallbackRegistrar.popScope();
-
- mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar);
-
- // scroller
- {
- LLRect scroller_view_rect = getRect();
- scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
- LLScrollContainer::Params p;
- p.name("Inventory Scroller");
- p.rect(scroller_view_rect);
- p.follows.flags(FOLLOWS_ALL);
- p.reserve_scroll_corner(true);
- p.tab_stop(true);
- mScroller = LLUICtrlFactory::create<LLScrollContainer>(p);
- }
- addChild(mScroller);
- mScroller->addChild(mFolders);
-
- mFolders->setScrollContainer(mScroller);
-
- // set up the callbacks from the inventory we're viewing, and then
- // build everything.
- mInventoryObserver = new LLInventoryPanelObserver(this);
- mInventory->addObserver(mInventoryObserver);
-
- // determine the root folder, if any, so inventory contents show just the children
- // of that folder (i.e. not including the folder itself).
- const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(mStartFolderString);
-
- if ("INVENTORY" == mStartFolderString)
- {
- mStartFolderID = gInventory.getRootFolderID();
- }
- else if ("LIBRARY" == mStartFolderString)
- {
- mStartFolderID = gInventory.getLibraryRootFolderID();
- }
- else
- {
- mStartFolderID = (preferred_type != LLFolderType::FT_NONE ? gInventory.findCategoryUUIDForType(preferred_type) : LLUUID::null);
- }
-
- // build view of inventory if we need default full hierarchy and inventory ready, otherwise wait for modelChanged() callback
- if (mBuildDefaultHierarchy && mInventory->isInventoryUsable() && !mHasInventoryConnection)
- {
- rebuildViewsFor(mStartFolderID);
- mHasInventoryConnection = true;
- }
-
- // bit of a hack to make sure the inventory is open.
- mFolders->openFolder(preferred_type != LLFolderType::FT_NONE ? LLViewerFolderType::lookupNewCategoryName(preferred_type) : "My Inventory");
-
- if (mSortOrderSetting != INHERIT_SORT_ORDER)
- {
- setSortOrder(gSavedSettings.getU32(mSortOrderSetting));
- }
- else
- {
- setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER));
- }
- mFolders->setSortOrder(mFolders->getFilter()->getSortOrder());
-
- return TRUE;
-}
-
-LLInventoryPanel::~LLInventoryPanel()
-{
- // should this be a global setting?
- if (mFolders)
- {
- U32 sort_order = mFolders->getSortOrder();
- if (mSortOrderSetting != INHERIT_SORT_ORDER)
- {
- gSavedSettings.setU32(mSortOrderSetting, sort_order);
- }
- }
-
- // LLView destructor will take care of the sub-views.
- mInventory->removeObserver(mInventoryObserver);
- delete mInventoryObserver;
- mScroller = NULL;
-}
-
-LLMemType mt(LLMemType::MTYPE_INVENTORY_FROM_XML); // ! BUG ! Should this be removed?
-void LLInventoryPanel::draw()
-{
- // select the desired item (in case it wasn't loaded when the selection was requested)
- mFolders->updateSelection();
- LLPanel::draw();
-}
-
-LLInventoryFilter* LLInventoryPanel::getFilter()
-{
- if (mFolders) return mFolders->getFilter();
- return NULL;
-}
-
-void LLInventoryPanel::setFilterTypes(U64 filter_types, BOOL filter_for_categories)
-{
- mFolders->getFilter()->setFilterTypes(filter_types, filter_for_categories);
-}
-
-void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask)
-{
- mFolders->getFilter()->setFilterPermissions(filter_perm_mask);
-}
-
-void LLInventoryPanel::setFilterSubString(const std::string& string)
-{
- mFolders->getFilter()->setFilterSubString(string);
-}
-
-void LLInventoryPanel::setSortOrder(U32 order)
-{
- mFolders->getFilter()->setSortOrder(order);
- if (mFolders->getFilter()->isModified())
- {
- mFolders->setSortOrder(order);
- // try to keep selection onscreen, even if it wasn't to start with
- mFolders->scrollToShowSelection();
- }
-}
-
-void LLInventoryPanel::setSinceLogoff(BOOL sl)
-{
- mFolders->getFilter()->setDateRangeLastLogoff(sl);
-}
-
-void LLInventoryPanel::setHoursAgo(U32 hours)
-{
- mFolders->getFilter()->setHoursAgo(hours);
-}
-
-void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show)
-{
- mFolders->getFilter()->setShowFolderState(show);
-}
-
-LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState()
-{
- return mFolders->getFilter()->getShowFolderState();
-}
-
-static LLFastTimer::DeclareTimer FTM_REFRESH("Inventory Refresh");
-
-void LLInventoryPanel::modelChanged(U32 mask)
-{
- LLFastTimer t2(FTM_REFRESH);
-
- bool handled = false;
-
- // inventory just initialized, do complete build
- if ((mask & LLInventoryObserver::ADD) && gInventory.getChangedIDs().empty() && !mHasInventoryConnection)
- {
- rebuildViewsFor(mStartFolderID);
- mHasInventoryConnection = true;
- return;
- }
-
- if(mask & LLInventoryObserver::LABEL)
- {
- handled = true;
- // label change - empty out the display name for each object
- // in this change set.
- const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
- std::set<LLUUID>::const_iterator id_it = changed_items.begin();
- std::set<LLUUID>::const_iterator id_end = changed_items.end();
- LLFolderViewItem* view = NULL;
- LLInvFVBridge* bridge = NULL;
- for (;id_it != id_end; ++id_it)
- {
- view = mFolders->getItemByID(*id_it);
- if(view)
- {
- // request refresh on this item (also flags for filtering)
- bridge = (LLInvFVBridge*)view->getListener();
- if(bridge)
- { // Clear the display name first, so it gets properly re-built during refresh()
- bridge->clearDisplayName();
- }
- view->refresh();
- }
- }
- }
- if((mask & (LLInventoryObserver::STRUCTURE
- | LLInventoryObserver::ADD
- | LLInventoryObserver::REMOVE)) != 0)
- {
- handled = true;
- // Record which folders are open by uuid.
- LLInventoryModel* model = getModel();
- if (model)
- {
- const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
-
- std::set<LLUUID>::const_iterator id_it = changed_items.begin();
- std::set<LLUUID>::const_iterator id_end = changed_items.end();
- for (;id_it != id_end; ++id_it)
- {
- // sync view with model
- LLInventoryObject* model_item = model->getObject(*id_it);
- LLFolderViewItem* view_item = mFolders->getItemByID(*id_it);
-
- if (model_item)
- {
- if (!view_item)
- {
- // this object was just created, need to build a view for it
- if ((mask & LLInventoryObserver::ADD) != LLInventoryObserver::ADD)
- {
- llwarns << *id_it << " is in model but not in view, but ADD flag not set" << llendl;
- }
- buildNewViews(*id_it);
-
- // select any newly created object
- // that has the auto rename at top of folder
- // root set
- if(mFolders->getRoot()->needsAutoRename())
- {
- setSelection(*id_it, FALSE);
- }
- }
- else
- {
- // this object was probably moved, check its parent
- if ((mask & LLInventoryObserver::STRUCTURE) != LLInventoryObserver::STRUCTURE)
- {
- llwarns << *id_it << " is in model and in view, but STRUCTURE flag not set" << llendl;
- }
-
- LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolders->getItemByID(model_item->getParentUUID());
-
- // added check against NULL for cases when Inventory panel contains startFolder.
- // in this case parent is LLFolderView (LLInventoryPanel::mFolders) itself.
- // this check is a fix for bug EXT-1859.
- if (NULL != new_parent && view_item->getParentFolder() != new_parent)
- {
- view_item->getParentFolder()->extractItem(view_item);
- view_item->addToFolder(new_parent, mFolders);
- }
-/*
- on the other side in case Inventory Panel has content of the any folder
- it is possible that item moved to some folder which is absent in current
- Panel. For ex. removing item (via moving to trash).
- In this case we need to check if new parent is other then inventory start folder
- and simply remove its View from the hierarchy.
- See details in EXT-2098.
-*/
- // So, let check if item was moved into folder out of this Inventory Panel.
- else if (mStartFolderID.notNull() && NULL == new_parent && model_item->getParentUUID() != mStartFolderID)
- {
- view_item->getParentFolder()->extractItem(view_item);
- }
- }
- }
- else
- {
- if (view_item)
- {
- if ((mask & LLInventoryObserver::REMOVE) != LLInventoryObserver::REMOVE)
- {
- llwarns << *id_it << " is not in model but in view, but REMOVE flag not set" << llendl;
- }
- // item in view but not model, need to delete view
- view_item->destroyView();
- }
- else
- {
- llwarns << *id_it << "Item does not exist in either view or model, but notification triggered" << llendl;
- }
- }
- }
- }
- }
-
- if (!handled)
- {
- // it's a small change that only requires a refresh.
- // *TODO: figure out a more efficient way to do the refresh
- // since it is expensive on large inventories
- mFolders->refresh();
- }
-}
-
-
-void LLInventoryPanel::rebuildViewsFor(const LLUUID& id)
-{
- LLFolderViewItem* old_view = NULL;
-
- // get old LLFolderViewItem
- old_view = mFolders->getItemByID(id);
- if (old_view && id.notNull())
- {
- old_view->destroyView();
- }
-
- buildNewViews(id);
-}
-
-void LLInventoryPanel::buildNewViews(const LLUUID& id)
-{
- LLMemType mt(LLMemType::MTYPE_INVENTORY_BUILD_NEW_VIEWS);
- LLFolderViewItem* itemp = NULL;
- LLInventoryObject* objectp = NULL;
-
- // Don't add the start folder (the inventory panel will show contents
- // beginning with the children of the starting folder, excluding the starting folder itself).
- if (id != mStartFolderID)
- {
- objectp = gInventory.getObject(id);
- if (objectp)
- {
- const LLUUID &parent_id = objectp->getParentUUID();
- // If this item's parent is the starting folder, then just add it to the top level (recall that
- // the starting folder isn't actually represented in the view, parent_folder would be NULL in
- // this case otherwise).
- LLFolderViewFolder* parent_folder = (parent_id == mStartFolderID ?
- mFolders : (LLFolderViewFolder*)mFolders->getItemByID(parent_id));
-
- // This item exists outside the inventory's hierarchy, so don't add it.
- if (!parent_folder)
- {
- return;
- }
-
- if (objectp->getType() <= LLAssetType::AT_NONE ||
- objectp->getType() >= LLAssetType::AT_COUNT)
- {
- llwarns << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " <<
- ((S32) objectp->getType()) << llendl;
- return;
- }
-
- if (objectp->getType() == LLAssetType::AT_CATEGORY &&
- objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)
- {
- LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(),
- objectp->getType(),
- LLInventoryType::IT_CATEGORY,
- this,
- objectp->getUUID());
-
- if (new_listener)
- {
- LLFolderViewFolder::Params p;
- p.name = new_listener->getDisplayName();
- p.icon = new_listener->getIcon();
- p.root = mFolders;
- p.listener = new_listener;
- LLFolderViewFolder* folderp = LLUICtrlFactory::create<LLFolderViewFolder>(p);
-
- folderp->setItemSortOrder(mFolders->getSortOrder());
- itemp = folderp;
- }
- }
- else
- {
- // Build new view for item
- LLInventoryItem* item = (LLInventoryItem*)objectp;
- LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(),
- item->getActualType(),
- item->getInventoryType(),
- this,
- item->getUUID(),
- item->getFlags());
-
- if (new_listener)
- {
- LLFolderViewItem::Params params;
- params.name(new_listener->getDisplayName());
- params.icon(new_listener->getIcon());
- params.creation_date(new_listener->getCreationDate());
- params.root(mFolders);
- params.listener(new_listener);
- params.rect(LLRect (0, 0, 0, 0));
- itemp = LLUICtrlFactory::create<LLFolderViewItem> (params);
- }
- }
-
- if (itemp)
- {
- itemp->addToFolder(parent_folder, mFolders);
- }
- }
- }
-
- // If this is a folder, add the children of the folder and recursively add any
- // child folders.
- if ((id == mStartFolderID) ||
- (objectp && objectp->getType() == LLAssetType::AT_CATEGORY))
- {
- LLViewerInventoryCategory::cat_array_t* categories;
- LLViewerInventoryItem::item_array_t* items;
-
- mInventory->lockDirectDescendentArrays(id, categories, items);
- if(categories)
- {
- S32 count = categories->count();
- for(S32 i = 0; i < count; ++i)
- {
- LLInventoryCategory* cat = categories->get(i);
- buildNewViews(cat->getUUID());
- }
- }
- if(items)
- {
- S32 count = items->count();
- for(S32 i = 0; i < count; ++i)
- {
- LLInventoryItem* item = items->get(i);
- buildNewViews(item->getUUID());
- }
- }
- mInventory->unlockDirectDescendentArrays(id);
- }
-}
-
-struct LLConfirmPurgeData
-{
- LLUUID mID;
- LLInventoryModel* mModel;
-};
-
-class LLIsNotWorn : public LLInventoryCollectFunctor
-{
-public:
- LLIsNotWorn() {}
- virtual ~LLIsNotWorn() {}
- virtual bool operator()(LLInventoryCategory* cat,
- LLInventoryItem* item)
- {
- return !gAgentWearables.isWearingItem(item->getUUID());
- }
-};
-
-class LLOpenFolderByID : public LLFolderViewFunctor
-{
-public:
- LLOpenFolderByID(const LLUUID& id) : mID(id) {}
- virtual ~LLOpenFolderByID() {}
- virtual void doFolder(LLFolderViewFolder* folder)
- {
- if (folder->getListener() && folder->getListener()->getUUID() == mID) folder->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP);
- }
- virtual void doItem(LLFolderViewItem* item) {}
-protected:
- const LLUUID& mID;
-};
-
-
-void LLInventoryPanel::openSelected()
-{
- LLFolderViewItem* folder_item = mFolders->getCurSelectedItem();
- if(!folder_item) return;
- LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener();
- if(!bridge) return;
- bridge->openItem();
-}
-
-BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask)
-{
- BOOL handled = LLView::handleHover(x, y, mask);
- if(handled)
- {
- ECursorType cursor = getWindow()->getCursor();
- if (LLInventoryModel::backgroundFetchActive() && cursor == UI_CURSOR_ARROW)
- {
- // replace arrow cursor with arrow and hourglass cursor
- getWindow()->setCursor(UI_CURSOR_WORKING);
- }
- }
- else
- {
- getWindow()->setCursor(UI_CURSOR_ARROW);
- }
- return TRUE;
-}
-
-BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
-{
-
- BOOL handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
-
- if (handled)
- {
- mFolders->setDragAndDropThisFrame();
- }
-
- return handled;
-}
-
-void LLInventoryPanel::onFocusLost()
-{
- // inventory no longer handles cut/copy/paste/delete
- if (LLEditMenuHandler::gEditMenuHandler == mFolders)
- {
- LLEditMenuHandler::gEditMenuHandler = NULL;
- }
-
- LLPanel::onFocusLost();
-}
-
-void LLInventoryPanel::onFocusReceived()
-{
- // inventory now handles cut/copy/paste/delete
- LLEditMenuHandler::gEditMenuHandler = mFolders;
-
- LLPanel::onFocusReceived();
-}
-
-
-void LLInventoryPanel::openAllFolders()
-{
- mFolders->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_DOWN);
- mFolders->arrangeAll();
-}
-
-void LLInventoryPanel::openDefaultFolderForType(LLFolderType::EType type)
-{
- LLUUID category_id = mInventory->findCategoryUUIDForType(type);
- LLOpenFolderByID opener(category_id);
- mFolders->applyFunctorRecursively(opener);
-}
-
-void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus)
-{
- // Don't select objects in COF (e.g. to prevent refocus when items are worn).
- const LLInventoryObject *obj = gInventory.getObject(obj_id);
- if (obj && obj->getParentUUID() == LLAppearanceManager::getCOF())
- {
- return;
- }
- mFolders->setSelectionByID(obj_id, take_keyboard_focus);
-}
-
-void LLInventoryPanel::clearSelection()
-{
- mFolders->clearSelection();
-}
-
-void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& items, BOOL user_action)
-{
- LLFolderView* fv = getRootFolder();
- if (fv->needsAutoRename()) // auto-selecting a new user-created asset and preparing to rename
- {
- fv->setNeedsAutoRename(FALSE);
- if (items.size()) // new asset is visible and selected
- {
- fv->startRenamingSelectedItem();
- }
- }
- // Seraph - Put determineFolderType in here for ensemble typing?
-}
-
-//----------------------------------------------------------------------------
-
-void LLInventoryPanel::doToSelected(const LLSD& userdata)
-{
- mFolders->doToSelected(&gInventory, userdata);
-}
-
-void LLInventoryPanel::doCreate(const LLSD& userdata)
-{
- menu_create_inventory_item(mFolders, LLFolderBridge::sSelf, userdata);
-}
-
-bool LLInventoryPanel::beginIMSession()
-{
- std::set<LLUUID> selected_items;
- mFolders->getSelectionList(selected_items);
-
- std::string name;
- static int session_num = 1;
-
- LLDynamicArray<LLUUID> members;
- EInstantMessage type = IM_SESSION_CONFERENCE_START;
-
- std::set<LLUUID>::const_iterator iter;
- for (iter = selected_items.begin(); iter != selected_items.end(); iter++)
- {
-
- LLUUID item = *iter;
- LLFolderViewItem* folder_item = mFolders->getItemByID(item);
-
- if(folder_item)
- {
- LLFolderViewEventListener* fve_listener = folder_item->getListener();
- if (fve_listener && (fve_listener->getInventoryType() == LLInventoryType::IT_CATEGORY))
- {
-
- LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getListener();
- if(!bridge) return true;
- LLViewerInventoryCategory* cat = bridge->getCategory();
- if(!cat) return true;
- name = cat->getName();
- LLUniqueBuddyCollector is_buddy;
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- gInventory.collectDescendentsIf(bridge->getUUID(),
- cat_array,
- item_array,
- LLInventoryModel::EXCLUDE_TRASH,
- is_buddy);
- S32 count = item_array.count();
- if(count > 0)
- {
- LLFloaterReg::showInstance("communicate");
- // create the session
- LLAvatarTracker& at = LLAvatarTracker::instance();
- LLUUID id;
- for(S32 i = 0; i < count; ++i)
- {
- id = item_array.get(i)->getCreatorUUID();
- if(at.isBuddyOnline(id))
- {
- members.put(id);
- }
- }
- }
- }
- else
- {
- LLFolderViewItem* folder_item = mFolders->getItemByID(item);
- if(!folder_item) return true;
- LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getListener();
-
- if (listenerp->getInventoryType() == LLInventoryType::IT_CALLINGCARD)
- {
- LLInventoryItem* inv_item = gInventory.getItem(listenerp->getUUID());
-
- if (inv_item)
- {
- LLAvatarTracker& at = LLAvatarTracker::instance();
- LLUUID id = inv_item->getCreatorUUID();
-
- if(at.isBuddyOnline(id))
- {
- members.put(id);
- }
- }
- } //if IT_CALLINGCARD
- } //if !IT_CATEGORY
- }
- } //for selected_items
-
- // the session_id is randomly generated UUID which will be replaced later
- // with a server side generated number
-
- if (name.empty())
- {
- name = llformat("Session %d", session_num++);
- }
-
- LLUUID session_id = gIMMgr->addSession(name, type, members[0], members);
- if (session_id != LLUUID::null)
- {
- LLIMFloater::show(session_id);
- }
-
- return true;
-}
-
-bool LLInventoryPanel::attachObject(const LLSD& userdata)
-{
- std::set<LLUUID> selected_items;
- mFolders->getSelectionList(selected_items);
-
- std::string joint_name = userdata.asString();
- LLVOAvatar *avatarp = static_cast<LLVOAvatar*>(gAgent.getAvatarObject());
- LLViewerJointAttachment* attachmentp = NULL;
- for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin();
- iter != avatarp->mAttachmentPoints.end(); )
- {
- LLVOAvatar::attachment_map_t::iterator curiter = iter++;
- LLViewerJointAttachment* attachment = curiter->second;
- if (attachment->getName() == joint_name)
- {
- attachmentp = attachment;
- break;
- }
- }
- if (attachmentp == NULL)
- {
- return true;
- }
-
- for (std::set<LLUUID>::const_iterator set_iter = selected_items.begin();
- set_iter != selected_items.end();
- ++set_iter)
- {
- const LLUUID &id = *set_iter;
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(id);
- if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID()))
- {
- rez_attachment(item, attachmentp);
- }
- else if(item && item->isComplete())
- {
- // must be in library. copy it to our inventory and put it on.
- LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(attachmentp);
- copy_inventory_item(gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- LLUUID::null,
- std::string(),
- cb);
- }
- }
- gFocusMgr.setKeyboardFocus(NULL);
-
- return true;
-}
-
-
-//----------------------------------------------------------------------------
-
-// static DEBUG ONLY:
-void LLInventoryPanel::dumpSelectionInformation(void* user_data)
-{
- LLInventoryPanel* iv = (LLInventoryPanel*)user_data;
- iv->mFolders->dumpSelectionInformation();
-}
-
-BOOL LLInventoryPanel::getSinceLogoff()
-{
- return mFolders->getFilter()->isSinceLogoff();
-}
-
-void example_param_block_usage()
-{
- LLInventoryPanel::Params param_block;
- param_block.name(std::string("inventory"));
-
- param_block.sort_order_setting(LLInventoryPanel::RECENTITEMS_SORT_ORDER);
- param_block.allow_multi_select(true);
- param_block.filter(LLInventoryPanel::Filter()
- .sort_order(1)
- .types(0xffff0000));
- param_block.inventory(&gInventory);
- param_block.has_border(true);
-
- LLUICtrlFactory::create<LLInventoryPanel>(param_block);
-
- param_block = LLInventoryPanel::Params();
- param_block.name(std::string("inventory"));
-
- //LLSD param_block_sd;
- //param_block_sd["sort_order_setting"] = LLInventoryPanel::RECENTITEMS_SORT_ORDER;
- //param_block_sd["allow_multi_select"] = true;
- //param_block_sd["filter"]["sort_order"] = 1;
- //param_block_sd["filter"]["types"] = (S32)0xffff0000;
- //param_block_sd["has_border"] = true;
-
- //LLInitParam::LLSDParser(param_block_sd).parse(param_block);
-
- LLUICtrlFactory::create<LLInventoryPanel>(param_block);
-}
+/*
+ * @file llinventorypanel.cpp
+ * @brief Implementation of the inventory panel and associated stuff.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <utility> // for std::pair<>
+
+#include "llinventorypanel.h"
+
+// Seraph TODO: Remove unnecessary headers
+
+// library includes
+#include "llagent.h"
+#include "llagentwearables.h"
+#include "llcallingcard.h"
+#include "llfloaterreg.h"
+#include "llsdserialize.h"
+#include "llfiltereditor.h"
+#include "llspinctrl.h"
+#include "llui.h"
+#include "message.h"
+
+// newview includes
+#include "llappearancemgr.h"
+#include "llappviewer.h"
+#include "llfirstuse.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfocusmgr.h"
+#include "llfolderview.h"
+#include "llgesturemgr.h"
+#include "lliconctrl.h"
+#include "llimview.h"
+#include "llinventorybridge.h"
+#include "llinventoryclipboard.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llpreviewanim.h"
+#include "llpreviewgesture.h"
+#include "llpreviewnotecard.h"
+#include "llpreviewscript.h"
+#include "llpreviewsound.h"
+#include "llpreviewtexture.h"
+#include "llresmgr.h"
+#include "llscrollbar.h"
+#include "llscrollcontainer.h"
+#include "llselectmgr.h"
+#include "lltabcontainer.h"
+#include "lltooldraganddrop.h"
+#include "lluictrlfactory.h"
+#include "llviewerfoldertype.h"
+#include "llviewerinventory.h"
+#include "llviewermessage.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvoavatarself.h"
+#include "llwearablelist.h"
+#include "llimfloater.h"
+
+static LLDefaultChildRegistry::Register<LLInventoryPanel> r("inventory_panel");
+
+const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder");
+const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("RecentItemsSortOrder");
+const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string("");
+static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER;
+
+LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :
+ LLPanel(p),
+ mInventoryObserver(NULL),
+ mFolders(NULL),
+ mScroller(NULL),
+ mSortOrderSetting(p.sort_order_setting),
+ mInventory(p.inventory),
+ mAllowMultiSelect(p.allow_multi_select),
+ mHasInventoryConnection(false),
+ mStartFolderString(p.start_folder),
+ mBuildDefaultHierarchy(true),
+ mInvFVBridgeBuilder(NULL)
+{
+ mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER;
+
+ // contex menu callbacks
+ mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryPanel::doToSelected, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH));
+ mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND));
+ mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&LLInventoryPanel::doCreate, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.AttachObject", boost::bind(&LLInventoryPanel::attachObject, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this));
+
+ setBackgroundColor(LLUIColorTable::instance().getColor("InventoryBackgroundColor"));
+ setBackgroundVisible(TRUE);
+ setBackgroundOpaque(TRUE);
+}
+
+BOOL LLInventoryPanel::postBuild()
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_POST_BUILD);
+
+ mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves
+
+ // create root folder
+ {
+ LLRect folder_rect(0,
+ 0,
+ getRect().getWidth(),
+ 0);
+ LLFolderView::Params p;
+ p.name = getName();
+ p.rect = folder_rect;
+ p.parent_panel = this;
+ mFolders = LLUICtrlFactory::create<LLFolderView>(p);
+ mFolders->setAllowMultiSelect(mAllowMultiSelect);
+ }
+
+ mCommitCallbackRegistrar.popScope();
+
+ mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar);
+
+ // scroller
+ {
+ LLRect scroller_view_rect = getRect();
+ scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
+ LLScrollContainer::Params p;
+ p.name("Inventory Scroller");
+ p.rect(scroller_view_rect);
+ p.follows.flags(FOLLOWS_ALL);
+ p.reserve_scroll_corner(true);
+ p.tab_stop(true);
+ mScroller = LLUICtrlFactory::create<LLScrollContainer>(p);
+ }
+ addChild(mScroller);
+ mScroller->addChild(mFolders);
+
+ mFolders->setScrollContainer(mScroller);
+
+ // set up the callbacks from the inventory we're viewing, and then
+ // build everything.
+ mInventoryObserver = new LLInventoryPanelObserver(this);
+ mInventory->addObserver(mInventoryObserver);
+
+ // determine the root folder, if any, so inventory contents show just the children
+ // of that folder (i.e. not including the folder itself).
+ const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(mStartFolderString);
+
+ if ("INVENTORY" == mStartFolderString)
+ {
+ mStartFolderID = gInventory.getRootFolderID();
+ }
+ else if ("LIBRARY" == mStartFolderString)
+ {
+ mStartFolderID = gInventory.getLibraryRootFolderID();
+ }
+ else
+ {
+ mStartFolderID = (preferred_type != LLFolderType::FT_NONE ? gInventory.findCategoryUUIDForType(preferred_type) : LLUUID::null);
+ }
+
+ // build view of inventory if we need default full hierarchy and inventory ready, otherwise wait for modelChanged() callback
+ if (mBuildDefaultHierarchy && mInventory->isInventoryUsable() && !mHasInventoryConnection)
+ {
+ rebuildViewsFor(mStartFolderID);
+ mHasInventoryConnection = true;
+ }
+
+ // bit of a hack to make sure the inventory is open.
+ mFolders->openFolder(preferred_type != LLFolderType::FT_NONE ? LLViewerFolderType::lookupNewCategoryName(preferred_type) : "My Inventory");
+
+ if (mSortOrderSetting != INHERIT_SORT_ORDER)
+ {
+ setSortOrder(gSavedSettings.getU32(mSortOrderSetting));
+ }
+ else
+ {
+ setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER));
+ }
+ mFolders->setSortOrder(mFolders->getFilter()->getSortOrder());
+
+ return TRUE;
+}
+
+LLInventoryPanel::~LLInventoryPanel()
+{
+ // should this be a global setting?
+ if (mFolders)
+ {
+ U32 sort_order = mFolders->getSortOrder();
+ if (mSortOrderSetting != INHERIT_SORT_ORDER)
+ {
+ gSavedSettings.setU32(mSortOrderSetting, sort_order);
+ }
+ }
+
+ // LLView destructor will take care of the sub-views.
+ mInventory->removeObserver(mInventoryObserver);
+ delete mInventoryObserver;
+ mScroller = NULL;
+}
+
+LLMemType mt(LLMemType::MTYPE_INVENTORY_FROM_XML); // ! BUG ! Should this be removed?
+void LLInventoryPanel::draw()
+{
+ // select the desired item (in case it wasn't loaded when the selection was requested)
+ mFolders->updateSelection();
+ LLPanel::draw();
+}
+
+LLInventoryFilter* LLInventoryPanel::getFilter()
+{
+ if (mFolders) return mFolders->getFilter();
+ return NULL;
+}
+
+void LLInventoryPanel::setFilterTypes(U64 filter_types, BOOL filter_for_categories)
+{
+ mFolders->getFilter()->setFilterTypes(filter_types, filter_for_categories);
+}
+
+void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask)
+{
+ mFolders->getFilter()->setFilterPermissions(filter_perm_mask);
+}
+
+void LLInventoryPanel::setFilterSubString(const std::string& string)
+{
+ mFolders->getFilter()->setFilterSubString(string);
+}
+
+void LLInventoryPanel::setSortOrder(U32 order)
+{
+ mFolders->getFilter()->setSortOrder(order);
+ if (mFolders->getFilter()->isModified())
+ {
+ mFolders->setSortOrder(order);
+ // try to keep selection onscreen, even if it wasn't to start with
+ mFolders->scrollToShowSelection();
+ }
+}
+
+void LLInventoryPanel::setSinceLogoff(BOOL sl)
+{
+ mFolders->getFilter()->setDateRangeLastLogoff(sl);
+}
+
+void LLInventoryPanel::setHoursAgo(U32 hours)
+{
+ mFolders->getFilter()->setHoursAgo(hours);
+}
+
+void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show)
+{
+ mFolders->getFilter()->setShowFolderState(show);
+}
+
+LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState()
+{
+ return mFolders->getFilter()->getShowFolderState();
+}
+
+static LLFastTimer::DeclareTimer FTM_REFRESH("Inventory Refresh");
+
+void LLInventoryPanel::modelChanged(U32 mask)
+{
+ LLFastTimer t2(FTM_REFRESH);
+
+ bool handled = false;
+
+ // inventory just initialized, do complete build
+ if ((mask & LLInventoryObserver::ADD) && gInventory.getChangedIDs().empty() && !mHasInventoryConnection)
+ {
+ rebuildViewsFor(mStartFolderID);
+ mHasInventoryConnection = true;
+ return;
+ }
+
+ if(mask & LLInventoryObserver::LABEL)
+ {
+ handled = true;
+ // label change - empty out the display name for each object
+ // in this change set.
+ const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ LLFolderViewItem* view = NULL;
+ LLInvFVBridge* bridge = NULL;
+ for (;id_it != id_end; ++id_it)
+ {
+ view = mFolders->getItemByID(*id_it);
+ if(view)
+ {
+ // request refresh on this item (also flags for filtering)
+ bridge = (LLInvFVBridge*)view->getListener();
+ if(bridge)
+ { // Clear the display name first, so it gets properly re-built during refresh()
+ bridge->clearDisplayName();
+ }
+ view->refresh();
+ }
+ }
+ }
+ if((mask & (LLInventoryObserver::STRUCTURE
+ | LLInventoryObserver::ADD
+ | LLInventoryObserver::REMOVE)) != 0)
+ {
+ handled = true;
+ // Record which folders are open by uuid.
+ LLInventoryModel* model = getModel();
+ if (model)
+ {
+ const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
+
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ for (;id_it != id_end; ++id_it)
+ {
+ // sync view with model
+ LLInventoryObject* model_item = model->getObject(*id_it);
+ LLFolderViewItem* view_item = mFolders->getItemByID(*id_it);
+
+ if (model_item)
+ {
+ if (!view_item)
+ {
+ // this object was just created, need to build a view for it
+ if ((mask & LLInventoryObserver::ADD) != LLInventoryObserver::ADD)
+ {
+ llwarns << *id_it << " is in model but not in view, but ADD flag not set" << llendl;
+ }
+ buildNewViews(*id_it);
+
+ // select any newly created object
+ // that has the auto rename at top of folder
+ // root set
+ if(mFolders->getRoot()->needsAutoRename())
+ {
+ setSelection(*id_it, FALSE);
+ }
+ }
+ else
+ {
+ // this object was probably moved, check its parent
+ if ((mask & LLInventoryObserver::STRUCTURE) != LLInventoryObserver::STRUCTURE)
+ {
+ llwarns << *id_it << " is in model and in view, but STRUCTURE flag not set" << llendl;
+ }
+
+ LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolders->getItemByID(model_item->getParentUUID());
+
+ // added check against NULL for cases when Inventory panel contains startFolder.
+ // in this case parent is LLFolderView (LLInventoryPanel::mFolders) itself.
+ // this check is a fix for bug EXT-1859.
+ if (NULL != new_parent && view_item->getParentFolder() != new_parent)
+ {
+ view_item->getParentFolder()->extractItem(view_item);
+ view_item->addToFolder(new_parent, mFolders);
+ }
+/*
+ on the other side in case Inventory Panel has content of the any folder
+ it is possible that item moved to some folder which is absent in current
+ Panel. For ex. removing item (via moving to trash).
+ In this case we need to check if new parent is other then inventory start folder
+ and simply remove its View from the hierarchy.
+ See details in EXT-2098.
+*/
+ // So, let check if item was moved into folder out of this Inventory Panel.
+ else if (mStartFolderID.notNull() && NULL == new_parent && model_item->getParentUUID() != mStartFolderID)
+ {
+ view_item->getParentFolder()->extractItem(view_item);
+ }
+ }
+ }
+ else
+ {
+ if (view_item)
+ {
+ if ((mask & LLInventoryObserver::REMOVE) != LLInventoryObserver::REMOVE)
+ {
+ llwarns << *id_it << " is not in model but in view, but REMOVE flag not set" << llendl;
+ }
+ // item in view but not model, need to delete view
+ view_item->destroyView();
+ }
+ else
+ {
+ llwarns << *id_it << "Item does not exist in either view or model, but notification triggered" << llendl;
+ }
+ }
+ }
+ }
+ }
+
+ if (!handled)
+ {
+ // it's a small change that only requires a refresh.
+ // *TODO: figure out a more efficient way to do the refresh
+ // since it is expensive on large inventories
+ mFolders->refresh();
+ }
+}
+
+
+void LLInventoryPanel::rebuildViewsFor(const LLUUID& id)
+{
+ LLFolderViewItem* old_view = NULL;
+
+ // get old LLFolderViewItem
+ old_view = mFolders->getItemByID(id);
+ if (old_view && id.notNull())
+ {
+ old_view->destroyView();
+ }
+
+ buildNewViews(id);
+}
+
+void LLInventoryPanel::buildNewViews(const LLUUID& id)
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_BUILD_NEW_VIEWS);
+ LLFolderViewItem* itemp = NULL;
+ LLInventoryObject* objectp = NULL;
+
+ // Don't add the start folder (the inventory panel will show contents
+ // beginning with the children of the starting folder, excluding the starting folder itself).
+ if (id != mStartFolderID)
+ {
+ objectp = gInventory.getObject(id);
+ if (objectp)
+ {
+ const LLUUID &parent_id = objectp->getParentUUID();
+ // If this item's parent is the starting folder, then just add it to the top level (recall that
+ // the starting folder isn't actually represented in the view, parent_folder would be NULL in
+ // this case otherwise).
+ LLFolderViewFolder* parent_folder = (parent_id == mStartFolderID ?
+ mFolders : (LLFolderViewFolder*)mFolders->getItemByID(parent_id));
+
+ // This item exists outside the inventory's hierarchy, so don't add it.
+ if (!parent_folder)
+ {
+ return;
+ }
+
+ if (objectp->getType() <= LLAssetType::AT_NONE ||
+ objectp->getType() >= LLAssetType::AT_COUNT)
+ {
+ lldebugs << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " <<
+ ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() << llendl;
+ return;
+ }
+
+ if (objectp->getType() == LLAssetType::AT_CATEGORY &&
+ objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)
+ {
+ LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(),
+ objectp->getType(),
+ LLInventoryType::IT_CATEGORY,
+ this,
+ objectp->getUUID());
+
+ if (new_listener)
+ {
+ LLFolderViewFolder::Params p;
+ p.name = new_listener->getDisplayName();
+ p.icon = new_listener->getIcon();
+ p.root = mFolders;
+ p.listener = new_listener;
+ LLFolderViewFolder* folderp = LLUICtrlFactory::create<LLFolderViewFolder>(p);
+
+ folderp->setItemSortOrder(mFolders->getSortOrder());
+ itemp = folderp;
+ }
+ }
+ else
+ {
+ // Build new view for item
+ LLInventoryItem* item = (LLInventoryItem*)objectp;
+ LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(),
+ item->getActualType(),
+ item->getInventoryType(),
+ this,
+ item->getUUID(),
+ item->getFlags());
+
+ if (new_listener)
+ {
+ LLFolderViewItem::Params params;
+ params.name(new_listener->getDisplayName());
+ params.icon(new_listener->getIcon());
+ params.creation_date(new_listener->getCreationDate());
+ params.root(mFolders);
+ params.listener(new_listener);
+ params.rect(LLRect (0, 0, 0, 0));
+ itemp = LLUICtrlFactory::create<LLFolderViewItem> (params);
+ }
+ }
+
+ if (itemp)
+ {
+ itemp->addToFolder(parent_folder, mFolders);
+ }
+ }
+ }
+
+ // If this is a folder, add the children of the folder and recursively add any
+ // child folders.
+ if ((id == mStartFolderID) ||
+ (objectp && objectp->getType() == LLAssetType::AT_CATEGORY))
+ {
+ LLViewerInventoryCategory::cat_array_t* categories;
+ LLViewerInventoryItem::item_array_t* items;
+
+ mInventory->lockDirectDescendentArrays(id, categories, items);
+ if(categories)
+ {
+ S32 count = categories->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLInventoryCategory* cat = categories->get(i);
+ buildNewViews(cat->getUUID());
+ }
+ }
+ if(items)
+ {
+ S32 count = items->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLInventoryItem* item = items->get(i);
+ buildNewViews(item->getUUID());
+ }
+ }
+ mInventory->unlockDirectDescendentArrays(id);
+ }
+}
+
+struct LLConfirmPurgeData
+{
+ LLUUID mID;
+ LLInventoryModel* mModel;
+};
+
+class LLIsNotWorn : public LLInventoryCollectFunctor
+{
+public:
+ LLIsNotWorn() {}
+ virtual ~LLIsNotWorn() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+ {
+ return !gAgentWearables.isWearingItem(item->getUUID());
+ }
+};
+
+class LLOpenFolderByID : public LLFolderViewFunctor
+{
+public:
+ LLOpenFolderByID(const LLUUID& id) : mID(id) {}
+ virtual ~LLOpenFolderByID() {}
+ virtual void doFolder(LLFolderViewFolder* folder)
+ {
+ if (folder->getListener() && folder->getListener()->getUUID() == mID) folder->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP);
+ }
+ virtual void doItem(LLFolderViewItem* item) {}
+protected:
+ const LLUUID& mID;
+};
+
+
+void LLInventoryPanel::openSelected()
+{
+ LLFolderViewItem* folder_item = mFolders->getCurSelectedItem();
+ if(!folder_item) return;
+ LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener();
+ if(!bridge) return;
+ bridge->openItem();
+}
+
+BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleHover(x, y, mask);
+ if(handled)
+ {
+ ECursorType cursor = getWindow()->getCursor();
+ if (LLInventoryModel::backgroundFetchActive() && cursor == UI_CURSOR_ARROW)
+ {
+ // replace arrow cursor with arrow and hourglass cursor
+ getWindow()->setCursor(UI_CURSOR_WORKING);
+ }
+ }
+ else
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ return TRUE;
+}
+
+BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+
+ BOOL handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+
+ if (handled)
+ {
+ mFolders->setDragAndDropThisFrame();
+ }
+
+ return handled;
+}
+
+void LLInventoryPanel::onFocusLost()
+{
+ // inventory no longer handles cut/copy/paste/delete
+ if (LLEditMenuHandler::gEditMenuHandler == mFolders)
+ {
+ LLEditMenuHandler::gEditMenuHandler = NULL;
+ }
+
+ LLPanel::onFocusLost();
+}
+
+void LLInventoryPanel::onFocusReceived()
+{
+ // inventory now handles cut/copy/paste/delete
+ LLEditMenuHandler::gEditMenuHandler = mFolders;
+
+ LLPanel::onFocusReceived();
+}
+
+
+void LLInventoryPanel::openAllFolders()
+{
+ mFolders->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_DOWN);
+ mFolders->arrangeAll();
+}
+
+void LLInventoryPanel::openDefaultFolderForType(LLFolderType::EType type)
+{
+ LLUUID category_id = mInventory->findCategoryUUIDForType(type);
+ LLOpenFolderByID opener(category_id);
+ mFolders->applyFunctorRecursively(opener);
+}
+
+void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus)
+{
+ // Don't select objects in COF (e.g. to prevent refocus when items are worn).
+ const LLInventoryObject *obj = gInventory.getObject(obj_id);
+ if (obj && obj->getParentUUID() == LLAppearanceManager::getCOF())
+ {
+ return;
+ }
+ mFolders->setSelectionByID(obj_id, take_keyboard_focus);
+}
+
+void LLInventoryPanel::clearSelection()
+{
+ mFolders->clearSelection();
+}
+
+void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& items, BOOL user_action)
+{
+ LLFolderView* fv = getRootFolder();
+ if (fv->needsAutoRename()) // auto-selecting a new user-created asset and preparing to rename
+ {
+ fv->setNeedsAutoRename(FALSE);
+ if (items.size()) // new asset is visible and selected
+ {
+ fv->startRenamingSelectedItem();
+ }
+ }
+ // Seraph - Put determineFolderType in here for ensemble typing?
+}
+
+//----------------------------------------------------------------------------
+
+void LLInventoryPanel::doToSelected(const LLSD& userdata)
+{
+ mFolders->doToSelected(&gInventory, userdata);
+}
+
+void LLInventoryPanel::doCreate(const LLSD& userdata)
+{
+ menu_create_inventory_item(mFolders, LLFolderBridge::sSelf, userdata);
+}
+
+bool LLInventoryPanel::beginIMSession()
+{
+ std::set<LLUUID> selected_items;
+ mFolders->getSelectionList(selected_items);
+
+ std::string name;
+ static int session_num = 1;
+
+ LLDynamicArray<LLUUID> members;
+ EInstantMessage type = IM_SESSION_CONFERENCE_START;
+
+ std::set<LLUUID>::const_iterator iter;
+ for (iter = selected_items.begin(); iter != selected_items.end(); iter++)
+ {
+
+ LLUUID item = *iter;
+ LLFolderViewItem* folder_item = mFolders->getItemByID(item);
+
+ if(folder_item)
+ {
+ LLFolderViewEventListener* fve_listener = folder_item->getListener();
+ if (fve_listener && (fve_listener->getInventoryType() == LLInventoryType::IT_CATEGORY))
+ {
+
+ LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getListener();
+ if(!bridge) return true;
+ LLViewerInventoryCategory* cat = bridge->getCategory();
+ if(!cat) return true;
+ name = cat->getName();
+ LLUniqueBuddyCollector is_buddy;
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ gInventory.collectDescendentsIf(bridge->getUUID(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_buddy);
+ S32 count = item_array.count();
+ if(count > 0)
+ {
+ LLFloaterReg::showInstance("communicate");
+ // create the session
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLUUID id;
+ for(S32 i = 0; i < count; ++i)
+ {
+ id = item_array.get(i)->getCreatorUUID();
+ if(at.isBuddyOnline(id))
+ {
+ members.put(id);
+ }
+ }
+ }
+ }
+ else
+ {
+ LLFolderViewItem* folder_item = mFolders->getItemByID(item);
+ if(!folder_item) return true;
+ LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getListener();
+
+ if (listenerp->getInventoryType() == LLInventoryType::IT_CALLINGCARD)
+ {
+ LLInventoryItem* inv_item = gInventory.getItem(listenerp->getUUID());
+
+ if (inv_item)
+ {
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLUUID id = inv_item->getCreatorUUID();
+
+ if(at.isBuddyOnline(id))
+ {
+ members.put(id);
+ }
+ }
+ } //if IT_CALLINGCARD
+ } //if !IT_CATEGORY
+ }
+ } //for selected_items
+
+ // the session_id is randomly generated UUID which will be replaced later
+ // with a server side generated number
+
+ if (name.empty())
+ {
+ name = llformat("Session %d", session_num++);
+ }
+
+ LLUUID session_id = gIMMgr->addSession(name, type, members[0], members);
+ if (session_id != LLUUID::null)
+ {
+ LLIMFloater::show(session_id);
+ }
+
+ return true;
+}
+
+bool LLInventoryPanel::attachObject(const LLSD& userdata)
+{
+ std::set<LLUUID> selected_items;
+ mFolders->getSelectionList(selected_items);
+
+ std::string joint_name = userdata.asString();
+ LLVOAvatar *avatarp = static_cast<LLVOAvatar*>(gAgent.getAvatarObject());
+ LLViewerJointAttachment* attachmentp = NULL;
+ for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin();
+ iter != avatarp->mAttachmentPoints.end(); )
+ {
+ LLVOAvatar::attachment_map_t::iterator curiter = iter++;
+ LLViewerJointAttachment* attachment = curiter->second;
+ if (attachment->getName() == joint_name)
+ {
+ attachmentp = attachment;
+ break;
+ }
+ }
+ if (attachmentp == NULL)
+ {
+ return true;
+ }
+
+ for (std::set<LLUUID>::const_iterator set_iter = selected_items.begin();
+ set_iter != selected_items.end();
+ ++set_iter)
+ {
+ const LLUUID &id = *set_iter;
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(id);
+ if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID()))
+ {
+ rez_attachment(item, attachmentp);
+ }
+ else if(item && item->isComplete())
+ {
+ // must be in library. copy it to our inventory and put it on.
+ LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(attachmentp);
+ copy_inventory_item(gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ }
+ gFocusMgr.setKeyboardFocus(NULL);
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+
+// static DEBUG ONLY:
+void LLInventoryPanel::dumpSelectionInformation(void* user_data)
+{
+ LLInventoryPanel* iv = (LLInventoryPanel*)user_data;
+ iv->mFolders->dumpSelectionInformation();
+}
+
+BOOL LLInventoryPanel::getSinceLogoff()
+{
+ return mFolders->getFilter()->isSinceLogoff();
+}
+
+void example_param_block_usage()
+{
+ LLInventoryPanel::Params param_block;
+ param_block.name(std::string("inventory"));
+
+ param_block.sort_order_setting(LLInventoryPanel::RECENTITEMS_SORT_ORDER);
+ param_block.allow_multi_select(true);
+ param_block.filter(LLInventoryPanel::Filter()
+ .sort_order(1)
+ .types(0xffff0000));
+ param_block.inventory(&gInventory);
+ param_block.has_border(true);
+
+ LLUICtrlFactory::create<LLInventoryPanel>(param_block);
+
+ param_block = LLInventoryPanel::Params();
+ param_block.name(std::string("inventory"));
+
+ //LLSD param_block_sd;
+ //param_block_sd["sort_order_setting"] = LLInventoryPanel::RECENTITEMS_SORT_ORDER;
+ //param_block_sd["allow_multi_select"] = true;
+ //param_block_sd["filter"]["sort_order"] = 1;
+ //param_block_sd["filter"]["types"] = (S32)0xffff0000;
+ //param_block_sd["has_border"] = true;
+
+ //LLInitParam::LLSDParser(param_block_sd).parse(param_block);
+
+ LLUICtrlFactory::create<LLInventoryPanel>(param_block);
+}
diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp
index b1829b3945..e0dc1b6f0f 100644
--- a/indra/newview/lllandmarkactions.cpp
+++ b/indra/newview/lllandmarkactions.cpp
@@ -49,6 +49,7 @@
#include "llstring.h"
#include "llviewerinventory.h"
#include "llviewerparcelmgr.h"
+#include "llworldmapmessage.h"
#include "llviewerwindow.h"
#include "llwindow.h"
#include "llworldmap.h"
@@ -305,13 +306,13 @@ void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slur
{
U64 new_region_handle = to_region_handle(global_pos);
- LLWorldMap::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseSLURL,
+ LLWorldMapMessage::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseSLURL,
cb,
global_pos,
escaped,
_2);
- LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false);
+ LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false);
}
}
@@ -322,18 +323,19 @@ void LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(const LLVector3d& gl
if (sim_infop)
{
LLVector3 pos = sim_infop->getLocalPos(global_pos);
- cb(sim_infop->mName, llround(pos.mV[VX]), llround(pos.mV[VY]));
+ std::string name = sim_infop->getName() ;
+ cb(name, llround(pos.mV[VX]), llround(pos.mV[VY]));
}
else
{
U64 new_region_handle = to_region_handle(global_pos);
- LLWorldMap::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseNameAndCoords,
+ LLWorldMapMessage::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseNameAndCoords,
cb,
global_pos,
_1);
- LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false);
+ LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false);
}
}
@@ -365,7 +367,8 @@ void LLLandmarkActions::onRegionResponseNameAndCoords(region_name_and_coords_cal
if (sim_infop)
{
LLVector3 local_pos = sim_infop->getLocalPos(global_pos);
- cb(sim_infop->mName, llround(local_pos.mV[VX]), llround(local_pos.mV[VY]));
+ std::string name = sim_infop->getName() ;
+ cb(name, llround(local_pos.mV[VX]), llround(local_pos.mV[VY]));
}
}
diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp
index 4f145891ea..63794be085 100644
--- a/indra/newview/llnavigationbar.cpp
+++ b/indra/newview/llnavigationbar.cpp
@@ -52,7 +52,7 @@
#include "llviewerinventory.h"
#include "llviewermenu.h"
#include "llviewerparcelmgr.h"
-#include "llworldmap.h"
+#include "llworldmapmessage.h"
#include "llappviewer.h"
#include "llviewercontrol.h"
#include "llfloatermediabrowser.h"
@@ -393,14 +393,13 @@ void LLNavigationBar::onLocationSelection()
// Resolve the region name to its global coordinates.
// If resolution succeeds we'll teleport.
- LLWorldMap::url_callback_t cb = boost::bind(
+ LLWorldMapMessage::url_callback_t cb = boost::bind(
&LLNavigationBar::onRegionNameResponse, this,
typed_location, region_name, local_coords, _1, _2, _3, _4);
// connect the callback each time, when user enter new location to get real location of agent after teleport
mTeleportFinishConnection = LLViewerParcelMgr::getInstance()->
setTeleportFinishedCallback(boost::bind(&LLNavigationBar::onTeleportFinished, this, _1,typed_location));
-
- LLWorldMap::getInstance()->sendNamedRegionRequest(region_name, cb, std::string("unused"), false);
+ LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, cb, std::string("unused"), false);
}
void LLNavigationBar::onTeleportFinished(const LLVector3d& global_agent_pos, const std::string& typed_location)
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index b6b433c28f..4286582cdc 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -65,9 +65,15 @@
static LLDefaultChildRegistry::Register<LLNetMap> r1("net_map");
+const F32 LLNetMap::MAP_SCALE_MIN = 32;
+const F32 LLNetMap::MAP_SCALE_MID = 1024;
+const F32 LLNetMap::MAP_SCALE_MAX = 4096;
+
const F32 MAP_SCALE_INCREMENT = 16;
-const F32 MAP_MIN_PICK_DIST = 4;
-const F32 MAX_PRIM_RADIUS = 256.0f; // Don't try to draw giant mega-prims on the mini map
+const F32 MAP_SCALE_ZOOM_FACTOR = 1.04f; // Zoom in factor per click of scroll wheel (4%)
+const F32 MIN_DOT_RADIUS = 3.5f;
+const F32 DOT_SCALE = 0.75f;
+const F32 MIN_PICK_SCALE = 2.f;
LLNetMap::LLNetMap (const Params & p)
: LLUICtrl (p),
@@ -89,6 +95,7 @@ LLNetMap::LLNetMap (const Params & p)
mRotateMap(FALSE),
mToolTipMsg()
{
+ mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS);
}
LLNetMap::~LLNetMap()
@@ -101,17 +108,18 @@ void LLNetMap::setScale( F32 scale )
if (mObjectImagep.notNull())
{
- F32 half_width = (F32)(getRect().getWidth() / 2);
- F32 half_height = (F32)(getRect().getHeight() / 2);
- F32 radius = sqrt( half_width * half_width + half_height * half_height );
- F32 region_widths = (2.f*radius)/mScale;
+ F32 width = (F32)(getRect().getWidth());
+ F32 height = (F32)(getRect().getHeight());
+ F32 diameter = sqrt(width * width + height * height);
+ F32 region_widths = diameter / mScale;
F32 meters = region_widths * LLWorld::getInstance()->getRegionWidthInMeters();
F32 num_pixels = (F32)mObjectImagep->getWidth();
- mObjectMapTPM = num_pixels/meters;
- mObjectMapPixels = 2.f*radius;
+ mObjectMapTPM = num_pixels / meters;
+ mObjectMapPixels = diameter;
}
mPixelsPerMeter = mScale / REGION_WIDTH_METERS;
+ mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS);
mUpdateNow = TRUE;
}
@@ -302,6 +310,7 @@ void LLNetMap::draw()
LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
mClosestAgentToCursor.setNull();
F32 closest_dist = F32_MAX;
+ F32 min_pick_dist = mDotRadius * MIN_PICK_SCALE;
// Draw avatars
for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
@@ -345,10 +354,10 @@ void LLNetMap::draw()
LLWorldMapView::drawAvatar(
pos_map.mV[VX], pos_map.mV[VY],
show_as_friend ? map_avatar_friend_color : map_avatar_color,
- pos_map.mV[VZ]);
+ pos_map.mV[VZ], mDotRadius);
F32 dist_to_cursor = dist_vec(LLVector2(pos_map.mV[VX], pos_map.mV[VY]), LLVector2(local_mouse_x,local_mouse_y));
- if(dist_to_cursor < MAP_MIN_PICK_DIST && dist_to_cursor < closest_dist)
+ if(dist_to_cursor < min_pick_dist && dist_to_cursor < closest_dist)
{
closest_dist = dist_to_cursor;
mClosestAgentToCursor = regionp->mMapAvatarIDs.get(i);
@@ -378,10 +387,12 @@ void LLNetMap::draw()
// Draw dot for self avatar position
pos_global = gAgent.getPositionGlobal();
pos_map = globalPosToView(pos_global);
- LLUIImagePtr you = LLWorldMapView::sAvatarYouSmallImage;
- you->draw(
- llround(pos_map.mV[VX]) - you->getWidth()/2,
- llround(pos_map.mV[VY]) - you->getHeight()/2);
+ LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage;
+ S32 dot_width = llround(mDotRadius * 2.f);
+ you->draw(llround(pos_map.mV[VX] - mDotRadius),
+ llround(pos_map.mV[VY] - mDotRadius),
+ dot_width,
+ dot_width);
// Draw frustum
F32 meters_to_pixels = mScale/ LLWorld::getInstance()->getRegionWidthInMeters();
@@ -429,6 +440,12 @@ void LLNetMap::draw()
LLUICtrl::draw();
}
+void LLNetMap::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLUICtrl::reshape(width, height, called_from_parent);
+ createObjectImage();
+}
+
LLVector3 LLNetMap::globalPosToView( const LLVector3d& global_pos )
{
LLVector3d relative_pos_global = global_pos - gAgent.getCameraPositionGlobal();
@@ -504,8 +521,12 @@ LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y )
BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
{
- // note that clicks are reversed from what you'd think
- setScale(llclamp(mScale - clicks*MAP_SCALE_INCREMENT, MAP_SCALE_MIN, MAP_SCALE_MAX));
+ // note that clicks are reversed from what you'd think: i.e. > 0 means zoom out, < 0 means zoom in
+ F32 scale = mScale;
+
+ scale *= pow(MAP_SCALE_ZOOM_FACTOR, -clicks);
+ setScale(llclamp(scale, MAP_SCALE_MIN, MAP_SCALE_MAX));
+
return TRUE;
}
@@ -567,9 +588,7 @@ void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &
LLVector3 local_pos;
local_pos.setVec( pos - mObjectImageCenterGlobal );
- F32 radius_clamped = llmin(radius_meters, MAX_PRIM_RADIUS);
-
- S32 diameter_pixels = llround(2 * radius_clamped * mObjectMapTPM);
+ S32 diameter_pixels = llround(2 * radius_meters * mObjectMapTPM);
renderPoint( local_pos, color, diameter_pixels );
}
@@ -662,13 +681,13 @@ void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color,
void LLNetMap::createObjectImage()
{
// Find the size of the side of a square that surrounds the circle that surrounds getRect().
- F32 half_width = (F32)(getRect().getWidth() / 2);
- F32 half_height = (F32)(getRect().getHeight() / 2);
- F32 radius = sqrt( half_width * half_width + half_height * half_height );
- S32 square_size = S32( 2 * radius );
+ // ... which is, the diagonal of the rect.
+ F32 width = (F32)getRect().getWidth();
+ F32 height = (F32)getRect().getHeight();
+ S32 square_size = llround( sqrt(width*width + height*height) );
// Find the least power of two >= the minimum size.
- const S32 MIN_SIZE = 32;
+ const S32 MIN_SIZE = 64;
const S32 MAX_SIZE = 256;
S32 img_size = MIN_SIZE;
while( (img_size*2 < square_size ) && (img_size < MAX_SIZE) )
@@ -684,7 +703,7 @@ void LLNetMap::createObjectImage()
U8* data = mObjectRawImagep->getData();
memset( data, 0, img_size * img_size * 4 );
mObjectImagep = LLViewerTextureManager::getLocalTexture( mObjectRawImagep.get(), FALSE);
- setScale(mScale);
}
+ setScale(mScale);
mUpdateNow = TRUE;
}
diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h
index 5ebdd13384..7088ab3e70 100644
--- a/indra/newview/llnetmap.h
+++ b/indra/newview/llnetmap.h
@@ -70,9 +70,14 @@ protected:
public:
virtual ~LLNetMap();
+ static const F32 MAP_SCALE_MIN;
+ static const F32 MAP_SCALE_MID;
+ static const F32 MAP_SCALE_MAX;
+
/*virtual*/ void draw();
/*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
/*virtual*/ BOOL handleToolTip( S32 x, S32 y, MASK mask);
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
void setScale( F32 scale );
void setRotateMap( BOOL b ) { mRotateMap = b; }
@@ -94,16 +99,17 @@ private:
void drawTracking( const LLVector3d& pos_global,
const LLColor4& color,
BOOL draw_arrow = TRUE);
-
- void createObjectImage();
+ void createObjectImage();
+
private:
LLUIColor mBackgroundColor;
F32 mScale; // Size of a region in pixels
F32 mPixelsPerMeter; // world meters to map pixels
F32 mObjectMapTPM; // texels per meter on map
- F32 mObjectMapPixels; // Width of object map in pixels;
+ F32 mObjectMapPixels; // Width of object map in pixels
+ F32 mDotRadius; // Size of avatar markers
F32 mTargetPanX;
F32 mTargetPanY;
F32 mCurPanX;
diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp
index 9840c91158..ce1a7f98df 100644
--- a/indra/newview/llpanellandmarks.cpp
+++ b/indra/newview/llpanellandmarks.cpp
@@ -267,7 +267,7 @@ LLLandmark* LLLandmarksPanel::getCurSelectedLandmark() const
return NULL;
}
-LLFolderViewItem* LLLandmarksPanel::getCurSelectedItem () const
+LLFolderViewItem* LLLandmarksPanel::getCurSelectedItem() const
{
return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->getCurSelectedItem() : NULL;
}
diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h
index d54761ed59..745f9364c2 100644
--- a/indra/newview/llpanellandmarks.h
+++ b/indra/newview/llpanellandmarks.h
@@ -69,7 +69,7 @@ protected:
bool isLandmarkSelected() const;
bool isReceivedFolderSelected() const;
LLLandmark* getCurSelectedLandmark() const;
- LLFolderViewItem* getCurSelectedItem () const;
+ LLFolderViewItem* getCurSelectedItem() const;
void updateSortOrder(LLInventoryPanel* panel, bool byDate);
//LLRemoteParcelInfoObserver interface
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index 25be09fa24..15a75cb930 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -1,818 +1,1025 @@
-/**
- * @file llsidepanelmaininventory.cpp
- * @brief Implementation of llsidepanelmaininventory.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-#include "llpanelmaininventory.h"
-
-#include "llfloaterinventory.h"
-#include "llinventorybridge.h"
-#include "llinventoryfunctions.h"
-#include "llinventorypanel.h"
-#include "llfiltereditor.h"
-#include "llfloaterreg.h"
-#include "llscrollcontainer.h"
-#include "llsdserialize.h"
-#include "llspinctrl.h"
-#include "lltooldraganddrop.h"
-
-static LLRegisterPanelClassWrapper<LLPanelMainInventory> t_inventory("panel_main_inventory"); // Seraph is this redundant with constructor?
-
-///----------------------------------------------------------------------------
-/// LLFloaterInventoryFinder
-///----------------------------------------------------------------------------
-
-class LLFloaterInventoryFinder : public LLFloater
-{
-public:
- LLFloaterInventoryFinder( LLPanelMainInventory* inventory_view);
- virtual void draw();
- /*virtual*/ BOOL postBuild();
- void changeFilter(LLInventoryFilter* filter);
- void updateElementsFromFilter();
- BOOL getCheckShowEmpty();
- BOOL getCheckSinceLogoff();
-
- static void onTimeAgo(LLUICtrl*, void *);
- static void onCheckSinceLogoff(LLUICtrl*, void *);
- static void onCloseBtn(void* user_data);
- static void selectAllTypes(void* user_data);
- static void selectNoTypes(void* user_data);
-private:
- LLPanelMainInventory* mPanelInventoryDecorated;
- LLSpinCtrl* mSpinSinceDays;
- LLSpinCtrl* mSpinSinceHours;
- LLInventoryFilter* mFilter;
-};
-
-///----------------------------------------------------------------------------
-/// LLPanelMainInventory
-///----------------------------------------------------------------------------
-
-LLPanelMainInventory::LLPanelMainInventory()
- : LLPanel()
-{
- LLMemType mt(LLMemType::MTYPE_INVENTORY_VIEW_INIT);
- // Menu Callbacks (non contex menus)
- mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelMainInventory::doToSelected, this, _2));
- mCommitCallbackRegistrar.add("Inventory.CloseAllFolders", boost::bind(&LLPanelMainInventory::closeAllFolders, this));
- mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH));
- mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND));
- mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&LLPanelMainInventory::doCreate, this, _2));
- mCommitCallbackRegistrar.add("Inventory.NewWindow", boost::bind(&LLPanelMainInventory::newWindow, this));
- 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));
-
- // Controls
- // *TODO: Just use persistant settings for each of these
- U32 sort_order = gSavedSettings.getU32("InventorySortOrder");
- BOOL sort_by_name = ! ( sort_order & LLInventoryFilter::SO_DATE );
- BOOL sort_folders_by_name = ( sort_order & LLInventoryFilter::SO_FOLDERS_BY_NAME );
- BOOL sort_system_folders_to_top = ( sort_order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP );
-
- gSavedSettings.declareBOOL("Inventory.SortByName", sort_by_name, "Declared in code", FALSE);
- gSavedSettings.declareBOOL("Inventory.SortByDate", !sort_by_name, "Declared in code", FALSE);
- gSavedSettings.declareBOOL("Inventory.FoldersAlwaysByName", sort_folders_by_name, "Declared in code", FALSE);
- gSavedSettings.declareBOOL("Inventory.SystemFoldersToTop", sort_system_folders_to_top, "Declared in code", FALSE);
-
- mSavedFolderState = new LLSaveFolderState();
- mSavedFolderState->setApply(FALSE);
-}
-
-BOOL LLPanelMainInventory::postBuild()
-{
- gInventory.addObserver(this);
-
- mFilterTabs = getChild<LLTabContainer>("inventory filter tabs");
- mFilterTabs->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterSelected, this));
-
- //panel->getFilter()->markDefault();
-
- // Set up the default inv. panel/filter settings.
- mActivePanel = getChild<LLInventoryPanel>("All Items");
- if (mActivePanel)
- {
- // "All Items" is the previous only view, so it gets the InventorySortOrder
- mActivePanel->setSortOrder(gSavedSettings.getU32("InventorySortOrder"));
- mActivePanel->getFilter()->markDefault();
- mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
- mActivePanel->setSelectCallback(boost::bind(&LLInventoryPanel::onSelectionChange, mActivePanel, _1, _2));
- }
- LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items");
- if (recent_items_panel)
- {
- recent_items_panel->setSinceLogoff(TRUE);
- recent_items_panel->setSortOrder(LLInventoryFilter::SO_DATE);
- recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
- recent_items_panel->getFilter()->markDefault();
- recent_items_panel->setSelectCallback(boost::bind(&LLInventoryPanel::onSelectionChange, recent_items_panel, _1, _2));
- }
-
- // Now load the stored settings from disk, if available.
- std::ostringstream filterSaveName;
- filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml");
- llinfos << "LLPanelMainInventory::init: reading from " << filterSaveName << llendl;
- llifstream file(filterSaveName.str());
- LLSD savedFilterState;
- if (file.is_open())
- {
- LLSDSerialize::fromXML(savedFilterState, file);
- file.close();
-
- // Load the persistent "Recent Items" settings.
- // Note that the "All Items" settings do not persist.
- if(recent_items_panel)
- {
- if(savedFilterState.has(recent_items_panel->getFilter()->getName()))
- {
- LLSD recent_items = savedFilterState.get(
- recent_items_panel->getFilter()->getName());
- recent_items_panel->getFilter()->fromLLSD(recent_items);
- }
- }
-
- }
-
-
- mFilterEditor = getChild<LLFilterEditor>("inventory search editor");
- if (mFilterEditor)
- {
- mFilterEditor->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterEdit, this, _2));
- }
-
- // *TODO:Get the cost info from the server
- const std::string upload_cost("10");
- childSetLabelArg("Upload Image", "[COST]", upload_cost);
- childSetLabelArg("Upload Sound", "[COST]", upload_cost);
- childSetLabelArg("Upload Animation", "[COST]", upload_cost);
- childSetLabelArg("Bulk Upload", "[COST]", upload_cost);
-
- return TRUE;
-}
-
-// Destroys the object
-LLPanelMainInventory::~LLPanelMainInventory( void )
-{
- // Save the filters state.
- LLSD filterRoot;
- LLInventoryPanel* all_items_panel = getChild<LLInventoryPanel>("All Items");
- if (all_items_panel)
- {
- LLInventoryFilter* filter = all_items_panel->getFilter();
- if (filter)
- {
- LLSD filterState;
- filter->toLLSD(filterState);
- filterRoot[filter->getName()] = filterState;
- }
- }
-
- LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items");
- if (recent_items_panel)
- {
- LLInventoryFilter* filter = recent_items_panel->getFilter();
- if (filter)
- {
- LLSD filterState;
- filter->toLLSD(filterState);
- filterRoot[filter->getName()] = filterState;
- }
- }
-
- std::ostringstream filterSaveName;
- filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml");
- llofstream filtersFile(filterSaveName.str());
- if(!LLSDSerialize::toPrettyXML(filterRoot, filtersFile))
- {
- llwarns << "Could not write to filters save file " << filterSaveName << llendl;
- }
- else
- filtersFile.close();
-
- gInventory.removeObserver(this);
- delete mSavedFolderState;
-}
-
-void LLPanelMainInventory::startSearch()
-{
- // this forces focus to line editor portion of search editor
- if (mFilterEditor)
- {
- mFilterEditor->focusFirstItem(TRUE);
- }
-}
-
-BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask)
-{
- LLFolderView* root_folder = mActivePanel ? mActivePanel->getRootFolder() : NULL;
- if (root_folder)
- {
- // first check for user accepting current search results
- if (mFilterEditor
- && mFilterEditor->hasFocus()
- && (key == KEY_RETURN
- || key == KEY_DOWN)
- && mask == MASK_NONE)
- {
- // move focus to inventory proper
- mActivePanel->setFocus(TRUE);
- root_folder->scrollToShowSelection();
- return TRUE;
- }
-
- if (mActivePanel->hasFocus() && key == KEY_UP)
- {
- startSearch();
- }
- }
-
- return LLPanel::handleKeyHere(key, mask);
-
-}
-
-//----------------------------------------------------------------------------
-// menu callbacks
-
-void LLPanelMainInventory::doToSelected(const LLSD& userdata)
-{
- getPanel()->getRootFolder()->doToSelected(&gInventory, userdata);
-}
-
-void LLPanelMainInventory::closeAllFolders()
-{
- getPanel()->getRootFolder()->closeAllFolders();
-}
-
-void LLPanelMainInventory::newWindow()
-{
- LLFloaterInventory::showAgentInventory();
-}
-
-void LLPanelMainInventory::doCreate(const LLSD& userdata)
-{
- menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata);
-}
-
-void LLPanelMainInventory::resetFilters()
-{
- LLFloaterInventoryFinder *finder = getFinder();
- getActivePanel()->getFilter()->resetDefault();
- if (finder)
- {
- finder->updateElementsFromFilter();
- }
-
- setFilterTextFromFilter();
-}
-
-void LLPanelMainInventory::setSortBy(const LLSD& userdata)
-{
- std::string sort_field = userdata.asString();
- if (sort_field == "name")
- {
- U32 order = getActivePanel()->getSortOrder();
- getActivePanel()->setSortOrder( order & ~LLInventoryFilter::SO_DATE );
-
- gSavedSettings.setBOOL("Inventory.SortByName", TRUE );
- gSavedSettings.setBOOL("Inventory.SortByDate", FALSE );
- }
- else if (sort_field == "date")
- {
- U32 order = getActivePanel()->getSortOrder();
- getActivePanel()->setSortOrder( order | LLInventoryFilter::SO_DATE );
-
- gSavedSettings.setBOOL("Inventory.SortByName", FALSE );
- gSavedSettings.setBOOL("Inventory.SortByDate", TRUE );
- }
- else if (sort_field == "foldersalwaysbyname")
- {
- U32 order = getActivePanel()->getSortOrder();
- if ( order & LLInventoryFilter::SO_FOLDERS_BY_NAME )
- {
- order &= ~LLInventoryFilter::SO_FOLDERS_BY_NAME;
-
- gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", FALSE );
- }
- else
- {
- order |= LLInventoryFilter::SO_FOLDERS_BY_NAME;
-
- gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", TRUE );
- }
- getActivePanel()->setSortOrder( order );
- }
- else if (sort_field == "systemfolderstotop")
- {
- U32 order = getActivePanel()->getSortOrder();
- if ( order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP )
- {
- order &= ~LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
-
- gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", FALSE );
- }
- else
- {
- order |= LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
-
- gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", TRUE );
- }
- getActivePanel()->setSortOrder( order );
- }
-}
-
-// static
-BOOL LLPanelMainInventory::filtersVisible(void* user_data)
-{
- LLPanelMainInventory* self = (LLPanelMainInventory*)user_data;
- if(!self) return FALSE;
-
- return self->getFinder() != NULL;
-}
-
-void LLPanelMainInventory::onClearSearch()
-{
- LLFloater *finder = getFinder();
- if (mActivePanel)
- {
- mActivePanel->setFilterSubString(LLStringUtil::null);
- mActivePanel->setFilterTypes(0xffffffff);
- }
-
- if (finder)
- {
- LLFloaterInventoryFinder::selectAllTypes(finder);
- }
-
- // re-open folders that were initially open
- if (mActivePanel)
- {
- mSavedFolderState->setApply(TRUE);
- mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
- LLOpenFoldersWithSelection opener;
- mActivePanel->getRootFolder()->applyFunctorRecursively(opener);
- mActivePanel->getRootFolder()->scrollToShowSelection();
- }
-}
-
-void LLPanelMainInventory::onFilterEdit(const std::string& search_string )
-{
- if (search_string == "")
- {
- onClearSearch();
- }
- if (!mActivePanel)
- {
- return;
- }
-
- gInventory.startBackgroundFetch();
-
- std::string uppercase_search_string = search_string;
- LLStringUtil::toUpper(uppercase_search_string);
- if (mActivePanel->getFilterSubString().empty() && uppercase_search_string.empty())
- {
- // current filter and new filter empty, do nothing
- return;
- }
-
- // save current folder open state if no filter currently applied
- if (!mActivePanel->getRootFolder()->isFilterModified())
- {
- mSavedFolderState->setApply(FALSE);
- mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
- }
-
- // set new filter string
- mActivePanel->setFilterSubString(uppercase_search_string);
-}
-
-
- //static
- BOOL LLPanelMainInventory::incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward)
- {
- LLPanelMainInventory* active_view = NULL;
-
- 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)
- {
- LLPanelMainInventory* iv = dynamic_cast<LLPanelMainInventory*>(*iter);
- if (iv)
- {
- if (gFocusMgr.childHasKeyboardFocus(iv))
- {
- active_view = iv;
- break;
- }
- }
- }
-
- if (!active_view)
- {
- return FALSE;
- }
-
- std::string search_string(find_text);
-
- if (search_string.empty())
- {
- return FALSE;
- }
-
- if (active_view->getPanel() &&
- active_view->getPanel()->getRootFolder()->search(first_item, search_string, backward))
- {
- return TRUE;
- }
-
- return FALSE;
- }
-
-void LLPanelMainInventory::onFilterSelected()
-{
- // Find my index
- mActivePanel = (LLInventoryPanel*)childGetVisibleTab("inventory filter tabs");
-
- if (!mActivePanel)
- {
- return;
- }
- LLInventoryFilter* filter = mActivePanel->getFilter();
- LLFloaterInventoryFinder *finder = getFinder();
- if (finder)
- {
- finder->changeFilter(filter);
- }
- if (filter->isActive())
- {
- // If our filter is active we may be the first thing requiring a fetch so we better start it here.
- gInventory.startBackgroundFetch();
- }
- setFilterTextFromFilter();
-}
-
-const std::string LLPanelMainInventory::getFilterSubString()
-{
- return mActivePanel->getFilterSubString();
-}
-
-void LLPanelMainInventory::setFilterSubString(const std::string& string)
-{
- mActivePanel->setFilterSubString(string);
-}
-
-BOOL LLPanelMainInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
-{
- // Check to see if we are auto scrolling from the last frame
- LLInventoryPanel* panel = (LLInventoryPanel*)this->getActivePanel();
- BOOL needsToScroll = panel->getScrollableContainer()->autoScroll(x, y);
- if(mFilterTabs)
- {
- if(needsToScroll)
- {
- mFilterTabs->startDragAndDropDelayTimer();
- }
- }
-
- BOOL handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
-
- return handled;
-}
-
-void LLPanelMainInventory::changed(U32 mask)
-{
-}
-
-
-void LLPanelMainInventory::setFilterTextFromFilter()
-{
- mFilterText = mActivePanel->getFilter()->getFilterText();
-}
-
-void LLPanelMainInventory::toggleFindOptions()
-{
- LLMemType mt(LLMemType::MTYPE_INVENTORY_VIEW_TOGGLE);
- LLFloater *floater = getFinder();
- if (!floater)
- {
- LLFloaterInventoryFinder * finder = new LLFloaterInventoryFinder(this);
- mFinderHandle = finder->getHandle();
- finder->openFloater();
-
- LLFloater* parent_floater = gFloaterView->getParentFloater(this);
- if (parent_floater) // Seraph: Fix this, shouldn't be null even for sidepanel
- parent_floater->addDependentFloater(mFinderHandle);
- // start background fetch of folders
- gInventory.startBackgroundFetch();
- }
- else
- {
- floater->closeFloater();
- }
-}
-
-void LLPanelMainInventory::setSelectCallback(const LLFolderView::signal_t::slot_type& cb)
-{
- getChild<LLInventoryPanel>("All Items")->setSelectCallback(cb);
- getChild<LLInventoryPanel>("Recent Items")->setSelectCallback(cb);
-}
-
-///----------------------------------------------------------------------------
-/// LLFloaterInventoryFinder
-///----------------------------------------------------------------------------
-
-LLFloaterInventoryFinder* LLPanelMainInventory::getFinder()
-{
- return (LLFloaterInventoryFinder*)mFinderHandle.get();
-}
-
-
-LLFloaterInventoryFinder::LLFloaterInventoryFinder(LLPanelMainInventory* inventory_view) :
- LLFloater(LLSD()),
- mPanelInventoryDecorated(inventory_view),
- mFilter(inventory_view->getPanel()->getFilter())
-{
- LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inventory_view_finder.xml", NULL);
- updateElementsFromFilter();
-}
-
-
-void LLFloaterInventoryFinder::onCheckSinceLogoff(LLUICtrl *ctrl, void *user_data)
-{
- LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data;
- if (!self) return;
-
- bool since_logoff= self->childGetValue("check_since_logoff");
-
- if (!since_logoff &&
- !( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() ) )
- {
- self->mSpinSinceHours->set(1.0f);
- }
-}
-BOOL LLFloaterInventoryFinder::postBuild()
-{
- const LLRect& viewrect = mPanelInventoryDecorated->getRect();
- setRect(LLRect(viewrect.mLeft - getRect().getWidth(), viewrect.mTop, viewrect.mLeft, viewrect.mTop - getRect().getHeight()));
-
- childSetAction("All", selectAllTypes, this);
- childSetAction("None", selectNoTypes, this);
-
- mSpinSinceHours = getChild<LLSpinCtrl>("spin_hours_ago");
- childSetCommitCallback("spin_hours_ago", onTimeAgo, this);
-
- mSpinSinceDays = getChild<LLSpinCtrl>("spin_days_ago");
- childSetCommitCallback("spin_days_ago", onTimeAgo, this);
-
- // mCheckSinceLogoff = getChild<LLSpinCtrl>("check_since_logoff");
- childSetCommitCallback("check_since_logoff", onCheckSinceLogoff, this);
-
- childSetAction("Close", onCloseBtn, this);
-
- updateElementsFromFilter();
- return TRUE;
-}
-void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data)
-{
- LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data;
- if (!self) return;
-
- bool since_logoff=true;
- if ( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() )
- {
- since_logoff = false;
- }
- self->childSetValue("check_since_logoff", since_logoff);
-}
-
-void LLFloaterInventoryFinder::changeFilter(LLInventoryFilter* filter)
-{
- mFilter = filter;
- updateElementsFromFilter();
-}
-
-void LLFloaterInventoryFinder::updateElementsFromFilter()
-{
- if (!mFilter)
- return;
-
- // Get data needed for filter display
- U32 filter_types = mFilter->getFilterTypes();
- std::string filter_string = mFilter->getFilterSubString();
- LLInventoryFilter::EFolderShow show_folders = mFilter->getShowFolderState();
- U32 hours = mFilter->getHoursAgo();
-
- // update the ui elements
- setTitle(mFilter->getName());
-
- childSetValue("check_animation", (S32) (filter_types & 0x1 << LLInventoryType::IT_ANIMATION));
-
- childSetValue("check_calling_card", (S32) (filter_types & 0x1 << LLInventoryType::IT_CALLINGCARD));
- childSetValue("check_clothing", (S32) (filter_types & 0x1 << LLInventoryType::IT_WEARABLE));
- childSetValue("check_gesture", (S32) (filter_types & 0x1 << LLInventoryType::IT_GESTURE));
- childSetValue("check_landmark", (S32) (filter_types & 0x1 << LLInventoryType::IT_LANDMARK));
- childSetValue("check_notecard", (S32) (filter_types & 0x1 << LLInventoryType::IT_NOTECARD));
- childSetValue("check_object", (S32) (filter_types & 0x1 << LLInventoryType::IT_OBJECT));
- childSetValue("check_script", (S32) (filter_types & 0x1 << LLInventoryType::IT_LSL));
- childSetValue("check_sound", (S32) (filter_types & 0x1 << LLInventoryType::IT_SOUND));
- childSetValue("check_texture", (S32) (filter_types & 0x1 << LLInventoryType::IT_TEXTURE));
- childSetValue("check_snapshot", (S32) (filter_types & 0x1 << LLInventoryType::IT_SNAPSHOT));
- childSetValue("check_show_empty", show_folders == LLInventoryFilter::SHOW_ALL_FOLDERS);
- childSetValue("check_since_logoff", mFilter->isSinceLogoff());
- mSpinSinceHours->set((F32)(hours % 24));
- mSpinSinceDays->set((F32)(hours / 24));
-}
-
-void LLFloaterInventoryFinder::draw()
-{
- LLMemType mt(LLMemType::MTYPE_INVENTORY_DRAW);
- U32 filter = 0xffffffff;
- BOOL filtered_by_all_types = TRUE;
-
- if (!childGetValue("check_animation"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_ANIMATION);
- filtered_by_all_types = FALSE;
- }
-
-
- if (!childGetValue("check_calling_card"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_CALLINGCARD);
- filtered_by_all_types = FALSE;
- }
-
- if (!childGetValue("check_clothing"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_WEARABLE);
- filtered_by_all_types = FALSE;
- }
-
- if (!childGetValue("check_gesture"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_GESTURE);
- filtered_by_all_types = FALSE;
- }
-
- if (!childGetValue("check_landmark"))
-
-
- {
- filter &= ~(0x1 << LLInventoryType::IT_LANDMARK);
- filtered_by_all_types = FALSE;
- }
-
- if (!childGetValue("check_notecard"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_NOTECARD);
- filtered_by_all_types = FALSE;
- }
-
- if (!childGetValue("check_object"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_OBJECT);
- filter &= ~(0x1 << LLInventoryType::IT_ATTACHMENT);
- filtered_by_all_types = FALSE;
- }
-
- if (!childGetValue("check_script"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_LSL);
- filtered_by_all_types = FALSE;
- }
-
- if (!childGetValue("check_sound"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_SOUND);
- filtered_by_all_types = FALSE;
- }
-
- if (!childGetValue("check_texture"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_TEXTURE);
- filtered_by_all_types = FALSE;
- }
-
- if (!childGetValue("check_snapshot"))
- {
- filter &= ~(0x1 << LLInventoryType::IT_SNAPSHOT);
- filtered_by_all_types = FALSE;
- }
-
- if (!filtered_by_all_types)
- {
- // don't include folders in filter, unless I've selected everything
- filter &= ~(0x1 << LLInventoryType::IT_CATEGORY);
- }
-
- // update the panel, panel will update the filter
- mPanelInventoryDecorated->getPanel()->setShowFolderState(getCheckShowEmpty() ?
- LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
- mPanelInventoryDecorated->getPanel()->setFilterTypes(filter);
- if (getCheckSinceLogoff())
- {
- mSpinSinceDays->set(0);
- mSpinSinceHours->set(0);
- }
- U32 days = (U32)mSpinSinceDays->get();
- U32 hours = (U32)mSpinSinceHours->get();
- if (hours > 24)
- {
- days += hours / 24;
- hours = (U32)hours % 24;
- mSpinSinceDays->set((F32)days);
- mSpinSinceHours->set((F32)hours);
- }
- hours += days * 24;
- mPanelInventoryDecorated->getPanel()->setHoursAgo(hours);
- mPanelInventoryDecorated->getPanel()->setSinceLogoff(getCheckSinceLogoff());
- mPanelInventoryDecorated->setFilterTextFromFilter();
-
- LLPanel::draw();
-}
-
-BOOL LLFloaterInventoryFinder::getCheckShowEmpty()
-{
- return childGetValue("check_show_empty");
-}
-
-BOOL LLFloaterInventoryFinder::getCheckSinceLogoff()
-{
- return childGetValue("check_since_logoff");
-}
-
-void LLFloaterInventoryFinder::onCloseBtn(void* user_data)
-{
- LLFloaterInventoryFinder* finderp = (LLFloaterInventoryFinder*)user_data;
- finderp->closeFloater();
-}
-
-// static
-void LLFloaterInventoryFinder::selectAllTypes(void* user_data)
-{
- LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data;
- if(!self) return;
-
- self->childSetValue("check_animation", TRUE);
- self->childSetValue("check_calling_card", TRUE);
- self->childSetValue("check_clothing", TRUE);
- self->childSetValue("check_gesture", TRUE);
- self->childSetValue("check_landmark", TRUE);
- self->childSetValue("check_notecard", TRUE);
- self->childSetValue("check_object", TRUE);
- self->childSetValue("check_script", TRUE);
- self->childSetValue("check_sound", TRUE);
- self->childSetValue("check_texture", TRUE);
- self->childSetValue("check_snapshot", TRUE);
-}
-
-//static
-void LLFloaterInventoryFinder::selectNoTypes(void* user_data)
-{
- LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data;
- if(!self) return;
-
- self->childSetValue("check_animation", FALSE);
- self->childSetValue("check_calling_card", FALSE);
- self->childSetValue("check_clothing", FALSE);
- self->childSetValue("check_gesture", FALSE);
- self->childSetValue("check_landmark", FALSE);
- self->childSetValue("check_notecard", FALSE);
- self->childSetValue("check_object", FALSE);
- self->childSetValue("check_script", FALSE);
- self->childSetValue("check_sound", FALSE);
- self->childSetValue("check_texture", FALSE);
- self->childSetValue("check_snapshot", FALSE);
-}
+/**
+ * @file llsidepanelmaininventory.cpp
+ * @brief Implementation of llsidepanelmaininventory.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llpanelmaininventory.h"
+
+#include "lldndbutton.h"
+#include "llfilepicker.h"
+#include "llfloaterinventory.h"
+#include "llinventorybridge.h"
+#include "llinventoryfunctions.h"
+#include "llinventorypanel.h"
+#include "llfiltereditor.h"
+#include "llfloaterreg.h"
+#include "llpreviewtexture.h"
+#include "llscrollcontainer.h"
+#include "llsdserialize.h"
+#include "llspinctrl.h"
+#include "lltooldraganddrop.h"
+#include "llviewermenu.h"
+#include "llviewertexturelist.h"
+
+static LLRegisterPanelClassWrapper<LLPanelMainInventory> t_inventory("panel_main_inventory"); // Seraph is this redundant with constructor?
+
+void on_file_loaded_for_save(BOOL success,
+ LLViewerFetchedTexture *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata);
+
+///----------------------------------------------------------------------------
+/// LLFloaterInventoryFinder
+///----------------------------------------------------------------------------
+
+class LLFloaterInventoryFinder : public LLFloater
+{
+public:
+ LLFloaterInventoryFinder( LLPanelMainInventory* inventory_view);
+ virtual void draw();
+ /*virtual*/ BOOL postBuild();
+ void changeFilter(LLInventoryFilter* filter);
+ void updateElementsFromFilter();
+ BOOL getCheckShowEmpty();
+ BOOL getCheckSinceLogoff();
+
+ static void onTimeAgo(LLUICtrl*, void *);
+ static void onCheckSinceLogoff(LLUICtrl*, void *);
+ static void onCloseBtn(void* user_data);
+ static void selectAllTypes(void* user_data);
+ static void selectNoTypes(void* user_data);
+private:
+ LLPanelMainInventory* mPanelMainInventory;
+ LLSpinCtrl* mSpinSinceDays;
+ LLSpinCtrl* mSpinSinceHours;
+ LLInventoryFilter* mFilter;
+};
+
+///----------------------------------------------------------------------------
+/// LLPanelMainInventory
+///----------------------------------------------------------------------------
+
+LLPanelMainInventory::LLPanelMainInventory()
+ : LLPanel(),
+ mActivePanel(NULL),
+ mSavedFolderState(NULL),
+ mFilterText(""),
+ mMenuGearDefault(NULL),
+ mMenuAdd(NULL)
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_VIEW_INIT);
+ // Menu Callbacks (non contex menus)
+ mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelMainInventory::doToSelected, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.CloseAllFolders", boost::bind(&LLPanelMainInventory::closeAllFolders, this));
+ mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH));
+ mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND));
+ mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&LLPanelMainInventory::doCreate, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.NewWindow", boost::bind(&LLPanelMainInventory::newWindow, this));
+ 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));
+
+ // Controls
+ // *TODO: Just use persistant settings for each of these
+ U32 sort_order = gSavedSettings.getU32("InventorySortOrder");
+ BOOL sort_by_name = ! ( sort_order & LLInventoryFilter::SO_DATE );
+ BOOL sort_folders_by_name = ( sort_order & LLInventoryFilter::SO_FOLDERS_BY_NAME );
+ BOOL sort_system_folders_to_top = ( sort_order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP );
+
+ gSavedSettings.declareBOOL("Inventory.SortByName", sort_by_name, "Declared in code", FALSE);
+ gSavedSettings.declareBOOL("Inventory.SortByDate", !sort_by_name, "Declared in code", FALSE);
+ gSavedSettings.declareBOOL("Inventory.FoldersAlwaysByName", sort_folders_by_name, "Declared in code", FALSE);
+ gSavedSettings.declareBOOL("Inventory.SystemFoldersToTop", sort_system_folders_to_top, "Declared in code", FALSE);
+
+ mSavedFolderState = new LLSaveFolderState();
+ mSavedFolderState->setApply(FALSE);
+}
+
+BOOL LLPanelMainInventory::postBuild()
+{
+ gInventory.addObserver(this);
+
+ mFilterTabs = getChild<LLTabContainer>("inventory filter tabs");
+ mFilterTabs->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterSelected, this));
+
+ //panel->getFilter()->markDefault();
+
+ // Set up the default inv. panel/filter settings.
+ mActivePanel = getChild<LLInventoryPanel>("All Items");
+ if (mActivePanel)
+ {
+ // "All Items" is the previous only view, so it gets the InventorySortOrder
+ mActivePanel->setSortOrder(gSavedSettings.getU32("InventorySortOrder"));
+ mActivePanel->getFilter()->markDefault();
+ mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
+ mActivePanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, mActivePanel, _1, _2));
+ }
+ LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items");
+ if (recent_items_panel)
+ {
+ recent_items_panel->setSinceLogoff(TRUE);
+ recent_items_panel->setSortOrder(LLInventoryFilter::SO_DATE);
+ recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ recent_items_panel->getFilter()->markDefault();
+ recent_items_panel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, recent_items_panel, _1, _2));
+ }
+
+ // Now load the stored settings from disk, if available.
+ std::ostringstream filterSaveName;
+ filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml");
+ llinfos << "LLPanelMainInventory::init: reading from " << filterSaveName << llendl;
+ llifstream file(filterSaveName.str());
+ LLSD savedFilterState;
+ if (file.is_open())
+ {
+ LLSDSerialize::fromXML(savedFilterState, file);
+ file.close();
+
+ // Load the persistent "Recent Items" settings.
+ // Note that the "All Items" settings do not persist.
+ if(recent_items_panel)
+ {
+ if(savedFilterState.has(recent_items_panel->getFilter()->getName()))
+ {
+ LLSD recent_items = savedFilterState.get(
+ recent_items_panel->getFilter()->getName());
+ recent_items_panel->getFilter()->fromLLSD(recent_items);
+ }
+ }
+
+ }
+
+ mFilterEditor = getChild<LLFilterEditor>("inventory search editor");
+ if (mFilterEditor)
+ {
+ mFilterEditor->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterEdit, this, _2));
+ }
+
+ // *TODO:Get the cost info from the server
+ const std::string upload_cost("10");
+ childSetLabelArg("Upload Image", "[COST]", upload_cost);
+ childSetLabelArg("Upload Sound", "[COST]", upload_cost);
+ childSetLabelArg("Upload Animation", "[COST]", upload_cost);
+ childSetLabelArg("Bulk Upload", "[COST]", upload_cost);
+
+ initListCommandsHandlers();
+ return TRUE;
+}
+
+void LLPanelMainInventory::initListCommandsHandlers()
+{
+ mListCommands = getChild<LLPanel>("bottom_panel");
+
+ mListCommands->childSetAction("options_gear_btn", boost::bind(&LLPanelMainInventory::onGearButtonClick, this));
+ mListCommands->childSetAction("trash_btn", boost::bind(&LLPanelMainInventory::onTrashButtonClick, this));
+ mListCommands->childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddButtonClick, this));
+ /*
+ mListCommands->getChild<LLButton>("add_btn")->setHeldDownCallback(boost::bind(&LLPanelMainInventory::onAddButtonHeldDown, this));
+ static const LLSD add_landmark_command("add_landmark");
+ mListCommands->childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddAction, this, add_landmark_command));
+ */
+
+ LLDragAndDropButton* trash_btn = mListCommands->getChild<LLDragAndDropButton>("trash_btn");
+ trash_btn->setDragAndDropHandler(boost::bind(&LLPanelMainInventory::handleDragAndDropToTrash, this
+ , _4 // BOOL drop
+ , _5 // EDragAndDropType cargo_type
+ , _7 // EAcceptance* accept
+ ));
+
+ mCommitCallbackRegistrar.add("Inventory.GearDefault.Custom.Action", boost::bind(&LLPanelMainInventory::onCustomAction, this, _2));
+ mEnableCallbackRegistrar.add("Inventory.GearDefault.Enable", boost::bind(&LLPanelMainInventory::isActionEnabled, this, _2));
+ mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ mMenuAdd = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory_add.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+
+}
+
+// Destroys the object
+LLPanelMainInventory::~LLPanelMainInventory( void )
+{
+ // Save the filters state.
+ LLSD filterRoot;
+ LLInventoryPanel* all_items_panel = getChild<LLInventoryPanel>("All Items");
+ if (all_items_panel)
+ {
+ LLInventoryFilter* filter = all_items_panel->getFilter();
+ if (filter)
+ {
+ LLSD filterState;
+ filter->toLLSD(filterState);
+ filterRoot[filter->getName()] = filterState;
+ }
+ }
+
+ LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items");
+ if (recent_items_panel)
+ {
+ LLInventoryFilter* filter = recent_items_panel->getFilter();
+ if (filter)
+ {
+ LLSD filterState;
+ filter->toLLSD(filterState);
+ filterRoot[filter->getName()] = filterState;
+ }
+ }
+
+ std::ostringstream filterSaveName;
+ filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml");
+ llofstream filtersFile(filterSaveName.str());
+ if(!LLSDSerialize::toPrettyXML(filterRoot, filtersFile))
+ {
+ llwarns << "Could not write to filters save file " << filterSaveName << llendl;
+ }
+ else
+ filtersFile.close();
+
+ gInventory.removeObserver(this);
+ delete mSavedFolderState;
+}
+
+void LLPanelMainInventory::startSearch()
+{
+ // this forces focus to line editor portion of search editor
+ if (mFilterEditor)
+ {
+ mFilterEditor->focusFirstItem(TRUE);
+ }
+}
+
+BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask)
+{
+ LLFolderView* root_folder = mActivePanel ? mActivePanel->getRootFolder() : NULL;
+ if (root_folder)
+ {
+ // first check for user accepting current search results
+ if (mFilterEditor
+ && mFilterEditor->hasFocus()
+ && (key == KEY_RETURN
+ || key == KEY_DOWN)
+ && mask == MASK_NONE)
+ {
+ // move focus to inventory proper
+ mActivePanel->setFocus(TRUE);
+ root_folder->scrollToShowSelection();
+ return TRUE;
+ }
+
+ if (mActivePanel->hasFocus() && key == KEY_UP)
+ {
+ startSearch();
+ }
+ }
+
+ return LLPanel::handleKeyHere(key, mask);
+
+}
+
+//----------------------------------------------------------------------------
+// menu callbacks
+
+void LLPanelMainInventory::doToSelected(const LLSD& userdata)
+{
+ getPanel()->getRootFolder()->doToSelected(&gInventory, userdata);
+}
+
+void LLPanelMainInventory::closeAllFolders()
+{
+ getPanel()->getRootFolder()->closeAllFolders();
+}
+
+void LLPanelMainInventory::newWindow()
+{
+ LLFloaterInventory::showAgentInventory();
+}
+
+void LLPanelMainInventory::doCreate(const LLSD& userdata)
+{
+ menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata);
+}
+
+void LLPanelMainInventory::resetFilters()
+{
+ LLFloaterInventoryFinder *finder = getFinder();
+ getActivePanel()->getFilter()->resetDefault();
+ if (finder)
+ {
+ finder->updateElementsFromFilter();
+ }
+
+ setFilterTextFromFilter();
+}
+
+void LLPanelMainInventory::setSortBy(const LLSD& userdata)
+{
+ std::string sort_field = userdata.asString();
+ if (sort_field == "name")
+ {
+ U32 order = getActivePanel()->getSortOrder();
+ getActivePanel()->setSortOrder( order & ~LLInventoryFilter::SO_DATE );
+
+ gSavedSettings.setBOOL("Inventory.SortByName", TRUE );
+ gSavedSettings.setBOOL("Inventory.SortByDate", FALSE );
+ }
+ else if (sort_field == "date")
+ {
+ U32 order = getActivePanel()->getSortOrder();
+ getActivePanel()->setSortOrder( order | LLInventoryFilter::SO_DATE );
+
+ gSavedSettings.setBOOL("Inventory.SortByName", FALSE );
+ gSavedSettings.setBOOL("Inventory.SortByDate", TRUE );
+ }
+ else if (sort_field == "foldersalwaysbyname")
+ {
+ U32 order = getActivePanel()->getSortOrder();
+ if ( order & LLInventoryFilter::SO_FOLDERS_BY_NAME )
+ {
+ order &= ~LLInventoryFilter::SO_FOLDERS_BY_NAME;
+
+ gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", FALSE );
+ }
+ else
+ {
+ order |= LLInventoryFilter::SO_FOLDERS_BY_NAME;
+
+ gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", TRUE );
+ }
+ getActivePanel()->setSortOrder( order );
+ }
+ else if (sort_field == "systemfolderstotop")
+ {
+ U32 order = getActivePanel()->getSortOrder();
+ if ( order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP )
+ {
+ order &= ~LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
+
+ gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", FALSE );
+ }
+ else
+ {
+ order |= LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
+
+ gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", TRUE );
+ }
+ getActivePanel()->setSortOrder( order );
+ }
+}
+
+// static
+BOOL LLPanelMainInventory::filtersVisible(void* user_data)
+{
+ LLPanelMainInventory* self = (LLPanelMainInventory*)user_data;
+ if(!self) return FALSE;
+
+ return self->getFinder() != NULL;
+}
+
+void LLPanelMainInventory::onClearSearch()
+{
+ LLFloater *finder = getFinder();
+ if (mActivePanel)
+ {
+ mActivePanel->setFilterSubString(LLStringUtil::null);
+ mActivePanel->setFilterTypes(0xffffffff);
+ }
+
+ if (finder)
+ {
+ LLFloaterInventoryFinder::selectAllTypes(finder);
+ }
+
+ // re-open folders that were initially open
+ if (mActivePanel)
+ {
+ mSavedFolderState->setApply(TRUE);
+ mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
+ LLOpenFoldersWithSelection opener;
+ mActivePanel->getRootFolder()->applyFunctorRecursively(opener);
+ mActivePanel->getRootFolder()->scrollToShowSelection();
+ }
+}
+
+void LLPanelMainInventory::onFilterEdit(const std::string& search_string )
+{
+ if (search_string == "")
+ {
+ onClearSearch();
+ }
+ if (!mActivePanel)
+ {
+ return;
+ }
+
+ gInventory.startBackgroundFetch();
+
+ std::string uppercase_search_string = search_string;
+ LLStringUtil::toUpper(uppercase_search_string);
+ if (mActivePanel->getFilterSubString().empty() && uppercase_search_string.empty())
+ {
+ // current filter and new filter empty, do nothing
+ return;
+ }
+
+ // save current folder open state if no filter currently applied
+ if (!mActivePanel->getRootFolder()->isFilterModified())
+ {
+ mSavedFolderState->setApply(FALSE);
+ mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
+ }
+
+ // set new filter string
+ mActivePanel->setFilterSubString(uppercase_search_string);
+}
+
+
+ //static
+ BOOL LLPanelMainInventory::incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward)
+ {
+ LLPanelMainInventory* active_view = NULL;
+
+ 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)
+ {
+ LLPanelMainInventory* iv = dynamic_cast<LLPanelMainInventory*>(*iter);
+ if (iv)
+ {
+ if (gFocusMgr.childHasKeyboardFocus(iv))
+ {
+ active_view = iv;
+ break;
+ }
+ }
+ }
+
+ if (!active_view)
+ {
+ return FALSE;
+ }
+
+ std::string search_string(find_text);
+
+ if (search_string.empty())
+ {
+ return FALSE;
+ }
+
+ if (active_view->getPanel() &&
+ active_view->getPanel()->getRootFolder()->search(first_item, search_string, backward))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+void LLPanelMainInventory::onFilterSelected()
+{
+ // Find my index
+ mActivePanel = (LLInventoryPanel*)childGetVisibleTab("inventory filter tabs");
+
+ if (!mActivePanel)
+ {
+ return;
+ }
+ LLInventoryFilter* filter = mActivePanel->getFilter();
+ LLFloaterInventoryFinder *finder = getFinder();
+ if (finder)
+ {
+ finder->changeFilter(filter);
+ }
+ if (filter->isActive())
+ {
+ // If our filter is active we may be the first thing requiring a fetch so we better start it here.
+ gInventory.startBackgroundFetch();
+ }
+ setFilterTextFromFilter();
+}
+
+const std::string LLPanelMainInventory::getFilterSubString()
+{
+ return mActivePanel->getFilterSubString();
+}
+
+void LLPanelMainInventory::setFilterSubString(const std::string& string)
+{
+ mActivePanel->setFilterSubString(string);
+}
+
+BOOL LLPanelMainInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // Check to see if we are auto scrolling from the last frame
+ LLInventoryPanel* panel = (LLInventoryPanel*)this->getActivePanel();
+ BOOL needsToScroll = panel->getScrollableContainer()->autoScroll(x, y);
+ if(mFilterTabs)
+ {
+ if(needsToScroll)
+ {
+ mFilterTabs->startDragAndDropDelayTimer();
+ }
+ }
+
+ BOOL handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+
+ return handled;
+}
+
+void LLPanelMainInventory::changed(U32 mask)
+{
+}
+
+
+void LLPanelMainInventory::setFilterTextFromFilter()
+{
+ mFilterText = mActivePanel->getFilter()->getFilterText();
+}
+
+void LLPanelMainInventory::toggleFindOptions()
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_VIEW_TOGGLE);
+ LLFloater *floater = getFinder();
+ if (!floater)
+ {
+ LLFloaterInventoryFinder * finder = new LLFloaterInventoryFinder(this);
+ mFinderHandle = finder->getHandle();
+ finder->openFloater();
+
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ if (parent_floater) // Seraph: Fix this, shouldn't be null even for sidepanel
+ parent_floater->addDependentFloater(mFinderHandle);
+ // start background fetch of folders
+ gInventory.startBackgroundFetch();
+ }
+ else
+ {
+ floater->closeFloater();
+ }
+}
+
+void LLPanelMainInventory::setSelectCallback(const LLFolderView::signal_t::slot_type& cb)
+{
+ getChild<LLInventoryPanel>("All Items")->setSelectCallback(cb);
+ getChild<LLInventoryPanel>("Recent Items")->setSelectCallback(cb);
+}
+
+void LLPanelMainInventory::onSelectionChange(LLInventoryPanel *panel, const std::deque<LLFolderViewItem*>& items, BOOL user_action)
+{
+ updateListCommands();
+ panel->onSelectionChange(items, user_action);
+}
+
+///----------------------------------------------------------------------------
+/// LLFloaterInventoryFinder
+///----------------------------------------------------------------------------
+
+LLFloaterInventoryFinder* LLPanelMainInventory::getFinder()
+{
+ return (LLFloaterInventoryFinder*)mFinderHandle.get();
+}
+
+
+LLFloaterInventoryFinder::LLFloaterInventoryFinder(LLPanelMainInventory* inventory_view) :
+ LLFloater(LLSD()),
+ mPanelMainInventory(inventory_view),
+ mFilter(inventory_view->getPanel()->getFilter())
+{
+ LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inventory_view_finder.xml", NULL);
+ updateElementsFromFilter();
+}
+
+
+void LLFloaterInventoryFinder::onCheckSinceLogoff(LLUICtrl *ctrl, void *user_data)
+{
+ LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data;
+ if (!self) return;
+
+ bool since_logoff= self->childGetValue("check_since_logoff");
+
+ if (!since_logoff &&
+ !( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() ) )
+ {
+ self->mSpinSinceHours->set(1.0f);
+ }
+}
+BOOL LLFloaterInventoryFinder::postBuild()
+{
+ const LLRect& viewrect = mPanelMainInventory->getRect();
+ setRect(LLRect(viewrect.mLeft - getRect().getWidth(), viewrect.mTop, viewrect.mLeft, viewrect.mTop - getRect().getHeight()));
+
+ childSetAction("All", selectAllTypes, this);
+ childSetAction("None", selectNoTypes, this);
+
+ mSpinSinceHours = getChild<LLSpinCtrl>("spin_hours_ago");
+ childSetCommitCallback("spin_hours_ago", onTimeAgo, this);
+
+ mSpinSinceDays = getChild<LLSpinCtrl>("spin_days_ago");
+ childSetCommitCallback("spin_days_ago", onTimeAgo, this);
+
+ // mCheckSinceLogoff = getChild<LLSpinCtrl>("check_since_logoff");
+ childSetCommitCallback("check_since_logoff", onCheckSinceLogoff, this);
+
+ childSetAction("Close", onCloseBtn, this);
+
+ updateElementsFromFilter();
+ return TRUE;
+}
+void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data)
+{
+ LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data;
+ if (!self) return;
+
+ bool since_logoff=true;
+ if ( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() )
+ {
+ since_logoff = false;
+ }
+ self->childSetValue("check_since_logoff", since_logoff);
+}
+
+void LLFloaterInventoryFinder::changeFilter(LLInventoryFilter* filter)
+{
+ mFilter = filter;
+ updateElementsFromFilter();
+}
+
+void LLFloaterInventoryFinder::updateElementsFromFilter()
+{
+ if (!mFilter)
+ return;
+
+ // Get data needed for filter display
+ U32 filter_types = mFilter->getFilterTypes();
+ std::string filter_string = mFilter->getFilterSubString();
+ LLInventoryFilter::EFolderShow show_folders = mFilter->getShowFolderState();
+ U32 hours = mFilter->getHoursAgo();
+
+ // update the ui elements
+ setTitle(mFilter->getName());
+
+ childSetValue("check_animation", (S32) (filter_types & 0x1 << LLInventoryType::IT_ANIMATION));
+
+ childSetValue("check_calling_card", (S32) (filter_types & 0x1 << LLInventoryType::IT_CALLINGCARD));
+ childSetValue("check_clothing", (S32) (filter_types & 0x1 << LLInventoryType::IT_WEARABLE));
+ childSetValue("check_gesture", (S32) (filter_types & 0x1 << LLInventoryType::IT_GESTURE));
+ childSetValue("check_landmark", (S32) (filter_types & 0x1 << LLInventoryType::IT_LANDMARK));
+ childSetValue("check_notecard", (S32) (filter_types & 0x1 << LLInventoryType::IT_NOTECARD));
+ childSetValue("check_object", (S32) (filter_types & 0x1 << LLInventoryType::IT_OBJECT));
+ childSetValue("check_script", (S32) (filter_types & 0x1 << LLInventoryType::IT_LSL));
+ childSetValue("check_sound", (S32) (filter_types & 0x1 << LLInventoryType::IT_SOUND));
+ childSetValue("check_texture", (S32) (filter_types & 0x1 << LLInventoryType::IT_TEXTURE));
+ childSetValue("check_snapshot", (S32) (filter_types & 0x1 << LLInventoryType::IT_SNAPSHOT));
+ childSetValue("check_show_empty", show_folders == LLInventoryFilter::SHOW_ALL_FOLDERS);
+ childSetValue("check_since_logoff", mFilter->isSinceLogoff());
+ mSpinSinceHours->set((F32)(hours % 24));
+ mSpinSinceDays->set((F32)(hours / 24));
+}
+
+void LLFloaterInventoryFinder::draw()
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_DRAW);
+ U32 filter = 0xffffffff;
+ BOOL filtered_by_all_types = TRUE;
+
+ if (!childGetValue("check_animation"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_ANIMATION);
+ filtered_by_all_types = FALSE;
+ }
+
+
+ if (!childGetValue("check_calling_card"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_CALLINGCARD);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_clothing"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_WEARABLE);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_gesture"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_GESTURE);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_landmark"))
+
+
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_LANDMARK);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_notecard"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_NOTECARD);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_object"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_OBJECT);
+ filter &= ~(0x1 << LLInventoryType::IT_ATTACHMENT);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_script"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_LSL);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_sound"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_SOUND);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_texture"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_TEXTURE);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_snapshot"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_SNAPSHOT);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!filtered_by_all_types)
+ {
+ // don't include folders in filter, unless I've selected everything
+ 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);
+ if (getCheckSinceLogoff())
+ {
+ mSpinSinceDays->set(0);
+ mSpinSinceHours->set(0);
+ }
+ U32 days = (U32)mSpinSinceDays->get();
+ U32 hours = (U32)mSpinSinceHours->get();
+ if (hours > 24)
+ {
+ days += hours / 24;
+ hours = (U32)hours % 24;
+ mSpinSinceDays->set((F32)days);
+ mSpinSinceHours->set((F32)hours);
+ }
+ hours += days * 24;
+ mPanelMainInventory->getPanel()->setHoursAgo(hours);
+ mPanelMainInventory->getPanel()->setSinceLogoff(getCheckSinceLogoff());
+ mPanelMainInventory->setFilterTextFromFilter();
+
+ LLPanel::draw();
+}
+
+BOOL LLFloaterInventoryFinder::getCheckShowEmpty()
+{
+ return childGetValue("check_show_empty");
+}
+
+BOOL LLFloaterInventoryFinder::getCheckSinceLogoff()
+{
+ return childGetValue("check_since_logoff");
+}
+
+void LLFloaterInventoryFinder::onCloseBtn(void* user_data)
+{
+ LLFloaterInventoryFinder* finderp = (LLFloaterInventoryFinder*)user_data;
+ finderp->closeFloater();
+}
+
+// static
+void LLFloaterInventoryFinder::selectAllTypes(void* user_data)
+{
+ LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data;
+ if(!self) return;
+
+ self->childSetValue("check_animation", TRUE);
+ self->childSetValue("check_calling_card", TRUE);
+ self->childSetValue("check_clothing", TRUE);
+ self->childSetValue("check_gesture", TRUE);
+ self->childSetValue("check_landmark", TRUE);
+ self->childSetValue("check_notecard", TRUE);
+ self->childSetValue("check_object", TRUE);
+ self->childSetValue("check_script", TRUE);
+ self->childSetValue("check_sound", TRUE);
+ self->childSetValue("check_texture", TRUE);
+ self->childSetValue("check_snapshot", TRUE);
+}
+
+//static
+void LLFloaterInventoryFinder::selectNoTypes(void* user_data)
+{
+ LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data;
+ if(!self) return;
+
+ self->childSetValue("check_animation", FALSE);
+ self->childSetValue("check_calling_card", FALSE);
+ self->childSetValue("check_clothing", FALSE);
+ self->childSetValue("check_gesture", FALSE);
+ self->childSetValue("check_landmark", FALSE);
+ self->childSetValue("check_notecard", FALSE);
+ self->childSetValue("check_object", FALSE);
+ self->childSetValue("check_script", FALSE);
+ self->childSetValue("check_sound", FALSE);
+ self->childSetValue("check_texture", FALSE);
+ self->childSetValue("check_snapshot", FALSE);
+}
+
+
+
+
+
+void LLPanelMainInventory::updateListCommands()
+{
+ bool trash_enabled = isActionEnabled("delete");
+
+ mListCommands->childSetEnabled("trash_btn", trash_enabled);
+}
+
+void LLPanelMainInventory::onGearButtonClick()
+{
+ showActionMenu(mMenuGearDefault,"options_gear_btn");
+}
+
+void LLPanelMainInventory::onAddButtonClick()
+{
+ showActionMenu(mMenuAdd,"add_btn");
+}
+
+void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_view_name)
+{
+ if (menu)
+ {
+ menu->buildDrawLabels();
+ menu->updateParent(LLMenuGL::sMenuContainer);
+ 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();
+ 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();
+ getActivePanel()->getRootFolder()->doToSelected(getActivePanel()->getModel(),command_name);
+}
+
+void LLPanelMainInventory::onCustomAction(const LLSD& userdata)
+{
+ if (!isActionEnabled(userdata))
+ return;
+
+ const std::string command_name = userdata.asString();
+ if (command_name == "new_window")
+ {
+ newWindow();
+ }
+ if (command_name == "sort_by_name")
+ {
+ const LLSD arg = "name";
+ setSortBy(arg);
+ }
+ if (command_name == "sort_by_recent")
+ {
+ const LLSD arg = "date";
+ setSortBy(arg);
+ }
+ if (command_name == "show_filters")
+ {
+ toggleFindOptions();
+ }
+ if (command_name == "reset_filters")
+ {
+ resetFilters();
+ }
+ if (command_name == "close_folders")
+ {
+ closeAllFolders();
+ }
+ if (command_name == "empty_trash")
+ {
+ const std::string notification = "ConfirmEmptyTrash";
+ gInventory.emptyFolderType(notification, LLFolderType::FT_TRASH);
+ }
+ if (command_name == "empty_lostnfound")
+ {
+ const std::string notification = "ConfirmEmptyLostAndFound";
+ gInventory.emptyFolderType(notification, LLFolderType::FT_LOST_AND_FOUND);
+ }
+ if (command_name == "save_texture")
+ {
+ LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (!current_item)
+ {
+ return;
+ }
+
+ const LLUUID& item_id = current_item->getListener()->getUUID();
+ LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item_id), TAKE_FOCUS_YES);
+ if (preview_texture)
+ {
+ preview_texture->openToSave();
+ }
+ }
+}
+
+BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)
+{
+ const std::string command_name = userdata.asString();
+ if (command_name == "delete")
+ {
+ BOOL can_delete = FALSE;
+ LLFolderView *folder = getActivePanel()->getRootFolder();
+ if (folder)
+ {
+ can_delete = TRUE;
+ std::set<LLUUID> selection_set;
+ folder->getSelectionList(selection_set);
+ for (std::set<LLUUID>::iterator iter = selection_set.begin();
+ iter != selection_set.end();
+ ++iter)
+ {
+ const LLUUID &item_id = (*iter);
+ LLFolderViewItem *item = folder->getItemByID(item_id);
+ can_delete &= item->getListener()->isItemRemovable();
+ }
+ return can_delete;
+ }
+ return FALSE;
+ }
+ if (command_name == "save_texture")
+ {
+ LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (current_item)
+ {
+ return (current_item->getListener()->getInventoryType() == LLInventoryType::IT_TEXTURE);
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+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 (is_enabled && drop)
+ {
+ onClipboardAction("delete");
+ }
+ return true;
+}
diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h
index a2b988e80c..29e9baa6cf 100644
--- a/indra/newview/llpanelmaininventory.h
+++ b/indra/newview/llpanelmaininventory.h
@@ -1,125 +1,145 @@
-/**
- * @file llpanelmaininventory.h
- * @brief llpanelmaininventory.h
- * class definition
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLPANELMAININVENTORY_H
-#define LL_LLPANELMAININVENTORY_H
-
-#include "llpanel.h"
-#include "llinventorymodel.h"
-#include "llfolderview.h"
-
-class LLFolderViewItem;
-class LLInventoryPanel;
-class LLSaveFolderState;
-class LLFilterEditor;
-class LLTabContainer;
-class LLFloaterInventoryFinder;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLPanelMainInventory
-//
-// This is a panel used to view and control an agent's inventory,
-// including all the fixin's (e.g. AllItems/RecentItems tabs, filter floaters).
-//
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLPanelMainInventory : public LLPanel, LLInventoryObserver
-{
-public:
- friend class LLFloaterInventoryFinder;
-
- LLPanelMainInventory();
- ~LLPanelMainInventory();
-
- BOOL postBuild();
-
- virtual BOOL handleKeyHere(KEY key, MASK mask);
-
- // Inherited functionality
- /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg);
- /*virtual*/ void changed(U32 mask);
-
- LLInventoryPanel* getPanel() { return mActivePanel; }
- LLInventoryPanel* getActivePanel() { return mActivePanel; }
-
- const std::string& getFilterText() const { return mFilterText; }
-
- void setSelectCallback(const LLFolderView::signal_t::slot_type& cb);
-
-protected:
- //
- // Misc functions
- //
- void setFilterTextFromFilter();
- void startSearch();
-
- void toggleFindOptions();
-
- static BOOL filtersVisible(void* user_data);
- void onClearSearch();
- static void onFoldersByName(void *user_data);
- static BOOL checkFoldersByName(void *user_data);
- void onFilterEdit(const std::string& search_string );
- static BOOL incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward);
- void onFilterSelected();
-
- const std::string getFilterSubString();
- void setFilterSubString(const std::string& string);
-
- // menu callbacks
- void doToSelected(const LLSD& userdata);
- void closeAllFolders();
- void newWindow();
- void doCreate(const LLSD& userdata);
- void resetFilters();
- void setSortBy(const LLSD& userdata);
-
-private:
- LLFloaterInventoryFinder* getFinder();
-
- LLFilterEditor* mFilterEditor;
- LLTabContainer* mFilterTabs;
- LLHandle<LLFloater> mFinderHandle;
- LLInventoryPanel* mActivePanel;
- LLSaveFolderState* mSavedFolderState;
-
- std::string mFilterText;
-};
-
-#endif // LL_LLPANELMAININVENTORY_H
-
-
-
+/**
+ * @file llpanelmaininventory.h
+ * @brief llpanelmaininventory.h
+ * class definition
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPANELMAININVENTORY_H
+#define LL_LLPANELMAININVENTORY_H
+
+#include "llpanel.h"
+#include "llinventorymodel.h"
+#include "llfolderview.h"
+
+class LLFolderViewItem;
+class LLInventoryPanel;
+class LLSaveFolderState;
+class LLFilterEditor;
+class LLTabContainer;
+class LLFloaterInventoryFinder;
+class LLMenuGL;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLPanelMainInventory
+//
+// This is a panel used to view and control an agent's inventory,
+// including all the fixin's (e.g. AllItems/RecentItems tabs, filter floaters).
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLPanelMainInventory : public LLPanel, LLInventoryObserver
+{
+public:
+ friend class LLFloaterInventoryFinder;
+
+ LLPanelMainInventory();
+ ~LLPanelMainInventory();
+
+ BOOL postBuild();
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask);
+
+ // Inherited functionality
+ /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+ /*virtual*/ void changed(U32 mask);
+
+ LLInventoryPanel* getPanel() { return mActivePanel; }
+ LLInventoryPanel* getActivePanel() { return mActivePanel; }
+ const LLInventoryPanel* getActivePanel() const { return mActivePanel; }
+
+ const std::string& getFilterText() const { return mFilterText; }
+
+ void setSelectCallback(const LLFolderView::signal_t::slot_type& cb);
+
+protected:
+ //
+ // Misc functions
+ //
+ void setFilterTextFromFilter();
+ void startSearch();
+
+ void toggleFindOptions();
+ void onSelectionChange(LLInventoryPanel *panel, const std::deque<LLFolderViewItem*>& items, BOOL user_action);
+
+ static BOOL filtersVisible(void* user_data);
+ void onClearSearch();
+ static void onFoldersByName(void *user_data);
+ static BOOL checkFoldersByName(void *user_data);
+ void onFilterEdit(const std::string& search_string );
+ static BOOL incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward);
+ void onFilterSelected();
+
+ const std::string getFilterSubString();
+ void setFilterSubString(const std::string& string);
+
+ // menu callbacks
+ void doToSelected(const LLSD& userdata);
+ void closeAllFolders();
+ void newWindow();
+ void doCreate(const LLSD& userdata);
+ void resetFilters();
+ void setSortBy(const LLSD& userdata);
+
+ // List Commands Handlers
+ void initListCommandsHandlers();
+ void updateListCommands();
+ void onGearButtonClick();
+ void onAddButtonClick();
+ void showActionMenu(LLMenuGL* menu, std::string spawning_view_name);
+ void onTrashButtonClick();
+ void onClipboardAction(const LLSD& userdata);
+ BOOL isActionEnabled(const LLSD& command_name);
+ void onCustomAction(const LLSD& command_name);
+ bool handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept);
+
+
+private:
+ LLFloaterInventoryFinder* getFinder();
+
+ LLFilterEditor* mFilterEditor;
+ LLTabContainer* mFilterTabs;
+ LLHandle<LLFloater> mFinderHandle;
+ LLInventoryPanel* mActivePanel;
+ LLSaveFolderState* mSavedFolderState;
+
+ LLPanel* mListCommands;
+ LLMenuGL* mMenuGearDefault;
+ LLMenuGL* mMenuAdd;
+
+ std::string mFilterText;
+};
+
+#endif // LL_LLPANELMAININVENTORY_H
+
+
+
diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp
index 85efe0f93e..5186a5888b 100644
--- a/indra/newview/llpanelmediasettingsgeneral.cpp
+++ b/indra/newview/llpanelmediasettingsgeneral.cpp
@@ -55,6 +55,8 @@
#include "llfloatertools.h"
#include "lltrans.h"
+const char *CHECKERBOARD_DATA_URL = "data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%%22 height=%22100%%22 %3E%3Cdefs%3E%3Cpattern id=%22checker%22 patternUnits=%22userSpaceOnUse%22 x=%220%22 y=%220%22 width=%22128%22 height=%22128%22 viewBox=%220 0 128 128%22 %3E%3Crect x=%220%22 y=%220%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3Crect x=%2264%22 y=%2264%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3C/pattern%3E%3C/defs%3E%3Crect x=%220%22 y=%220%22 width=%22100%%22 height=%22100%%22 fill=%22url(#checker)%22 /%3E%3C/svg%3E";
+
////////////////////////////////////////////////////////////////////////////////
//
LLPanelMediaSettingsGeneral::LLPanelMediaSettingsGeneral() :
@@ -97,7 +99,6 @@ BOOL LLPanelMediaSettingsGeneral::postBuild()
childSetCommitCallback( "current_url_reset_btn",onBtnResetCurrentUrl, this);
// interrogates controls and updates widgets as required
updateMediaPreview();
- updateCurrentURL();
return true;
}
@@ -160,9 +161,9 @@ void LLPanelMediaSettingsGeneral::draw()
};
};
- // current URL can change over time.
-// updateCurrentURL();
-
+ // current URL can change over time, update it here
+ updateCurrentUrl();
+
LLPermissions perm;
bool user_can_press_reset = mMediaEditable;
@@ -215,26 +216,15 @@ void LLPanelMediaSettingsGeneral::clearValues( void* userdata, bool editable)
self->updateMediaPreview();
}
-////////////////////////////////////////////////////////////////////////////////
-// static
-void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_settings ,bool editable)
+// static
+bool LLPanelMediaSettingsGeneral::isMultiple()
{
- LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata;
- self->mMediaEditable = editable;
-
- //llinfos << "---------------" << llendl;
- //llinfos << ll_pretty_print_sd(media_settings) << llendl;
- //llinfos << "---------------" << llendl;
-
// IF all the faces have media (or all dont have media)
if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo )
{
if(LLFloaterMediaSettings::getInstance()->mMultipleMedia)
{
- self->clearValues(self, self->mMediaEditable);
- // only show multiple
- self->mHomeURL ->setText(LLTrans::getString("Multiple Media"));
- return;
+ return true;
}
}
@@ -242,13 +232,32 @@ void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_
{
if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia)
{
- self->clearValues(self, self->mMediaEditable);
- // only show multiple
- self->mHomeURL ->setText(LLTrans::getString("Multiple Media"));
- return;
- }
-
+ return true;
+ }
}
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_settings ,bool editable)
+{
+ LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata;
+ self->mMediaEditable = editable;
+
+ //llinfos << "---------------" << llendl;
+ //llinfos << ll_pretty_print_sd(media_settings) << llendl;
+ //llinfos << "---------------" << llendl;
+
+ if ( LLPanelMediaSettingsGeneral::isMultiple() )
+ {
+ self->clearValues(self, self->mMediaEditable);
+ // only show multiple
+ self->mHomeURL->setText(LLTrans::getString("Multiple Media"));
+ self->mCurrentURL->setText(LLTrans::getString("Multiple Media"));
+ return;
+ }
+
std::string base_key( "" );
std::string tentative_key( "" );
@@ -305,7 +314,6 @@ void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_
// interrogates controls and updates widgets as required
self->updateMediaPreview();
- self->updateCurrentURL();
}
////////////////////////////////////////////////////////////////////////////////
@@ -320,22 +328,11 @@ void LLPanelMediaSettingsGeneral::updateMediaPreview()
// new home URL will be empty if media is deleted so display a
// "preview goes here" data url page
{
- mPreviewMedia->navigateTo( "data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%%22 height=%22100%%22 %3E%3Cdefs%3E%3Cpattern id=%22checker%22 patternUnits=%22userSpaceOnUse%22 x=%220%22 y=%220%22 width=%22128%22 height=%22128%22 viewBox=%220 0 128 128%22 %3E%3Crect x=%220%22 y=%220%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3Crect x=%2264%22 y=%2264%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3C/pattern%3E%3C/defs%3E%3Crect x=%220%22 y=%220%22 width=%22100%%22 height=%22100%%22 fill=%22url(#checker)%22 /%3E%3C/svg%3E" );
+ mPreviewMedia->navigateTo( CHECKERBOARD_DATA_URL );
};
}
////////////////////////////////////////////////////////////////////////////////
-// Helper to set current URL
-void LLPanelMediaSettingsGeneral::updateCurrentURL()
-{
- if( mCurrentURL->getText().empty() )
- {
- childSetText( "current_url", mHomeURL->getText() );
- }
-
-}
-
-////////////////////////////////////////////////////////////////////////////////
// virtual
void LLPanelMediaSettingsGeneral::onClose(bool app_quitting)
@@ -441,6 +438,8 @@ bool LLPanelMediaSettingsGeneral::navigateHomeSelectedFace()
LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection();
selected_objects->getSelectedTEValue( &functor_navigate_media, all_face_media_navigated );
+ // Note: we don't update the 'current URL' field until the media data itself changes
+
return all_face_media_navigated;
}
@@ -451,3 +450,31 @@ const std::string LLPanelMediaSettingsGeneral::getHomeUrl()
return mHomeURL->getValue().asString();
}
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLPanelMediaSettingsGeneral::updateCurrentUrl()
+{
+ // Get the current URL from the selection
+
+ const LLMediaEntry default_media_data;
+ std::string value_str = default_media_data.getCurrentURL();
+ struct functor_getter_current_url : public LLSelectedTEGetFunctor< std::string >
+ {
+ functor_getter_current_url(const LLMediaEntry& entry): mMediaEntry(entry) {}
+
+ std::string get( LLViewerObject* object, S32 face )
+ {
+ if ( object )
+ if ( object->getTE(face) )
+ if ( object->getTE(face)->getMediaData() )
+ return object->getTE(face)->getMediaData()->getCurrentURL();
+ return mMediaEntry.getCurrentURL();
+ };
+
+ const LLMediaEntry & mMediaEntry;
+
+ } func_current_url(default_media_data);
+ bool identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_current_url, value_str );
+ mCurrentURL->setText(value_str);
+ mCurrentURL->setTentative(identical);
+}
diff --git a/indra/newview/llpanelmediasettingsgeneral.h b/indra/newview/llpanelmediasettingsgeneral.h
index e82a31382e..b48e081a1b 100644
--- a/indra/newview/llpanelmediasettingsgeneral.h
+++ b/indra/newview/llpanelmediasettingsgeneral.h
@@ -63,7 +63,6 @@ public:
bool navigateHomeSelectedFace();
void updateMediaPreview();
- void updateCurrentURL();
const std::string getHomeUrl();
@@ -72,8 +71,12 @@ protected:
bool mMediaEditable;
private:
+ void updateCurrentUrl();
+
static void onBtnResetCurrentUrl(LLUICtrl* ctrl, void *userdata);
static void onCommitHomeURL(LLUICtrl* ctrl, void *userdata );
+
+ static bool isMultiple();
LLComboBox* mControls;
LLCheckBoxCtrl* mAutoLoop;
diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp
index 0b2a7e8756..73e19b3b2a 100644
--- a/indra/newview/llpanelprimmediacontrols.cpp
+++ b/indra/newview/llpanelprimmediacontrols.cpp
@@ -228,6 +228,7 @@ void LLPanelPrimMediaControls::updateShape()
bool can_navigate = parcel->getMediaAllowNavigate();
bool enabled = false;
+ bool is_zoomed = (mCurrentZoom != ZOOM_NONE);
// There is no such thing as "has_focus" being different from normal controls set
// anymore (as of user feedback from bri 10/09). So we cheat here and force 'has_focus'
// to 'true' (or, actually, we use a setting)
@@ -256,7 +257,7 @@ void LLPanelPrimMediaControls::updateShape()
LLUICtrl* stop_ctrl = getChild<LLUICtrl>("stop");
LLUICtrl* media_stop_ctrl = getChild<LLUICtrl>("media_stop");
LLUICtrl* home_ctrl = getChild<LLUICtrl>("home");
- LLUICtrl* close_ctrl = getChild<LLUICtrl>("close");
+ LLUICtrl* unzoom_ctrl = getChild<LLUICtrl>("close"); // This is actually "unzoom"
LLUICtrl* open_ctrl = getChild<LLUICtrl>("new_window");
LLUICtrl* zoom_ctrl = getChild<LLUICtrl>("zoom_frame");
LLPanel* media_loading_panel = getChild<LLPanel>("media_progress_indicator");
@@ -283,7 +284,8 @@ void LLPanelPrimMediaControls::updateShape()
reload_ctrl->setVisible(has_focus);
stop_ctrl->setVisible(false);
home_ctrl->setVisible(has_focus);
- close_ctrl->setVisible(has_focus);
+ zoom_ctrl->setVisible(!is_zoomed);
+ unzoom_ctrl->setVisible(has_focus && is_zoomed);
open_ctrl->setVisible(true);
media_address_ctrl->setVisible(has_focus && !mini_controls);
media_play_slider_panel->setVisible(has_focus && !mini_controls);
@@ -294,6 +296,7 @@ void LLPanelPrimMediaControls::updateShape()
whitelist_icon->setVisible(!mini_controls && (media_data)?media_data->getWhiteListEnable():false);
// Disable zoom if HUD
zoom_ctrl->setEnabled(!objectp->isHUDAttachment());
+ unzoom_ctrl->setEnabled(!objectp->isHUDAttachment());
secure_lock_icon->setVisible(false);
mCurrentURL = media_impl->getCurrentMediaURL();
diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp
index 58ecfb60c2..72856d5b82 100644
--- a/indra/newview/llpanelteleporthistory.cpp
+++ b/indra/newview/llpanelteleporthistory.cpp
@@ -359,7 +359,7 @@ void LLTeleportHistoryPanel::onCopySLURL()
U64 new_region_handle = to_region_handle(global_pos);
- LLWorldMap::url_callback_t cb = boost::bind(
+ LLWorldMapMessage::url_callback_t cb = boost::bind(
&LLPanelPlacesTab::onRegionResponse, this,
global_pos, _1, _2, _3, _4);
diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp
index 86fa2c4695..3eab13fc4a 100644
--- a/indra/newview/llpreviewtexture.cpp
+++ b/indra/newview/llpreviewtexture.cpp
@@ -48,6 +48,7 @@
#include "lltextbox.h"
#include "lltextureview.h"
#include "llui.h"
+#include "llviewerinventory.h"
#include "llviewertexture.h"
#include "llviewertexturelist.h"
#include "lluictrlfactory.h"
@@ -63,7 +64,7 @@ const F32 PREVIEW_TEXTURE_MIN_ASPECT = 0.005f;
LLPreviewTexture::LLPreviewTexture(const LLSD& key)
- : LLPreview( key ),
+ : LLPreview(key),
mLoadingFullImage( FALSE ),
mShowKeepDiscard(FALSE),
mCopyToInv(FALSE),
@@ -71,7 +72,8 @@ LLPreviewTexture::LLPreviewTexture(const LLSD& key)
mUpdateDimensions(TRUE),
mLastHeight(0),
mLastWidth(0),
- mAspectRatio(0.f)
+ mAspectRatio(0.f),
+ mPreviewToSave(FALSE)
{
const LLInventoryItem *item = getItem();
if(item)
@@ -104,6 +106,10 @@ LLPreviewTexture::LLPreviewTexture(const LLSD& key)
mIsCopyable = TRUE;
}
+ if (key.has("save_as"))
+ {
+ mPreviewToSave = TRUE;
+ }
//Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this, "floater_preview_texture.xml", FALSE);
}
@@ -181,6 +187,12 @@ void LLPreviewTexture::draw()
if ( mImage.notNull() )
{
+ // Automatically bring up SaveAs dialog if we opened this to save the texture.
+ if (mPreviewToSave)
+ {
+ mPreviewToSave = FALSE;
+ saveAs();
+ }
// Draw the texture
glColor3f( 1.f, 1.f, 1.f );
gl_draw_scaled_image(interior.mLeft,
@@ -209,7 +221,7 @@ void LLPreviewTexture::draw()
if( mLoadingFullImage )
{
- LLFontGL::getFontSansSerif()->renderUTF8(LLTrans::getString("Receiving:"), 0,
+ LLFontGL::getFontSansSerif()->renderUTF8(LLTrans::getString("Receiving"), 0,
interior.mLeft + 4,
interior.mBottom + 4,
LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
@@ -304,6 +316,11 @@ void LLPreviewTexture::onFocusReceived()
LLPreview::onFocusReceived();
}
+void LLPreviewTexture::openToSave()
+{
+ mPreviewToSave = TRUE;
+}
+
// static
void LLPreviewTexture::onFileLoadedForSave(BOOL success,
LLViewerFetchedTexture *src_vi,
@@ -356,6 +373,7 @@ void LLPreviewTexture::onFileLoadedForSave(BOOL success,
{
LLNotifications::instance().add("CannotDownloadFile");
}
+
}
@@ -554,6 +572,7 @@ void LLPreviewTexture::loadAsset()
{
mImage = LLViewerTextureManager::getFetchedTexture(mImageID, MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
mImage->setBoostLevel(LLViewerTexture::BOOST_PREVIEW);
+ mImage->forceToSaveRawImage(0) ;
mAssetStatus = PREVIEW_ASSET_LOADING;
updateDimensions();
}
diff --git a/indra/newview/llpreviewtexture.h b/indra/newview/llpreviewtexture.h
index 520626b49f..9b3c91d831 100644
--- a/indra/newview/llpreviewtexture.h
+++ b/indra/newview/llpreviewtexture.h
@@ -67,8 +67,7 @@ public:
S32 discard_level,
BOOL final,
void* userdata );
-
-
+ void openToSave();
protected:
void init();
/* virtual */ BOOL postBuild();
@@ -77,14 +76,17 @@ protected:
private:
void updateDimensions();
- LLUUID mImageID;
+ LLUUID mImageID;
LLPointer<LLViewerFetchedTexture> mImage;
BOOL mLoadingFullImage;
std::string mSaveFileName;
LLFrameTimer mSavedFileTimer;
BOOL mShowKeepDiscard;
BOOL mCopyToInv;
-
+
+ // Save the image once it's loaded.
+ BOOL mPreviewToSave;
+
// This is stored off in a member variable, because the save-as
// button and drag and drop functionality need to know.
BOOL mIsCopyable;
@@ -94,6 +96,4 @@ private:
F32 mAspectRatio;
BOOL mUpdateDimensions;
};
-
-
#endif // LL_LLPREVIEWTEXTURE_H
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index dd07a3c015..4cb561381d 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -67,6 +67,7 @@
#include "llinventorymodel.h"
#include "llmenugl.h"
#include "llmutelist.h"
+#include "llsidepaneltaskinfo.h"
#include "llslurl.h"
#include "llstatusbar.h"
#include "llsurface.h"
@@ -4907,12 +4908,20 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud)
}
if (mSelectedObjects->getNumNodes())
{
- LLFloaterInspect* inspect_instance = LLFloaterReg::getTypedInstance<LLFloaterInspect>("inspect");
LLUUID inspect_item_id= LLUUID::null;
+#if 0
+ LLFloaterInspect* inspect_instance = LLFloaterReg::getTypedInstance<LLFloaterInspect>("inspect");
if(inspect_instance)
{
inspect_item_id = inspect_instance->getSelectedUUID();
}
+#endif
+ LLSidepanelTaskInfo *panel_task_info = LLSidepanelTaskInfo::getActivePanel();
+ if (panel_task_info)
+ {
+ inspect_item_id = panel_task_info->getSelectedUUID();
+ }
+
LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getFocusedObjectID();
for (S32 pass = 0; pass < 2; pass++)
{
@@ -5487,12 +5496,19 @@ void dialog_refresh_all()
}
LLFloaterProperties::dirtyAll();
-
+
+#if 0
LLFloaterInspect* inspect_instance = LLFloaterReg::getTypedInstance<LLFloaterInspect>("inspect");
if(inspect_instance)
{
inspect_instance->dirty();
}
+#endif
+ LLSidepanelTaskInfo *panel_task_info = LLSidepanelTaskInfo::getActivePanel();
+ if (panel_task_info)
+ {
+ panel_task_info->dirty();
+ }
}
S32 get_family_count(LLViewerObject *parent)
diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp
index 3cf17fb7f2..6aa5c53194 100644
--- a/indra/newview/llsidepanelinventory.cpp
+++ b/indra/newview/llsidepanelinventory.cpp
@@ -1,244 +1,274 @@
-/**
- * @file LLSidepanelInventory.cpp
- * @brief Side Bar "Inventory" panel
- *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-#include "llsidepanelinventory.h"
-
-#include "llagent.h"
-#include "llbutton.h"
-#include "llinventorybridge.h"
-#include "llinventorypanel.h"
-#include "llpanelmaininventory.h"
-#include "llsidepanelobjectinfo.h"
-#include "lltabcontainer.h"
-
-static const S32 LANDMARK_FOLDERS_MENU_WIDTH = 250;
-static const std::string AGENT_INFO_TYPE = "agent";
-static const std::string CREATE_LANDMARK_INFO_TYPE = "create_landmark";
-static const std::string LANDMARK_INFO_TYPE = "landmark";
-static const std::string REMOTE_PLACE_INFO_TYPE = "remote_place";
-static const std::string TELEPORT_HISTORY_INFO_TYPE = "teleport_history";
-
-static LLRegisterPanelClassWrapper<LLSidepanelInventory> t_inventory("sidepanel_inventory");
-
-LLSidepanelInventory::LLSidepanelInventory()
- : LLPanel(),
- mSidepanelObjectInfo(NULL)
-{
-
- //LLUICtrlFactory::getInstance()->buildPanel(this, "panel_inventory.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()
-}
-
-LLSidepanelInventory::~LLSidepanelInventory()
-{
-}
-
-BOOL LLSidepanelInventory::postBuild()
-{
- mInfoBtn = getChild<LLButton>("info_btn");
- mInfoBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onInfoButtonClicked, this));
-
- mShareBtn = getChild<LLButton>("share_btn");
- mShareBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShareButtonClicked, this));
-
- mShareBtn = getChild<LLButton>("share_btn");
- mShareBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShareButtonClicked, this));
-
- mWearBtn = getChild<LLButton>("wear_btn");
- mWearBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onWearButtonClicked, this));
-
- mPlayBtn = getChild<LLButton>("play_btn");
- mPlayBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onPlayButtonClicked, this));
-
- mTeleportBtn = getChild<LLButton>("teleport_btn");
- mTeleportBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onTeleportButtonClicked, this));
-
- mOverflowBtn = getChild<LLButton>("overflow_btn");
- mOverflowBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onOverflowButtonClicked, this));
-
- mTabContainer = getChild<LLTabContainer>("Inventory Tabs");
- mSidepanelObjectInfo = getChild<LLSidepanelObjectInfo>("sidepanel_object_info");
-
- mPanelMainInventory = getChild<LLPanelMainInventory>("panel_main_inventory");
- mPanelMainInventory->setSelectCallback(boost::bind(&LLSidepanelInventory::onSelectionChange, this, _1, _2));
-
- LLButton* back_btn = mSidepanelObjectInfo->getChild<LLButton>("back_btn");
- back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this));
-
- return TRUE;
-}
-
-void LLSidepanelInventory::onOpen(const LLSD& key)
-{
- if(key.size() == 0)
- return;
-
- mSidepanelObjectInfo->reset();
-
- if (key.has("id"))
- {
- mSidepanelObjectInfo->setItemID(key["id"].asUUID());
- }
-
- if (key.has("object"))
- {
- mSidepanelObjectInfo->setObjectID(key["object"].asUUID());
- }
-
- toggleObjectInfoPanel(TRUE);
-}
-
-void LLSidepanelInventory::onInfoButtonClicked()
-{
- LLInventoryItem *item = getSelectedItem();
- if (item)
- {
- mSidepanelObjectInfo->reset();
- mSidepanelObjectInfo->setItemID(item->getUUID());
- toggleObjectInfoPanel(TRUE);
- }
-}
-
-void LLSidepanelInventory::onShareButtonClicked()
-{
-}
-
-void LLSidepanelInventory::performActionOnSelection(const std::string &action)
-{
- LLInventoryPanel *panel = mPanelMainInventory->getActivePanel();
- LLFolderViewItem* current_item = panel->getRootFolder()->getCurSelectedItem();
- if (!current_item)
- {
- return;
- }
- current_item->getListener()->performAction(panel->getRootFolder(), panel->getModel(), action);
-}
-
-void LLSidepanelInventory::onWearButtonClicked()
-{
- performActionOnSelection("wear");
- performActionOnSelection("attach");
-}
-
-void LLSidepanelInventory::onPlayButtonClicked()
-{
- performActionOnSelection("activate");
-}
-
-void LLSidepanelInventory::onTeleportButtonClicked()
-{
- performActionOnSelection("teleport");
-}
-
-void LLSidepanelInventory::onOverflowButtonClicked()
-{
-}
-
-void LLSidepanelInventory::onBackButtonClicked()
-{
- toggleObjectInfoPanel(FALSE);
- updateVerbs();
-}
-
-void LLSidepanelInventory::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action)
-{
- updateVerbs();
-}
-
-void LLSidepanelInventory::toggleObjectInfoPanel(BOOL visible)
-{
- mSidepanelObjectInfo->setVisible(visible);
- mTabContainer->setVisible(!visible);
-
- if (visible)
- {
- mSidepanelObjectInfo->reset();
- mSidepanelObjectInfo->setEditMode(FALSE);
-
- LLRect rect = getRect();
- LLRect new_rect = LLRect(rect.mLeft, rect.mTop, rect.mRight, mTabContainer->getRect().mBottom);
- mSidepanelObjectInfo->reshape(new_rect.getWidth(),new_rect.getHeight());
- }
-}
-
-void LLSidepanelInventory::updateVerbs()
-{
- mInfoBtn->setEnabled(FALSE);
- mShareBtn->setEnabled(FALSE);
-
- mWearBtn->setVisible(FALSE);
- mWearBtn->setEnabled(FALSE);
- mPlayBtn->setVisible(FALSE);
- mPlayBtn->setEnabled(FALSE);
- mTeleportBtn->setVisible(FALSE);
- mTeleportBtn->setEnabled(FALSE);
-
- const LLInventoryItem *item = getSelectedItem();
- if (!item)
- return;
-
- mInfoBtn->setEnabled(TRUE);
- mShareBtn->setEnabled(TRUE);
-
- switch(item->getInventoryType())
- {
- case LLInventoryType::IT_WEARABLE:
- case LLInventoryType::IT_OBJECT:
- case LLInventoryType::IT_ATTACHMENT:
- mWearBtn->setVisible(TRUE);
- mWearBtn->setEnabled(TRUE);
- break;
- case LLInventoryType::IT_SOUND:
- case LLInventoryType::IT_GESTURE:
- case LLInventoryType::IT_ANIMATION:
- mPlayBtn->setVisible(TRUE);
- mPlayBtn->setEnabled(TRUE);
- break;
- case LLInventoryType::IT_LANDMARK:
- mTeleportBtn->setVisible(TRUE);
- mTeleportBtn->setEnabled(TRUE);
- break;
- default:
- break;
- }
-}
-
-LLInventoryItem *LLSidepanelInventory::getSelectedItem()
-{
- LLFolderViewItem* current_item = mPanelMainInventory->getActivePanel()->getRootFolder()->getCurSelectedItem();
- if (!current_item)
- {
- return NULL;
- }
- const LLUUID &item_id = current_item->getListener()->getUUID();
- LLInventoryItem *item = gInventory.getItem(item_id);
- return item;
-}
+/**
+ * @file LLSidepanelInventory.cpp
+ * @brief Side Bar "Inventory" panel
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llsidepanelinventory.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llinventorybridge.h"
+#include "llinventorypanel.h"
+#include "llpanelmaininventory.h"
+#include "llsidepaneliteminfo.h"
+#include "llsidepaneltaskinfo.h"
+#include "lltabcontainer.h"
+#include "llselectmgr.h"
+
+static LLRegisterPanelClassWrapper<LLSidepanelInventory> t_inventory("sidepanel_inventory");
+
+LLSidepanelInventory::LLSidepanelInventory()
+ : LLPanel(),
+ mItemPanel(NULL)
+{
+
+ //LLUICtrlFactory::getInstance()->buildPanel(this, "panel_inventory.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()
+}
+
+LLSidepanelInventory::~LLSidepanelInventory()
+{
+}
+
+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));
+
+ 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));
+
+ mOverflowBtn = mInventoryPanel->getChild<LLButton>("overflow_btn");
+ mOverflowBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onOverflowButtonClicked, this));
+
+ LLPanelMainInventory *panel_main_inventory = mInventoryPanel->getChild<LLPanelMainInventory>("panel_main_inventory");
+ panel_main_inventory->setSelectCallback(boost::bind(&LLSidepanelInventory::onSelectionChange, this, _1, _2));
+ }
+
+ // 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 = getChild<LLSidepanelTaskInfo>("sidepanel__task_panel");
+ if (mTaskPanel)
+ {
+ LLButton* back_btn = mTaskPanel->getChild<LLButton>("back_btn");
+ back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this));
+ }
+ }
+
+ return TRUE;
+}
+
+void LLSidepanelInventory::onOpen(const LLSD& key)
+{
+ 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()
+{
+}
+
+void LLSidepanelInventory::performActionOnSelection(const std::string &action)
+{
+ LLPanelMainInventory *panel_main_inventory = mInventoryPanel->getChild<LLPanelMainInventory>("panel_main_inventory");
+ LLFolderViewItem* current_item = panel_main_inventory->getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (!current_item)
+ {
+ return;
+ }
+ current_item->getListener()->performAction(panel_main_inventory->getActivePanel()->getRootFolder(), panel_main_inventory->getActivePanel()->getModel(), action);
+}
+
+void LLSidepanelInventory::onWearButtonClicked()
+{
+ performActionOnSelection("wear");
+ performActionOnSelection("attach");
+}
+
+void LLSidepanelInventory::onPlayButtonClicked()
+{
+ performActionOnSelection("activate");
+}
+
+void LLSidepanelInventory::onTeleportButtonClicked()
+{
+ performActionOnSelection("teleport");
+}
+
+void LLSidepanelInventory::onOverflowButtonClicked()
+{
+}
+
+void LLSidepanelInventory::onBackButtonClicked()
+{
+ showInventoryPanel();
+}
+
+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()
+{
+ mInfoBtn->setEnabled(FALSE);
+ mShareBtn->setEnabled(FALSE);
+
+ mWearBtn->setVisible(FALSE);
+ mWearBtn->setEnabled(FALSE);
+ mPlayBtn->setVisible(FALSE);
+ mPlayBtn->setEnabled(FALSE);
+ mTeleportBtn->setVisible(FALSE);
+ mTeleportBtn->setEnabled(FALSE);
+
+ const LLInventoryItem *item = getSelectedItem();
+ if (!item)
+ return;
+
+ mInfoBtn->setEnabled(TRUE);
+ mShareBtn->setEnabled(TRUE);
+
+ switch(item->getInventoryType())
+ {
+ case LLInventoryType::IT_WEARABLE:
+ case LLInventoryType::IT_OBJECT:
+ case LLInventoryType::IT_ATTACHMENT:
+ mWearBtn->setVisible(TRUE);
+ mWearBtn->setEnabled(TRUE);
+ break;
+ case LLInventoryType::IT_SOUND:
+ case LLInventoryType::IT_GESTURE:
+ case LLInventoryType::IT_ANIMATION:
+ mPlayBtn->setVisible(TRUE);
+ mPlayBtn->setEnabled(TRUE);
+ break;
+ case LLInventoryType::IT_LANDMARK:
+ mTeleportBtn->setVisible(TRUE);
+ mTeleportBtn->setEnabled(TRUE);
+ break;
+ default:
+ break;
+ }
+}
+
+LLInventoryItem *LLSidepanelInventory::getSelectedItem()
+{
+ LLPanelMainInventory *panel_main_inventory = mInventoryPanel->getChild<LLPanelMainInventory>("panel_main_inventory");
+ LLFolderViewItem* current_item = panel_main_inventory->getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (!current_item)
+ {
+ return NULL;
+ }
+ const LLUUID &item_id = current_item->getListener()->getUUID();
+ LLInventoryItem *item = gInventory.getItem(item_id);
+ return item;
+}
diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h
index 62eeecc5e2..681af7fafa 100644
--- a/indra/newview/llsidepanelinventory.h
+++ b/indra/newview/llsidepanelinventory.h
@@ -1,80 +1,90 @@
-/**
- * @file LLSidepanelInventory.h
- * @brief Side Bar "Inventory" panel
- *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLSIDEPANELINVENTORY_H
-#define LL_LLSIDEPANELINVENTORY_H
-
-#include "llpanel.h"
-
-class LLInventoryItem;
-class LLSidepanelObjectInfo;
-class LLTabContainer;
-class LLPanelMainInventory;
-class LLFolderViewItem;
-
-class LLSidepanelInventory : public LLPanel
-{
-public:
- LLSidepanelInventory();
- virtual ~LLSidepanelInventory();
-
- /*virtual*/ BOOL postBuild();
- /*virtual*/ void onOpen(const LLSD& key);
-
-protected:
- LLInventoryItem *getSelectedItem();
- void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action);
- void onTabSelected();
- void toggleObjectInfoPanel(BOOL visible);
- void updateVerbs();
- void performActionOnSelection(const std::string &action);
-
- LLTabContainer* mTabContainer;
- LLSidepanelObjectInfo* mSidepanelObjectInfo;
- LLPanelMainInventory* mPanelMainInventory;
-
- void onInfoButtonClicked();
- void onShareButtonClicked();
- void onWearButtonClicked();
- void onPlayButtonClicked();
- void onTeleportButtonClicked();
- void onOverflowButtonClicked();
- void onBackButtonClicked();
-
- LLButton* mInfoBtn;
- LLButton* mShareBtn;
- LLButton* mWearBtn;
- LLButton* mPlayBtn;
- LLButton* mTeleportBtn;
- LLButton* mOverflowBtn;
-};
-
-#endif //LL_LLSIDEPANELINVENTORY_H
+/**
+ * @file LLSidepanelInventory.h
+ * @brief Side Bar "Inventory" panel
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSIDEPANELINVENTORY_H
+#define LL_LLSIDEPANELINVENTORY_H
+
+#include "llpanel.h"
+
+class LLFolderViewItem;
+class LLInventoryItem;
+class LLPanelMainInventory;
+class LLSidepanelItemInfo;
+class LLSidepanelTaskInfo;
+
+class LLSidepanelInventory : public LLPanel
+{
+public:
+ LLSidepanelInventory();
+ virtual ~LLSidepanelInventory();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onOpen(const LLSD& key);
+
+protected:
+ // Tracks highlighted (selected) item in inventory panel.
+ LLInventoryItem *getSelectedItem();
+ void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action);
+ // "wear", "teleport", etc.
+ void performActionOnSelection(const std::string &action);
+
+ void showItemInfoPanel();
+ void showTaskInfoPanel();
+ void showInventoryPanel();
+ void updateVerbs();
+
+ //
+ // UI Elements
+ //
+private:
+ LLPanel* mInventoryPanel; // Main inventory view
+ LLSidepanelItemInfo* mItemPanel; // Individual item view
+ LLSidepanelTaskInfo* mTaskPanel; // Individual in-world object view
+
+protected:
+ void onInfoButtonClicked();
+ void onShareButtonClicked();
+ void onWearButtonClicked();
+ void onPlayButtonClicked();
+ void onTeleportButtonClicked();
+ void onOverflowButtonClicked();
+ void onBackButtonClicked();
+private:
+ LLButton* mInfoBtn;
+ LLButton* mShareBtn;
+ LLButton* mWearBtn;
+ LLButton* mPlayBtn;
+ LLButton* mTeleportBtn;
+ LLButton* mOverflowBtn;
+
+};
+
+#endif //LL_LLSIDEPANELINVENTORY_H
diff --git a/indra/newview/llsidepanelinventorysubpanel.cpp b/indra/newview/llsidepanelinventorysubpanel.cpp
new file mode 100644
index 0000000000..162198f1c5
--- /dev/null
+++ b/indra/newview/llsidepanelinventorysubpanel.cpp
@@ -0,0 +1,155 @@
+/**
+ * @file llsidepanelinventorysubpanel.cpp
+ * @brief A floater which shows an inventory item's properties.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llsidepanelinventorysubpanel.h"
+
+#include "roles_constants.h"
+
+#include "llagent.h"
+#include "llavataractions.h"
+#include "llbutton.h"
+#include "llfloaterreg.h"
+#include "llgroupactions.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llradiogroup.h"
+#include "llviewercontrol.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+
+
+///----------------------------------------------------------------------------
+/// Class LLSidepanelInventorySubpanel
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLSidepanelInventorySubpanel::LLSidepanelInventorySubpanel()
+ : LLPanel(),
+ mIsDirty(TRUE),
+ mIsEditing(FALSE),
+ mEditBtn(NULL),
+ mCancelBtn(NULL),
+ mSaveBtn(NULL)
+{
+}
+
+// Destroys the object
+LLSidepanelInventorySubpanel::~LLSidepanelInventorySubpanel()
+{
+}
+
+// virtual
+BOOL LLSidepanelInventorySubpanel::postBuild()
+{
+ mEditBtn = getChild<LLButton>("edit_btn");
+ mEditBtn->setClickedCallback(boost::bind(&LLSidepanelInventorySubpanel::onEditButtonClicked, this));
+
+ mSaveBtn = getChild<LLButton>("save_btn");
+ mSaveBtn->setClickedCallback(boost::bind(&LLSidepanelInventorySubpanel::onSaveButtonClicked, this));
+
+ mCancelBtn = getChild<LLButton>("cancel_btn");
+ mCancelBtn->setClickedCallback(boost::bind(&LLSidepanelInventorySubpanel::onCancelButtonClicked, this));
+ return TRUE;
+}
+
+void LLSidepanelInventorySubpanel::setVisible(BOOL visible)
+{
+ if (visible)
+ {
+ dirty();
+ }
+ LLPanel::setVisible(visible);
+}
+
+void LLSidepanelInventorySubpanel::setIsEditing(BOOL edit)
+{
+ mIsEditing = edit;
+ mIsDirty = TRUE;
+}
+
+BOOL LLSidepanelInventorySubpanel::getIsEditing() const
+{
+ return mIsEditing;
+}
+
+void LLSidepanelInventorySubpanel::reset()
+{
+ mIsDirty = TRUE;
+}
+
+void LLSidepanelInventorySubpanel::draw()
+{
+ if (mIsDirty)
+ {
+ mIsDirty = FALSE;
+ refresh();
+ updateVerbs();
+ }
+
+ LLPanel::draw();
+}
+
+void LLSidepanelInventorySubpanel::dirty()
+{
+ mIsDirty = TRUE;
+ setIsEditing(FALSE);
+}
+
+void LLSidepanelInventorySubpanel::updateVerbs()
+{
+ mEditBtn->setVisible(!mIsEditing);
+ mSaveBtn->setVisible(mIsEditing);
+ mCancelBtn->setVisible(mIsEditing);
+}
+
+void LLSidepanelInventorySubpanel::onEditButtonClicked()
+{
+ setIsEditing(TRUE);
+ refresh();
+ updateVerbs();
+}
+
+void LLSidepanelInventorySubpanel::onSaveButtonClicked()
+{
+ save();
+ setIsEditing(FALSE);
+ refresh();
+ updateVerbs();
+}
+
+void LLSidepanelInventorySubpanel::onCancelButtonClicked()
+{
+ setIsEditing(FALSE);
+ refresh();
+ updateVerbs();
+}
diff --git a/indra/newview/llsidepanelinventorysubpanel.h b/indra/newview/llsidepanelinventorysubpanel.h
new file mode 100644
index 0000000000..6503887cd1
--- /dev/null
+++ b/indra/newview/llsidepanelinventorysubpanel.h
@@ -0,0 +1,82 @@
+/**
+ * @file llsidepanelinventorysubpanel.h
+ * @brief A panel which shows an inventory item's properties.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSIDEPANELINVENTORYSUBPANEL_H
+#define LL_LLSIDEPANELINVENTORYSUBPANEL_H
+
+#include "llpanel.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLSidepanelInventorySubpanel
+// Base class for inventory sidepanel panels (e.g. item info, task info).
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLButton;
+class LLInventoryItem;
+
+class LLSidepanelInventorySubpanel : public LLPanel
+{
+public:
+ LLSidepanelInventorySubpanel();
+ virtual ~LLSidepanelInventorySubpanel();
+
+ /*virtual*/ void setVisible(BOOL visible);
+ virtual BOOL postBuild();
+ virtual void draw();
+ virtual void reset();
+
+ void dirty();
+ void setIsEditing(BOOL edit);
+protected:
+ virtual void refresh() = 0;
+ virtual void save() = 0;
+ virtual void updateVerbs();
+
+ BOOL getIsEditing() const;
+
+ //
+ // UI Elements
+ //
+protected:
+ void onEditButtonClicked();
+ void onSaveButtonClicked();
+ void onCancelButtonClicked();
+ LLButton* mEditBtn;
+ LLButton* mSaveBtn;
+ LLButton* mCancelBtn;
+
+private:
+ BOOL mIsDirty; // item properties need to be updated
+ BOOL mIsEditing; // if we're in edit mode
+};
+
+#endif // LL_LLSIDEPANELINVENTORYSUBPANEL_H
diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp
new file mode 100644
index 0000000000..9d2960fbed
--- /dev/null
+++ b/indra/newview/llsidepaneliteminfo.cpp
@@ -0,0 +1,882 @@
+/**
+ * @file llsidepaneliteminfo.cpp
+ * @brief A floater which shows an inventory item's properties.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llsidepaneliteminfo.h"
+
+#include "roles_constants.h"
+
+#include "llagent.h"
+#include "llavataractions.h"
+#include "llbutton.h"
+#include "llfloaterreg.h"
+#include "llgroupactions.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llradiogroup.h"
+#include "llviewercontrol.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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;
+};
+
+void LLItemPropertiesObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
+ {
+ mFloater->dirty();
+ }
+}
+
+
+
+///----------------------------------------------------------------------------
+/// Class LLSidepanelItemInfo
+///----------------------------------------------------------------------------
+
+static LLRegisterPanelClassWrapper<LLSidepanelItemInfo> t_item_info("sidepanel_item_info");
+
+// Default constructor
+LLSidepanelItemInfo::LLSidepanelItemInfo()
+ : mItemID(LLUUID::null)
+{
+ mPropertiesObserver = new LLItemPropertiesObserver(this);
+
+ //LLUICtrlFactory::getInstance()->buildFloater(this,"floater_inventory_item_properties.xml");
+}
+
+// Destroys the object
+LLSidepanelItemInfo::~LLSidepanelItemInfo()
+{
+ delete mPropertiesObserver;
+ mPropertiesObserver = NULL;
+}
+
+// virtual
+BOOL LLSidepanelItemInfo::postBuild()
+{
+ LLSidepanelInventorySubpanel::postBuild();
+
+ // build the UI
+ // item name & description
+ childSetPrevalidate("LabelItemName",&LLLineEditor::prevalidatePrintableNotPipe);
+ //getChild<LLUICtrl>("LabelItemName")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onCommitName,this));
+ childSetPrevalidate("LabelItemDesc",&LLLineEditor::prevalidatePrintableNotPipe);
+ //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));
+
+ refresh();
+ return TRUE;
+}
+
+void LLSidepanelItemInfo::setObjectID(const LLUUID& object_id)
+{
+ mObjectID = object_id;
+}
+
+void LLSidepanelItemInfo::setItemID(const LLUUID& item_id)
+{
+ mItemID = item_id;
+}
+
+void LLSidepanelItemInfo::reset()
+{
+ LLSidepanelInventorySubpanel::reset();
+
+ mObjectID = LLUUID::null;
+ mItemID = LLUUID::null;
+}
+
+void LLSidepanelItemInfo::refresh()
+{
+ LLInventoryItem* item = findItem();
+ if(item)
+ {
+ refreshFromItem(item);
+ updateVerbs();
+ }
+ else
+ {
+ if (getIsEditing())
+ {
+ setIsEditing(FALSE);
+ return;
+ }
+ }
+
+ if (!getIsEditing())
+ {
+ const std::string no_item_names[]={
+ "LabelItemName",
+ "LabelItemDesc",
+ "LabelCreatorName",
+ "LabelOwnerName",
+ "CheckOwnerModify",
+ "CheckOwnerCopy",
+ "CheckOwnerTransfer",
+ "CheckShareWithGroup",
+ "CheckEveryoneCopy",
+ "CheckNextOwnerModify",
+ "CheckNextOwnerCopy",
+ "CheckNextOwnerTransfer",
+ "CheckPurchase",
+ "RadioSaleType",
+ "Edit Cost"
+ };
+
+ for(size_t t=0; t<LL_ARRAY_SIZE(no_item_names); ++t)
+ {
+ childSetEnabled(no_item_names[t],false);
+ }
+
+ const std::string hide_names[]={
+ "BaseMaskDebug",
+ "OwnerMaskDebug",
+ "GroupMaskDebug",
+ "EveryoneMaskDebug",
+ "NextMaskDebug"
+ };
+ for(size_t t=0; t<LL_ARRAY_SIZE(hide_names); ++t)
+ {
+ childSetVisible(hide_names[t],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)
+ {
+ childSetEnabled(no_edit_mode_names[t],false);
+ }
+ }
+
+ updateVerbs();
+}
+
+void LLSidepanelItemInfo::refreshFromItem(LLInventoryItem* item)
+{
+ ////////////////////////
+ // PERMISSIONS LOOKUP //
+ ////////////////////////
+
+ // do not enable the UI for incomplete items.
+ LLViewerInventoryItem* i = (LLViewerInventoryItem*)item;
+ BOOL is_complete = i->isComplete();
+ 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;
+
+ childSetEnabled("LabelItemNameTitle",TRUE);
+ childSetEnabled("LabelItemName",is_modifiable && !is_calling_card); // for now, don't allow rename of calling cards
+ childSetText("LabelItemName",item->getName());
+ childSetEnabled("LabelItemDescTitle",TRUE);
+ childSetEnabled("LabelItemDesc",is_modifiable);
+ childSetVisible("IconLocked",!is_modifiable);
+ childSetText("LabelItemDesc",item->getDescription());
+
+ //////////////////
+ // CREATOR NAME //
+ //////////////////
+ if(!gCacheName) return;
+ if(!gAgent.getRegion()) return;
+
+ if (item->getCreatorUUID().notNull())
+ {
+ std::string name;
+ gCacheName->getFullName(item->getCreatorUUID(), name);
+ childSetEnabled("BtnCreator",TRUE);
+ childSetEnabled("LabelCreatorTitle",TRUE);
+ childSetEnabled("LabelCreatorName",TRUE);
+ childSetText("LabelCreatorName",name);
+ }
+ else
+ {
+ childSetEnabled("BtnCreator",FALSE);
+ childSetEnabled("LabelCreatorTitle",FALSE);
+ childSetEnabled("LabelCreatorName",FALSE);
+ childSetText("LabelCreatorName",getString("unknown"));
+ }
+
+ ////////////////
+ // OWNER NAME //
+ ////////////////
+ if(perm.isOwned())
+ {
+ std::string name;
+ if (perm.isGroupOwned())
+ {
+ gCacheName->getGroupName(perm.getGroup(), name);
+ }
+ else
+ {
+ gCacheName->getFullName(perm.getOwner(), name);
+ }
+ childSetEnabled("BtnOwner",TRUE);
+ childSetEnabled("LabelOwnerTitle",TRUE);
+ childSetEnabled("LabelOwnerName",TRUE);
+ childSetText("LabelOwnerName",name);
+ }
+ else
+ {
+ childSetEnabled("BtnOwner",FALSE);
+ childSetEnabled("LabelOwnerTitle",FALSE);
+ childSetEnabled("LabelOwnerName",FALSE);
+ childSetText("LabelOwnerName",getString("public"));
+ }
+
+ //////////////////
+ // ACQUIRE DATE //
+ //////////////////
+
+ time_t time_utc = item->getCreationDate();
+ if (0 == time_utc)
+ {
+ childSetText("LabelAcquiredDate",getString("unknown"));
+ }
+ else
+ {
+ std::string timeStr = getString("acquiredDate");
+ LLSD substitution;
+ substitution["datetime"] = (S32) time_utc;
+ LLStringUtil::format (timeStr, substitution);
+ childSetText ("LabelAcquiredDate", timeStr);
+ }
+
+ ///////////////////////
+ // OWNER PERMISSIONS //
+ ///////////////////////
+ if(can_agent_manipulate)
+ {
+ childSetText("OwnerLabel",getString("you_can"));
+ }
+ else
+ {
+ childSetText("OwnerLabel",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();
+
+ childSetEnabled("OwnerLabel",TRUE);
+ childSetEnabled("CheckOwnerModify",FALSE);
+ childSetValue("CheckOwnerModify",LLSD((BOOL)(owner_mask & PERM_MODIFY)));
+ childSetEnabled("CheckOwnerCopy",FALSE);
+ childSetValue("CheckOwnerCopy",LLSD((BOOL)(owner_mask & PERM_COPY)));
+ childSetEnabled("CheckOwnerTransfer",FALSE);
+ childSetValue("CheckOwnerTransfer",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 & LLInventoryItem::II_FLAGS_OBJECT_SLAM_PERM;
+ overwrite_everyone = flags & LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
+ overwrite_group = flags & LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
+ }
+
+ std::string perm_string;
+
+ perm_string = "B: ";
+ perm_string += mask_to_string(base_mask);
+ childSetText("BaseMaskDebug",perm_string);
+ childSetVisible("BaseMaskDebug",TRUE);
+
+ perm_string = "O: ";
+ perm_string += mask_to_string(owner_mask);
+ childSetText("OwnerMaskDebug",perm_string);
+ childSetVisible("OwnerMaskDebug",TRUE);
+
+ perm_string = "G";
+ perm_string += overwrite_group ? "*: " : ": ";
+ perm_string += mask_to_string(group_mask);
+ childSetText("GroupMaskDebug",perm_string);
+ childSetVisible("GroupMaskDebug",TRUE);
+
+ perm_string = "E";
+ perm_string += overwrite_everyone ? "*: " : ": ";
+ perm_string += mask_to_string(everyone_mask);
+ childSetText("EveryoneMaskDebug",perm_string);
+ childSetVisible("EveryoneMaskDebug",TRUE);
+
+ perm_string = "N";
+ perm_string += slam_perm ? "*: " : ": ";
+ perm_string += mask_to_string(next_owner_mask);
+ childSetText("NextMaskDebug",perm_string);
+ childSetVisible("NextMaskDebug",TRUE);
+ }
+ else
+ {
+ childSetVisible("BaseMaskDebug",FALSE);
+ childSetVisible("OwnerMaskDebug",FALSE);
+ childSetVisible("GroupMaskDebug",FALSE);
+ childSetVisible("EveryoneMaskDebug",FALSE);
+ childSetVisible("NextMaskDebug",FALSE);
+ }
+
+ /////////////
+ // SHARING //
+ /////////////
+
+ // Check for ability to change values.
+ if (is_link || cannot_restrict_permissions)
+ {
+ childSetEnabled("CheckShareWithGroup",FALSE);
+ childSetEnabled("CheckEveryoneCopy",FALSE);
+ }
+ else if (is_obj_modify && can_agent_manipulate)
+ {
+ childSetEnabled("CheckShareWithGroup",TRUE);
+ childSetEnabled("CheckEveryoneCopy",(owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER));
+ }
+ else
+ {
+ childSetEnabled("CheckShareWithGroup",FALSE);
+ childSetEnabled("CheckEveryoneCopy",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)
+ {
+ childSetValue("CheckShareWithGroup",LLSD((BOOL)TRUE));
+
+ LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(FALSE);
+ }
+ }
+ else if (!is_group_copy && !is_group_modify && !is_group_move)
+ {
+ childSetValue("CheckShareWithGroup",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);
+ }
+ }
+
+ childSetValue("CheckEveryoneCopy",LLSD((BOOL)(everyone_mask & PERM_COPY)));
+
+ ///////////////
+ // SALE INFO //
+ ///////////////
+
+ const LLSaleInfo& sale_info = item->getSaleInfo();
+ BOOL is_for_sale = sale_info.isForSale();
+ // Check for ability to change values.
+ if (is_obj_modify && can_agent_sell
+ && gAgent.allowOperation(PERM_TRANSFER, perm, GP_OBJECT_MANIPULATE))
+ {
+ childSetEnabled("SaleLabel",is_complete);
+ childSetEnabled("CheckPurchase",is_complete);
+
+ childSetEnabled("NextOwnerLabel",TRUE);
+ childSetEnabled("CheckNextOwnerModify",(base_mask & PERM_MODIFY) && !cannot_restrict_permissions);
+ childSetEnabled("CheckNextOwnerCopy",(base_mask & PERM_COPY) && !cannot_restrict_permissions);
+ childSetEnabled("CheckNextOwnerTransfer",(next_owner_mask & PERM_COPY) && !cannot_restrict_permissions);
+
+ childSetEnabled("RadioSaleType",is_complete && is_for_sale);
+ childSetEnabled("TextPrice",is_complete && is_for_sale);
+ childSetEnabled("Edit Cost",is_complete && is_for_sale);
+ }
+ else
+ {
+ childSetEnabled("SaleLabel",FALSE);
+ childSetEnabled("CheckPurchase",FALSE);
+
+ childSetEnabled("NextOwnerLabel",FALSE);
+ childSetEnabled("CheckNextOwnerModify",FALSE);
+ childSetEnabled("CheckNextOwnerCopy",FALSE);
+ childSetEnabled("CheckNextOwnerTransfer",FALSE);
+
+ childSetEnabled("RadioSaleType",FALSE);
+ childSetEnabled("TextPrice",FALSE);
+ childSetEnabled("Edit Cost",FALSE);
+ }
+
+ // Set values.
+ childSetValue("CheckPurchase", is_for_sale);
+ childSetEnabled("combobox sale copy", is_for_sale);
+ childSetEnabled("Edit Cost", is_for_sale);
+ childSetValue("CheckNextOwnerModify",LLSD(BOOL(next_owner_mask & PERM_MODIFY)));
+ childSetValue("CheckNextOwnerCopy",LLSD(BOOL(next_owner_mask & PERM_COPY)));
+ childSetValue("CheckNextOwnerTransfer",LLSD(BOOL(next_owner_mask & PERM_TRANSFER)));
+
+ LLRadioGroup* radioSaleType = getChild<LLRadioGroup>("RadioSaleType");
+ if (is_for_sale)
+ {
+ radioSaleType->setSelectedIndex((S32)sale_info.getSaleType() - 1);
+ S32 numerical_price;
+ numerical_price = sale_info.getSalePrice();
+ childSetText("Edit Cost",llformat("%d",numerical_price));
+ }
+ else
+ {
+ radioSaleType->setSelectedIndex(-1);
+ childSetText("Edit Cost",llformat("%d",0));
+ }
+}
+
+void LLSidepanelItemInfo::onClickCreator()
+{
+ LLInventoryItem* item = findItem();
+ if(!item) return;
+ if(!item->getCreatorUUID().isNull())
+ {
+ LLAvatarActions::showProfile(item->getCreatorUUID());
+ }
+}
+
+// static
+void LLSidepanelItemInfo::onClickOwner()
+{
+ LLInventoryItem* item = findItem();
+ if(!item) return;
+ if(item->getPermissions().isGroupOwned())
+ {
+ LLGroupActions::show(item->getPermissions().getGroup());
+ }
+ else
+ {
+ LLAvatarActions::showProfile(item->getPermissions().getOwner());
+ }
+}
+
+// static
+void LLSidepanelItemInfo::onCommitName()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitName()" << llendl;
+ 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 LLSidepanelItemInfo::onCommitDescription()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitDescription()" << llendl;
+ 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 LLSidepanelItemInfo::onCommitPermissions()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitPermissions()" << llendl;
+ 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->isComplete())
+ {
+ 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 |= LLInventoryItem::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 |= LLInventoryItem::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 |= LLInventoryItem::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 LLSidepanelItemInfo::onCommitSaleInfo()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitSaleInfo()" << llendl;
+ updateSaleInfo();
+}
+
+// static
+void LLSidepanelItemInfo::onCommitSaleType()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitSaleType()" << llendl;
+ updateSaleInfo();
+}
+
+void LLSidepanelItemInfo::updateSaleInfo()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
+ if(!item) return;
+ LLSaleInfo sale_info(item->getSaleInfo());
+ if(!gAgent.allowOperation(PERM_TRANSFER, item->getPermissions(), GP_OBJECT_SET_SALE))
+ {
+ childSetValue("CheckPurchase",LLSD((BOOL)FALSE));
+ }
+
+ if((BOOL)childGetValue("CheckPurchase"))
+ {
+ // turn on sale info
+ LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_COPY;
+
+ LLRadioGroup* RadioSaleType = getChild<LLRadioGroup>("RadioSaleType");
+ if(RadioSaleType)
+ {
+ switch (RadioSaleType->getSelectedIndex())
+ {
+ case 0:
+ sale_type = LLSaleInfo::FS_ORIGINAL;
+ break;
+ case 1:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ case 2:
+ sale_type = LLSaleInfo::FS_CONTENTS;
+ break;
+ default:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ }
+ }
+
+ 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->isComplete())
+ {
+ 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 |= LLInventoryItem::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* LLSidepanelItemInfo::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;
+}
+
+// virtual
+void LLSidepanelItemInfo::updateVerbs()
+{
+ LLSidepanelInventorySubpanel::updateVerbs();
+
+ const LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
+ if (item)
+ {
+ const LLPermissions& perm = item->getPermissions();
+ BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, perm,
+ GP_OBJECT_MANIPULATE);
+ mEditBtn->setEnabled(is_modifiable);
+ }
+}
+
+// virtual
+void LLSidepanelItemInfo::save()
+{
+ onCommitName();
+ onCommitDescription();
+ onCommitPermissions();
+ onCommitSaleInfo();
+ onCommitSaleType();
+}
diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h
new file mode 100644
index 0000000000..b348b5cceb
--- /dev/null
+++ b/indra/newview/llsidepaneliteminfo.h
@@ -0,0 +1,91 @@
+/**
+ * @file llsidepaneliteminfo.h
+ * @brief A panel which shows an inventory item's properties.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSIDEPANELITEMINFO_H
+#define LL_LLSIDEPANELITEMINFO_H
+
+#include "llsidepanelinventorysubpanel.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLSidepanelItemInfo
+// Object properties for inventory side panel.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLButton;
+class LLInventoryItem;
+class LLItemPropertiesObserver;
+class LLViewerObject;
+class LLPermissions;
+
+class LLSidepanelItemInfo : public LLSidepanelInventorySubpanel
+{
+public:
+ LLSidepanelItemInfo();
+ virtual ~LLSidepanelItemInfo();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void reset();
+
+ void setObjectID(const LLUUID& object_id);
+ void setItemID(const LLUUID& item_id);
+ void setEditMode(BOOL edit);
+
+protected:
+ /*virtual*/ void refresh();
+ /*virtual*/ void save();
+ /*virtual*/ void updateVerbs();
+
+ LLInventoryItem* findItem() const;
+ LLViewerObject* findObject() const;
+
+ void refreshFromItem(LLInventoryItem* item);
+
+private:
+ 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
+
+ //
+ // UI Elements
+ //
+protected:
+ void onClickCreator();
+ void onClickOwner();
+ void onCommitName();
+ void onCommitDescription();
+ void onCommitPermissions();
+ void onCommitSaleInfo();
+ void onCommitSaleType();
+ void updateSaleInfo();
+};
+
+#endif // LL_LLSIDEPANELITEMINFO_H
diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp
new file mode 100644
index 0000000000..01c832d7d5
--- /dev/null
+++ b/indra/newview/llsidepaneltaskinfo.cpp
@@ -0,0 +1,1124 @@
+/**
+ * @file llsidepaneltaskinfo.cpp
+ * @brief LLSidepanelTaskInfo class implementation
+ * This class represents the panel in the build view for
+ * viewing/editing object names, owners, permissions, etc.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsidepaneltaskinfo.h"
+
+#include "lluuid.h"
+#include "llpermissions.h"
+#include "llcategory.h"
+#include "llclickaction.h"
+#include "llfocusmgr.h"
+#include "llstring.h"
+
+#include "llviewerwindow.h"
+#include "llresmgr.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llviewerobject.h"
+#include "llselectmgr.h"
+#include "llagent.h"
+#include "llstatusbar.h" // for getBalance()
+#include "lllineeditor.h"
+#include "llcombobox.h"
+#include "lluiconstants.h"
+#include "lldbstrings.h"
+#include "llfloatergroups.h"
+#include "llfloaterreg.h"
+#include "llavataractions.h"
+#include "llnamebox.h"
+#include "llviewercontrol.h"
+#include "llviewermenu.h"
+#include "lluictrlfactory.h"
+#include "llspinctrl.h"
+#include "roles_constants.h"
+#include "llgroupactions.h"
+
+///----------------------------------------------------------------------------
+/// Class llsidepaneltaskinfo
+///----------------------------------------------------------------------------
+
+LLSidepanelTaskInfo* LLSidepanelTaskInfo::sActivePanel = NULL;
+
+static LLRegisterPanelClassWrapper<LLSidepanelTaskInfo> t_task_info("sidepanel_task_info");
+
+// Default constructor
+LLSidepanelTaskInfo::LLSidepanelTaskInfo()
+{
+ setMouseOpaque(FALSE);
+}
+
+
+LLSidepanelTaskInfo::~LLSidepanelTaskInfo()
+{
+ if (sActivePanel == this)
+ sActivePanel = NULL;
+}
+
+// virtual
+BOOL LLSidepanelTaskInfo::postBuild()
+{
+ LLSidepanelInventorySubpanel::postBuild();
+
+ mOpenBtn = getChild<LLButton>("open_btn");
+ mOpenBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onOpenButtonClicked, this));
+ mPayBtn = getChild<LLButton>("pay_btn");
+ mPayBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onPayButtonClicked, this));
+ mBuyBtn = getChild<LLButton>("buy_btn");
+ mBuyBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onBuyButtonClicked, this));
+
+ childSetPrevalidate("Object Name",LLLineEditor::prevalidatePrintableNotPipe);
+ childSetPrevalidate("Object Description",LLLineEditor::prevalidatePrintableNotPipe);
+
+// getChild<LLUICtrl>("button set group")->setCommitCallback(boost::bind(&LLSidepanelTaskInfo::onClickGroup,this));
+// childSetAction("button deed",LLSidepanelTaskInfo::onClickDeedToGroup,this);
+
+ mLabelGroupName = getChild<LLNameBox>("Group Name Proxy");
+
+ return TRUE;
+}
+
+// virtual
+void LLSidepanelTaskInfo::setVisible(BOOL visible)
+{
+ LLPanel::setVisible(visible);
+ if (visible)
+ {
+ sActivePanel = this;
+ }
+ else
+ {
+ sActivePanel = NULL;
+ }
+}
+
+void LLSidepanelTaskInfo::refresh()
+{
+ LLButton* BtnDeedToGroup = getChild<LLButton>("button deed");
+ if(BtnDeedToGroup)
+ {
+ std::string deedText;
+ if (gWarningSettings.getBOOL("DeedObject"))
+ {
+ deedText = getString("text deed continued");
+ }
+ else
+ {
+ deedText = getString("text deed");
+ }
+ BtnDeedToGroup->setLabelSelected(deedText);
+ BtnDeedToGroup->setLabelUnselected(deedText);
+ }
+ BOOL root_selected = TRUE;
+ LLSelectNode* nodep = mObjectSelection->getFirstRootNode();
+ S32 object_count = mObjectSelection->getRootObjectCount();
+ if(!nodep || 0 == object_count)
+ {
+ nodep = mObjectSelection->getFirstNode();
+ object_count = mObjectSelection->getObjectCount();
+ root_selected = FALSE;
+ }
+
+ //BOOL attachment_selected = mObjectSelection->isAttachment();
+ //attachment_selected = false;
+ LLViewerObject* objectp = NULL;
+ if(nodep) objectp = nodep->getObject();
+ if(!nodep || !objectp)// || attachment_selected)
+ {
+ // ...nothing selected
+ childSetEnabled("perm_modify",false);
+ childSetText("perm_modify",LLStringUtil::null);
+
+ childSetEnabled("Creator:",false);
+ childSetText("Creator Name",LLStringUtil::null);
+ childSetEnabled("Creator Name",false);
+
+ childSetEnabled("Owner:",false);
+ childSetText("Owner Name",LLStringUtil::null);
+ childSetEnabled("Owner Name",false);
+
+ childSetEnabled("Group:",false);
+ childSetText("Group Name",LLStringUtil::null);
+ childSetEnabled("Group Name",false);
+ childSetEnabled("button set group",false);
+
+ childSetText("Object Name",LLStringUtil::null);
+ childSetEnabled("Object Name",false);
+ childSetEnabled("Name:",false);
+ childSetText("Group Name",LLStringUtil::null);
+ childSetEnabled("Group Name",false);
+ childSetEnabled("Description:",false);
+ childSetText("Object Description",LLStringUtil::null);
+ childSetEnabled("Object Description",false);
+
+ childSetEnabled("Permissions:",false);
+
+ childSetValue("checkbox share with group",FALSE);
+ childSetEnabled("checkbox share with group",false);
+ childSetEnabled("button deed",false);
+
+ childSetValue("checkbox allow everyone move",FALSE);
+ childSetEnabled("checkbox allow everyone move",false);
+ childSetValue("checkbox allow everyone copy",FALSE);
+ childSetEnabled("checkbox allow everyone copy",false);
+
+ //Next owner can:
+ childSetEnabled("Next owner can:",false);
+ childSetValue("checkbox next owner can modify",FALSE);
+ childSetEnabled("checkbox next owner can modify",false);
+ childSetValue("checkbox next owner can copy",FALSE);
+ childSetEnabled("checkbox next owner can copy",false);
+ childSetValue("checkbox next owner can transfer",FALSE);
+ childSetEnabled("checkbox next owner can transfer",false);
+
+ //checkbox for sale
+ childSetValue("checkbox for sale",FALSE);
+ childSetEnabled("checkbox for sale",false);
+
+ //checkbox include in search
+ childSetValue("search_check", FALSE);
+ childSetEnabled("search_check", false);
+
+ LLComboBox* combo_sale_type = getChild<LLComboBox>("sale type");
+ combo_sale_type->setValue(LLSaleInfo::FS_COPY);
+ combo_sale_type->setEnabled(FALSE);
+
+ childSetEnabled("Cost",false);
+ childSetText("Cost",getString("Cost Default"));
+ childSetText("Edit Cost",LLStringUtil::null);
+ childSetEnabled("Edit Cost",false);
+
+ childSetEnabled("label click action",false);
+ LLComboBox* ComboClickAction = getChild<LLComboBox>("clickaction");
+ if(ComboClickAction)
+ {
+ ComboClickAction->setEnabled(FALSE);
+ ComboClickAction->clear();
+ }
+ childSetVisible("B:",false);
+ childSetVisible("O:",false);
+ childSetVisible("G:",false);
+ childSetVisible("E:",false);
+ childSetVisible("N:",false);
+ childSetVisible("F:",false);
+
+ return;
+ }
+
+ // figure out a few variables
+ BOOL is_one_object = (object_count == 1);
+
+ // BUG: fails if a root and non-root are both single-selected.
+ BOOL is_perm_modify = (mObjectSelection->getFirstRootNode()
+ && LLSelectMgr::getInstance()->selectGetRootsModify())
+ || LLSelectMgr::getInstance()->selectGetModify();
+ const LLFocusableElement* keyboard_focus_view = gFocusMgr.getKeyboardFocus();
+ S32 string_index = 0;
+ std::string MODIFY_INFO_STRINGS[] =
+ {
+ getString("text modify info 1"),
+ getString("text modify info 2"),
+ getString("text modify info 3"),
+ getString("text modify info 4")
+ };
+ if(!is_perm_modify)
+ {
+ string_index += 2;
+ }
+ if(!is_one_object)
+ {
+ ++string_index;
+ }
+ childSetEnabled("perm_modify",true);
+ childSetText("perm_modify",MODIFY_INFO_STRINGS[string_index]);
+
+ childSetEnabled("Permissions:",true);
+
+ // Update creator text field
+ childSetEnabled("Creator:",true);
+ BOOL creators_identical;
+ std::string creator_name;
+ creators_identical = LLSelectMgr::getInstance()->selectGetCreator(mCreatorID,
+ creator_name);
+
+ childSetText("Creator Name",creator_name);
+ childSetEnabled("Creator Name",TRUE);
+
+ // Update owner text field
+ childSetEnabled("Owner:",true);
+
+ BOOL owners_identical;
+ std::string owner_name;
+ owners_identical = LLSelectMgr::getInstance()->selectGetOwner(mOwnerID, owner_name);
+
+// llinfos << "owners_identical " << (owners_identical ? "TRUE": "FALSE") << llendl;
+
+ if (mOwnerID.isNull())
+ {
+ if(LLSelectMgr::getInstance()->selectIsGroupOwned())
+ {
+ // Group owned already displayed by selectGetOwner
+ }
+ else
+ {
+ // Display last owner if public
+ std::string last_owner_name;
+ LLSelectMgr::getInstance()->selectGetLastOwner(mLastOwnerID, last_owner_name);
+
+ // It should never happen that the last owner is null and the owner
+ // is null, but it seems to be a bug in the simulator right now. JC
+ if (!mLastOwnerID.isNull() && !last_owner_name.empty())
+ {
+ owner_name.append(", last ");
+ owner_name.append( last_owner_name );
+ }
+ }
+ }
+
+ childSetText("Owner Name",owner_name);
+ childSetEnabled("Owner Name",TRUE);
+
+ // update group text field
+ childSetEnabled("Group:",true);
+ childSetText("Group Name",LLStringUtil::null);
+ LLUUID group_id;
+ BOOL groups_identical = LLSelectMgr::getInstance()->selectGetGroup(group_id);
+ if (groups_identical)
+ {
+ if(mLabelGroupName)
+ {
+ mLabelGroupName->setNameID(group_id, TRUE);
+ mLabelGroupName->setEnabled(TRUE);
+ }
+ }
+ else
+ {
+ if(mLabelGroupName)
+ {
+ mLabelGroupName->setNameID(LLUUID::null, TRUE);
+ mLabelGroupName->refresh(LLUUID::null, LLStringUtil::null, LLStringUtil::null, TRUE);
+ mLabelGroupName->setEnabled(FALSE);
+ }
+ }
+
+ childSetEnabled("button set group",owners_identical && (mOwnerID == gAgent.getID()));
+
+ // figure out the contents of the name, description, & category
+ BOOL edit_name_desc = FALSE;
+ if(is_one_object && objectp->permModify())
+ {
+ edit_name_desc = TRUE;
+ }
+
+ childSetEnabled("Name:",true);
+ LLLineEditor* LineEditorObjectName = getChild<LLLineEditor>("Object Name");
+ childSetEnabled("Description:",true);
+ LLLineEditor* LineEditorObjectDesc = getChild<LLLineEditor>("Object Description");
+
+ if(is_one_object)
+ {
+ if(keyboard_focus_view != LineEditorObjectName)
+ {
+ childSetText("Object Name",nodep->mName);
+ }
+
+ if(LineEditorObjectDesc)
+ {
+ if(keyboard_focus_view != LineEditorObjectDesc)
+ {
+ LineEditorObjectDesc->setText(nodep->mDescription);
+ }
+ }
+ }
+ else
+ {
+ childSetText("Object Name",LLStringUtil::null);
+ LineEditorObjectDesc->setText(LLStringUtil::null);
+ }
+
+ if(edit_name_desc)
+ {
+ childSetEnabled("Object Name",true);
+ childSetEnabled("Object Description",true);
+ }
+ else
+ {
+ childSetEnabled("Object Name",false);
+ childSetEnabled("Object Description",false);
+ }
+
+ S32 total_sale_price = 0;
+ S32 individual_sale_price = 0;
+ BOOL is_for_sale_mixed = FALSE;
+ BOOL is_sale_price_mixed = FALSE;
+ U32 num_for_sale = FALSE;
+ LLSelectMgr::getInstance()->selectGetAggregateSaleInfo(num_for_sale,
+ is_for_sale_mixed,
+ is_sale_price_mixed,
+ total_sale_price,
+ individual_sale_price);
+
+ const BOOL self_owned = (gAgent.getID() == mOwnerID);
+ const BOOL group_owned = LLSelectMgr::getInstance()->selectIsGroupOwned() ;
+ const BOOL public_owned = (mOwnerID.isNull() && !LLSelectMgr::getInstance()->selectIsGroupOwned());
+ const BOOL can_transfer = LLSelectMgr::getInstance()->selectGetRootsTransfer();
+ const BOOL can_copy = LLSelectMgr::getInstance()->selectGetRootsCopy();
+
+ if(!owners_identical)
+ {
+ childSetEnabled("Cost",false);
+ childSetText("Edit Cost",LLStringUtil::null);
+ childSetEnabled("Edit Cost",false);
+ }
+ // You own these objects.
+ else if(self_owned || (group_owned && gAgent.hasPowerInGroup(group_id,GP_OBJECT_SET_SALE)))
+ {
+ // If there are multiple items for sale then set text to PRICE PER UNIT.
+ if (num_for_sale > 1)
+ {
+ childSetText("Cost",getString("Cost Per Unit"));
+ }
+ else
+ {
+ childSetText("Cost",getString("Cost Default"));
+ }
+
+ LLSpinCtrl *edit_price = getChild<LLSpinCtrl>("Edit Cost");
+ if(!edit_price->hasFocus())
+ {
+ // If the sale price is mixed then set the cost to MIXED, otherwise
+ // set to the actual cost.
+ if (num_for_sale > 0 && is_for_sale_mixed)
+ {
+ edit_price->setTentative(TRUE);
+ }
+ else if (num_for_sale > 0 && is_sale_price_mixed)
+ {
+ edit_price->setTentative(TRUE);
+ }
+ else
+ {
+ edit_price->setValue(individual_sale_price);
+ }
+ }
+ // The edit fields are only enabled if you can sell this object
+ // and the sale price is not mixed.
+ bool enable_edit = (num_for_sale && can_transfer) ? !is_for_sale_mixed : false;
+ childSetEnabled("Cost",enable_edit);
+ childSetEnabled("Edit Cost",enable_edit);
+ }
+ // Someone, not you, owns these objects.
+ else if(!public_owned)
+ {
+ childSetEnabled("Cost",false);
+ childSetEnabled("Edit Cost",false);
+
+ // Don't show a price if none of the items are for sale.
+ if (num_for_sale)
+ childSetText("Edit Cost",llformat("%d",total_sale_price));
+ else
+ childSetText("Edit Cost",LLStringUtil::null);
+
+ // If multiple items are for sale, set text to TOTAL PRICE.
+ if (num_for_sale > 1)
+ childSetText("Cost",getString("Cost Total"));
+ else
+ childSetText("Cost",getString("Cost Default"));
+ }
+ // This is a public object.
+ else
+ {
+ childSetEnabled("Cost",false);
+ childSetText("Cost",getString("Cost Default"));
+
+ childSetText("Edit Cost",LLStringUtil::null);
+ childSetEnabled("Edit Cost",false);
+ }
+
+ // Enable and disable the permissions checkboxes
+ // based on who owns the object.
+ // TODO: Creator permissions
+
+ BOOL valid_base_perms = FALSE;
+ BOOL valid_owner_perms = FALSE;
+ BOOL valid_group_perms = FALSE;
+ BOOL valid_everyone_perms = FALSE;
+ BOOL valid_next_perms = FALSE;
+
+ U32 base_mask_on;
+ U32 base_mask_off;
+ U32 owner_mask_on;
+ U32 owner_mask_off;
+ U32 group_mask_on;
+ U32 group_mask_off;
+ U32 everyone_mask_on;
+ U32 everyone_mask_off;
+ U32 next_owner_mask_on = 0;
+ U32 next_owner_mask_off = 0;
+
+ valid_base_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_BASE,
+ &base_mask_on,
+ &base_mask_off);
+
+ valid_owner_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_OWNER,
+ &owner_mask_on,
+ &owner_mask_off);
+
+ valid_group_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_GROUP,
+ &group_mask_on,
+ &group_mask_off);
+
+ valid_everyone_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_EVERYONE,
+ &everyone_mask_on,
+ &everyone_mask_off);
+
+ valid_next_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_NEXT_OWNER,
+ &next_owner_mask_on,
+ &next_owner_mask_off);
+
+
+ if( gSavedSettings.getBOOL("DebugPermissions") )
+ {
+ std::string perm_string;
+ if (valid_base_perms)
+ {
+ perm_string = "B: ";
+ perm_string += mask_to_string(base_mask_on);
+ childSetText("B:",perm_string);
+ childSetVisible("B:",true);
+
+ perm_string = "O: ";
+ perm_string += mask_to_string(owner_mask_on);
+ childSetText("O:",perm_string);
+ childSetVisible("O:",true);
+
+ perm_string = "G: ";
+ perm_string += mask_to_string(group_mask_on);
+ childSetText("G:",perm_string);
+ childSetVisible("G:",true);
+
+ perm_string = "E: ";
+ perm_string += mask_to_string(everyone_mask_on);
+ childSetText("E:",perm_string);
+ childSetVisible("E:",true);
+
+ perm_string = "N: ";
+ perm_string += mask_to_string(next_owner_mask_on);
+ childSetText("N:",perm_string);
+ childSetVisible("N:",true);
+ }
+ perm_string = "F: ";
+ U32 flag_mask = 0x0;
+ if (objectp->permMove())
+ flag_mask |= PERM_MOVE;
+ if (objectp->permModify())
+ flag_mask |= PERM_MODIFY;
+ if (objectp->permCopy())
+ flag_mask |= PERM_COPY;
+ if (objectp->permTransfer())
+ flag_mask |= PERM_TRANSFER;
+ perm_string += mask_to_string(flag_mask);
+ childSetText("F:",perm_string);
+ childSetVisible("F:",true);
+ }
+ else
+ {
+ childSetVisible("B:",false);
+ childSetVisible("O:",false);
+ childSetVisible("G:",false);
+ childSetVisible("E:",false);
+ childSetVisible("N:",false);
+ childSetVisible("F:",false);
+ }
+
+ bool has_change_perm_ability = false;
+ bool has_change_sale_ability = false;
+
+ if(valid_base_perms
+ && (self_owned
+ || (group_owned && gAgent.hasPowerInGroup(group_id, GP_OBJECT_MANIPULATE))))
+ {
+ has_change_perm_ability = true;
+ }
+ if(valid_base_perms
+ && (self_owned
+ || (group_owned && gAgent.hasPowerInGroup(group_id, GP_OBJECT_SET_SALE))))
+ {
+ has_change_sale_ability = true;
+ }
+
+ if (!has_change_perm_ability && !has_change_sale_ability && !root_selected)
+ {
+ // ...must select root to choose permissions
+ childSetValue("perm_modify", getString("text modify warning"));
+ }
+
+ if (has_change_perm_ability)
+ {
+ childSetEnabled("checkbox share with group",true);
+ childSetEnabled("checkbox allow everyone move",owner_mask_on & PERM_MOVE);
+ childSetEnabled("checkbox allow everyone copy",owner_mask_on & PERM_COPY && owner_mask_on & PERM_TRANSFER);
+ }
+ else
+ {
+ childSetEnabled("checkbox share with group", FALSE);
+ childSetEnabled("checkbox allow everyone move", FALSE);
+ childSetEnabled("checkbox allow everyone copy", FALSE);
+ }
+
+ if (has_change_sale_ability && (owner_mask_on & PERM_TRANSFER))
+ {
+ childSetEnabled("checkbox for sale", can_transfer || (!can_transfer && num_for_sale));
+ // Set the checkbox to tentative if the prices of each object selected
+ // are not the same.
+ childSetTentative("checkbox for sale", is_for_sale_mixed);
+ childSetEnabled("sale type",num_for_sale && can_transfer && !is_sale_price_mixed);
+
+ childSetEnabled("Next owner can:", TRUE);
+ childSetEnabled("checkbox next owner can modify",base_mask_on & PERM_MODIFY);
+ childSetEnabled("checkbox next owner can copy",base_mask_on & PERM_COPY);
+ childSetEnabled("checkbox next owner can transfer",next_owner_mask_on & PERM_COPY);
+ }
+ else
+ {
+ childSetEnabled("checkbox for sale",FALSE);
+ childSetEnabled("sale type",FALSE);
+
+ childSetEnabled("Next owner can:",FALSE);
+ childSetEnabled("checkbox next owner can modify",FALSE);
+ childSetEnabled("checkbox next owner can copy",FALSE);
+ childSetEnabled("checkbox next owner can transfer",FALSE);
+ }
+
+ if(valid_group_perms)
+ {
+ if((group_mask_on & PERM_COPY) && (group_mask_on & PERM_MODIFY) && (group_mask_on & PERM_MOVE))
+ {
+ childSetValue("checkbox share with group",TRUE);
+ childSetTentative("checkbox share with group",FALSE);
+ childSetEnabled("button deed",gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && (owner_mask_on & PERM_TRANSFER) && !group_owned && can_transfer);
+ }
+ else if((group_mask_off & PERM_COPY) && (group_mask_off & PERM_MODIFY) && (group_mask_off & PERM_MOVE))
+ {
+ childSetValue("checkbox share with group",FALSE);
+ childSetTentative("checkbox share with group",false);
+ childSetEnabled("button deed",false);
+ }
+ else
+ {
+ childSetValue("checkbox share with group",TRUE);
+ childSetTentative("checkbox share with group",true);
+ childSetEnabled("button deed",gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && (group_mask_on & PERM_MOVE) && (owner_mask_on & PERM_TRANSFER) && !group_owned && can_transfer);
+ }
+ }
+
+ if(valid_everyone_perms)
+ {
+ // Move
+ if(everyone_mask_on & PERM_MOVE)
+ {
+ childSetValue("checkbox allow everyone move",TRUE);
+ childSetTentative("checkbox allow everyone move",false);
+ }
+ else if(everyone_mask_off & PERM_MOVE)
+ {
+ childSetValue("checkbox allow everyone move",FALSE);
+ childSetTentative("checkbox allow everyone move",false);
+ }
+ else
+ {
+ childSetValue("checkbox allow everyone move",TRUE);
+ childSetTentative("checkbox allow everyone move",true);
+ }
+
+ // Copy == everyone can't copy
+ if(everyone_mask_on & PERM_COPY)
+ {
+ childSetValue("checkbox allow everyone copy",TRUE);
+ childSetTentative("checkbox allow everyone copy",!can_copy || !can_transfer);
+ }
+ else if(everyone_mask_off & PERM_COPY)
+ {
+ childSetValue("checkbox allow everyone copy",FALSE);
+ childSetTentative("checkbox allow everyone copy",false);
+ }
+ else
+ {
+ childSetValue("checkbox allow everyone copy",TRUE);
+ childSetTentative("checkbox allow everyone copy",true);
+ }
+ }
+
+ if(valid_next_perms)
+ {
+ // Modify == next owner canot modify
+ if(next_owner_mask_on & PERM_MODIFY)
+ {
+ childSetValue("checkbox next owner can modify",TRUE);
+ childSetTentative("checkbox next owner can modify",false);
+ }
+ else if(next_owner_mask_off & PERM_MODIFY)
+ {
+ childSetValue("checkbox next owner can modify",FALSE);
+ childSetTentative("checkbox next owner can modify",false);
+ }
+ else
+ {
+ childSetValue("checkbox next owner can modify",TRUE);
+ childSetTentative("checkbox next owner can modify",true);
+ }
+
+ // Copy == next owner cannot copy
+ if(next_owner_mask_on & PERM_COPY)
+ {
+ childSetValue("checkbox next owner can copy",TRUE);
+ childSetTentative("checkbox next owner can copy",!can_copy);
+ }
+ else if(next_owner_mask_off & PERM_COPY)
+ {
+ childSetValue("checkbox next owner can copy",FALSE);
+ childSetTentative("checkbox next owner can copy",FALSE);
+ }
+ else
+ {
+ childSetValue("checkbox next owner can copy",TRUE);
+ childSetTentative("checkbox next owner can copy",TRUE);
+ }
+
+ // Transfer == next owner cannot transfer
+ if(next_owner_mask_on & PERM_TRANSFER)
+ {
+ childSetValue("checkbox next owner can transfer",TRUE);
+ childSetTentative("checkbox next owner can transfer",!can_transfer);
+ }
+ else if(next_owner_mask_off & PERM_TRANSFER)
+ {
+ childSetValue("checkbox next owner can transfer",FALSE);
+ childSetTentative("checkbox next owner can transfer",FALSE);
+ }
+ else
+ {
+ childSetValue("checkbox next owner can transfer",TRUE);
+ childSetTentative("checkbox next owner can transfer",TRUE);
+ }
+ }
+
+ // reflect sale information
+ LLSaleInfo sale_info;
+ BOOL valid_sale_info = LLSelectMgr::getInstance()->selectGetSaleInfo(sale_info);
+ LLSaleInfo::EForSale sale_type = sale_info.getSaleType();
+
+ LLComboBox* combo_sale_type = getChild<LLComboBox>("sale type");
+ if (valid_sale_info)
+ {
+ combo_sale_type->setValue(sale_type == LLSaleInfo::FS_NOT ? LLSaleInfo::FS_COPY : sale_type);
+ combo_sale_type->setTentative(FALSE); // unfortunately this doesn't do anything at the moment.
+ }
+ else
+ {
+ // default option is sell copy, determined to be safest
+ combo_sale_type->setValue(LLSaleInfo::FS_COPY);
+ combo_sale_type->setTentative(TRUE); // unfortunately this doesn't do anything at the moment.
+ }
+
+ childSetValue("checkbox for sale", num_for_sale != 0);
+
+ // HACK: There are some old objects in world that are set for sale,
+ // but are no-transfer. We need to let users turn for-sale off, but only
+ // if for-sale is set.
+ bool cannot_actually_sell = !can_transfer || (!can_copy && sale_type == LLSaleInfo::FS_COPY);
+ if (num_for_sale && has_change_sale_ability && cannot_actually_sell)
+ {
+ childSetEnabled("checkbox for sale", true);
+ }
+
+ // Check search status of objects
+ BOOL all_volume = LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME );
+ bool include_in_search;
+ bool all_include_in_search = LLSelectMgr::getInstance()->selectionGetIncludeInSearch(&include_in_search);
+ childSetEnabled("search_check", has_change_sale_ability && all_volume);
+ childSetValue("search_check", include_in_search);
+ childSetTentative("search_check", ! all_include_in_search);
+
+ // Click action (touch, sit, buy)
+ U8 click_action = 0;
+ if (LLSelectMgr::getInstance()->selectionGetClickAction(&click_action))
+ {
+ LLComboBox* ComboClickAction = getChild<LLComboBox>("clickaction");
+ if(ComboClickAction)
+ {
+ ComboClickAction->setCurrentByIndex((S32)click_action);
+ }
+ }
+ childSetEnabled("label click action",is_perm_modify && all_volume);
+ childSetEnabled("clickaction",is_perm_modify && 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)
+ {
+ childSetEnabled(no_item_names[t],false);
+ }
+ }
+ updateVerbs();
+}
+
+
+// static
+void LLSidepanelTaskInfo::onClickClaim(void*)
+{
+ // try to claim ownership
+ LLSelectMgr::getInstance()->sendOwner(gAgent.getID(), gAgent.getGroupID());
+}
+
+// static
+void LLSidepanelTaskInfo::onClickRelease(void*)
+{
+ // try to release ownership
+ LLSelectMgr::getInstance()->sendOwner(LLUUID::null, LLUUID::null);
+}
+
+void LLSidepanelTaskInfo::onClickGroup()
+{
+ LLUUID owner_id;
+ std::string name;
+ BOOL owners_identical = LLSelectMgr::getInstance()->selectGetOwner(owner_id, name);
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+
+ if(owners_identical && (owner_id == gAgent.getID()))
+ {
+ LLFloaterGroupPicker* fg = LLFloaterReg::showTypedInstance<LLFloaterGroupPicker>("group_picker", LLSD(gAgent.getID()));
+ if (fg)
+ {
+ fg->setSelectGroupCallback( boost::bind(&LLSidepanelTaskInfo::cbGroupID, this, _1) );
+
+ if (parent_floater)
+ {
+ LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, fg);
+ fg->setOrigin(new_rect.mLeft, new_rect.mBottom);
+ parent_floater->addDependentFloater(fg);
+ }
+ }
+ }
+}
+
+void LLSidepanelTaskInfo::cbGroupID(LLUUID group_id)
+{
+ if(mLabelGroupName)
+ {
+ mLabelGroupName->setNameID(group_id, TRUE);
+ }
+ LLSelectMgr::getInstance()->sendGroup(group_id);
+}
+
+static bool callback_deed_to_group(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotification::getSelectedOption(notification, response);
+ if (0 == option)
+ {
+ LLUUID group_id;
+ BOOL groups_identical = LLSelectMgr::getInstance()->selectGetGroup(group_id);
+ if(group_id.notNull() && groups_identical && (gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED)))
+ {
+ LLSelectMgr::getInstance()->sendOwner(LLUUID::null, group_id, FALSE);
+// LLViewerStats::getInstance()->incStat(LLViewerStats::ST_RELEASE_COUNT);
+ }
+ }
+ return false;
+}
+
+void LLSidepanelTaskInfo::onClickDeedToGroup()
+{
+ LLNotifications::instance().add( "DeedObjectToGroup", LLSD(), LLSD(), callback_deed_to_group);
+}
+
+///----------------------------------------------------------------------------
+/// Permissions checkboxes
+///----------------------------------------------------------------------------
+
+void LLSidepanelTaskInfo::onCommitPerm(LLCheckBoxCtrl *ctrl, U8 field, U32 perm)
+{
+ LLViewerObject* object = mObjectSelection->getFirstRootObject();
+ if(!object) return;
+
+ BOOL new_state = ctrl->get();
+ LLSelectMgr::getInstance()->selectionSetObjectPermissions(field, new_state, perm);
+}
+
+void LLSidepanelTaskInfo::onCommitGroupShare()
+{
+ LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox share with group");
+ onCommitPerm(ctrl, PERM_GROUP, PERM_MODIFY | PERM_MOVE | PERM_COPY);
+}
+
+void LLSidepanelTaskInfo::onCommitEveryoneMove()
+{
+ LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox allow everyone move");
+ onCommitPerm(ctrl, PERM_EVERYONE, PERM_MOVE);
+}
+
+
+void LLSidepanelTaskInfo::onCommitEveryoneCopy()
+{
+ LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox allow everyone copy");
+ onCommitPerm(ctrl, PERM_EVERYONE, PERM_COPY);
+}
+
+void LLSidepanelTaskInfo::onCommitNextOwnerModify()
+{
+ LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox next owner can modify");
+ onCommitPerm(ctrl, PERM_NEXT_OWNER, PERM_MODIFY);
+}
+
+void LLSidepanelTaskInfo::onCommitNextOwnerCopy()
+{
+ LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox next owner can copy");
+ onCommitPerm(ctrl, PERM_NEXT_OWNER, PERM_COPY);
+}
+
+void LLSidepanelTaskInfo::onCommitNextOwnerTransfer()
+{
+ LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox next owner can transfer");
+ onCommitPerm(ctrl, PERM_NEXT_OWNER, PERM_TRANSFER);
+}
+
+void LLSidepanelTaskInfo::onCommitName()
+{
+ LLLineEditor* tb = getChild<LLLineEditor>("Object Name");
+ LLSelectMgr::getInstance()->selectionSetObjectName(tb->getText());
+}
+
+void LLSidepanelTaskInfo::onCommitDesc()
+{
+ LLLineEditor* le = getChild<LLLineEditor>("Object Description");
+ LLSelectMgr::getInstance()->selectionSetObjectDescription(le->getText());
+}
+
+void LLSidepanelTaskInfo::onCommitSaleInfo()
+{
+ setAllSaleInfo();
+}
+
+void LLSidepanelTaskInfo::onCommitSaleType()
+{
+ setAllSaleInfo();
+}
+
+void LLSidepanelTaskInfo::setAllSaleInfo()
+{
+ llinfos << "LLSidepanelTaskInfo::setAllSaleInfo()" << llendl;
+ LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_NOT;
+
+ LLCheckBoxCtrl *checkPurchase = getChild<LLCheckBoxCtrl>("checkbox for sale");
+
+ // Set the sale type if the object(s) are for sale.
+ if(checkPurchase && checkPurchase->get())
+ {
+ sale_type = static_cast<LLSaleInfo::EForSale>(getChild<LLComboBox>("sale type")->getValue().asInteger());
+ }
+
+ S32 price = -1;
+
+ LLSpinCtrl *edit_price = getChild<LLSpinCtrl>("Edit Cost");
+ price = (edit_price->getTentative()) ? DEFAULT_PRICE : edit_price->getValue().asInteger();
+
+ // If somehow an invalid price, turn the sale off.
+ if (price < 0)
+ sale_type = LLSaleInfo::FS_NOT;
+
+ LLSaleInfo sale_info(sale_type, price);
+ LLSelectMgr::getInstance()->selectionSetObjectSaleInfo(sale_info);
+
+ // If turned off for-sale, make sure click-action buy is turned
+ // off as well
+ if (sale_type == LLSaleInfo::FS_NOT)
+ {
+ U8 click_action = 0;
+ LLSelectMgr::getInstance()->selectionGetClickAction(&click_action);
+ if (click_action == CLICK_ACTION_BUY)
+ {
+ LLSelectMgr::getInstance()->selectionSetClickAction(CLICK_ACTION_TOUCH);
+ }
+ }
+}
+
+struct LLSelectionPayable : public LLSelectedObjectFunctor
+{
+ virtual bool apply(LLViewerObject* obj)
+ {
+ // can pay if you or your parent has money() event in script
+ LLViewerObject* parent = (LLViewerObject*)obj->getParent();
+ return (obj->flagTakesMoney()
+ || (parent && parent->flagTakesMoney()));
+ }
+};
+
+// static
+void LLSidepanelTaskInfo::onCommitClickAction(U8 click_action)
+{
+ if (click_action == CLICK_ACTION_BUY)
+ {
+ LLSaleInfo sale_info;
+ LLSelectMgr::getInstance()->selectGetSaleInfo(sale_info);
+ if (!sale_info.isForSale())
+ {
+ LLNotifications::instance().add("CantSetBuyObject");
+
+ // Set click action back to its old value
+ U8 click_action = 0;
+ LLSelectMgr::getInstance()->selectionGetClickAction(&click_action);
+// box->setCurrentByIndex((S32)click_action);
+
+ return;
+ }
+ }
+ else if (click_action == CLICK_ACTION_PAY)
+ {
+ // Verify object has script with money() handler
+ LLSelectionPayable payable;
+ bool can_pay = mObjectSelection->applyToObjects(&payable);
+ if (!can_pay)
+ {
+ // Warn, but do it anyway.
+ LLNotifications::instance().add("ClickActionNotPayable");
+ }
+ }
+ LLSelectMgr::getInstance()->selectionSetClickAction(click_action);
+}
+
+// static
+void LLSidepanelTaskInfo::onCommitIncludeInSearch()
+{
+ LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("search_check");
+ LLSelectMgr::getInstance()->selectionSetIncludeInSearch(ctrl->get());
+}
+
+// virtual
+void LLSidepanelTaskInfo::updateVerbs()
+{
+ LLSidepanelInventorySubpanel::updateVerbs();
+
+ mOpenBtn->setVisible(!getIsEditing());
+ mPayBtn->setVisible(!getIsEditing());
+ mBuyBtn->setVisible(!getIsEditing());
+
+ mOpenBtn->setEnabled(enable_object_open());
+ const LLViewerObject *obj = getFirstSelectedObject();
+ mEditBtn->setEnabled(obj && obj->permModify());
+}
+
+void LLSidepanelTaskInfo::onOpenButtonClicked()
+{
+ if (enable_object_open())
+ {
+ handle_object_open();
+ }
+}
+
+void LLSidepanelTaskInfo::onPayButtonClicked()
+{
+ onCommitClickAction(CLICK_ACTION_PAY);
+}
+
+void LLSidepanelTaskInfo::onBuyButtonClicked()
+{
+ onCommitClickAction(CLICK_ACTION_BUY);
+}
+
+// virtual
+void LLSidepanelTaskInfo::save()
+{
+ onCommitGroupShare();
+ onCommitEveryoneMove();
+ onCommitEveryoneCopy();
+ onCommitNextOwnerModify();
+ onCommitNextOwnerCopy();
+ onCommitNextOwnerTransfer();
+ onCommitName();
+ onCommitDesc();
+ onCommitSaleInfo();
+ onCommitSaleType();
+ onCommitIncludeInSearch();
+}
+
+void LLSidepanelTaskInfo::setObjectSelection(LLObjectSelectionHandle selection)
+{
+ mObjectSelection = selection;
+}
+
+LLSidepanelTaskInfo* LLSidepanelTaskInfo::getActivePanel()
+{
+ return sActivePanel;
+}
+
+LLViewerObject* LLSidepanelTaskInfo::getFirstSelectedObject()
+{
+ LLSelectNode *node = mObjectSelection->getFirstRootNode();
+ if (node)
+ {
+ return node->getObject();
+ }
+ return NULL;
+}
+
+const LLUUID& LLSidepanelTaskInfo::getSelectedUUID()
+{
+ const LLViewerObject* obj = getFirstSelectedObject();
+ if (obj)
+ {
+ return obj->getID();
+ }
+ return LLUUID::null;
+}
diff --git a/indra/newview/llsidepaneltaskinfo.h b/indra/newview/llsidepaneltaskinfo.h
new file mode 100644
index 0000000000..b6dd4dfb2c
--- /dev/null
+++ b/indra/newview/llsidepaneltaskinfo.h
@@ -0,0 +1,115 @@
+/**
+ * @file llsidepaneltaskinfo.h
+ * @brief LLSidepanelTaskInfo class header file
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSIDEPANELTASKINFO_H
+#define LL_LLSIDEPANELTASKINFO_H
+
+#include "llsidepanelinventorysubpanel.h"
+#include "lluuid.h"
+#include "llselectmgr.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLSidepanelTaskInfo
+//
+// Panel for permissions of an object.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLNameBox;
+class LLCheckBoxCtrl;
+class LLViewerObject;
+
+class LLSidepanelTaskInfo : public LLSidepanelInventorySubpanel
+{
+public:
+ LLSidepanelTaskInfo();
+ virtual ~LLSidepanelTaskInfo();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void setVisible(BOOL visible);
+
+ void setObjectSelection(LLObjectSelectionHandle selection);
+
+ const LLUUID& getSelectedUUID();
+ LLViewerObject* getFirstSelectedObject();
+
+ static LLSidepanelTaskInfo *getActivePanel();
+protected:
+ /*virtual*/ void refresh(); // refresh all labels as needed
+ /*virtual*/ void save();
+ /*virtual*/ void updateVerbs();
+
+ // statics
+ static void onClickClaim(void*);
+ static void onClickRelease(void*);
+ void onClickGroup();
+ void cbGroupID(LLUUID group_id);
+
+ void onClickDeedToGroup();
+ void onCommitPerm(LLCheckBoxCtrl* ctrl, U8 field, U32 perm);
+ void onCommitGroupShare();
+ void onCommitEveryoneMove();
+ void onCommitEveryoneCopy();
+ void onCommitNextOwnerModify();
+ void onCommitNextOwnerCopy();
+ void onCommitNextOwnerTransfer();
+ void onCommitName();
+ void onCommitDesc();
+ void onCommitSaleInfo();
+ void onCommitSaleType();
+
+ void onCommitClickAction(U8 click_action);
+ void onCommitIncludeInSearch();
+
+ void setAllSaleInfo();
+
+private:
+ LLNameBox* mLabelGroupName; // group name
+
+ LLUUID mCreatorID;
+ LLUUID mOwnerID;
+ LLUUID mLastOwnerID;
+
+protected:
+ void onOpenButtonClicked();
+ void onPayButtonClicked();
+ void onBuyButtonClicked();
+private:
+ LLButton* mOpenBtn;
+ LLButton* mPayBtn;
+ LLButton* mBuyBtn;
+
+ LLObjectSelectionHandle mObjectSelection;
+ static LLSidepanelTaskInfo* sActivePanel;
+};
+
+
+#endif // LL_LLSIDEPANELTASKINFO_H
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 279e143851..8c5439d47e 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -2679,8 +2679,7 @@ void renderTexturePriority(LLDrawable* drawable)
//LLViewerTexture* imagep = facep->getTexture();
//if (imagep)
{
-
- //F32 vsize = LLVOVolume::getTextureVirtualSize(facep);
+
//F32 vsize = imagep->mMaxVirtualSize;
F32 vsize = facep->getPixelArea();
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index b23e7feda2..4c322810d5 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -60,6 +60,7 @@
#include "llfocusmgr.h"
#include "llhttpsender.h"
#include "lllocationhistory.h"
+#include "llimageworker.h"
#include "llloginflags.h"
#include "llmd5.h"
#include "llmemorystream.h"
@@ -168,7 +169,7 @@
#include "llvoclouds.h"
#include "llweb.h"
#include "llworld.h"
-#include "llworldmap.h"
+#include "llworldmapmessage.h"
#include "llxfermanager.h"
#include "pipeline.h"
#include "llappviewer.h"
@@ -1310,6 +1311,7 @@ bool idle_startup()
gViewerWindow->moveProgressViewToFront();
LLError::logToFixedBuffer(gDebugView->mDebugConsolep);
+
// set initial visibility of debug console
gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole"));
}
@@ -2525,9 +2527,8 @@ void register_viewer_callbacks(LLMessageSystem* msg)
msg->setHandlerFunc("AvatarPickerReply", LLFloaterAvatarPicker::processAvatarPickerReply);
- msg->setHandlerFunc("MapLayerReply", LLWorldMap::processMapLayerReply);
- msg->setHandlerFunc("MapBlockReply", LLWorldMap::processMapBlockReply);
- msg->setHandlerFunc("MapItemReply", LLWorldMap::processMapItemReply);
+ msg->setHandlerFunc("MapBlockReply", LLWorldMapMessage::processMapBlockReply);
+ msg->setHandlerFunc("MapItemReply", LLWorldMapMessage::processMapItemReply);
msg->setHandlerFunc("EventInfoReply", LLPanelEvent::processEventInfoReply);
msg->setHandlerFunc("PickInfoReply", &LLAvatarPropertiesProcessor::processPickInfoReply);
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
index 5440b2c9ad..1d479bac8c 100644
--- a/indra/newview/llsurface.cpp
+++ b/indra/newview/llsurface.cpp
@@ -234,12 +234,7 @@ void LLSurface::createSTexture()
{
if (!mSTexturep)
{
- // Fill with dummy gray data.
-
- //mSTexturep = LLViewerTextureManager::getLocalTexture(sTextureSize, sTextureSize, 3, FALSE);
- //mSTexturep->dontDiscard();
- //mSTexturep->setAddressMode(LLTexUnit::TAM_CLAMP);
-
+ // Fill with dummy gray data.
// GL NOT ACTIVE HERE
LLPointer<LLImageRaw> raw = new LLImageRaw(sTextureSize, sTextureSize, 3);
U8 *default_texture = raw->getData();
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
index 5d9046ac90..5d682cad3c 100644
--- a/indra/newview/lltexlayer.cpp
+++ b/indra/newview/lltexlayer.cpp
@@ -670,8 +670,6 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height )
LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
gGL.setColorMask(true, true);
- BOOL render_morph = mAvatar->morphMaskNeedsUpdate(mBakedTexIndex);
-
// clear buffer area to ensure we don't pick up UI elements
{
gGL.flush();
@@ -691,12 +689,8 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height )
if (layer->getRenderPass() == LLTexLayer::RP_COLOR)
{
gGL.flush();
- success &= layer->render(x, y, width, height, render_morph);
+ success &= layer->render(x, y, width, height);
gGL.flush();
- if (layer->isMorphValid())
- {
- mAvatar->setMorphMasksValid(TRUE, mBakedTexIndex);
- }
}
}
@@ -786,12 +780,10 @@ void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 width, S32 height)
{
memset(data, 255, width * height);
- BOOL render_morph = mAvatar->morphMaskNeedsUpdate(mBakedTexIndex);
-
for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
{
LLTexLayerInterface* layer = *iter;
- layer->gatherAlphaMasks(data, mComposite->getOriginX(),mComposite->getOriginY(), width, height, render_morph);
+ layer->gatherAlphaMasks(data, mComposite->getOriginX(),mComposite->getOriginY(), width, height);
}
// Set alpha back to that of our alpha masks.
@@ -863,6 +855,31 @@ void LLTexLayerSet::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_
mAvatar->applyMorphMask(tex_data, width, height, num_components, mBakedTexIndex);
}
+BOOL LLTexLayerSet::isMorphValid()
+{
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayerInterface* layer = *iter;
+ if (layer && !layer->isMorphValid())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void LLTexLayerSet::invalidateMorphMasks()
+{
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayerInterface* layer = *iter;
+ if (layer)
+ {
+ layer->invalidateMorphMasks();
+ }
+ }
+}
+
//-----------------------------------------------------------------------------
// LLTexLayerInfo
@@ -1282,7 +1299,7 @@ void LLTexLayer::calculateTexLayerColor(const param_color_list_t &param_list, LL
}
}
-BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph)
+BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height)
{
LLGLEnable color_mat(GL_COLOR_MATERIAL);
gPipeline.disableLights();
@@ -1333,7 +1350,7 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph)
}
}//*/
- renderMorphMasks(x, y, width, height, net_color, render_morph);
+ renderMorphMasks(x, y, width, height, net_color);
alpha_mask_specified = TRUE;
gGL.flush();
gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ONE_MINUS_DEST_ALPHA);
@@ -1371,7 +1388,7 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph)
LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode();
- gGL.getTexUnit(0)->bind(tex);
+ gGL.getTexUnit(0)->bind(tex, TRUE);
gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
gl_rect_2d_simple_tex( width, height );
@@ -1393,7 +1410,7 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph)
LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
if( tex )
{
- gGL.getTexUnit(0)->bind(tex);
+ gGL.getTexUnit(0)->bind(tex, TRUE);
gl_rect_2d_simple_tex( width, height );
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
@@ -1506,7 +1523,7 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
if( tex )
{
LLGLSNoAlphaTest gls_no_alpha_test;
- gGL.getTexUnit(0)->bind(tex);
+ gGL.getTexUnit(0)->bind(tex, TRUE);
gl_rect_2d_simple_tex( width, height );
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
@@ -1534,12 +1551,12 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
return success;
}
-/*virtual*/ void LLTexLayer::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph)
+/*virtual*/ void LLTexLayer::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height)
{
- addAlphaMask(data, originX, originY, width, height, render_morph);
+ addAlphaMask(data, originX, originY, width, height);
}
-BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, BOOL render_morph)
+BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color)
{
BOOL success = TRUE;
@@ -1578,46 +1595,38 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
// Accumulate the alpha component of the texture
if( getInfo()->mLocalTexture != -1 )
{
- LLViewerTexture* tex = mLocalTextureObject->getImage();
- if( tex && (tex->getComponents() == 4) )
- {
- LLGLSNoAlphaTest gls_no_alpha_test;
+ LLViewerTexture* tex = mLocalTextureObject->getImage();
+ if( tex && (tex->getComponents() == 4) )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
- LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode();
-
- gGL.getTexUnit(0)->bind(tex);
- gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+ LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode();
+
+ gGL.getTexUnit(0)->bind(tex, TRUE);
+ gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
- gl_rect_2d_simple_tex( width, height );
+ gl_rect_2d_simple_tex( width, height );
- gGL.getTexUnit(0)->setTextureAddressMode(old_mode);
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- }
- else
- {
- success = FALSE;
- }
+ gGL.getTexUnit(0)->setTextureAddressMode(old_mode);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
+ }
if( !getInfo()->mStaticImageFileName.empty() )
{
- LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
- if( tex )
- {
- if( (tex->getComponents() == 4) ||
- ( (tex->getComponents() == 1) && getInfo()->mStaticImageIsMask ) )
- {
- LLGLSNoAlphaTest gls_no_alpha_test;
- gGL.getTexUnit(0)->bind(tex);
- gl_rect_2d_simple_tex( width, height );
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- }
- }
- else
+ LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
+ if( tex )
+ {
+ if( (tex->getComponents() == 4) ||
+ ( (tex->getComponents() == 1) && getInfo()->mStaticImageIsMask ) )
{
- success = FALSE;
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ gGL.getTexUnit(0)->bind(tex, TRUE);
+ gl_rect_2d_simple_tex( width, height );
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
}
+ }
// Draw a rectangle with the layer color to multiply the alpha by that color's alpha.
// Note: we're still using gGL.blendFunc( GL_DST_ALPHA, GL_ZERO );
@@ -1634,7 +1643,7 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
gGL.setColorMask(true, true);
- if (render_morph && mHasMorph && success)
+ if (hasMorph() && success)
{
LLCRC alpha_mask_crc;
const LLUUID& uuid = getUUID();
@@ -1674,7 +1683,7 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
return success;
}
-void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph)
+void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height)
{
S32 size = width * height;
U8* alphaData = getAlphaData();
@@ -1684,7 +1693,7 @@ void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32
findNetColor( &net_color );
// TODO: eliminate need for layer morph mask valid flag
invalidateMorphMasks();
- renderMorphMasks(originX, originY, width, height, net_color, render_morph);
+ renderMorphMasks(originX, originY, width, height, net_color);
alphaData = getAlphaData();
}
if (alphaData)
@@ -1805,7 +1814,7 @@ LLTexLayer* LLTexLayerTemplate::getLayer(U32 i)
return layer;
}
-/*virtual*/ BOOL LLTexLayerTemplate::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph)
+/*virtual*/ BOOL LLTexLayerTemplate::render(S32 x, S32 y, S32 width, S32 height)
{
BOOL success = TRUE;
updateWearableCache();
@@ -1827,7 +1836,7 @@ LLTexLayer* LLTexLayerTemplate::getLayer(U32 i)
{
wearable->writeToAvatar(FALSE, FALSE);
layer->setLTO(lto);
- success &= layer->render(x,y,width,height,render_morph);
+ success &= layer->render(x,y,width,height);
}
}
@@ -1849,7 +1858,7 @@ LLTexLayer* LLTexLayerTemplate::getLayer(U32 i)
return success;
}
-/*virtual*/ void LLTexLayerTemplate::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph)
+/*virtual*/ void LLTexLayerTemplate::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height)
{
U32 num_wearables = updateWearableCache();
for (U32 i = 0; i < num_wearables; i++)
@@ -1857,7 +1866,7 @@ LLTexLayer* LLTexLayerTemplate::getLayer(U32 i)
LLTexLayer *layer = getLayer(i);
if (layer)
{
- layer->addAlphaMask(data, originX, originY, width, height, render_morph);
+ layer->addAlphaMask(data, originX, originY, width, height);
}
}
}
@@ -2034,7 +2043,7 @@ LLViewerTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_n
// that once an image is a mask it's always a mask.
tex->setExplicitFormat( GL_ALPHA8, GL_ALPHA );
}
- tex->createGLTexture(0, image_raw);
+ tex->createGLTexture(0, image_raw, 0, TRUE, LLViewerTexture::LOCAL);
gGL.getTexUnit(0)->bind(tex);
tex->setAddressMode(LLTexUnit::TAM_CLAMP);
diff --git a/indra/newview/lltexlayer.h b/indra/newview/lltexlayer.h
index e4a6e82ba5..cd8f27a96b 100644
--- a/indra/newview/lltexlayer.h
+++ b/indra/newview/lltexlayer.h
@@ -81,13 +81,14 @@ public:
const LLTexLayerInfo* getInfo() const { return mInfo; }
virtual BOOL setInfo(const LLTexLayerInfo *info, LLWearable* wearable ); // This sets mInfo and calls initialization functions
- virtual BOOL render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) = 0;
+ virtual BOOL render(S32 x, S32 y, S32 width, S32 height) = 0;
void requestUpdate();
LLTexLayerSet* const getTexLayerSet() const { return mTexLayerSet; }
virtual void deleteCaches() = 0;
void invalidateMorphMasks();
virtual void setHasMorph(BOOL newval) { mHasMorph = newval; }
+ BOOL hasMorph() { return mHasMorph; }
BOOL isMorphValid() { return mMorphMasksValid; }
const std::string& getName() const;
@@ -95,7 +96,7 @@ public:
const std::string& getGlobalColor() const;
virtual BOOL blendAlphaTexture( S32 x, S32 y, S32 width, S32 height) = 0;
- virtual void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph) = 0;
+ virtual void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height) = 0;
BOOL hasAlphaParams() const { return !mParamAlphaList.empty(); }
BOOL isVisibilityMask() const;
@@ -134,10 +135,10 @@ public:
LLTexLayerTemplate(const LLTexLayerTemplate &layer);
/*virtual*/ ~LLTexLayerTemplate();
- /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph);
+ /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height);
/*virtual*/ BOOL setInfo(const LLTexLayerInfo *info, LLWearable* wearable ); // This sets mInfo and calls initialization functions
/*virtual*/ BOOL blendAlphaTexture( S32 x, S32 y, S32 width, S32 height); // Multiplies a single alpha texture against the frame buffer
- /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph);
+ /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height);
/*virtual*/ void setHasMorph(BOOL newval);
/*virtual*/ void deleteCaches();
private:
@@ -162,16 +163,16 @@ public:
/*virtual*/ ~LLTexLayer();
/*virtual*/ BOOL setInfo(const LLTexLayerInfo *info, LLWearable* wearable ); // This sets mInfo and calls initialization functions
- /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph);
+ /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height);
/*virtual*/ void deleteCaches();
U8* getAlphaData();
BOOL findNetColor(LLColor4* color) const;
/*virtual*/ BOOL blendAlphaTexture( S32 x, S32 y, S32 width, S32 height); // Multiplies a single alpha texture against the frame buffer
- /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph);
- BOOL renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, BOOL render_morph);
- void addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph);
+ /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height);
+ BOOL renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color);
+ void addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height);
void setLTO(LLLocalTextureObject *lto) { mLocalTextureObject = lto; }
LLLocalTextureObject* getLTO() { return mLocalTextureObject; }
@@ -261,6 +262,8 @@ public:
void deleteCaches();
void gatherMorphMaskAlpha(U8 *data, S32 width, S32 height);
void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components);
+ BOOL isMorphValid();
+ void invalidateMorphMasks();
LLTexLayerInterface* findLayerByName(const std::string& name);
void cloneTemplates(LLLocalTextureObject *lto, LLVOAvatarDefines::ETextureIndex tex_index, LLWearable* wearable);
diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp
index 1b249d75d1..69a2d1d7a6 100644
--- a/indra/newview/lltexturecache.cpp
+++ b/indra/newview/lltexturecache.cpp
@@ -43,11 +43,17 @@
// Included to allow LLTextureCache::purgeTextures() to pause watchdog timeout
#include "llappviewer.h"
-#define USE_LFS_READ 0
-#define USE_LFS_WRITE 0
-
-// Note: first 4 bytes store file size, rest is j2c data
-const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; //1024;
+// Cache organization:
+// cache/texture.entries
+// Unordered array of Entry structs
+// cache/texture.cache
+// First TEXTURE_CACHE_ENTRY_SIZE bytes of each texture in texture.entries in same order
+// cache/textures/[0-F]/UUID.texture
+// Actual texture body files
+
+const S32 TEXTURE_CACHE_ENTRY_SIZE = 1024;
+const F32 TEXTURE_CACHE_PURGE_AMOUNT = .20f; // % amount to reduce the cache by when it exceeds its limit
+const F32 TEXTURE_CACHE_LRU_SIZE = .10f; // % amount for LRU list (low overhead to regenerate)
class LLTextureCacheWorker : public LLWorkerClass
{
@@ -309,94 +315,75 @@ void LLTextureCacheWorker::startWork(S32 param)
{
}
+// This is where a texture is read from the cache system (header and body)
+// Current assumption are:
+// - the whole data are in a raw form, will be stored at mReadData
+// - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache)
+// - the code supports offset reading but this is actually never exercised in the viewer
bool LLTextureCacheRemoteWorker::doRead()
{
+ bool done = false;
+ S32 idx = -1;
+
S32 local_size = 0;
std::string local_filename;
+ // First state / stage : find out if the file is local
if (mState == INIT)
{
std::string filename = mCache->getLocalFileName(mID);
- local_filename = filename + ".j2c";
- local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
- if (local_size == 0)
+ // Is it a JPEG2000 file?
{
- local_filename = filename + ".tga";
+ local_filename = filename + ".j2c";
local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
if (local_size > 0)
{
- mImageFormat = IMG_CODEC_TGA;
- mDataSize = local_size; // Only a complete .tga file is valid
+ mImageFormat = IMG_CODEC_J2C;
}
}
- if (local_size > 0)
- {
- mState = LOCAL;
- }
- else
- {
- mState = CACHE;
- }
- }
-
- if (mState == LOCAL)
- {
-#if USE_LFS_READ
- if (mFileHandle == LLLFSThread::nullHandle())
+ // If not, is it a jpeg file?
+ if (local_size == 0)
{
- mImageLocal = TRUE;
- mImageSize = local_size;
- if (!mDataSize || mDataSize + mOffset > local_size)
+ local_filename = filename + ".jpg";
+ local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
+ if (local_size > 0)
{
- mDataSize = local_size - mOffset;
+ mImageFormat = IMG_CODEC_JPEG;
+ mDataSize = local_size; // Only a complete .jpg file is valid
}
- if (mDataSize <= 0)
- {
- // no more data to read
- mDataSize = 0;
- return true;
- }
- mReadData = new U8[mDataSize];
- mBytesRead = -1;
- mBytesToRead = mDataSize;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- mFileHandle = LLLFSThread::sLocal->read(local_filename, mReadData, mOffset, mDataSize,
- new ReadResponder(mCache, mRequestHandle));
- return false;
}
- else
+ // Hmm... What about a targa file? (used for UI texture mostly)
+ if (local_size == 0)
{
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "Error reading file from local cache: " << local_filename
-// << " Bytes: " << mDataSize << " Offset: " << mOffset
-// << " / " << mDataSize << llendl;
- mDataSize = 0; // failed
- delete[] mReadData;
- mReadData = NULL;
- }
- return true;
- }
- else
+ local_filename = filename + ".tga";
+ local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
+ if (local_size > 0)
{
- return false;
+ mImageFormat = IMG_CODEC_TGA;
+ mDataSize = local_size; // Only a complete .tga file is valid
}
}
-#else
+ // Determine the next stage: if we found a file, then LOCAL else CACHE
+ mState = (local_size > 0 ? LOCAL : CACHE);
+ }
+
+ // Second state / stage : if the file is local, load it and leave
+ if (!done && (mState == LOCAL))
+ {
+ llassert(local_size != 0); // we're assuming there is a non empty local file here...
if (!mDataSize || mDataSize > local_size)
{
mDataSize = local_size;
}
+ // Allocate read buffer
mReadData = new U8[mDataSize];
S32 bytes_read = LLAPRFile::readEx(local_filename,
mReadData, mOffset, mDataSize, mCache->getLocalAPRFilePool());
if (bytes_read != mDataSize)
{
-// llwarns << "Error reading file from local cache: " << local_filename
-// << " Bytes: " << mDataSize << " Offset: " << mOffset
-// << " / " << mDataSize << llendl;
+ llwarns << "Error reading file from local cache: " << local_filename
+ << " Bytes: " << mDataSize << " Offset: " << mOffset
+ << " / " << mDataSize << llendl;
mDataSize = 0;
delete[] mReadData;
mReadData = NULL;
@@ -406,405 +393,275 @@ bool LLTextureCacheRemoteWorker::doRead()
mImageSize = local_size;
mImageLocal = TRUE;
}
- return true;
-#endif
+ // We're done...
+ done = true;
}
- S32 idx = -1;
-
- if (mState == CACHE)
+ // Second state / stage : identify the cache or not...
+ if (!done && (mState == CACHE))
{
- llassert_always(mImageSize == 0);
- idx = mCache->getHeaderCacheEntry(mID, false, &mImageSize);
- if (idx >= 0 && mImageSize > mOffset)
+ idx = mCache->getHeaderCacheEntry(mID, mImageSize);
+ if (idx < 0)
{
- llassert_always(mImageSize > 0);
- if (!mDataSize || mDataSize > mImageSize)
- {
- mDataSize = mImageSize;
- }
- mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
+ // The texture is *not* cached. We're done here...
+ mDataSize = 0; // no data
+ done = true;
}
else
{
- mDataSize = 0; // no data
- return true;
+ // If the read offset is bigger than the header cache, we read directly from the body
+ // Note that currently, we *never* read with offset from the cache, so the result is *always* HEADER
+ mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
}
}
- if (mState == HEADER)
+ // Third state / stage : read data from the header cache (texture.entries) file
+ if (!done && (mState == HEADER))
{
-#if USE_LFS_READ
- if (mFileHandle == LLLFSThread::nullHandle())
- {
- llassert_always(idx >= 0);
- llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
- S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
- S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- llassert_always(mReadData == NULL);
- mReadData = new U8[size];
- mBytesRead = -1;
- mBytesToRead = size;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- mFileHandle = LLLFSThread::sLocal->read(mCache->mHeaderDataFileName,
- mReadData, offset, mBytesToRead,
- new ReadResponder(mCache, mRequestHandle));
- return false;
- }
- else
- {
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes read from header: " << mBytesRead
-// << " != " << mBytesToRead << llendl;
- mDataSize = -1; // failed
- return true;
- }
- if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
- {
- return true; // done
- }
- else
- {
- mFileHandle = LLLFSThread::nullHandle();
- mState = BODY;
- }
- }
- else
- {
- return false;
- }
- }
-#else
- llassert_always(idx >= 0);
+ llassert_always(idx >= 0); // we need an entry here or reading the header makes no sense
llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
+ // Compute the size we need to read (in bytes)
S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+ size = llmin(size, mDataSize);
+ // Allocate the read buffer
mReadData = new U8[size];
S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName,
mReadData, offset, size, mCache->getLocalAPRFilePool());
if (bytes_read != size)
{
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes read from header: " << bytes_read
-// << " / " << size << llendl;
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " incorrect number of bytes read from header: " << bytes_read
+ << " / " << size << llendl;
+ delete[] mReadData;
+ mReadData = NULL;
mDataSize = -1; // failed
- return true;
+ done = true;
}
- if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
+ // If we already read all we expected, we're actually done
+ if (mDataSize <= bytes_read)
{
- return true; // done
+ done = true;
}
else
{
mState = BODY;
}
-#endif
}
- if (mState == BODY)
+ // Fourth state / stage : read the rest of the data from the UUID based cached file
+ if (!done && (mState == BODY))
{
-#if USE_LFS_READ
- if (mFileHandle == LLLFSThread::nullHandle())
- {
- std::string filename = mCache->getTextureFileName(mID);
- S32 filesize = LLAPRFile::size(filename, mCache->getLocalAPRFilePool());
- if (filesize > mOffset)
- {
- S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
- mDataSize = llmin(datasize, mDataSize);
- S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- data_offset = llmax(data_offset, 0);
- S32 file_size = mDataSize - data_offset;
- S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
- file_offset = llmax(file_offset, 0);
-
- llassert_always(mDataSize > 0);
- U8* data = new U8[mDataSize];
- if (data_offset > 0)
- {
- llassert_always(mReadData);
- llassert_always(data_offset <= mDataSize);
- memcpy(data, mReadData, data_offset);
- delete[] mReadData;
- mReadData = NULL;
- }
- llassert_always(mReadData == NULL);
- mReadData = data;
-
- mBytesRead = -1;
- mBytesToRead = file_size;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- llassert_always(data_offset + mBytesToRead <= mDataSize);
- mFileHandle = LLLFSThread::sLocal->read(filename,
- mReadData + data_offset, file_offset, mBytesToRead,
- new ReadResponder(mCache, mRequestHandle));
- return false;
- }
- else
- {
- mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
- return true; // done
- }
- }
- else
- {
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes read from body: " << mBytesRead
-// << " != " << mBytesToRead << llendl;
- mDataSize = -1; // failed
- }
- return true;
- }
- else
- {
- return false;
- }
- }
-#else
std::string filename = mCache->getTextureFileName(mID);
S32 filesize = LLAPRFile::size(filename, mCache->getLocalAPRFilePool());
- S32 bytes_read = 0;
- if (filesize > mOffset)
+
+ if (filesize && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset)
{
- S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
- mDataSize = llmin(datasize, mDataSize);
- S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- data_offset = llmax(data_offset, 0);
- S32 file_size = mDataSize - data_offset;
- S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
- file_offset = llmax(file_offset, 0);
+ S32 max_datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize - mOffset;
+ mDataSize = llmin(max_datasize, mDataSize);
+
+ S32 data_offset, file_size, file_offset;
+ // Reserve the whole data buffer first
U8* data = new U8[mDataSize];
- if (data_offset > 0)
+
+ // Set the data file pointers taking the read offset into account. 2 cases:
+ if (mOffset < TEXTURE_CACHE_ENTRY_SIZE)
{
+ // Offset within the header record. That means we read something from the header cache.
+ // Note: most common case is (mOffset = 0), so this is the "normal" code path.
+ data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; // i.e. TEXTURE_CACHE_ENTRY_SIZE if mOffset nul (common case)
+ file_offset = 0;
+ file_size = mDataSize - data_offset;
+ // Copy the raw data we've been holding from the header cache into the new sized buffer
llassert_always(mReadData);
memcpy(data, mReadData, data_offset);
delete[] mReadData;
+ mReadData = NULL;
+ }
+ else
+ {
+ // Offset bigger than the header record. That means we haven't read anything yet.
+ data_offset = 0;
+ file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
+ file_size = mDataSize;
+ // No data from header cache to copy in that case, we skipped it all
}
+
+ // Now use that buffer as the object read buffer
+ llassert_always(mReadData == NULL);
mReadData = data;
- bytes_read = LLAPRFile::readEx(filename,
+
+ // Read the data at last
+ S32 bytes_read = LLAPRFile::readEx(filename,
mReadData + data_offset,
file_offset, file_size,
mCache->getLocalAPRFilePool());
if (bytes_read != file_size)
{
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes read from body: " << bytes_read
-// << " / " << file_size << llendl;
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " incorrect number of bytes read from body: " << bytes_read
+ << " / " << file_size << llendl;
+ delete[] mReadData;
+ mReadData = NULL;
mDataSize = -1; // failed
- return true;
+ done = true;
}
}
else
{
- mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
- }
-
- return true;
-#endif
+ // No body, we're done.
+ mDataSize = llmax(TEXTURE_CACHE_ENTRY_SIZE - mOffset, 0);
+ lldebugs << "No body file for: " << filename << llendl;
+ }
+ // Nothing else to do at that point...
+ done = true;
}
-
- return false;
+
+ // Clean up and exit
+ return done;
}
+// This is where *everything* about a texture is written down in the cache system (entry map, header and body)
+// Current assumption are:
+// - the whole data are in a raw form, starting at mWriteData
+// - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache)
+// - the code *does not* support offset writing so there are no difference between buffer addresses and start of data
bool LLTextureCacheRemoteWorker::doWrite()
{
+ bool done = false;
S32 idx = -1;
- // No LOCAL state for write()
-
+ // First state / stage : check that what we're trying to cache is in an OK shape
if (mState == INIT)
{
+ llassert_always(mOffset == 0); // We currently do not support write offsets
+ llassert_always(mDataSize > 0); // Things will go badly wrong if mDataSize is nul or negative...
+ mState = CACHE;
+ }
+
+ // No LOCAL state for write(): because it doesn't make much sense to cache a local file...
+
+ // Second state / stage : set an entry in the headers entry (texture.entries) file
+ if (!done && (mState == CACHE))
+ {
+ bool alreadyCached = false;
S32 cur_imagesize = 0;
- S32 offset = mOffset;
- idx = mCache->getHeaderCacheEntry(mID, false, &cur_imagesize);
- if (idx >= 0 && cur_imagesize > 0)
+ // Checks if this image is already in the entry list
+ idx = mCache->getHeaderCacheEntry(mID, cur_imagesize);
+ if (idx >= 0 && (cur_imagesize >= 0))
{
- offset = TEXTURE_CACHE_ENTRY_SIZE; // don't re-write header
+ alreadyCached = true; // already there and non empty
}
- idx = mCache->getHeaderCacheEntry(mID, true, &mImageSize); // touch entry
- if (idx >= 0)
+ idx = mCache->setHeaderCacheEntry(mID, mImageSize); // create or touch the entry
+ if (idx < 0)
{
- if(cur_imagesize > 0 && mImageSize != cur_imagesize)
- {
-// llwarns << "Header cache entry size: " << cur_imagesize << " != mImageSize: " << mImageSize << llendl;
- offset = 0; // re-write header
- }
- mState = offset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " Unable to create header entry for writing!" << llendl;
+ mDataSize = -1; // failed
+ done = true;
}
else
{
- mDataSize = -1; // failed
- return true;
+ if (cur_imagesize > 0 && (mImageSize != cur_imagesize))
+ {
+ alreadyCached = false; // re-write the header if the size changed in all cases
+ }
+ if (alreadyCached && (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE))
+ {
+ // Small texture already cached case: we're done with writing
+ done = true;
+ }
+ else
+ {
+ // If the texture has already been cached, we don't resave the header and go directly to the body part
+ mState = alreadyCached ? BODY : HEADER;
+ }
}
}
-
- if (mState == HEADER)
+
+ // Third stage / state : write the header record in the header file (texture.cache)
+ if (!done && (mState == HEADER))
{
-#if USE_LFS_WRITE
- if (mFileHandle == LLLFSThread::nullHandle())
+ llassert_always(idx >= 0); // we need an entry here or storing the header makes no sense
+ S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE; // skip to the correct spot in the header file
+ S32 size = TEXTURE_CACHE_ENTRY_SIZE; // record size is fixed for the header
+ S32 bytes_written;
+
+ if (mDataSize < TEXTURE_CACHE_ENTRY_SIZE)
{
- llassert_always(idx >= 0);
- llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
- S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
- S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- mBytesRead = -1;
- mBytesToRead = size;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- mFileHandle = LLLFSThread::sLocal->write(mCache->mHeaderDataFileName,
- mWriteData, offset, mBytesToRead,
- new WriteResponder(mCache, mRequestHandle));
- return false;
+ // We need to write a full record in the header cache so, if the amount of data is smaller
+ // than a record, we need to transfer the data to a buffer padded with 0 and write that
+ U8* padBuffer = new U8[TEXTURE_CACHE_ENTRY_SIZE];
+ memset(padBuffer, 0, TEXTURE_CACHE_ENTRY_SIZE); // Init with zeros
+ memcpy(padBuffer, mWriteData, mDataSize); // Copy the write buffer
+ bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size, mCache->getLocalAPRFilePool());
+ delete [] padBuffer;
}
else
{
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes written to header: " << mBytesRead
-// << " != " << mBytesToRead << llendl;
- mDataSize = -1; // failed
- return true;
- }
- if (mDataSize <= mBytesToRead)
- {
- return true; // done
- }
- else
- {
- mFileHandle = LLLFSThread::nullHandle();
- mState = BODY;
- }
- }
- else
- {
- return false;
- }
+ // Write the header record (== first TEXTURE_CACHE_ENTRY_SIZE bytes of the raw file) in the header file
+ bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, mCache->getLocalAPRFilePool());
}
-#else
- llassert_always(idx >= 0);
- llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
- S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
- S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- S32 bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, mCache->getLocalAPRFilePool());
if (bytes_written <= 0)
{
-// llwarns << "LLTextureCacheWorker: missing entry: " << mID << llendl;
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " Unable to write header entry!" << llendl;
mDataSize = -1; // failed
- return true;
+ done = true;
}
- if (mDataSize <= size)
+ // If we wrote everything (may be more with padding) in the header cache,
+ // we're done so we don't have a body to store
+ if (mDataSize <= bytes_written)
{
- return true; // done
+ done = true;
}
else
{
mState = BODY;
}
-#endif
}
- if (mState == BODY)
+ // Fourth stage / state : write the body file, i.e. the rest of the texture in a "UUID" file name
+ if (!done && (mState == BODY))
{
-#if USE_LFS_WRITE
- if (mFileHandle == LLLFSThread::nullHandle())
+ llassert(mDataSize > TEXTURE_CACHE_ENTRY_SIZE); // wouldn't make sense to be here otherwise...
+ S32 file_size = mDataSize - TEXTURE_CACHE_ENTRY_SIZE;
+ if ((file_size > 0) && mCache->updateTextureEntryList(mID, file_size))
{
- S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- data_offset = llmax(data_offset, 0);
- S32 file_size = mDataSize - data_offset;
- S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
- file_offset = llmax(file_offset, 0);
- if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
- {
- std::string filename = mCache->getTextureFileName(mID);
- mBytesRead = -1;
- mBytesToRead = file_size;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- mFileHandle = LLLFSThread::sLocal->write(filename,
- mWriteData + data_offset, file_offset, mBytesToRead,
- new WriteResponder(mCache, mRequestHandle));
- return false;
- }
- else
- {
- mDataSize = 0; // no data written
- return true; // done
- }
- }
- else
- {
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes written to body: " << mBytesRead
-// << " != " << mBytesToRead << llendl;
- mDataSize = -1; // failed
- }
- return true;
- }
- else
- {
- return false;
- }
- }
-#else
- S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- data_offset = llmax(data_offset, 0);
- S32 file_size = mDataSize - data_offset;
- S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
- file_offset = llmax(file_offset, 0);
- S32 bytes_written = 0;
- if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
- {
- std::string filename = mCache->getTextureFileName(mID);
-
- bytes_written = LLAPRFile::writeEx(filename,
- mWriteData + data_offset,
- file_offset, file_size,
- mCache->getLocalAPRFilePool());
+ // build the cache file name from the UUID
+ std::string filename = mCache->getTextureFileName(mID);
+// llinfos << "Writing Body: " << filename << " Bytes: " << file_offset+file_size << llendl;
+ S32 bytes_written = LLAPRFile::writeEx( filename,
+ mWriteData + TEXTURE_CACHE_ENTRY_SIZE,
+ 0, file_size,
+ mCache->getLocalAPRFilePool());
if (bytes_written <= 0)
{
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " incorrect number of bytes written to body: " << bytes_written
+ << " / " << file_size << llendl;
mDataSize = -1; // failed
+ done = true;
}
}
else
{
mDataSize = 0; // no data written
}
-
- return true;
-#endif
+ // Nothing else to do at that point...
+ done = true;
}
-
- return false;
+
+ // Clean up and exit
+ return done;
}
//virtual
bool LLTextureCacheWorker::doWork(S32 param)
{
-// *TODO reenable disabled apr_pool usage disabled due to maint-render-9 merge breakage -brad
- //allocate a new local apr_pool
-// LLAPRPool pool ;
-
- //save the current mFileAPRPool to avoid breaking anything.
-// apr_pool_t* old_pool = mCache->getFileAPRPool() ;
- //make mFileAPRPool to point to the local one
-// mCache->setFileAPRPool(pool.getAPRPool()) ;
-
bool res = false;
if (param == 0) // read
{
@@ -818,10 +675,6 @@ bool LLTextureCacheWorker::doWork(S32 param)
{
llassert_always(0);
}
-
- //set mFileAPRPool back, the local one will be released automatically.
-// mCache->setFileAPRPool(old_pool) ;
-
return res;
}
@@ -887,6 +740,7 @@ LLTextureCache::LLTextureCache(bool threaded)
mWorkersMutex(NULL),
mHeaderMutex(NULL),
mListMutex(NULL),
+ mHeaderAPRFile(NULL),
mReadOnly(FALSE),
mTexturesSizeTotal(0),
mDoPurge(FALSE)
@@ -926,6 +780,9 @@ S32 LLTextureCache::update(U32 max_time_ms)
}
}
+ unlockWorkers();
+
+ // call 'completed' with workers list unlocked (may call readComplete() or writeComplete()
for (responder_list_t::iterator iter1 = completed_list.begin();
iter1 != completed_list.end(); ++iter1)
{
@@ -934,8 +791,6 @@ S32 LLTextureCache::update(U32 max_time_ms)
responder->completed(success);
}
- unlockWorkers();
-
return res;
}
@@ -954,33 +809,48 @@ std::string LLTextureCache::getTextureFileName(const LLUUID& id)
{
std::string idstr = id.asString();
std::string delem = gDirUtilp->getDirDelimiter();
- std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr;
+ std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr + ".texture";
return filename;
}
-bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize)
+bool LLTextureCache::updateTextureEntryList(const LLUUID& id, S32 bodysize)
{
bool res = false;
bool purge = false;
- // Append UUID to end of texture entries
{
LLMutexLock lock(&mHeaderMutex);
- size_map_t::iterator iter = mTexturesSizeMap.find(id);
- if (iter == mTexturesSizeMap.end() || iter->second < bodysize)
+ size_map_t::iterator iter1 = mTexturesSizeMap.find(id);
+ if (iter1 == mTexturesSizeMap.end() || iter1->second < bodysize)
{
llassert_always(bodysize > 0);
- Entry* entry = new Entry(id, bodysize, time(NULL));
- LLAPRFile::writeEx(mTexturesDirEntriesFileName,
- (U8*)entry, -1, 1*sizeof(Entry),
- getLocalAPRFilePool());
- delete entry;
- if (iter != mTexturesSizeMap.end())
+ S32 oldbodysize = 0;
+ if (iter1 != mTexturesSizeMap.end())
+ {
+ oldbodysize = iter1->second;
+ }
+
+ Entry entry;
+ S32 idx = openAndReadEntry(id, entry, false);
+ if (idx < 0)
+ {
+ // TODO: change to llwarns
+ llerrs << "Failed to open entry: " << id << llendl;
+ removeFromCache(id);
+ return false;
+ }
+ else if (oldbodysize != entry.mBodySize)
{
- mTexturesSizeTotal -= iter->second;
+ // TODO: change to llwarns
+ llerrs << "Entry mismatch in mTextureSizeMap / mHeaderIDMap"
+ << " idx=" << idx << " oldsize=" << oldbodysize << " entrysize=" << entry.mBodySize << llendl;
}
+ entry.mBodySize = bodysize;
+ writeEntryAndClose(idx, entry);
+
+ mTexturesSizeTotal -= oldbodysize;
mTexturesSizeTotal += bodysize;
- mTexturesSizeMap[id] = bodysize;
+
if (mTexturesSizeTotal > sCacheMaxTexturesSize)
{
purge = true;
@@ -995,11 +865,59 @@ bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize)
return res;
}
+//debug
+BOOL LLTextureCache::isInCache(const LLUUID& id)
+{
+ LLMutexLock lock(&mHeaderMutex);
+ id_map_t::const_iterator iter = mHeaderIDMap.find(id);
+
+ return (iter != mHeaderIDMap.end()) ;
+}
+
+//debug
+BOOL LLTextureCache::isInLocal(const LLUUID& id)
+{
+ S32 local_size = 0;
+ std::string local_filename;
+
+ std::string filename = getLocalFileName(id);
+ // Is it a JPEG2000 file?
+ {
+ local_filename = filename + ".j2c";
+ local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool());
+ if (local_size > 0)
+ {
+ return TRUE ;
+ }
+ }
+
+ // If not, is it a jpeg file?
+ {
+ local_filename = filename + ".jpg";
+ local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool());
+ if (local_size > 0)
+ {
+ return TRUE ;
+ }
+ }
+
+ // Hmm... What about a targa file? (used for UI texture mostly)
+ {
+ local_filename = filename + ".tga";
+ local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool());
+ if (local_size > 0)
+ {
+ return TRUE ;
+ }
+ }
+
+ return FALSE ;
+}
//////////////////////////////////////////////////////////////////////////////
//static
const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB
-F32 LLTextureCache::sHeaderCacheVersion = 1.0f;
+F32 LLTextureCache::sHeaderCacheVersion = 1.3f;
U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE;
S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit
const char* entries_filename = "texture.entries";
@@ -1012,7 +930,6 @@ void LLTextureCache::setDirNames(ELLPath location)
mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename);
mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename);
mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname);
- mTexturesDirEntriesFileName = mTexturesDirName + delem + entries_filename;
}
void LLTextureCache::purgeCache(ELLPath location)
@@ -1020,7 +937,7 @@ void LLTextureCache::purgeCache(ELLPath location)
if (!mReadOnly)
{
setDirNames(location);
-
+ llassert_always(mHeaderAPRFile == NULL);
LLAPRFile::remove(mHeaderEntriesFileName, getLocalAPRFilePool());
LLAPRFile::remove(mHeaderDataFileName, getLocalAPRFilePool());
}
@@ -1063,83 +980,317 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only)
return max_size; // unused cache space
}
-struct lru_data
+//----------------------------------------------------------------------------
+// mHeaderMutex must be locked for the following functions!
+
+LLAPRFile* LLTextureCache::openHeaderEntriesFile(bool readonly, S32 offset)
+{
+ llassert_always(mHeaderAPRFile == NULL);
+ apr_int32_t flags = readonly ? APR_READ|APR_BINARY : APR_READ|APR_WRITE|APR_BINARY;
+ mHeaderAPRFile = new LLAPRFile(mHeaderEntriesFileName, flags, getLocalAPRFilePool());
+ mHeaderAPRFile->seek(APR_SET, offset);
+ return mHeaderAPRFile;
+}
+
+void LLTextureCache::closeHeaderEntriesFile()
+{
+ llassert_always(mHeaderAPRFile != NULL);
+ delete mHeaderAPRFile;
+ mHeaderAPRFile = NULL;
+}
+
+void LLTextureCache::readEntriesHeader()
+{
+ // mHeaderEntriesInfo initializes to default values so safe not to read it
+ llassert_always(mHeaderAPRFile == NULL);
+ if (LLAPRFile::isExist(mHeaderEntriesFileName, getLocalAPRFilePool()))
+ {
+ LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
+ getLocalAPRFilePool());
+ }
+}
+
+void LLTextureCache::writeEntriesHeader()
{
- lru_data(U32 t, S32 i, const LLUUID& id) { time=t; index=i; uuid=id; }
- U32 time;
- S32 index;
- LLUUID uuid;
- struct Compare
- {
- // lhs < rhs
- typedef const lru_data* lru_data_ptr;
- bool operator()(const lru_data_ptr& a, const lru_data_ptr& b) const
+ llassert_always(mHeaderAPRFile == NULL);
+ if (!mReadOnly)
+ {
+ LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
+ getLocalAPRFilePool());
+ }
+}
+
+static S32 mHeaderEntriesMaxWriteIdx = 0;
+
+S32 LLTextureCache::openAndReadEntry(const LLUUID& id, Entry& entry, bool create)
+{
+ S32 idx = -1;
+
+ id_map_t::iterator iter1 = mHeaderIDMap.find(id);
+ if (iter1 != mHeaderIDMap.end())
+ {
+ idx = iter1->second;
+ }
+
+ if (idx < 0)
+ {
+ if (create && !mReadOnly)
{
- if(a->time == b->time)
- return (a->index < b->index);
+ if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries)
+ {
+ // Add an entry to the end of the list
+ idx = mHeaderEntriesInfo.mEntries++;
+
+ }
+ else if (!mFreeList.empty())
+ {
+ idx = *(mFreeList.begin());
+ mFreeList.erase(mFreeList.begin());
+ }
else
- return (a->time >= b->time);
+ {
+ // Look for a still valid entry in the LRU
+ for (std::set<LLUUID>::iterator iter2 = mLRU.begin(); iter2 != mLRU.end();)
+ {
+ std::set<LLUUID>::iterator curiter2 = iter2++;
+ LLUUID oldid = *curiter2;
+ // Erase entry from LRU regardless
+ mLRU.erase(curiter2);
+ // Look up entry and use it if it is valid
+ id_map_t::iterator iter3 = mHeaderIDMap.find(oldid);
+ if (iter3 != mHeaderIDMap.end() && iter3->second >= 0)
+ {
+ idx = iter3->second;
+ mHeaderIDMap.erase(oldid);
+ mTexturesSizeMap.erase(oldid);
+ break;
+ }
+ }
+ // if (idx < 0) at this point, we will rebuild the LRU
+ // and retry if called from setHeaderCacheEntry(),
+ // otherwise this shouldn't happen and will trigger an error
+ }
+ if (idx >= 0)
+ {
+ // Set the header index
+ mHeaderIDMap[id] = idx;
+ llassert_always(mTexturesSizeMap.erase(id) == 0);
+ // Initialize the entry (will get written later)
+ entry.init(id, time(NULL));
+ // Update Header
+ writeEntriesHeader();
+ // Write Entry
+ S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+ LLAPRFile* aprfile = openHeaderEntriesFile(false, offset);
+ S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry));
+ llassert_always(bytes_written == sizeof(Entry));
+ mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx);
+ closeHeaderEntriesFile();
+ }
}
- };
-};
+ }
+ else
+ {
+ // Remove this entry from the LRU if it exists
+ mLRU.erase(id);
+ // Read the entry
+ S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+ LLAPRFile* aprfile = openHeaderEntriesFile(true, offset);
+ S32 bytes_read = aprfile->read((void*)&entry, (S32)sizeof(Entry));
+ llassert_always(bytes_read == sizeof(Entry));
+ llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize);
+ closeHeaderEntriesFile();
+ }
+ return idx;
+}
+
+void LLTextureCache::writeEntryAndClose(S32 idx, Entry& entry)
+{
+ if (idx >= 0)
+ {
+ if (!mReadOnly)
+ {
+ entry.mTime = time(NULL);
+ llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize);
+ if (entry.mBodySize > 0)
+ {
+ mTexturesSizeMap[entry.mID] = entry.mBodySize;
+ }
+// llinfos << "Updating TE: " << idx << ": " << id << " Size: " << entry.mBodySize << " Time: " << entry.mTime << llendl;
+ S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+ LLAPRFile* aprfile = openHeaderEntriesFile(false, offset);
+ S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry));
+ llassert_always(bytes_written == sizeof(Entry));
+ mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx);
+ closeHeaderEntriesFile();
+ }
+ }
+}
+
+U32 LLTextureCache::openAndReadEntries(std::vector<Entry>& entries)
+{
+ U32 num_entries = mHeaderEntriesInfo.mEntries;
+
+ mHeaderIDMap.clear();
+ mTexturesSizeMap.clear();
+ mFreeList.clear();
+ mTexturesSizeTotal = 0;
+
+ LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo));
+ for (U32 idx=0; idx<num_entries; idx++)
+ {
+ Entry entry;
+ S32 bytes_read = aprfile->read((void*)(&entry), (S32)sizeof(Entry));
+ if (bytes_read < sizeof(Entry))
+ {
+ llwarns << "Corrupted header entries, failed at " << idx << " / " << num_entries << llendl;
+ closeHeaderEntriesFile();
+ purgeAllTextures(false);
+ return 0;
+ }
+ entries.push_back(entry);
+// llinfos << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << llendl;
+ if (entry.mImageSize < 0)
+ {
+ mFreeList.insert(idx);
+ }
+ else
+ {
+ mHeaderIDMap[entry.mID] = idx;
+ if (entry.mBodySize > 0)
+ {
+ mTexturesSizeMap[entry.mID] = entry.mBodySize;
+ mTexturesSizeTotal += entry.mBodySize;
+ }
+ llassert_always(entry.mImageSize == 0 || entry.mImageSize > entry.mBodySize);
+ }
+ }
+ closeHeaderEntriesFile();
+ return num_entries;
+}
+
+void LLTextureCache::writeEntriesAndClose(const std::vector<Entry>& entries)
+{
+ S32 num_entries = entries.size();
+ llassert_always(num_entries == mHeaderEntriesInfo.mEntries);
+
+ if (!mReadOnly)
+ {
+ LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo));
+ for (S32 idx=0; idx<num_entries; idx++)
+ {
+ S32 bytes_written = aprfile->write((void*)(&entries[idx]), (S32)sizeof(Entry));
+ llassert_always(bytes_written == sizeof(Entry));
+ }
+ mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, num_entries-1);
+ closeHeaderEntriesFile();
+ }
+}
+
+//----------------------------------------------------------------------------
// Called from either the main thread or the worker thread
void LLTextureCache::readHeaderCache()
{
LLMutexLock lock(&mHeaderMutex);
- mHeaderEntriesInfo.mVersion = 0.f;
- mHeaderEntriesInfo.mEntries = 0;
- if (LLAPRFile::isExist(mHeaderEntriesFileName, getLocalAPRFilePool()))
- {
- LLAPRFile::readEx(mHeaderEntriesFileName,
- (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
- getLocalAPRFilePool());
- }
+
+ mLRU.clear(); // always clear the LRU
+
+ readEntriesHeader();
+
if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion)
{
if (!mReadOnly)
{
- // Info with 0 entries
- mHeaderEntriesInfo.mVersion = sHeaderCacheVersion;
-
- LLAPRFile::writeEx(mHeaderEntriesFileName,
- (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
- getLocalAPRFilePool());
+ purgeAllTextures(false);
}
}
else
{
- S32 num_entries = mHeaderEntriesInfo.mEntries;
+ std::vector<Entry> entries;
+ U32 num_entries = openAndReadEntries(entries);
if (num_entries)
{
- Entry* entries = new Entry[num_entries];
+ U32 empty_entries = 0;
+ typedef std::pair<U32, LLUUID> lru_data_t;
+ std::set<lru_data_t> lru;
+ std::vector<LLUUID> purge_list;
+ for (U32 i=0; i<num_entries; i++)
+ {
+ Entry& entry = entries[i];
+ const LLUUID& id = entry.mID;
+ if (entry.mImageSize < 0)
+ {
+ // This will be in the Free List, don't put it in the LRY
+ ++empty_entries;
+ }
+ else
+ {
+ lru.insert(std::make_pair(entry.mTime, id));
+ if (entry.mBodySize > 0)
+ {
+ if (entry.mBodySize > entry.mImageSize)
+ {
+ // Shouldn't happen, failsafe only
+ llwarns << "Bad entry: " << i << ": " << id << ": BodySize: " << entry.mBodySize << llendl;
+ purge_list.push_back(id);
+ }
+ }
+ }
+ }
+ if (num_entries > sCacheMaxEntries)
+ {
+ // Special case: cache size was reduced, need to remove entries
+ // Note: After we prune entries, we will call this again and create the LRU
+ U32 entries_to_purge = (num_entries-empty_entries) - sCacheMaxEntries;
+ if (entries_to_purge > 0)
+ {
+ for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter)
+ {
+ purge_list.push_back(iter->second);
+ if (--entries_to_purge <= 0)
+ break;
+ }
+ }
+ }
+ else
{
- LLAPRFile::readEx(mHeaderEntriesFileName,
- (U8*)entries, sizeof(EntriesInfo), num_entries*sizeof(Entry),
- getLocalAPRFilePool());
+ S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE);
+ for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter)
+ {
+ mLRU.insert(iter->second);
+// llinfos << "LRU: " << iter->first << " : " << iter->second << llendl;
+ if (--lru_entries <= 0)
+ break;
+ }
}
- typedef std::set<lru_data*, lru_data::Compare> lru_set_t;
- lru_set_t lru;
- for (S32 i=0; i<num_entries; i++)
+
+ if (purge_list.size() > 0)
{
- if (entries[i].mSize >= 0) // -1 indicates erased entry, skip
+ for (std::vector<LLUUID>::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter)
{
- const LLUUID& id = entries[i].mID;
- lru.insert(new lru_data(entries[i].mTime, i, id));
- mHeaderIDMap[id] = i;
+ removeFromCache(*iter);
}
+ // If we removed any entries, we need to rebuild the entries list,
+ // write the header, and call this again
+ std::vector<Entry> new_entries;
+ for (U32 i=0; i<num_entries; i++)
+ {
+ const Entry& entry = entries[i];
+ if (entry.mImageSize >=0)
+ {
+ new_entries.push_back(entry);
+ }
+ }
+ llassert_always(new_entries.size() <= sCacheMaxEntries);
+ mHeaderEntriesInfo.mEntries = new_entries.size();
+ writeEntriesAndClose(new_entries);
+ readHeaderCache(); // repeat with new entries file
}
- mLRU.clear();
- S32 lru_entries = sCacheMaxEntries / 10;
- for (lru_set_t::iterator iter = lru.begin(); iter != lru.end(); ++iter)
+ else
{
- lru_data* data = *iter;
- mLRU[data->index] = data->uuid;
- if (--lru_entries <= 0)
- break;
+ writeEntriesAndClose(entries);
}
- for_each(lru.begin(), lru.end(), DeletePointer());
- delete[] entries;
}
}
}
@@ -1162,13 +1313,21 @@ void LLTextureCache::purgeAllTextures(bool purge_directories)
LLFile::rmdir(dirname);
}
}
- LLAPRFile::remove(mTexturesDirEntriesFileName, getLocalAPRFilePool());
if (purge_directories)
{
LLFile::rmdir(mTexturesDirName);
}
}
+ mHeaderIDMap.clear();
mTexturesSizeMap.clear();
+ mTexturesSizeTotal = 0;
+ mFreeList.clear();
+ mTexturesSizeTotal = 0;
+
+ // Info with 0 entries
+ mHeaderEntriesInfo.mVersion = sHeaderCacheVersion;
+ mHeaderEntriesInfo.mEntries = 0;
+ writeEntriesHeader();
}
void LLTextureCache::purgeTextures(bool validate)
@@ -1182,51 +1341,37 @@ void LLTextureCache::purgeTextures(bool validate)
LLAppViewer::instance()->pauseMainloopTimeout();
LLMutexLock lock(&mHeaderMutex);
-
- S32 filesize = LLAPRFile::size(mTexturesDirEntriesFileName, getLocalAPRFilePool());
- S32 num_entries = filesize / sizeof(Entry);
- if (num_entries * (S32)sizeof(Entry) != filesize)
- {
- LL_WARNS("TextureCache") << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL;
- purgeAllTextures(false);
- return;
- }
- if (num_entries == 0)
+
+ llinfos << "TEXTURE CACHE: Purging." << llendl;
+
+ // Read the entries list
+ std::vector<Entry> entries;
+ U32 num_entries = openAndReadEntries(entries);
+ if (!num_entries)
{
- return; // nothing to do
+ writeEntriesAndClose(entries);
+ return; // nothing to purge
}
- Entry* entries = new Entry[num_entries];
- S32 bytes_read = LLAPRFile::readEx(mTexturesDirEntriesFileName,
- (U8*)entries, 0, num_entries*sizeof(Entry),
- getLocalAPRFilePool());
- if (bytes_read != filesize)
- {
- LL_WARNS("TextureCache") << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL;
- purgeAllTextures(false);
- return;
- }
-
- LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Reading Entries..." << LL_ENDL;
-
- std::map<LLUUID, S32> entry_idx_map;
- S64 total_size = 0;
- for (S32 idx=0; idx<num_entries; idx++)
- {
- const LLUUID& id = entries[idx].mID;
- LL_DEBUGS("TextureCache") << "Entry: " << id << " Size: " << entries[idx].mSize << " Time: " << entries[idx].mTime << LL_ENDL;
- std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id);
- if (iter != entry_idx_map.end())
+ // Use mTexturesSizeMap to collect UUIDs of textures with bodies
+ typedef std::set<std::pair<U32,S32> > time_idx_set_t;
+ std::set<std::pair<U32,S32> > time_idx_set;
+ for (size_map_t::iterator iter1 = mTexturesSizeMap.begin();
+ iter1 != mTexturesSizeMap.end(); ++iter1)
+ {
+ if (iter1->second > 0)
{
- // Newer entry replacing older entry
- S32 pidx = iter->second;
- total_size -= entries[pidx].mSize;
- entries[pidx].mSize = 0; // flag: skip older entry
+ id_map_t::iterator iter2 = mHeaderIDMap.find(iter1->first);
+ if (iter2 != mHeaderIDMap.end())
+ {
+ S32 idx = iter2->second;
+ time_idx_set.insert(std::make_pair(entries[idx].mTime, idx));
+// llinfos << "TIME: " << entries[idx].mTime << " TEX: " << entries[idx].mID << " IDX: " << idx << " Size: " << entries[idx].mImageSize << llendl;
+ }
}
- entry_idx_map[id] = idx;
- total_size += entries[idx].mSize;
}
-
+
+ // Validate 1/256th of the files on startup
U32 validate_idx = 0;
if (validate)
{
@@ -1235,19 +1380,17 @@ void LLTextureCache::purgeTextures(bool validate)
gSavedSettings.setU32("CacheValidateCounter", next_idx);
LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL;
}
-
- S64 min_cache_size = (sCacheMaxTexturesSize * 9) / 10;
+
+ S64 cache_size = mTexturesSizeTotal;
+ S64 purged_cache_size = (sCacheMaxTexturesSize * (S64)((1.f-TEXTURE_CACHE_PURGE_AMOUNT)*100)) / 100;
S32 purge_count = 0;
- S32 next_idx = 0;
- for (S32 idx=0; idx<num_entries; idx++)
+ for (time_idx_set_t::iterator iter = time_idx_set.begin();
+ iter != time_idx_set.end(); ++iter)
{
- if (entries[idx].mSize == 0)
- {
- continue;
- }
+ S32 idx = iter->second;
bool purge_entry = false;
std::string filename = getTextureFileName(entries[idx].mID);
- if (total_size >= min_cache_size)
+ if (cache_size >= purged_cache_size)
{
purge_entry = true;
}
@@ -1257,60 +1400,44 @@ void LLTextureCache::purgeTextures(bool validate)
U32 uuididx = entries[idx].mID.mData[0];
if (uuididx == validate_idx)
{
- LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mSize << LL_ENDL;
+ LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL;
S32 bodysize = LLAPRFile::size(filename, getLocalAPRFilePool());
- if (bodysize != entries[idx].mSize)
+ if (bodysize != entries[idx].mBodySize)
{
- LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize
+ LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mBodySize
<< filename << LL_ENDL;
purge_entry = true;
}
}
}
+ else
+ {
+ break;
+ }
+
if (purge_entry)
{
purge_count++;
LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL;
LLAPRFile::remove(filename, getLocalAPRFilePool());
- total_size -= entries[idx].mSize;
- entries[idx].mSize = 0;
- }
- else
- {
- if (next_idx != idx)
- {
- entries[next_idx] = entries[idx];
- }
- ++next_idx;
+ cache_size -= entries[idx].mBodySize;
+ mTexturesSizeTotal -= entries[idx].mBodySize;
+ entries[idx].mBodySize = 0;
+ mTexturesSizeMap.erase(entries[idx].mID);
}
}
- num_entries = next_idx;
LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " << num_entries << LL_ENDL;
-
- LLAPRFile::remove(mTexturesDirEntriesFileName, getLocalAPRFilePool());
- LLAPRFile::writeEx(mTexturesDirEntriesFileName,
- (U8*)&entries[0], 0, num_entries*sizeof(Entry),
- getLocalAPRFilePool());
-
- mTexturesSizeTotal = 0;
- mTexturesSizeMap.clear();
- for (S32 idx=0; idx<num_entries; idx++)
- {
- mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize;
- mTexturesSizeTotal += entries[idx].mSize;
- }
- llassert(mTexturesSizeTotal == total_size);
-
- delete[] entries;
+ writeEntriesAndClose(entries);
+
// *FIX:Mani - watchdog back on.
LLAppViewer::instance()->resumeMainloopTimeout();
LL_INFOS("TextureCache") << "TEXTURE CACHE:"
<< " PURGED: " << purge_count
<< " ENTRIES: " << num_entries
- << " CACHE SIZE: " << total_size / 1024*1024 << " MB"
+ << " CACHE SIZE: " << mTexturesSizeTotal / 1024*1024 << " MB"
<< llendl;
}
@@ -1340,78 +1467,39 @@ LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle)
}
//////////////////////////////////////////////////////////////////////////////
-
// Called from work thread
-S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize)
-{
- bool retry = false;
- S32 idx = -1;
+// Reads imagesize from the header, updates timestamp
+S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, S32& imagesize)
+{
+ LLMutexLock lock(&mHeaderMutex);
+ Entry entry;
+ S32 idx = openAndReadEntry(id, entry, false);
+ if (idx >= 0)
{
- LLMutexLock lock(&mHeaderMutex);
- id_map_t::iterator iter = mHeaderIDMap.find(id);
- if (iter != mHeaderIDMap.end())
- {
- idx = iter->second;
- }
- else if (touch && !mReadOnly)
- {
- if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries)
- {
- // Add an entry
- idx = mHeaderEntriesInfo.mEntries++;
- mHeaderIDMap[id] = idx;
- // Update Info
- LLAPRFile::writeEx(mHeaderEntriesFileName,
- (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
- getLocalAPRFilePool());
- }
- else if (!mLRU.empty())
- {
- idx = mLRU.begin()->first; // will be erased below
- const LLUUID& oldid = mLRU.begin()->second;
- mHeaderIDMap.erase(oldid);
- mTexturesSizeMap.erase(oldid);
- mHeaderIDMap[id] = idx;
- }
- else
- {
- idx = -1;
- retry = true;
- }
- }
- if (idx >= 0)
- {
- if (touch && !mReadOnly)
- {
- // Update the lru entry
- mLRU.erase(idx);
- llassert_always(imagesize && *imagesize > 0);
- Entry* entry = new Entry(id, *imagesize, time(NULL));
- S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
- LLAPRFile::writeEx(mHeaderEntriesFileName,
- (U8*)entry, offset, sizeof(Entry),
- getLocalAPRFilePool());
- delete entry;
- }
- else if (imagesize)
- {
- // Get the image size
- Entry entry;
- S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+ imagesize = entry.mImageSize;
+ writeEntryAndClose(idx, entry); // updates time
+ }
+ return idx;
+}
- LLAPRFile::readEx(mHeaderEntriesFileName,
- (U8*)&entry, offset, sizeof(Entry),
- getLocalAPRFilePool());
- *imagesize = entry.mSize;
- }
- }
+// Writes imagesize to the header, updates timestamp
+S32 LLTextureCache::setHeaderCacheEntry(const LLUUID& id, S32 imagesize)
+{
+ LLMutexLock lock(&mHeaderMutex);
+ llassert_always(imagesize >= 0);
+ Entry entry;
+ S32 idx = openAndReadEntry(id, entry, true);
+ if (idx >= 0)
+ {
+ entry.mImageSize = imagesize;
+ writeEntryAndClose(idx, entry);
}
- if (retry)
+ else // retry
{
- readHeaderCache(); // updates the lru
+ readHeaderCache(); // We couldn't write an entry, so refresh the LRU
llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries);
- idx = getHeaderCacheEntry(id, touch, imagesize); // assert above ensures no inf. recursion
+ idx = setHeaderCacheEntry(id, imagesize); // assert above ensures no inf. recursion
}
return idx;
}
@@ -1427,8 +1515,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const std::string& filena
// so let the thread handle it
LLMutexLock lock(&mWorkersMutex);
LLTextureCacheWorker* worker = new LLTextureCacheLocalFileWorker(this, priority, filename, id,
- NULL, size, offset, 0,
- responder);
+ NULL, size, offset, 0,
+ responder);
handle_t handle = worker->read();
mReaders[handle] = worker;
return handle;
@@ -1441,8 +1529,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 pri
// so let the thread handle it
LLMutexLock lock(&mWorkersMutex);
LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id,
- NULL, size, offset, 0,
- responder);
+ NULL, size, offset,
+ 0, responder);
handle_t handle = worker->read();
mReaders[handle] = worker;
return handle;
@@ -1453,7 +1541,7 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort)
{
lockWorkers();
handle_map_t::iterator iter = mReaders.find(handle);
- llassert_always(iter != mReaders.end());
+ llassert_always(iter != mReaders.end() || abort);
LLTextureCacheWorker* worker = iter->second;
bool res = worker->complete();
if (res || abort)
@@ -1487,19 +1575,13 @@ LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 prio
purgeTextures(false);
mDoPurge = FALSE;
}
- if (datasize >= TEXTURE_CACHE_ENTRY_SIZE)
- {
- LLMutexLock lock(&mWorkersMutex);
- llassert_always(imagesize > 0);
- LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id,
- data, datasize, 0,
- imagesize, responder);
- handle_t handle = worker->write();
- mWriters[handle] = worker;
- return handle;
- }
- delete responder;
- return LLWorkerThread::nullHandle();
+ LLMutexLock lock(&mWorkersMutex);
+ LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id,
+ data, datasize, 0,
+ imagesize, responder);
+ handle_t handle = worker->write();
+ mWriters[handle] = worker;
+ return handle;
}
bool LLTextureCache::writeComplete(handle_t handle, bool abort)
@@ -1542,25 +1624,17 @@ void LLTextureCache::addCompleted(Responder* responder, bool success)
bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id)
{
- if (mReadOnly)
- {
- return false;
- }
- LLMutexLock lock(&mHeaderMutex);
- id_map_t::iterator iter = mHeaderIDMap.find(id);
- if (iter != mHeaderIDMap.end())
+ if (!mReadOnly)
{
- S32 idx = iter->second;
+ LLMutexLock lock(&mHeaderMutex);
+ Entry entry;
+ S32 idx = openAndReadEntry(id, entry, false);
if (idx >= 0)
{
- Entry* entry = new Entry(id, -1, time(NULL));
- S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
-
- LLAPRFile::writeEx(mHeaderEntriesFileName,
- (U8*)entry, offset, sizeof(Entry),
- getLocalAPRFilePool());
- delete entry;
- mLRU[idx] = id;
+ entry.mImageSize = -1;
+ entry.mBodySize = 0;
+ writeEntryAndClose(idx, entry);
+ mFreeList.insert(idx);
mHeaderIDMap.erase(id);
mTexturesSizeMap.erase(id);
return true;
diff --git a/indra/newview/lltexturecache.h b/indra/newview/lltexturecache.h
index 68b1458e9a..bc9c988648 100644
--- a/indra/newview/lltexturecache.h
+++ b/indra/newview/lltexturecache.h
@@ -48,6 +48,27 @@ class LLTextureCache : public LLWorkerThread
friend class LLTextureCacheRemoteWorker;
friend class LLTextureCacheLocalFileWorker;
+private:
+ // Entries
+ struct EntriesInfo
+ {
+ EntriesInfo() : mVersion(0.f), mEntries(0) {}
+ F32 mVersion;
+ U32 mEntries;
+ };
+ struct Entry
+ {
+ Entry() {}
+ Entry(const LLUUID& id, S32 imagesize, S32 bodysize, U32 time) :
+ mID(id), mImageSize(imagesize), mBodySize(bodysize), mTime(time) {}
+ void init(const LLUUID& id, U32 time) { mID = id, mImageSize = 0; mBodySize = 0; mTime = time; }
+ LLUUID mID; // 16 bytes
+ S32 mImageSize; // total size of image if known
+ S32 mBodySize; // size of body file in body cache
+ U32 mTime; // seconds since 1/1/1970
+ };
+
+
public:
class Responder : public LLResponder
@@ -106,10 +127,16 @@ public:
// debug
S32 getNumReads() { return mReaders.size(); }
S32 getNumWrites() { return mWriters.size(); }
+ S64 getUsage() { return mTexturesSizeTotal; }
+ S64 getMaxUsage() { return sCacheMaxTexturesSize; }
+ U32 getEntries() { return mHeaderEntriesInfo.mEntries; }
+ U32 getMaxEntries() { return sCacheMaxEntries; };
+ BOOL isInCache(const LLUUID& id) ;
+ BOOL isInLocal(const LLUUID& id) ;
protected:
// Accessed by LLTextureCacheWorker
- bool appendToTextureEntryList(const LLUUID& id, S32 size);
+ bool updateTextureEntryList(const LLUUID& id, S32 size);
std::string getLocalFileName(const LLUUID& id);
std::string getTextureFileName(const LLUUID& id);
void addCompleted(Responder* responder, bool success);
@@ -122,7 +149,16 @@ private:
void readHeaderCache();
void purgeAllTextures(bool purge_directories);
void purgeTextures(bool validate);
- S32 getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize = NULL);
+ LLAPRFile* openHeaderEntriesFile(bool readonly, S32 offset);
+ void closeHeaderEntriesFile();
+ void readEntriesHeader();
+ void writeEntriesHeader();
+ S32 openAndReadEntry(const LLUUID& id, Entry& entry, bool create);
+ void writeEntryAndClose(S32 idx, Entry& entry);
+ U32 openAndReadEntries(std::vector<Entry>& entries);
+ void writeEntriesAndClose(const std::vector<Entry>& entries);
+ S32 getHeaderCacheEntry(const LLUUID& id, S32& imagesize);
+ S32 setHeaderCacheEntry(const LLUUID& id, S32 imagesize);
bool removeHeaderCacheEntry(const LLUUID& id);
void lockHeaders() { mHeaderMutex.lock(); }
void unlockHeaders() { mHeaderMutex.unlock(); }
@@ -132,6 +168,7 @@ private:
LLMutex mWorkersMutex;
LLMutex mHeaderMutex;
LLMutex mListMutex;
+ LLAPRFile* mHeaderAPRFile;
typedef std::map<handle_t, LLTextureCacheWorker*> handle_map_t;
handle_map_t mReaders;
@@ -145,42 +182,28 @@ private:
BOOL mReadOnly;
- // Entries
- struct EntriesInfo
- {
- F32 mVersion;
- U32 mEntries;
- };
- struct Entry
- {
- Entry() {}
- Entry(const LLUUID& id, S32 size, U32 time) : mID(id), mSize(size), mTime(time) {}
- LLUUID mID; // 128 bits
- S32 mSize; // total size of image if known (NOT size cached)
- U32 mTime; // seconds since 1/1/1970
- };
-
// HEADERS (Include first mip)
std::string mHeaderEntriesFileName;
std::string mHeaderDataFileName;
EntriesInfo mHeaderEntriesInfo;
- typedef std::map<S32,LLUUID> index_map_t;
- index_map_t mLRU; // index, id; stored as a map for fast removal
+ std::set<S32> mFreeList; // deleted entries
+ std::set<LLUUID> mLRU;
typedef std::map<LLUUID,S32> id_map_t;
id_map_t mHeaderIDMap;
// BODIES (TEXTURES minus headers)
std::string mTexturesDirName;
- std::string mTexturesDirEntriesFileName;
typedef std::map<LLUUID,S32> size_map_t;
size_map_t mTexturesSizeMap;
S64 mTexturesSizeTotal;
LLAtomic32<BOOL> mDoPurge;
-
+
// Statics
static F32 sHeaderCacheVersion;
static U32 sCacheMaxEntries;
static S64 sCacheMaxTexturesSize;
};
+extern const S32 TEXTURE_CACHE_ENTRY_SIZE;
+
#endif // LL_LLTEXTURECACHE_H
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 88fc7f98c0..c918f98895 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -32,108 +32,35 @@
#include "llviewerprecompiledheaders.h"
+#include <iostream>
+
#include "llstl.h"
#include "lltexturefetch.h"
#include "llcurl.h"
+#include "lldir.h"
#include "llhttpclient.h"
+#include "llhttpstatuscodes.h"
#include "llimage.h"
#include "llimageworker.h"
#include "llworkerthread.h"
#include "llagent.h"
#include "lltexturecache.h"
+#include "llviewercontrol.h"
#include "llviewertexturelist.h"
#include "llviewertexture.h"
#include "llviewerregion.h"
+#include "llworld.h"
//////////////////////////////////////////////////////////////////////////////
-//static
class LLTextureFetchWorker : public LLWorkerClass
{
-friend class LLTextureFetch;
-
-private:
-#if 0
- class URLResponder : public LLHTTPClient::Responder
- {
- public:
- URLResponder(LLTextureFetch* fetcher, const LLUUID& id)
- : mFetcher(fetcher), mID(id)
- {
- }
- ~URLResponder()
- {
- }
- virtual void error(U32 status, const std::string& reason)
- {
- mFetcher->lockQueue();
- LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
- if (worker)
- {
-// llwarns << "LLTextureFetchWorker::URLResponder::error " << status << ": " << reason << llendl;
- worker->callbackURLReceived(LLSD(), false);
- }
- mFetcher->unlockQueue();
- }
- virtual void result(const LLSD& content)
- {
- mFetcher->lockQueue();
- LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
- if (worker)
- {
- worker->callbackURLReceived(content, true);
- }
- mFetcher->unlockQueue();
- }
- private:
- LLTextureFetch* mFetcher;
- LLUUID mID;
- };
-
- class HTTPGetResponder : public LLHTTPClient::Responder
- {
- public:
- HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id)
- : mFetcher(fetcher), mID(id)
- {
- }
- ~HTTPGetResponder()
- {
- }
- virtual void completed(U32 status, const std::stringstream& content)
- {
- mFetcher->lockQueue();
- LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
- if (worker)
- {
- const std::string& cstr = content.str();
- if (200 <= status && status < 300)
- {
- if (203 == status) // partial information (i.e. last block)
- {
- worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), true);
- }
- else
- {
- worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), false);
- }
- }
- else
- {
-// llinfos << "LLTextureFetchWorker::HTTPGetResponder::error " << status << ": " << cstr << llendl;
- worker->callbackHttpGet(NULL, -1, true);
- }
- }
- mFetcher->unlockQueue();
- }
- private:
- LLTextureFetch* mFetcher;
- LLUUID mID;
- };
-#endif
+ friend class LLTextureFetch;
+ friend class HTTPGetResponder;
+private:
class CacheReadResponder : public LLTextureCache::ReadResponder
{
public:
@@ -179,20 +106,20 @@ private:
LLUUID mID;
};
- class DecodeResponder : public LLResponder
+ class DecodeResponder : public LLImageDecodeThread::Responder
{
public:
DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker)
: mFetcher(fetcher), mID(id), mWorker(worker)
{
}
- virtual void completed(bool success)
+ virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
{
mFetcher->lockQueue();
LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
if (worker)
{
- worker->callbackDecoded(success);
+ worker->callbackDecoded(success, raw, aux);
}
mFetcher->unlockQueue();
}
@@ -227,37 +154,45 @@ public:
~LLTextureFetchWorker();
void relese() { --mActiveCount; }
+ void callbackHttpGet(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer,
+ bool last_block, bool success);
+ void callbackCacheRead(bool success, LLImageFormatted* image,
+ S32 imagesize, BOOL islocal);
+ void callbackCacheWrite(bool success);
+ void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux);
+
+ void setGetStatus(U32 status, const std::string& reason)
+ {
+ mGetStatus = status;
+ mGetReason = reason;
+ }
+
protected:
LLTextureFetchWorker(LLTextureFetch* fetcher, const LLUUID& id, const LLHost& host,
F32 priority, S32 discard, S32 size);
+ LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host,
+ F32 priority, S32 discard, S32 size);
private:
/*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD)
/*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
- virtual std::string getName() { return LLStringUtil::null; }
void resetFormattedData();
void setImagePriority(F32 priority);
void setDesiredDiscard(S32 discard, S32 size);
bool insertPacket(S32 index, U8* data, S32 size);
void clearPackets();
+ void setupPacketData();
U32 calcWorkPriority();
void removeFromCache();
bool processSimulatorPackets();
- bool decodeImage();
bool writeToCacheComplete();
- void lockWorkData() { mWorkMutex.lock(); }
- void unlockWorkData() { mWorkMutex.unlock(); }
+ void lockWorkMutex() { mWorkMutex.lock(); }
+ void unlockWorkMutex() { mWorkMutex.unlock(); }
- void callbackURLReceived(const LLSD& data, bool success);
- void callbackHttpGet(U8* data, S32 data_size, bool last_block);
- void callbackCacheRead(bool success, LLImageFormatted* image,
- S32 imagesize, BOOL islocal);
- void callbackCacheWrite(bool success);
- void callbackDecoded(bool success);
-
private:
enum e_state // mState
{
@@ -268,8 +203,8 @@ private:
CACHE_POST,
LOAD_FROM_NETWORK,
LOAD_FROM_SIMULATOR,
- LOAD_FROM_HTTP_GET_URL,
- LOAD_FROM_HTTP_GET_DATA,
+ SEND_HTTP_REQ,
+ WAIT_HTTP_REQ,
DECODE_IMAGE,
DECODE_IMAGE_UPDATE,
WRITE_TO_CACHE,
@@ -280,19 +215,17 @@ private:
{
UNSENT = 0,
QUEUED = 1,
- SENT_SIM = 2,
- SENT_URL = 3,
- SENT_HTTP = 4
+ SENT_SIM = 2
};
static const char* sStateDescs[];
e_state mState;
LLTextureFetch* mFetcher;
- LLImageWorker* mImageWorker;
LLPointer<LLImageFormatted> mFormattedImage;
LLPointer<LLImageRaw> mRawImage;
LLPointer<LLImageRaw> mAuxImage;
LLUUID mID;
LLHost mHost;
+ std::string mUrl;
U8 mType;
F32 mImagePriority;
U32 mWorkPriority;
@@ -314,15 +247,18 @@ private:
S32 mCachedSize;
BOOL mLoaded;
e_request_state mSentRequest;
+ handle_t mDecodeHandle;
BOOL mDecoded;
BOOL mWritten;
BOOL mNeedsAux;
BOOL mHaveAllData;
BOOL mInLocalCache;
+ S32 mHTTPFailCount;
S32 mRetryAttempt;
- std::string mURL;
S32 mActiveCount;
-
+ U32 mGetStatus;
+ std::string mGetReason;
+
// Work Data
LLMutex mWorkMutex;
struct PacketData
@@ -340,25 +276,79 @@ private:
U8 mImageCodec;
};
-class LLTextureFetchLocalFileWorker : public LLTextureFetchWorker
-{
-friend class LLTextureFetch;
-
-protected:
- LLTextureFetchLocalFileWorker(LLTextureFetch* fetcher, const std::string& filename, const LLUUID& id, const LLHost& host,
- F32 priority, S32 discard, S32 size)
- : LLTextureFetchWorker(fetcher, id, host, priority, discard, size),
- mFileName(filename)
- {}
+//////////////////////////////////////////////////////////////////////////////
-private:
- /*virtual*/ std::string getName() { return mFileName; }
+class HTTPGetResponder : public LLCurl::Responder
+{
+ LOG_CLASS(HTTPGetResponder);
+public:
+ HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset)
+ : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset)
+ {
+ }
+ ~HTTPGetResponder()
+ {
+ }
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ if ((gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog")) || (gSavedSettings.getBOOL("LogTextureDownloadsToSimulator")))
+ {
+ mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime);
+ U64 timeNow = LLTimer::getTotalTime();
+ mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+ mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
+ mFetcher->mTextureInfo.setRequestOffset(mID, mOffset);
+ mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
+ }
+ lldebugs << "HTTP COMPLETE: " << mID << llendl;
+ mFetcher->lockQueue();
+ LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+ if (worker)
+ {
+ bool success = false;
+ bool partial = false;
+ if (200 <= status && status < 300)
+ {
+ success = true;
+ if (203 == status) // partial information (i.e. last block)
+ {
+ partial = true;
+ }
+ }
+ else
+ {
+ worker->setGetStatus(status, reason);
+// llwarns << status << ": " << reason << llendl;
+ }
+ if (!success)
+ {
+ worker->setGetStatus(status, reason);
+// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl;
+ }
+ mFetcher->removeFromHTTPQueue(mID);
+ worker->callbackHttpGet(channels, buffer, partial, success);
+ }
+ else
+ {
+ mFetcher->removeFromHTTPQueue(mID);
+ llwarns << "Worker not found: " << mID << llendl;
+ }
+ mFetcher->unlockQueue();
+ }
+
private:
- std::string mFileName;
+ LLTextureFetch* mFetcher;
+ LLUUID mID;
+ U64 mStartTime;
+ S32 mRequestedSize;
+ U32 mOffset;
};
+//////////////////////////////////////////////////////////////////////////////
//static
const char* LLTextureFetchWorker::sStateDescs[] = {
@@ -368,8 +358,8 @@ const char* LLTextureFetchWorker::sStateDescs[] = {
"CACHE_POST",
"LOAD_FROM_NETWORK",
"LOAD_FROM_SIMULATOR",
- "LOAD_FROM_HTTP_URL",
- "LOAD_FROM_HTTP_DATA",
+ "SEND_HTTP_REQ",
+ "WAIT_HTTP_REQ",
"DECODE_IMAGE",
"DECODE_IMAGE_UPDATE",
"WRITE_TO_CACHE",
@@ -380,6 +370,7 @@ const char* LLTextureFetchWorker::sStateDescs[] = {
// called from MAIN THREAD
LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
+ const std::string& url, // Optional URL
const LLUUID& id, // Image UUID
const LLHost& host, // Simulator host
F32 priority, // Priority
@@ -388,9 +379,9 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
: LLWorkerClass(fetcher, "TextureFetch"),
mState(INIT),
mFetcher(fetcher),
- mImageWorker(NULL),
mID(id),
mHost(host),
+ mUrl(url),
mImagePriority(priority),
mWorkPriority(0),
mRequestedPriority(0.f),
@@ -404,18 +395,21 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mBuffer(NULL),
mBufferSize(0),
mRequestedSize(0),
- mDesiredSize(FIRST_PACKET_SIZE),
+ mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE),
mFileSize(0),
mCachedSize(0),
mLoaded(FALSE),
mSentRequest(UNSENT),
+ mDecodeHandle(0),
mDecoded(FALSE),
mWritten(FALSE),
mNeedsAux(FALSE),
mHaveAllData(FALSE),
mInLocalCache(FALSE),
+ mHTTPFailCount(0),
mRetryAttempt(0),
mActiveCount(0),
+ mGetStatus(0),
mWorkMutex(NULL),
mFirstPacket(0),
mLastPacket(-1),
@@ -440,7 +434,7 @@ LLTextureFetchWorker::~LLTextureFetchWorker()
// << " Requested=" << mRequestedDiscard
// << " Desired=" << mDesiredDiscard << llendl;
llassert_always(!haveWork());
- lockWorkData();
+ lockWorkMutex();
if (mCacheReadHandle != LLTextureCache::nullHandle())
{
mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
@@ -449,13 +443,9 @@ LLTextureFetchWorker::~LLTextureFetchWorker()
{
mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
}
- if (mImageWorker)
- {
- mImageWorker->scheduleDelete();
- }
mFormattedImage = NULL;
clearPackets();
- unlockWorkData();
+ unlockWorkMutex();
}
void LLTextureFetchWorker::clearPackets()
@@ -467,6 +457,38 @@ void LLTextureFetchWorker::clearPackets()
mFirstPacket = 0;
}
+void LLTextureFetchWorker::setupPacketData()
+{
+ S32 data_size = 0;
+ if (mFormattedImage.notNull())
+ {
+ data_size = mFormattedImage->getDataSize();
+ }
+ if (data_size > 0)
+ {
+ // Only used for simulator requests
+ mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1;
+ if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size)
+ {
+ llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl;
+ removeFromCache();
+ resetFormattedData();
+ clearPackets();
+ }
+ else if (mFileSize > 0)
+ {
+ mLastPacket = mFirstPacket-1;
+ mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1;
+ }
+ else
+ {
+ // This file was cached using HTTP so we have to refetch the first packet
+ resetFormattedData();
+ clearPackets();
+ }
+ }
+}
+
U32 LLTextureFetchWorker::calcWorkPriority()
{
// llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerTexture::maxDecodePriority());
@@ -538,7 +560,6 @@ void LLTextureFetchWorker::resetFormattedData()
// Called from MAIN thread
void LLTextureFetchWorker::startWork(S32 param)
{
- llassert(mImageWorker == NULL);
llassert(mFormattedImage.isNull());
}
@@ -549,6 +570,14 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
LLMutexLock lock(&mWorkMutex);
+ if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED)))
+ {
+ if (mState < WRITE_TO_CACHE)
+ {
+ return true; // abort
+ }
+ }
+
if (mFetcher->mDebugPause)
{
return false; // debug: don't do any work
@@ -563,16 +592,9 @@ bool LLTextureFetchWorker::doWork(S32 param)
mFetchTimer.reset();
}
- if (mImagePriority <= 0.0f)
- {
- if (mState < WRITE_TO_CACHE)
- {
- return true; // cancel request
- }
- }
-
if (mState == INIT)
{
+ mRawImage = NULL ;
mRequestedDiscard = -1;
mLoadedDiscard = -1;
mDecodedDiscard = -1;
@@ -590,8 +612,9 @@ bool LLTextureFetchWorker::doWork(S32 param)
clearPackets(); // TODO: Shouldn't be necessary
mCacheReadHandle = LLTextureCache::nullHandle();
mCacheWriteHandle = LLTextureCache::nullHandle();
- mURL.clear();
mState = LOAD_FROM_TEXTURE_CACHE;
+ LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority)
+ << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
// fall through
}
@@ -612,16 +635,27 @@ bool LLTextureFetchWorker::doWork(S32 param)
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
- if (getName().empty())
+ if (mUrl.compare(0, 7, "file://") == 0)
+ {
+ // read file from local disk
+ std::string filename = mUrl.substr(7, std::string::npos);
+ mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
+ offset, size, responder);
+ }
+ else if (mUrl.empty())
{
mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
offset, size, responder);
}
else
{
- // read file from local disk
- mCacheReadHandle = mFetcher->mTextureCache->readFromCache(getName(), mID, cache_priority,
- offset, size, responder);
+ if (!(mUrl.compare(0, 7, "http://") == 0))
+ {
+ // *TODO:?remove this warning
+ llwarns << "Unknown URL Type: " << mUrl << llendl;
+ }
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ mState = SEND_HTTP_REQ;
}
}
@@ -647,75 +681,101 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == CACHE_POST)
{
- mDesiredSize = llmax(mDesiredSize, FIRST_PACKET_SIZE);
+ mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE);
mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
// Successfully loaded
if ((mCachedSize >= mDesiredSize) || mHaveAllData)
{
// we have enough data, decode it
llassert_always(mFormattedImage->getDataSize() > 0);
+ mLoadedDiscard = mDesiredDiscard;
mState = DECODE_IMAGE;
+ LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
+ << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
+ << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
// fall through
}
else
{
- if (!getName().empty())
+ if (mUrl.compare(0, 7, "file://") == 0)
{
// failed to load local file, we're done.
return true;
}
// need more data
- mState = LOAD_FROM_NETWORK;
+ else
+ {
+ LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL;
+ mState = LOAD_FROM_NETWORK;
+ }
// fall through
}
}
if (mState == LOAD_FROM_NETWORK)
{
- if (mSentRequest == UNSENT)
+ bool get_url = gSavedSettings.getBOOL("ImagePipelineUseHTTP");
+ if (!mUrl.empty()) get_url = false;
+// if (mHost != LLHost::invalid) get_url = false;
+ if ( get_url )
{
- if (mFormattedImage.isNull())
- {
- mFormattedImage = new LLImageJ2C;
- }
- // Add this to the network queue and sit here.
- // LLTextureFetch::update() will send off a request which will change our state
- S32 data_size = mFormattedImage->getDataSize();
- if (data_size > 0)
+ LLViewerRegion* region = NULL;
+ if (mHost == LLHost::invalid)
+ region = gAgent.getRegion();
+ else
+ region = LLWorld::getInstance()->getRegion(mHost);
+
+ if (region)
{
- // Only used for simulator requests
- mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1;
- if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size)
- {
-// llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl;
- removeFromCache();
- resetFormattedData();
- clearPackets();
- }
- else
+ std::string http_url = region->getCapability("GetTexture");
+ if (!http_url.empty())
{
- mLastPacket = mFirstPacket-1;
- mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1;
+ mUrl = http_url + "/?texture_id=" + mID.asString().c_str();
}
}
+ else
+ {
+ llwarns << "Region not found for host: " << mHost << llendl;
+ }
+ }
+ if (!mUrl.empty())
+ {
+ mState = LLTextureFetchWorker::SEND_HTTP_REQ;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ // don't return, fall through to next state
+ }
+ else if (mSentRequest == UNSENT)
+ {
+ // Add this to the network queue and sit here.
+ // LLTextureFetch::update() will send off a request which will change our state
mRequestedSize = mDesiredSize;
mRequestedDiscard = mDesiredDiscard;
mSentRequest = QUEUED;
- mFetcher->lockQueue();
mFetcher->addToNetworkQueue(this);
- mFetcher->unlockQueue();
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return false;
+ }
+ else
+ {
+ // Shouldn't need to do anything here
+ //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
+ // Make certain this is in the network queue
+ //mFetcher->addToNetworkQueue(this);
+ //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return false;
}
- return false;
}
if (mState == LOAD_FROM_SIMULATOR)
{
+ if (mFormattedImage.isNull())
+ {
+ mFormattedImage = new LLImageJ2C;
+ }
if (processSimulatorPackets())
{
- mFetcher->lockQueue();
- mFetcher->removeFromNetworkQueue(this);
- mFetcher->unlockQueue();
+ LL_DEBUGS("Texture") << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL;
+ mFetcher->removeFromNetworkQueue(this, false);
if (mFormattedImage.isNull() || !mFormattedImage->getDataSize())
{
// processSimulatorPackets() failed
@@ -727,108 +787,99 @@ bool LLTextureFetchWorker::doWork(S32 param)
}
else
{
+ mFetcher->addToNetworkQueue(this); // failsafe
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
}
return false;
}
-#if 0
- if (mState == LOAD_FROM_HTTP_GET_URL)
- {
- if (!mSentRequest)
- {
- mSentRequest = TRUE;
- mLoaded = FALSE;
- std::string url;
- LLViewerRegion* region = gAgent.getRegion();
- if (region)
+ if (mState == SEND_HTTP_REQ)
+ {
+ {
+ const S32 HTTP_QUEUE_MAX_SIZE = 32;
+ // *TODO: Integrate this with llviewerthrottle
+ // Note: LLViewerThrottle uses dynamic throttling which makes sense for UDP,
+ // but probably not for Textures.
+ // Set the throttle to the entire bandwidth, assuming UDP packets will get priority
+ // when they are needed
+ F32 max_bandwidth = mFetcher->mMaxBandwidth;
+ if ((mFetcher->getHTTPQueueSize() >= HTTP_QUEUE_MAX_SIZE) ||
+ (mFetcher->getTextureBandwidth() > max_bandwidth))
{
- url = region->getCapability("RequestTextureDownload");
- }
- if (!url.empty())
- {
- LLSD sd;
- sd = mID.asString();
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- LLHTTPClient::post(url, sd, new URLResponder(mFetcher, mID));
+ // Make normal priority and return (i.e. wait until there is room in the queue)
+ setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority);
return false;
}
- else
- {
-// llwarns << mID << ": HTTP get url failed, requesting from simulator" << llendl;
- mSentRequest = FALSE;
- mState = LOAD_FROM_SIMULATOR;
- return false;
- }
- }
- else
- {
- if (mLoaded)
+
+ mFetcher->removeFromNetworkQueue(this, false);
+
+ S32 cur_size = 0;
+ if (mFormattedImage.notNull())
{
- if (!mURL.empty())
- {
- mState = LOAD_FROM_HTTP_GET_DATA;
- mSentRequest = FALSE; // reset
- mLoaded = FALSE; // reset
- }
- else
- {
-// llwarns << mID << ": HTTP get url is empty, requesting from simulator" << llendl;
- mSentRequest = FALSE;
- mState = LOAD_FROM_SIMULATOR;
- return false;
- }
+ cur_size = mFormattedImage->getDataSize(); // amount of data we already have
}
- }
- // fall through
- }
-
- if (mState == LOAD_FROM_HTTP_GET_DATA)
- {
- if (!mSentRequest)
- {
- mSentRequest = TRUE;
- S32 cur_size = mFormattedImage->getDataSize(); // amount of data we already have
mRequestedSize = mDesiredSize;
mRequestedDiscard = mDesiredDiscard;
-#if 1 // *TODO: LLCurl::getByteRange is broken (ignores range)
- cur_size = 0;
- mFormattedImage->deleteData();
-#endif
mRequestedSize -= cur_size;
- // F32 priority = mImagePriority / (F32)LLViewerTexture::maxDecodePriority(); // 0-1
S32 offset = cur_size;
mBufferSize = cur_size; // This will get modified by callbackHttpGet()
- std::string url;
- if (mURL.empty())
+
+ bool res = false;
+ if (!mUrl.empty())
{
- //url = "http://asset.agni/0000002f-38ae-0e17-8e72-712e58964e9c.texture";
- std::stringstream urlstr;
- urlstr << "http://asset.agni/" << mID.asString() << ".texture";
- url = urlstr.str();
+ mLoaded = FALSE;
+ mGetStatus = 0;
+ mGetReason.clear();
+ lldebugs << "HTTP GET: " << mID << " Offset: " << offset
+ << " Bytes: " << mRequestedSize
+ << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << max_bandwidth
+ << llendl;
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ mState = WAIT_HTTP_REQ;
+
+ mFetcher->addToHTTPQueue(mID);
+ // Will call callbackHttpGet when curl request completes
+ std::vector<std::string> headers;
+ headers.push_back("Accept: image/x-j2c");
+ res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize,
+ new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset));
}
- else
+ if (!res)
{
- url = mURL;
+ llwarns << "HTTP GET request failed for " << mID << llendl;
+ resetFormattedData();
+ ++mHTTPFailCount;
+ return true; // failed
}
- mLoaded = FALSE;
-// llinfos << "HTTP GET: " << mID << " Offset: " << offset << " Bytes: " << mRequestedSize << llendl;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- LLCurl::getByteRange(url, offset, mRequestedSize,
- new HTTPGetResponder(mFetcher, mID)); // *TODO: use mWorkPriority
- return false; // not done
+ // fall through
}
-
+ }
+
+ if (mState == WAIT_HTTP_REQ)
+ {
if (mLoaded)
{
- S32 cur_size = mFormattedImage->getDataSize();
+ S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
if (mRequestedSize < 0)
{
-// llwarns << "http get failed for: " << mID << llendl;
+ const S32 HTTP_MAX_RETRY_COUNT = 3;
+ S32 max_attempts = (mGetStatus == HTTP_NOT_FOUND) ? 1 : HTTP_MAX_RETRY_COUNT + 1;
+ llinfos << "HTTP GET failed for: " << mUrl
+ << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'"
+ << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl;
if (cur_size == 0)
{
- resetFormattedData();
- return true; // failed
+ ++mHTTPFailCount;
+ if (mHTTPFailCount >= max_attempts)
+ {
+ resetFormattedData();
+ return true; // failed
+ }
+ else
+ {
+ mState = SEND_HTTP_REQ;
+ return false; // retry
+ }
}
else
{
@@ -836,6 +887,18 @@ bool LLTextureFetchWorker::doWork(S32 param)
return false; // use what we have
}
}
+
+ if (mFormattedImage.isNull())
+ {
+ // For now, create formatted image based on extension
+ std::string extension = gDirUtilp->getExtension(mUrl);
+ mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension));
+ if (mFormattedImage.isNull())
+ {
+ mFormattedImage = new LLImageJ2C; // default
+ }
+ }
+
llassert_always(mBufferSize == cur_size + mRequestedSize);
if (mHaveAllData)
{
@@ -854,43 +917,51 @@ bool LLTextureFetchWorker::doWork(S32 param)
mBuffer = NULL;
mBufferSize = 0;
mLoadedDiscard = mRequestedDiscard;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
mState = DECODE_IMAGE;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ return false;
+ }
+ else
+ {
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return false;
}
-
- // NOTE: Priority gets updated when the http get completes (in callbackHTTPGet())
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- return false;
}
-#endif
if (mState == DECODE_IMAGE)
{
- llassert_always(mFormattedImage->getDataSize() > 0);
+ if (mFormattedImage->getDataSize() <= 0)
+ {
+ llerrs << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl;
+ }
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
mRawImage = NULL;
mAuxImage = NULL;
- llassert_always(mImageWorker == NULL);
llassert_always(mFormattedImage.notNull());
+ llassert_always(mLoadedDiscard >= 0);
S32 discard = mHaveAllData ? 0 : mLoadedDiscard;
U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority;
mDecoded = FALSE;
mState = DECODE_IMAGE_UPDATE;
- mImageWorker = new LLImageWorker(mFormattedImage, image_priority, discard, new DecodeResponder(mFetcher, mID, this));
- // fall though (need to call requestDecodedData() to start work)
+ LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard
+ << " All Data: " << mHaveAllData << LL_ENDL;
+ mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux,
+ new DecodeResponder(mFetcher, mID, this));
+ // fall though
}
if (mState == DECODE_IMAGE_UPDATE)
{
- if (decodeImage())
+ if (mDecoded)
{
if (mDecodedDiscard < 0)
{
+ LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL;
if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0)
{
// Cache file should be deleted, try again
// llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl;
+ llassert_always(mDecodeHandle == 0);
mFormattedImage = NULL;
++mRetryAttempt;
setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
@@ -905,6 +976,9 @@ bool LLTextureFetchWorker::doWork(S32 param)
}
else
{
+ llassert_always(mRawImage.notNull());
+ LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard
+ << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
mState = WRITE_TO_CACHE;
}
@@ -918,9 +992,10 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == WRITE_TO_CACHE)
{
- if (mInLocalCache || !mFileSize || mSentRequest == UNSENT)
+ if (mInLocalCache || mSentRequest == UNSENT || mFormattedImage.isNull())
{
- // If we're in a local cache or we didn't actually receive any new data, skip
+ // If we're in a local cache or we didn't actually receive any new data,
+ // or we failed to load anything, skip
mState = DONE;
return false;
}
@@ -979,10 +1054,10 @@ bool LLTextureFetchWorker::doWork(S32 param)
// Called from MAIN thread
void LLTextureFetchWorker::endWork(S32 param, bool aborted)
{
- if (mImageWorker)
+ if (mDecodeHandle != 0)
{
- mImageWorker->scheduleDelete();
- mImageWorker = NULL;
+ mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false);
+ mDecodeHandle = 0;
}
mFormattedImage = NULL;
}
@@ -1035,7 +1110,7 @@ bool LLTextureFetchWorker::deleteOK()
if ((haveWork() &&
// not ok to delete from these states
- ((mState >= LOAD_FROM_HTTP_GET_URL && mState <= LOAD_FROM_HTTP_GET_DATA) ||
+ ((mState >= SEND_HTTP_REQ && mState <= WAIT_HTTP_REQ) ||
(mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE))))
{
delete_ok = false;
@@ -1044,7 +1119,6 @@ bool LLTextureFetchWorker::deleteOK()
return delete_ok;
}
-
void LLTextureFetchWorker::removeFromCache()
{
if (!mInLocalCache)
@@ -1061,6 +1135,7 @@ bool LLTextureFetchWorker::processSimulatorPackets()
if (mFormattedImage.isNull() || mRequestedSize < 0)
{
// not sure how we got here, but not a valid state, abort!
+ llassert_always(mDecodeHandle == 0);
mFormattedImage = NULL;
return true;
}
@@ -1074,6 +1149,12 @@ bool LLTextureFetchWorker::processSimulatorPackets()
buffer_size += mPackets[i]->mSize;
}
bool have_all_data = mLastPacket >= mTotalPackets-1;
+ if (mRequestedSize <= 0)
+ {
+ // We received a packed but haven't requested anything yet (edge case)
+ // Return true (we're "done") since we didn't request anything
+ return true;
+ }
if (buffer_size >= mRequestedSize || have_all_data)
{
/// We have enough (or all) data
@@ -1109,50 +1190,36 @@ bool LLTextureFetchWorker::processSimulatorPackets()
//////////////////////////////////////////////////////////////////////////////
-void LLTextureFetchWorker::callbackURLReceived(const LLSD& data, bool success)
+void LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer,
+ bool last_block, bool success)
{
-#if 0
LLMutexLock lock(&mWorkMutex);
- if (!mSentRequest || mState != LOAD_FROM_HTTP_GET_URL)
- {
- llwarns << "callbackURLReceived for unrequested fetch worker, req="
- << mSentRequest << " state= " << mState << llendl;
- return;
- }
- if (success)
- {
- mURL = data.asString();
- }
- mLoaded = TRUE;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-#endif
-}
-
-//////////////////////////////////////////////////////////////////////////////
-void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_block)
-{
-#if 0
- LLMutexLock lock(&mWorkMutex);
- if (!mSentRequest || mState != LOAD_FROM_HTTP_GET_DATA)
+ if (mState != WAIT_HTTP_REQ)
{
- llwarns << "callbackHttpGet for unrequested fetch worker, req="
- << mSentRequest << " state= " << mState << llendl;
+ llwarns << "callbackHttpGet for unrequested fetch worker: " << mID
+ << " req=" << mSentRequest << " state= " << mState << llendl;
return;
}
-// llinfos << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << llendl;
if (mLoaded)
{
llwarns << "Duplicate callback for " << mID.asString() << llendl;
return; // ignore duplicate callback
}
- if (data_size >= 0)
+ if (success)
{
+ // get length of stream:
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ gTextureList.sTextureBits += data_size * 8; // Approximate - does not include header bits
+
+ //llinfos << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << llendl;
if (data_size > 0)
{
+ // *TODO: set the formatted image data here directly to avoid the copy
mBuffer = new U8[data_size];
- // *TODO: set the formatted image data here
- memcpy(mBuffer, data, data_size);
+ buffer->readAfter(channels.in(), NULL, mBuffer, data_size);
mBufferSize += data_size;
if (data_size < mRequestedSize || last_block == true)
{
@@ -1160,10 +1227,11 @@ void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_bl
}
else if (data_size > mRequestedSize)
{
- // *TODO: This will happen until we fix LLCurl::getByteRange()
-// llinfos << "HUH?" << llendl;
+ // *TODO: This shouldn't be happening any more
+ llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl;
mHaveAllData = TRUE;
- mFormattedImage->deleteData();
+ llassert_always(mDecodeHandle == 0);
+ mFormattedImage = NULL; // discard any previous data we had
mBufferSize = data_size;
}
}
@@ -1181,7 +1249,6 @@ void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_bl
}
mLoaded = TRUE;
setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-#endif
}
//////////////////////////////////////////////////////////////////////////////
@@ -1197,7 +1264,7 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima
}
if (success)
{
- llassert_always(imagesize > 0);
+ llassert_always(imagesize >= 0);
mFileSize = imagesize;
mFormattedImage = image;
mImageCodec = image->getCodec();
@@ -1225,65 +1292,40 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success)
//////////////////////////////////////////////////////////////////////////////
-void LLTextureFetchWorker::callbackDecoded(bool success)
+void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux)
{
+ LLMutexLock lock(&mWorkMutex);
+ if (mDecodeHandle == 0)
+ {
+ return; // aborted, ignore
+ }
if (mState != DECODE_IMAGE_UPDATE)
{
// llwarns << "Decode callback for " << mID << " with state = " << mState << llendl;
+ mDecodeHandle = 0;
return;
}
-// llinfos << mID << " : DECODE COMPLETE " << llendl;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool LLTextureFetchWorker::decodeImage()
-{
- if(!mImageWorker)
- {
- //LLTextureFetchWorker is aborted, skip image decoding.
- return true ;
- }
-
- bool res = true;
- if (mRawImage.isNull())
- {
- res = false;
- if (mImageWorker->requestDecodedData(mRawImage, -1))
- {
- res = true;
-// llinfos << mID << " : BASE DECODE FINISHED" << llendl;
- }
- }
- if (res &&
- (mRawImage.notNull() && mRawImage->getDataSize() > 0) &&
- (mNeedsAux && mAuxImage.isNull()))
+ llassert_always(mFormattedImage.notNull());
+
+ mDecodeHandle = 0;
+ if (success)
{
- res = false;
- if (mImageWorker->requestDecodedAuxData(mAuxImage, 4, -1))
- {
- res = true;
-// llinfos << mID << " : AUX DECODE FINISHED" << llendl;
- }
+ llassert_always(raw);
+ mRawImage = raw;
+ mAuxImage = aux;
+ mDecodedDiscard = mFormattedImage->getDiscardLevel();
+ LL_DEBUGS("Texture") << mID << ": Decode Finished. Discard: " << mDecodedDiscard
+ << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
}
- if (res)
+ else
{
- if ((mRawImage.notNull() && mRawImage->getDataSize() > 0) &&
- (!mNeedsAux || (mAuxImage.notNull() && mAuxImage->getDataSize() > 0)))
- {
- mDecodedDiscard = mFormattedImage->getDiscardLevel();
-// llinfos << mID << " : DECODE FINISHED. DISCARD: " << mDecodedDiscard << llendl;
- }
- else
- {
-// llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl;
- removeFromCache();
- }
- mImageWorker->scheduleDelete();
- mImageWorker = NULL;
+ llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl;
+ removeFromCache();
+ mDecodedDiscard = -1; // Redundant, here for clarity and paranoia
}
- return res;
+ mDecoded = TRUE;
+// llinfos << mID << " : DECODE COMPLETE " << llendl;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
}
//////////////////////////////////////////////////////////////////////////////
@@ -1314,15 +1356,21 @@ bool LLTextureFetchWorker::writeToCacheComplete()
//////////////////////////////////////////////////////////////////////////////
// public
-LLTextureFetch::LLTextureFetch(LLTextureCache* cache, bool threaded)
+LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded)
: LLWorkerThread("TextureFetch", threaded),
mDebugCount(0),
mDebugPause(FALSE),
mPacketCount(0),
mBadPacketCount(0),
mQueueMutex(getAPRPool()),
- mTextureCache(cache)
+ mNetworkQueueMutex(getAPRPool()),
+ mTextureCache(cache),
+ mImageDecodeThread(imagedecodethread),
+ mTextureBandwidth(0),
+ mCurlGetRequest(NULL)
{
+ mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
+ mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
}
LLTextureFetch::~LLTextureFetch()
@@ -1330,13 +1378,7 @@ LLTextureFetch::~LLTextureFetch()
// ~LLQueuedThread() called here
}
-bool LLTextureFetch::createRequest(const LLUUID& id, const LLHost& host, F32 priority,
- S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux)
-{
- return createRequest(LLStringUtil::null, id, host, priority, w, h, c, desired_discard, needs_aux);
-}
-
-bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id, const LLHost& host, F32 priority,
+bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority,
S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux)
{
if (mDebugPause)
@@ -1361,7 +1403,14 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id
}
S32 desired_size;
- if (desired_discard == 0)
+ std::string exten = gDirUtilp->getExtension(url);
+ if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C))
+ {
+ // Only do partial requests for J2C at the moment
+ //llinfos << "Merov : LLTextureFetch::createRequest(), blocking fetch on " << url << llendl;
+ desired_size = MAX_IMAGE_DATA_SIZE;
+ }
+ else if (desired_discard == 0)
{
// if we want the entire image, and we know its size, then get it all
// (calcDataSizeJ2C() below makes assumptions about how the image
@@ -1378,7 +1427,7 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id
}
else
{
- desired_size = FIRST_PACKET_SIZE;
+ desired_size = TEXTURE_CACHE_ENTRY_SIZE;
desired_discard = MAX_DISCARD_LEVEL;
}
@@ -1389,10 +1438,10 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id
{
return false; // need to wait for previous aborted request to complete
}
- worker->lockWorkData();
+ worker->lockWorkMutex();
worker->setImagePriority(priority);
worker->setDesiredDiscard(desired_discard, desired_size);
- worker->unlockWorkData();
+ worker->unlockWorkMutex();
if (!worker->haveWork())
{
worker->mState = LLTextureFetchWorker::INIT;
@@ -1401,16 +1450,7 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id
}
else
{
- if (filename.empty())
- {
- // do remote fetch
- worker = new LLTextureFetchWorker(this, id, host, priority, desired_discard, desired_size);
- }
- else
- {
- // do local file fetch
- worker = new LLTextureFetchLocalFileWorker(this, filename, id, host, priority, desired_discard, desired_size);
- }
+ worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size);
mRequestMap[id] = worker;
}
worker->mActiveCount++;
@@ -1430,10 +1470,9 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel)
}
// protected
-
-// call lockQueue() first!
void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker)
{
+ LLMutexLock lock(&mNetworkQueueMutex);
if (mRequestMap.find(worker->mID) != mRequestMap.end())
{
// only add to the queue if in the request map
@@ -1447,10 +1486,27 @@ void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker)
}
}
-// call lockQueue() first!
-void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker)
+void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel)
{
- mNetworkQueue.erase(worker->mID);
+ LLMutexLock lock(&mNetworkQueueMutex);
+ size_t erased = mNetworkQueue.erase(worker->mID);
+ if (cancel && erased > 0)
+ {
+ mCancelQueue[worker->mHost].insert(worker->mID);
+ }
+}
+
+// protected
+void LLTextureFetch::addToHTTPQueue(const LLUUID& id)
+{
+ LLMutexLock lock(&mNetworkQueueMutex);
+ mHTTPTextureQueue.insert(id);
+}
+
+void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id)
+{
+ LLMutexLock lock(&mNetworkQueueMutex);
+ mHTTPTextureQueue.erase(id);
}
// call lockQueue() first!
@@ -1458,11 +1514,7 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel)
{
size_t erased_1 = mRequestMap.erase(worker->mID);
llassert_always(erased_1 > 0) ;
- size_t erased = mNetworkQueue.erase(worker->mID);
- if (cancel && erased > 0)
- {
- mCancelQueue[worker->mHost].insert(worker->mID);
- }
+ removeFromNetworkQueue(worker, cancel);
llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
worker->scheduleDelete();
@@ -1504,24 +1556,27 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
}
else if (worker->checkWork())
{
+ worker->lockWorkMutex();
discard_level = worker->mDecodedDiscard;
- raw = worker->mRawImage; worker->mRawImage = NULL;
- aux = worker->mAuxImage; worker->mAuxImage = NULL;
+ raw = worker->mRawImage;
+ aux = worker->mAuxImage;
res = true;
+ LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
+ worker->unlockWorkMutex();
}
else
{
- worker->lockWorkData();
+ worker->lockWorkMutex();
if ((worker->mDecodedDiscard >= 0) &&
(worker->mDecodedDiscard < discard_level || discard_level < 0) &&
(worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE))
{
// Not finished, but data is ready
discard_level = worker->mDecodedDiscard;
- if (worker->mRawImage) raw = worker->mRawImage;
- if (worker->mAuxImage) aux = worker->mAuxImage;
+ raw = worker->mRawImage;
+ aux = worker->mAuxImage;
}
- worker->unlockWorkData();
+ worker->unlockWorkMutex();
}
}
else
@@ -1538,9 +1593,9 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
LLTextureFetchWorker* worker = getWorker(id);
if (worker)
{
- worker->lockWorkData();
+ worker->lockWorkMutex();
worker->setImagePriority(priority);
- worker->unlockWorkData();
+ worker->unlockWorkMutex();
res = true;
}
return res;
@@ -1548,40 +1603,106 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
//////////////////////////////////////////////////////////////////////////////
+// MAIN THREAD
//virtual
S32 LLTextureFetch::update(U32 max_time_ms)
{
S32 res;
+
+ mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
+
res = LLWorkerThread::update(max_time_ms);
- const F32 REQUEST_TIME = 1.f;
-
- // Periodically, gather the list of textures that need data from the network
- // And send the requests out to the simulators
- if (mNetworkTimer.getElapsedTimeF32() >= REQUEST_TIME)
+ if (!mDebugPause)
{
- mNetworkTimer.reset();
sendRequestListToSimulators();
}
return res;
}
+// WORKER THREAD
+void LLTextureFetch::startThread()
+{
+ // Construct mCurlGetRequest from Worker Thread
+ mCurlGetRequest = new LLCurlRequest();
+}
+
+// WORKER THREAD
+void LLTextureFetch::endThread()
+{
+ // Destroy mCurlGetRequest from Worker Thread
+ delete mCurlGetRequest;
+ mCurlGetRequest = NULL;
+}
+
+// WORKER THREAD
+void LLTextureFetch::threadedUpdate()
+{
+ llassert_always(mCurlGetRequest);
+
+ // Limit update frequency
+ const F32 PROCESS_TIME = 0.05f;
+ static LLFrameTimer process_timer;
+ if (process_timer.getElapsedTimeF32() < PROCESS_TIME)
+ {
+ return;
+ }
+ process_timer.reset();
+
+ // Update Curl on same thread as mCurlGetRequest was constructed
+ S32 processed = mCurlGetRequest->process();
+ if (processed > 0)
+ {
+ lldebugs << "processed: " << processed << " messages." << llendl;
+ }
+
+#if 0
+ const F32 INFO_TIME = 1.0f;
+ static LLFrameTimer info_timer;
+ if (info_timer.getElapsedTimeF32() >= INFO_TIME)
+ {
+ S32 q = mCurlGetRequest->getQueued();
+ if (q > 0)
+ {
+ llinfos << "Queued gets: " << q << llendl;
+ info_timer.reset();
+ }
+ }
+#endif
+
+}
+
//////////////////////////////////////////////////////////////////////////////
void LLTextureFetch::sendRequestListToSimulators()
{
+ // All requests
+ const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps
+
+ // Sim requests
const S32 IMAGES_PER_REQUEST = 50;
- const F32 LAZY_FLUSH_TIMEOUT = 15.f; // 10.0f // temp
+ const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp
const F32 MIN_REQUEST_TIME = 1.0f;
const F32 MIN_DELTA_PRIORITY = 1000.f;
- LLMutexLock lock(&mQueueMutex);
+ // Periodically, gather the list of textures that need data from the network
+ // And send the requests out to the simulators
+ static LLFrameTimer timer;
+ if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME)
+ {
+ return;
+ }
+ timer.reset();
+ LLMutexLock lock(&mQueueMutex);
+
// Send requests
typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t;
typedef std::map< LLHost, request_list_t > work_request_map_t;
work_request_map_t requests;
+ {
+ LLMutexLock lock2(&mNetworkQueueMutex);
for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); )
{
queue_t::iterator curiter = iter++;
@@ -1591,65 +1712,65 @@ void LLTextureFetch::sendRequestListToSimulators()
mNetworkQueue.erase(curiter);
continue; // paranoia
}
+ if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) &&
+ (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR))
+ {
+ // We already received our URL, remove from the queue
+ llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl;
+ mNetworkQueue.erase(curiter);
+ continue;
+ }
if (req->mID == mDebugID)
{
mDebugCount++; // for setting breakpoints
}
- if (req->mTotalPackets > 0 && req->mLastPacket >= req->mTotalPackets-1)
+ if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM &&
+ req->mTotalPackets > 0 &&
+ req->mLastPacket >= req->mTotalPackets-1)
{
// We have all the packets... make sure this is high priority
// req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority);
continue;
}
F32 elapsed = req->mRequestedTimer.getElapsedTimeF32();
- F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority);
- if ((req->mSimRequestedDiscard != req->mDesiredDiscard) ||
- (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) ||
- (elapsed >= LAZY_FLUSH_TIMEOUT))
{
- requests[req->mHost].insert(req);
+ F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority);
+ if ((req->mSimRequestedDiscard != req->mDesiredDiscard) ||
+ (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) ||
+ (elapsed >= SIM_LAZY_FLUSH_TIMEOUT))
+ {
+ requests[req->mHost].insert(req);
+ }
}
}
-
- std::string http_url;
-#if 0
- if (gSavedSettings.getBOOL("ImagePipelineUseHTTP"))
- {
- LLViewerRegion* region = gAgent.getRegion();
- if (region)
- {
- http_url = region->getCapability("RequestTextureDownload");
- }
}
-#endif
-
+
for (work_request_map_t::iterator iter1 = requests.begin();
iter1 != requests.end(); ++iter1)
{
- bool use_http = http_url.empty() ? false : true;
LLHost host = iter1->first;
// invalid host = use agent host
if (host == LLHost::invalid)
{
host = gAgent.getRegionHost();
}
- else
- {
- use_http = false;
- }
- if (use_http)
+ S32 sim_request_count = 0;
+
+ for (request_list_t::iterator iter2 = iter1->second.begin();
+ iter2 != iter1->second.end(); ++iter2)
{
- }
- else
- {
- S32 request_count = 0;
- for (request_list_t::iterator iter2 = iter1->second.begin();
- iter2 != iter1->second.end(); ++iter2)
+ LLTextureFetchWorker* req = *iter2;
+ if (gMessageSystem)
{
- LLTextureFetchWorker* req = *iter2;
- req->mSentRequest = LLTextureFetchWorker::SENT_SIM;
- if (0 == request_count)
+ if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM)
+ {
+ // Initialize packet data based on data read from cache
+ req->lockWorkMutex();
+ req->setupPacketData();
+ req->unlockWorkMutex();
+ }
+ if (0 == sim_request_count)
{
gMessageSystem->newMessageFast(_PREHASH_RequestImage);
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
@@ -1666,30 +1787,42 @@ void LLTextureFetch::sendRequestListToSimulators()
// llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard
// << " Packet: " << packet << " Priority: " << req->mImagePriority << llendl;
- req->lockWorkData();
+ if ((gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog")) || (gSavedSettings.getBOOL("LogTextureDownloadsToSimulator")))
+ {
+ mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime());
+ mTextureInfo.setRequestOffset(req->mID, 0);
+ mTextureInfo.setRequestSize(req->mID, 0);
+ mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP);
+ }
+
+ req->lockWorkMutex();
+ req->mSentRequest = LLTextureFetchWorker::SENT_SIM;
req->mSimRequestedDiscard = req->mDesiredDiscard;
req->mRequestedPriority = req->mImagePriority;
req->mRequestedTimer.reset();
- req->unlockWorkData();
- request_count++;
- if (request_count >= IMAGES_PER_REQUEST)
+ req->unlockWorkMutex();
+ sim_request_count++;
+ if (sim_request_count >= IMAGES_PER_REQUEST)
{
-// llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
+// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
+
gMessageSystem->sendSemiReliable(host, NULL, NULL);
- request_count = 0;
+ sim_request_count = 0;
}
}
- if (request_count > 0 && request_count < IMAGES_PER_REQUEST)
- {
-// llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
- gMessageSystem->sendSemiReliable(host, NULL, NULL);
- request_count = 0;
- }
+ }
+ if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST)
+ {
+// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
+ gMessageSystem->sendSemiReliable(host, NULL, NULL);
+ sim_request_count = 0;
}
}
// Send cancelations
- if (!mCancelQueue.empty())
+ {
+ LLMutexLock lock2(&mNetworkQueueMutex);
+ if (gMessageSystem && !mCancelQueue.empty())
{
for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
iter1 != mCancelQueue.end(); ++iter1)
@@ -1732,6 +1865,7 @@ void LLTextureFetch::sendRequestListToSimulators()
}
mCancelQueue.clear();
}
+ }
}
//////////////////////////////////////////////////////////////////////////////
@@ -1808,7 +1942,7 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8
return false;
}
- worker->lockWorkData();
+ worker->lockWorkMutex();
// Copy header data into image object
worker->mImageCodec = codec;
@@ -1819,7 +1953,7 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8
res = worker->insertPacket(0, data, data_size);
worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
- worker->unlockWorkData();
+ worker->unlockWorkMutex();
return res;
}
@@ -1853,7 +1987,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1
return false;
}
- worker->lockWorkData();
+ worker->lockWorkMutex();
res = worker->insertPacket(packet_num, data, data_size);
@@ -1866,12 +2000,20 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1
else
{
// llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id
-// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl;
- removeFromNetworkQueue(worker); // failsafe
- mCancelQueue[host].insert(id);
+// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl;
+ removeFromNetworkQueue(worker, true); // failsafe
}
-
- worker->unlockWorkData();
+
+ if(packet_num >= (worker->mTotalPackets - 1))
+ {
+ if ((gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog")) || (gSavedSettings.getBOOL("LogTextureDownloadsToSimulator")))
+ {
+ U64 timeNow = LLTimer::getTotalTime();
+ mTextureInfo.setRequestSize(id, worker->mFileSize);
+ mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow);
+ }
+ }
+ worker->unlockWorkMutex();
return res;
}
@@ -1885,9 +2027,9 @@ BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id)
LLTextureFetchWorker* worker = getWorker(id);
if (worker)
{
- worker->lockWorkData();
+ worker->lockWorkMutex() ;
from_cache = worker->mInLocalCache ;
- worker->unlockWorkData();
+ worker->unlockWorkMutex() ;
}
return from_cache ;
@@ -1907,7 +2049,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r
LLTextureFetchWorker* worker = getWorker(id);
if (worker && worker->haveWork())
{
- worker->lockWorkData();
+ worker->lockWorkMutex();
state = worker->mState;
fetch_dtime = worker->mFetchTimer.getElapsedTimeF32();
request_dtime = worker->mRequestedTimer.getElapsedTimeF32();
@@ -1924,7 +2066,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r
data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize;
}
}
- if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::LOAD_FROM_HTTP_GET_DATA)
+ if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ)
{
requested_priority = worker->mRequestedPriority;
}
@@ -1933,7 +2075,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r
requested_priority = worker->mImagePriority;
}
fetch_priority = worker->getPriority();
- worker->unlockWorkData();
+ worker->unlockWorkMutex();
}
data_progress_p = data_progress;
requested_priority_p = requested_priority;
@@ -1959,5 +2101,3 @@ void LLTextureFetch::dump()
}
}
-
-//////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 97719a9468..373e38a83c 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -37,26 +37,29 @@
#include "llimage.h"
#include "lluuid.h"
#include "llworkerthread.h"
+#include "llcurl.h"
+#include "lltextureinfo.h"
class LLViewerTexture;
class LLTextureFetchWorker;
+class HTTPGetResponder;
class LLTextureCache;
+class LLImageDecodeThread;
class LLHost;
// Interface class
class LLTextureFetch : public LLWorkerThread
{
friend class LLTextureFetchWorker;
+ friend class HTTPGetResponder;
public:
- LLTextureFetch(LLTextureCache* cache, bool threaded);
+ LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded);
~LLTextureFetch();
/*virtual*/ S32 update(U32 max_time_ms);
- bool createRequest(const LLUUID& id, const LLHost& host, F32 priority,
- S32 w, S32 h, S32 c, S32 discard, bool needs_aux);
- bool createRequest(const std::string& filename, const LLUUID& id, const LLHost& host, F32 priority,
+ bool createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority,
S32 w, S32 h, S32 c, S32 discard, bool needs_aux);
void deleteRequest(const LLUUID& id, bool cancel);
bool getRequestFinished(const LLUUID& id, S32& discard_level,
@@ -66,25 +69,39 @@ public:
bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data);
bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data);
+ void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; }
+ F32 getTextureBandwidth() { return mTextureBandwidth; }
+
// Debug
BOOL isFromLocalCache(const LLUUID& id);
S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p,
U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p);
void dump();
S32 getNumRequests() { return mRequestMap.size(); }
+ S32 getNumHTTPRequests() { return mHTTPTextureQueue.size(); }
// Public for access by callbacks
void lockQueue() { mQueueMutex.lock(); }
void unlockQueue() { mQueueMutex.unlock(); }
LLTextureFetchWorker* getWorker(const LLUUID& id);
+
+ LLTextureInfo* getTextureInfo() { return &mTextureInfo; }
protected:
void addToNetworkQueue(LLTextureFetchWorker* worker);
- void removeFromNetworkQueue(LLTextureFetchWorker* worker);
+ void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel);
+ void addToHTTPQueue(const LLUUID& id);
+ void removeFromHTTPQueue(const LLUUID& id);
+ S32 getHTTPQueueSize() { return (S32)mHTTPTextureQueue.size(); }
void removeRequest(LLTextureFetchWorker* worker, bool cancel);
+ // Called from worker thread (during doWork)
+ void processCurlRequests();
private:
void sendRequestListToSimulators();
+ /*virtual*/ void startThread(void);
+ /*virtual*/ void endThread(void);
+ /*virtual*/ void threadedUpdate(void);
public:
LLUUID mDebugID;
@@ -95,8 +112,11 @@ public:
private:
LLMutex mQueueMutex;
+ LLMutex mNetworkQueueMutex;
LLTextureCache* mTextureCache;
+ LLImageDecodeThread* mImageDecodeThread;
+ LLCurlRequest* mCurlGetRequest;
// Map of all requests by UUID
typedef std::map<LLUUID,LLTextureFetchWorker*> map_t;
@@ -105,10 +125,13 @@ private:
// Set of requests that require network data
typedef std::set<LLUUID> queue_t;
queue_t mNetworkQueue;
+ queue_t mHTTPTextureQueue;
typedef std::map<LLHost,std::set<LLUUID> > cancel_queue_t;
cancel_queue_t mCancelQueue;
-
- LLFrameTimer mNetworkTimer;
+ F32 mTextureBandwidth;
+ F32 mMaxBandwidth;
+ LLTextureInfo mTextureInfo;
};
#endif // LL_LLTEXTUREFETCH_H
+
diff --git a/indra/newview/lltextureinfo.cpp b/indra/newview/lltextureinfo.cpp
new file mode 100644
index 0000000000..672a36a8bd
--- /dev/null
+++ b/indra/newview/lltextureinfo.cpp
@@ -0,0 +1,290 @@
+/**
+ * @file lltextureinfo.cpp
+ * @brief Object which handles local texture info
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltextureinfo.h"
+#include "lltexturestats.h"
+#include "llviewercontrol.h"
+
+LLTextureInfo::LLTextureInfo() :
+ mLogTextureDownloadsToViewerLog(false),
+ mLogTextureDownloadsToSimulator(false),
+ mTotalBytes(0),
+ mTotalMilliseconds(0),
+ mTextureDownloadsStarted(0),
+ mTextureDownloadsCompleted(0),
+ mTextureDownloadProtocol("NONE"),
+ mTextureLogThreshold(100 * 1024),
+ mCurrentStatsBundleStartTime(0)
+{
+ mTextures.clear();
+}
+
+void LLTextureInfo::setUpLogging(bool writeToViewerLog, bool sendToSim, U32 textureLogThreshold)
+{
+ mLogTextureDownloadsToViewerLog = writeToViewerLog;
+ mLogTextureDownloadsToSimulator = sendToSim;
+ mTextureLogThreshold = textureLogThreshold;
+}
+
+LLTextureInfo::~LLTextureInfo()
+{
+ std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator;
+ for (iterator = mTextures.begin(); iterator != mTextures.end(); iterator++)
+ {
+ LLTextureInfoDetails *info = (*iterator).second;
+ delete info;
+ }
+
+ mTextures.clear();
+}
+
+void LLTextureInfo::addRequest(const LLUUID& id)
+{
+ LLTextureInfoDetails *info = new LLTextureInfoDetails();
+ mTextures[id] = info;
+}
+
+U32 LLTextureInfo::getTextureInfoMapSize()
+{
+ return mTextures.size();
+}
+
+bool LLTextureInfo::has(const LLUUID& id)
+{
+ std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id);
+ if (iterator == mTextures.end())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+void LLTextureInfo::setRequestStartTime(const LLUUID& id, U64 startTime)
+{
+ if (!has(id))
+ {
+ addRequest(id);
+ }
+ mTextures[id]->mStartTime = startTime;
+ mTextureDownloadsStarted++;
+}
+
+void LLTextureInfo::setRequestSize(const LLUUID& id, U32 size)
+{
+ if (!has(id))
+ {
+ addRequest(id);
+ }
+ mTextures[id]->mSize = size;
+}
+
+void LLTextureInfo::setRequestOffset(const LLUUID& id, U32 offset)
+{
+ if (!has(id))
+ {
+ addRequest(id);
+ }
+ mTextures[id]->mOffset = offset;
+}
+
+void LLTextureInfo::setRequestType(const LLUUID& id, LLTextureInfoDetails::LLRequestType type)
+{
+ if (!has(id))
+ {
+ addRequest(id);
+ }
+ mTextures[id]->mType = type;
+}
+
+void LLTextureInfo::setRequestCompleteTimeAndLog(const LLUUID& id, U64 completeTime)
+{
+ if (!has(id))
+ {
+ addRequest(id);
+ }
+ mTextures[id]->mCompleteTime = completeTime;
+
+ std::string protocol = "NONE";
+ switch(mTextures[id]->mType)
+ {
+ case LLTextureInfoDetails::REQUEST_TYPE_HTTP:
+ protocol = "HTTP";
+ break;
+
+ case LLTextureInfoDetails::REQUEST_TYPE_UDP:
+ protocol = "UDP";
+ break;
+
+ case LLTextureInfoDetails::REQUEST_TYPE_NONE:
+ default:
+ break;
+ }
+
+ if (mLogTextureDownloadsToViewerLog)
+ {
+ llinfos << "texture=" << id
+ << " start=" << mTextures[id]->mStartTime
+ << " end=" << mTextures[id]->mCompleteTime
+ << " size=" << mTextures[id]->mSize
+ << " offset=" << mTextures[id]->mOffset
+ << " length_in_ms=" << (mTextures[id]->mCompleteTime - mTextures[id]->mStartTime) / 1000
+ << " protocol=" << protocol
+ << llendl;
+ }
+
+ if(mLogTextureDownloadsToSimulator)
+ {
+ S32 texture_stats_upload_threshold = mTextureLogThreshold;
+ mTotalBytes += mTextures[id]->mSize;
+ mTotalMilliseconds += mTextures[id]->mCompleteTime - mTextures[id]->mStartTime;
+ mTextureDownloadsCompleted++;
+ mTextureDownloadProtocol = protocol;
+ if (mTotalBytes >= texture_stats_upload_threshold)
+ {
+ LLSD texture_data;
+ std::stringstream startTime;
+ startTime << mCurrentStatsBundleStartTime;
+ texture_data["start_time"] = startTime.str();
+ std::stringstream endTime;
+ endTime << completeTime;
+ texture_data["end_time"] = endTime.str();
+ texture_data["averages"] = getAverages();
+ send_texture_stats_to_sim(texture_data);
+ resetTextureStatistics();
+ }
+ }
+
+ mTextures.erase(id);
+}
+
+LLSD LLTextureInfo::getAverages()
+{
+ LLSD averagedTextureData;
+ S32 averageDownloadRate;
+ if(mTotalMilliseconds == 0)
+ {
+ averageDownloadRate = 0;
+ }
+ else
+ {
+ averageDownloadRate = (mTotalBytes * 8) / mTotalMilliseconds;
+ }
+
+ averagedTextureData["bits_per_second"] = averageDownloadRate;
+ averagedTextureData["bytes_downloaded"] = mTotalBytes;
+ averagedTextureData["texture_downloads_started"] = mTextureDownloadsStarted;
+ averagedTextureData["texture_downloads_completed"] = mTextureDownloadsCompleted;
+ averagedTextureData["transport"] = mTextureDownloadProtocol;
+
+ return averagedTextureData;
+}
+
+void LLTextureInfo::resetTextureStatistics()
+{
+ mTotalMilliseconds = 0;
+ mTotalBytes = 0;
+ mTextureDownloadsStarted = 0;
+ mTextureDownloadsCompleted = 0;
+ mTextureDownloadProtocol = "NONE";
+ mCurrentStatsBundleStartTime = LLTimer::getTotalTime();
+}
+
+U32 LLTextureInfo::getRequestStartTime(const LLUUID& id)
+{
+ if (!has(id))
+ {
+ return 0;
+ }
+ else
+ {
+ std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id);
+ return (*iterator).second->mStartTime;
+ }
+}
+
+U32 LLTextureInfo::getRequestSize(const LLUUID& id)
+{
+ if (!has(id))
+ {
+ return 0;
+ }
+ else
+ {
+ std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id);
+ return (*iterator).second->mSize;
+ }
+}
+
+U32 LLTextureInfo::getRequestOffset(const LLUUID& id)
+{
+ if (!has(id))
+ {
+ return 0;
+ }
+ else
+ {
+ std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id);
+ return (*iterator).second->mOffset;
+ }
+}
+
+LLTextureInfoDetails::LLRequestType LLTextureInfo::getRequestType(const LLUUID& id)
+{
+ if (!has(id))
+ {
+ return LLTextureInfoDetails::REQUEST_TYPE_NONE;
+ }
+ else
+ {
+ std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id);
+ return (*iterator).second->mType;
+ }
+}
+
+U32 LLTextureInfo::getRequestCompleteTime(const LLUUID& id)
+{
+ if (!has(id))
+ {
+ return 0;
+ }
+ else
+ {
+ std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id);
+ return (*iterator).second->mCompleteTime;
+ }
+}
+
diff --git a/indra/newview/lltextureinfo.h b/indra/newview/lltextureinfo.h
new file mode 100644
index 0000000000..71b0ea431f
--- /dev/null
+++ b/indra/newview/lltextureinfo.h
@@ -0,0 +1,80 @@
+/**
+ * @file lltextureinfo.h
+ * @brief Object for managing texture information.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXTUREINFO_H
+#define LL_LLTEXTUREINFO_H
+
+#include "lluuid.h"
+#include "lltextureinfodetails.h"
+#include <map>
+
+class LLTextureInfo
+{
+public:
+ LLTextureInfo();
+ ~LLTextureInfo();
+
+ void setUpLogging(bool writeToViewerLog, bool sendToSim, U32 textureLogThreshold);
+ bool has(const LLUUID& id);
+ void setRequestStartTime(const LLUUID& id, U64 startTime);
+ void setRequestSize(const LLUUID& id, U32 size);
+ void setRequestOffset(const LLUUID& id, U32 offset);
+ void setRequestType(const LLUUID& id, LLTextureInfoDetails::LLRequestType type);
+ void setRequestCompleteTimeAndLog(const LLUUID& id, U64 completeTime);
+ U32 getRequestStartTime(const LLUUID& id);
+ U32 getRequestSize(const LLUUID& id);
+ U32 getRequestOffset(const LLUUID& id);
+ LLTextureInfoDetails::LLRequestType getRequestType(const LLUUID& id);
+ U32 getRequestCompleteTime(const LLUUID& id);
+ void resetTextureStatistics();
+ U32 getTextureInfoMapSize();
+ LLSD getAverages();
+
+private:
+ void addRequest(const LLUUID& id);
+
+ std::map<LLUUID, LLTextureInfoDetails *> mTextures;
+
+ LLSD mAverages;
+
+ bool mLogTextureDownloadsToViewerLog;
+ bool mLogTextureDownloadsToSimulator;
+ S32 mTotalBytes;
+ S32 mTotalMilliseconds;
+ S32 mTextureDownloadsStarted;
+ S32 mTextureDownloadsCompleted;
+ std::string mTextureDownloadProtocol;
+ U32 mTextureLogThreshold; // in bytes
+ U64 mCurrentStatsBundleStartTime;
+};
+
+#endif // LL_LLTEXTUREINFO_H
diff --git a/indra/newview/lltextureinfodetails.cpp b/indra/newview/lltextureinfodetails.cpp
new file mode 100644
index 0000000000..f6ef47a2ee
--- /dev/null
+++ b/indra/newview/lltextureinfodetails.cpp
@@ -0,0 +1,40 @@
+/**
+ * @file lltextureinfodetails.cpp
+ * @brief Object which handles details of any individual texture
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltextureinfodetails.h"
+
+LLTextureInfoDetails::LLTextureInfoDetails() : mStartTime(0), mCompleteTime(0), mSize(0), mType(REQUEST_TYPE_NONE), mOffset(0)
+{
+}
+
diff --git a/indra/newview/lltextureinfodetails.h b/indra/newview/lltextureinfodetails.h
new file mode 100644
index 0000000000..091fa01a3d
--- /dev/null
+++ b/indra/newview/lltextureinfodetails.h
@@ -0,0 +1,58 @@
+/**
+ * @file lltextureinfo.h
+ * @brief Object for managing texture information.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXTUREINFODETAILS_H
+#define LL_LLTEXTUREINFODETAILS_H
+
+#include "lluuid.h"
+
+class LLTextureInfoDetails
+{
+public:
+ enum LLRequestType
+ {
+ REQUEST_TYPE_NONE,
+ REQUEST_TYPE_HTTP,
+ REQUEST_TYPE_UDP
+ };
+
+ U32 mStartTime;
+ U32 mCompleteTime;
+ U32 mOffset;
+ U32 mSize;
+ LLRequestType mType;
+
+ LLTextureInfoDetails();
+};
+
+#endif // LL_LLTEXTUREINFODETAILS_H
+
diff --git a/indra/newview/lltexturestats.cpp b/indra/newview/lltexturestats.cpp
new file mode 100644
index 0000000000..c91bfd4df2
--- /dev/null
+++ b/indra/newview/lltexturestats.cpp
@@ -0,0 +1,61 @@
+/**
+ * @file lltexturerstats.cpp
+ * @brief texture stats helper methods
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "pipeline.h"
+#include "llagent.h"
+#include "lltexturefetch.h"
+#include "lltexturestats.h"
+#include "lltexturestatsuploader.h"
+#include "llviewerregion.h"
+
+void send_texture_stats_to_sim(const LLSD &texture_stats)
+{
+ LLSD texture_stats_report;
+ // Only send stats if the agent is connected to a region.
+ if (!gAgent.getRegion() || gNoRender)
+ {
+ return;
+ }
+
+ LLUUID agent_id = gAgent.getID();
+ texture_stats_report["agent_id"] = agent_id;
+ texture_stats_report["region_id"] = gAgent.getRegion()->getRegionID();
+ texture_stats_report["stats_data"] = texture_stats;
+
+ std::string texture_cap_url = gAgent.getRegion()->getCapability("TextureStats");
+ LLTextureStatsUploader tsu;
+ llinfos << "uploading texture stats data to simulator" << llendl;
+ tsu.uploadStatsToSimulator(texture_cap_url, texture_stats);
+}
+
diff --git a/indra/newview/lltexturestats.h b/indra/newview/lltexturestats.h
new file mode 100644
index 0000000000..2deb377dfd
--- /dev/null
+++ b/indra/newview/lltexturestats.h
@@ -0,0 +1,41 @@
+/**
+ * @file lltexturestats.h
+ * @brief texture stats utilities
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXTURESTATS_H
+#define LL_LLTEXTURESTATS_H
+
+#include "llappviewer.h"
+
+// utility functions to capture data on texture download speeds and send to simulator periodically
+void send_texture_stats_to_sim(const LLSD &texture_stats);
+
+#endif // LL_LLTEXTURESTATS_H
diff --git a/indra/newview/lltexturestatsuploader.cpp b/indra/newview/lltexturestatsuploader.cpp
new file mode 100644
index 0000000000..e0358e1fca
--- /dev/null
+++ b/indra/newview/lltexturestatsuploader.cpp
@@ -0,0 +1,59 @@
+/**
+ * @file lltexturerstats.cpp
+ * @brief texture stats upload class
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltexturestatsuploader.h"
+
+LLTextureStatsUploader::LLTextureStatsUploader()
+{
+}
+
+LLTextureStatsUploader::~LLTextureStatsUploader()
+{
+}
+
+void LLTextureStatsUploader::uploadStatsToSimulator(const std::string texture_cap_url, const LLSD &texture_stats)
+{
+ if ( texture_cap_url != "" )
+ {
+ LLHTTPClient::post(texture_cap_url, texture_stats, NULL);
+ }
+ else
+ {
+ llinfos << "Not sending texture stats: "
+ << texture_stats
+ << " as there is no cap url."
+ << llendl;
+ }
+}
+
diff --git a/indra/newview/lltexturestatsuploader.h b/indra/newview/lltexturestatsuploader.h
new file mode 100644
index 0000000000..f6cc8be8fe
--- /dev/null
+++ b/indra/newview/lltexturestatsuploader.h
@@ -0,0 +1,48 @@
+/**
+ * @file lltexturestatsuploader.h
+ * @brief Class to send the texture stats to the simulatore
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXTURESTATSUPLOADER_H
+#define LL_LLTEXTURESTATSUPLOADER_H
+
+#include "llappviewer.h"
+
+// utility functions to capture data on texture download speeds and send to simulator periodically
+
+class LLTextureStatsUploader
+{
+public:
+ LLTextureStatsUploader();
+ ~LLTextureStatsUploader();
+ void uploadStatsToSimulator(const std::string texture_cap_url, const LLSD &texture_stats);
+};
+
+#endif // LL_LLTEXTURESTATSUPLOADER_H
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index dafa4f25ca..b6e20608eb 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -44,19 +44,21 @@
#include "llrender.h"
#include "lltooltip.h"
+#include "llappviewer.h"
#include "llselectmgr.h"
#include "lltexlayer.h"
#include "lltexturecache.h"
#include "lltexturefetch.h"
+#include "llviewercontrol.h"
#include "llviewerobject.h"
#include "llviewertexture.h"
#include "llviewertexturelist.h"
-#include "llappviewer.h"
-
+#include "llvovolume.h"
extern F32 texmem_lower_bound_scale;
LLTextureView *gTextureView = NULL;
LLTextureSizeView *gTextureSizeView = NULL;
+LLTextureSizeView *gTextureCategoryView = NULL;
//static
std::set<LLViewerFetchedTexture*> LLTextureView::sDebugImages;
@@ -230,10 +232,10 @@ void LLTextureBar::draw()
{ "DSK", LLColor4::blue }, // CACHE_POST
{ "NET", LLColor4::green }, // LOAD_FROM_NETWORK
{ "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR
- { "URL", LLColor4::green2 },// LOAD_FROM_HTTP_GET_URL
- { "HTP", LLColor4::green }, // LOAD_FROM_HTTP_GET_DATA
+ { "REQ", LLColor4::yellow },// SEND_HTTP_REQ
+ { "HTP", LLColor4::green }, // WAIT_HTTP_REQ
{ "DEC", LLColor4::yellow },// DECODE_IMAGE
- { "DEC", LLColor4::yellow },// DECODE_IMAGE_UPDATE
+ { "DEC", LLColor4::green }, // DECODE_IMAGE_UPDATE
{ "WRT", LLColor4::purple },// WRITE_TO_CACHE
{ "WRT", LLColor4::orange },// WAIT_ON_WRITE
{ "END", LLColor4::red }, // DONE
@@ -261,7 +263,7 @@ void LLTextureBar::draw()
// Draw the progress bar.
S32 bar_width = 100;
- S32 bar_left = 280;
+ S32 bar_left = 260;
left = bar_left;
right = left + bar_width;
@@ -286,30 +288,31 @@ void LLTextureBar::draw()
S32 pip_x = title_x3 + pip_space/2;
// Draw the packet pip
+ const F32 pip_max_time = 5.f;
F32 last_event = mImagep->mLastPacketTimer.getElapsedTimeF32();
- if (last_event < 1.f)
+ if (last_event < pip_max_time)
{
clr = LLColor4::white;
}
else
{
last_event = mImagep->mRequestDeltaTime;
- if (last_event < 1.f)
+ if (last_event < pip_max_time)
{
clr = LLColor4::green;
}
else
{
last_event = mImagep->mFetchDeltaTime;
- if (last_event < 1.f)
+ if (last_event < pip_max_time)
{
clr = LLColor4::yellow;
}
}
}
- if (last_event < 1.f)
+ if (last_event < pip_max_time)
{
- clr.setAlpha(1.f - last_event);
+ clr.setAlpha(1.f - last_event/pip_max_time);
gGL.color4fv(clr.mV);
gl_rect_2d(pip_x, top, pip_x + pip_width, bottom);
}
@@ -406,89 +409,113 @@ void LLGLTexMemBar::draw()
S32 total_mem = BYTES_TO_MEGA_BYTES(LLViewerTexture::sTotalTextureMemoryInBytes);
S32 max_total_mem = LLViewerTexture::sMaxTotalTextureMemInMegaBytes;
F32 discard_bias = LLViewerTexture::sDesiredDiscardBias;
+ F32 cache_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getUsage()) ;
+ F32 cache_max_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getMaxUsage()) ;
S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f);
S32 h_offset = (S32)((texture_bar_height + 2.5f) * mTextureView->mNumTextureBars + 2.5f);
//----------------------------------------------------------------------------
LLGLSUIDefault gls_ui;
- F32 text_color[] = {1.f, 1.f, 1.f, 0.75f};
+ LLColor4 text_color(1.f, 1.f, 1.f, 0.75f);
+ LLColor4 color;
std::string text;
- text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Discard Bias: %.2f",
+ text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB",
total_mem,
max_total_mem,
bound_mem,
max_bound_mem,
- discard_bias);
+ LLImageRaw::sGlobalRawMemory >> 20, discard_bias,
+ cache_usage, cache_max_usage);
+ //, cache_entries, cache_max_entries
LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, h_offset + line_height*3,
- text_color, LLFontGL::LEFT, LLFontGL::TOP);
+ text_color, LLFontGL::LEFT, LLFontGL::TOP);
//----------------------------------------------------------------------------
- S32 bar_left = 380;
+#if 0
+ S32 bar_left = 400;
S32 bar_width = 200;
S32 top = line_height*3 - 2 + h_offset;
S32 bottom = top - 6;
S32 left = bar_left;
S32 right = left + bar_width;
-
- F32 bar_scale = (F32)bar_width / (max_bound_mem * 1.5f);
+ F32 bar_scale;
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f);
- gl_rect_2d(left, top, right, bottom);
-
+ // GL Mem Bar
+
left = bar_left;
- right = left + llfloor(bound_mem * bar_scale);
- if (bound_mem < llfloor(max_bound_mem * texmem_lower_bound_scale))
- {
- gGL.color4f(0.f, 1.f, 0.f, 0.75f);
- }
- else if (bound_mem < max_bound_mem)
- {
- gGL.color4f(1.f, 1.f, 0.f, 0.75f);
- }
- else
- {
- gGL.color4f(1.f, 0.f, 0.f, 0.75f);
- }
+ text = "GL";
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*3,
+ text_color, LLFontGL::LEFT, LLFontGL::TOP);
+
+ left = bar_left+20;
+ right = left + bar_width;
+
+ gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f); // grey
gl_rect_2d(left, top, right, bottom);
bar_scale = (F32)bar_width / (max_total_mem * 1.5f);
+ right = left + llfloor(total_mem * bar_scale);
+ right = llclamp(right, bar_left, bar_left + bar_width);
- top = bottom - 2;
- bottom = top - 6;
+ color = (total_mem < llfloor(max_total_mem * texmem_lower_bound_scale)) ? LLColor4::green :
+ (total_mem < max_total_mem) ? LLColor4::yellow : LLColor4::red;
+ color[VALPHA] = .75f;
+ glColor4fv(color.mV);
+
+ gl_rect_2d(left, top, right, bottom); // red/yellow/green
+
+ //
+ bar_left += bar_width + bar_space;
+ //top = bottom - 2; bottom = top - 6;
+
+ // Bound Mem Bar
+
left = bar_left;
- right = left + llfloor(total_mem * bar_scale);
- if (total_mem < llfloor(max_total_mem * texmem_lower_bound_scale))
- {
- gGL.color4f(0.f, 1.f, 0.f, 0.75f);
- }
- else if (total_mem < max_total_mem)
- {
- gGL.color4f(1.f, 1.f, 0.f, 0.75f);
- }
- else
- {
- gGL.color4f(1.f, 0.f, 0.f, 0.75f);
- }
+ text = "GL";
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*3,
+ text_color, LLFontGL::LEFT, LLFontGL::TOP);
+ left = bar_left + 20;
+ right = left + bar_width;
+
+ gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f);
gl_rect_2d(left, top, right, bottom);
+ color = (bound_mem < llfloor(max_bound_mem * texmem_lower_bound_scale)) ? LLColor4::green :
+ (bound_mem < max_bound_mem) ? LLColor4::yellow : LLColor4::red;
+ color[VALPHA] = .75f;
+ glColor4fv(color.mV);
+
+ gl_rect_2d(left, top, right, bottom);
+#else
+ S32 left = 0 ;
+#endif
//----------------------------------------------------------------------------
- text = llformat("Textures: Count: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d IW:%d(%d) RAW:%d mRaw:%d mAux:%d CB:%d",
+ text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d IW:%d RAW:%d HTP:%d",
gTextureList.getNumImages(),
LLAppViewer::getTextureFetch()->getNumRequests(), LLAppViewer::getTextureFetch()->getNumDeletes(),
LLAppViewer::getTextureFetch()->mPacketCount, LLAppViewer::getTextureFetch()->mBadPacketCount,
LLAppViewer::getTextureCache()->getNumReads(), LLAppViewer::getTextureCache()->getNumWrites(),
LLLFSThread::sLocal->getPending(),
- LLImageWorker::sCount, LLImageWorker::getWorkerThread()->getNumDeletes(),
- LLImageRaw::sRawImageCount, LLViewerFetchedTexture::sRawCount, LLViewerFetchedTexture::sAuxCount,
- gTextureList.mCallbackList.size());
+ LLAppViewer::getImageDecodeThread()->getPending(),
+ LLImageRaw::sRawImageCount,
+ LLAppViewer::getTextureFetch()->getNumHTTPRequests());
LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, h_offset + line_height*2,
text_color, LLFontGL::LEFT, LLFontGL::TOP);
+
+
+ left = 550;
+ F32 bandwidth = LLAppViewer::getTextureFetch()->getTextureBandwidth();
+ F32 max_bandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
+ color = bandwidth > max_bandwidth ? LLColor4::red : bandwidth > max_bandwidth*.75f ? LLColor4::yellow : text_color;
+ color[VALPHA] = text_color[VALPHA];
+ text = llformat("BW:%.0f/%.0f",bandwidth, max_bandwidth);
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*2,
+ color, LLFontGL::LEFT, LLFontGL::TOP);
S32 dx1 = 0;
if (LLAppViewer::getTextureFetch()->mDebugPause)
@@ -555,7 +582,7 @@ public:
void setTop(S32 loaded, S32 bound, F32 scale) {mTopLoaded = loaded ; mTopBound = bound; mScale = scale ;}
void draw();
- BOOL handleHover(S32 x, S32 y, MASK mask) ;
+ BOOL handleHover(S32 x, S32 y, MASK mask, BOOL set_pick_size) ;
private:
S32 mIndex ;
@@ -568,19 +595,16 @@ private:
F32 mScale ;
};
-BOOL LLGLTexSizeBar::handleHover(S32 x, S32 y, MASK mask)
+BOOL LLGLTexSizeBar::handleHover(S32 x, S32 y, MASK mask, BOOL set_pick_size)
{
-#if !LL_RELEASE_FOR_DOWNLOAD
if(y > mBottom && (y < mBottom + (S32)(mTopLoaded * mScale) || y < mBottom + (S32)(mTopBound * mScale)))
{
- LLImageGL::setCurTexSizebar(mIndex);
+ LLImageGL::setCurTexSizebar(mIndex, set_pick_size);
}
-#endif
return TRUE ;
}
void LLGLTexSizeBar::draw()
{
-#if !LL_RELEASE_FOR_DOWNLOAD
LLGLSUIDefault gls_ui;
if(LLImageGL::sCurTexSizeBar == mIndex)
@@ -601,7 +625,6 @@ void LLGLTexSizeBar::draw()
F32 bound_color[] = {1.0f, 1.0f, 0.0f, 0.75f};
gl_rect_2d(mLeft, mBottom + (S32)(mTopLoaded * mScale), (mLeft + mRight) / 2, mBottom, loaded_color) ;
gl_rect_2d((mLeft + mRight) / 2, mBottom + (S32)(mTopBound * mScale), mRight, mBottom, bound_color) ;
-#endif
}
////////////////////////////////////////////////////////////////////////////
@@ -675,7 +698,13 @@ void LLTextureView::draw()
<< "\t" << cur_discard
<< llendl;
}
-
+
+ if (imagep->getID() == LLAppViewer::getTextureFetch()->mDebugID)
+ {
+ static S32 debug_count = 0;
+ ++debug_count; // for breakpoints
+ }
+
#if 0
if (imagep->getDontDiscard())
{
@@ -889,8 +918,7 @@ BOOL LLTextureView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
}
//-----------------------------------------------------------------
-LLTextureSizeView::LLTextureSizeView(const LLTextureSizeView::Params& p)
- : LLView(p)
+LLTextureSizeView::LLTextureSizeView(const LLTextureSizeView::Params& p) : LLContainerView(p)
{
setVisible(FALSE) ;
@@ -910,7 +938,31 @@ LLTextureSizeView::~LLTextureSizeView()
}
void LLTextureSizeView::draw()
{
-#if !LL_RELEASE_FOR_DOWNLOAD
+ if(mType == TEXTURE_MEM_OVER_SIZE)
+ {
+ drawTextureSizeGraph();
+ }
+ else
+ {
+ drawTextureCategoryGraph() ;
+ }
+
+ LLView::draw();
+}
+
+BOOL LLTextureSizeView::handleHover(S32 x, S32 y, MASK mask)
+{
+ if(x > mTextureSizeBarRect.mLeft && x < mTextureSizeBarRect.mRight)
+ {
+ mTextureSizeBar[(x - mTextureSizeBarRect.mLeft) / mTextureSizeBarWidth]->handleHover(x, y, mask, (mType == TEXTURE_MEM_OVER_SIZE)) ;
+ }
+
+ return TRUE ;
+}
+
+//draw real-time texture mem bar over size
+void LLTextureSizeView::drawTextureSizeGraph()
+{
if(mTextureSizeBar.size() == 0)
{
S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f);
@@ -931,29 +983,16 @@ void LLTextureSizeView::draw()
mTextureSizeBar[i]->draw() ;
}
LLImageGL::resetCurTexSizebar();
-
- LLView::draw();
-#endif
-}
-
-BOOL LLTextureSizeView::handleHover(S32 x, S32 y, MASK mask)
-{
- if(x > mTextureSizeBarRect.mLeft && x < mTextureSizeBarRect.mRight)
- {
- mTextureSizeBar[(x - mTextureSizeBarRect.mLeft) / mTextureSizeBarWidth]->handleHover(x, y, mask) ;
- }
-
- return TRUE ;
}
//draw background of texture size bar graph
F32 LLTextureSizeView::drawTextureSizeDistributionGraph()
{
+ //scale
F32 scale = 1.0f ;
-#if !LL_RELEASE_FOR_DOWNLOAD
+
LLGLSUIDefault gls_ui;
- //scale
{
S32 count = 0 ;
for(U32 i = 0 ; i < LLImageGL::sTextureLoadedCounter.size() ; i++)
@@ -1043,8 +1082,137 @@ F32 LLTextureSizeView::drawTextureSizeDistributionGraph()
text = llformat("Texture Size Distribution") ;
LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 250, top + line_height * 3,
text_color, LLFontGL::LEFT, LLFontGL::TOP);
-
-#endif
return scale ;
}
+//draw real-time texture mem bar over category
+void LLTextureSizeView::drawTextureCategoryGraph()
+{
+ if(mTextureSizeBar.size() == 0)
+ {
+ S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f);
+ mTextureSizeBar.resize(LLViewerTexture::getTotalNumOfCategories()) ;
+ mTextureSizeBarRect.set(700, line_height * 2 + 400, 700 + mTextureSizeBar.size() * mTextureSizeBarWidth, line_height * 2) ;
+
+ for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
+ {
+ mTextureSizeBar[i] = new LLGLTexSizeBar(i, mTextureSizeBarRect.mLeft + i * mTextureSizeBarWidth ,
+ line_height * 2, mTextureSizeBarRect.mLeft + (i + 1) * mTextureSizeBarWidth, line_height) ;
+ }
+ }
+
+ F32 size_bar_scale = drawTextureCategoryDistributionGraph() ;
+ for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
+ {
+ U32 k = LLViewerTexture::getIndexFromCategory(i) ;
+ mTextureSizeBar[i]->setTop(LLImageGL::sTextureMemByCategory[k] >> 20, LLImageGL::sTextureMemByCategoryBound[k] >> 20, size_bar_scale) ;
+ mTextureSizeBar[i]->draw() ;
+ }
+ LLImageGL::resetCurTexSizebar();
+}
+
+//draw background for TEXTURE_MEM_OVER_CATEGORY
+F32 LLTextureSizeView::drawTextureCategoryDistributionGraph()
+{
+ //scale
+ F32 scale = 4.0f ;
+
+ LLGLSUIDefault gls_ui;
+
+ {
+ S32 count = 0 ;
+ for(U32 i = 0 ; i < LLImageGL::sTextureMemByCategory.size() ; i++)
+ {
+ S32 tmp = LLImageGL::sTextureMemByCategory[i] >> 20 ;
+ if(tmp > count)
+ {
+ count = tmp ;
+ }
+ }
+ if(count > mTextureSizeBarRect.getHeight() * 0.25f)
+ {
+ scale = (F32)mTextureSizeBarRect.getHeight() * 0.25f / count ;
+ }
+ }
+
+ S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f);
+ S32 left = mTextureSizeBarRect.mLeft ;
+ S32 bottom = mTextureSizeBarRect.mBottom ;
+ S32 right = mTextureSizeBarRect.mRight ;
+ S32 top = mTextureSizeBarRect.mTop ;
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ //background rect
+ gl_rect_2d(left - 25, top + 30, right + 100, bottom - 25, LLColor4(0.0f, 0.0f, 0.0f, 0.25f)) ;
+
+ //--------------------------------------------------
+ gGL.color4f(1.0f, 0.5f, 0.5f, 0.75f);
+ gl_line_2d(left, bottom, right, bottom) ; //x axis
+ gl_line_2d(left, bottom, left, top) ; //y axis
+
+ //ruler
+ //--------------------------------------------------
+ gGL.color4f(1.0f, 0.5f, 0.5f, 0.5f);
+ for(S32 i = bottom + 50 ; i <= top ; i += 50)
+ {
+ gl_line_2d(left, i, right, i) ;
+ }
+
+ //texts
+ //--------------------------------------------------
+ F32 text_color[] = {1.f, 1.f, 1.f, 0.75f};
+ std::string text;
+
+ //-------
+ //x axis: size label
+ static char category[LLViewerTexture::MAX_GL_IMAGE_CATEGORY][4] =
+ {"Non", "Bak", "Av", "Cld", "Scp", "Hi", "Trn", "Slt", "Hud", "Bsf", "UI", "Pvw", "Map", "Mvs", "Slf", "Loc", "Scr", "Dyn", "Mdi", "ALT", "Oth" } ;
+
+ text = llformat("%s", category[0]) ;
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 12, bottom - line_height / 2,
+ text_color, LLFontGL::LEFT, LLFontGL::TOP);
+ for(U32 i = 1 ; i < mTextureSizeBar.size() ; i++)
+ {
+ text = llformat("%s", category[i]) ;
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + i * mTextureSizeBarWidth + 12, bottom - line_height / 2,
+ text_color, LLFontGL::LEFT, LLFontGL::TOP);
+ }
+ //-------
+
+ //y axis: number label
+ for(S32 i = bottom + 50 ; i <= top ; i += 50)
+ {
+ text = llformat("%d", (S32)((i - bottom) / scale)) ;
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, i + line_height / 2 ,
+ text_color, LLFontGL::LEFT, LLFontGL::TOP);
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, right + 5, i + line_height / 2 ,
+ text_color, LLFontGL::LEFT, LLFontGL::TOP);
+ }
+
+ text = llformat("MB") ;
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, top + line_height * 2 ,
+ text_color, LLFontGL::LEFT, LLFontGL::TOP);
+ //--------------------------------------------------
+ F32 loaded_color[] = {1.0f, 0.0f, 0.0f, 0.75f};
+ gl_rect_2d(left + 70, top + line_height * 2, left + 90, top + line_height, loaded_color) ;
+ text = llformat("Loaded") ;
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 100, top + line_height * 2,
+ loaded_color,
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ F32 bound_color[] = {1.0f, 1.0f, 0.0f, 0.75f};
+ gl_rect_2d(left + 170, top + line_height * 2, left + 190, top + line_height, bound_color) ;
+ text = llformat("Bound") ;
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 200, top + line_height * 2,
+ bound_color, LLFontGL::LEFT, LLFontGL::TOP);
+
+ //--------------------------------------------------
+
+ //title
+ text = llformat("Texture Category Distribution") ;
+ LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 250, top + line_height * 3,
+ text_color, LLFontGL::LEFT, LLFontGL::TOP);
+
+ return scale ;
+}
diff --git a/indra/newview/lltextureview.h b/indra/newview/lltextureview.h
index e917c0235e..435a55df83 100644
--- a/indra/newview/lltextureview.h
+++ b/indra/newview/lltextureview.h
@@ -79,24 +79,41 @@ public:
};
class LLGLTexSizeBar;
-
-class LLTextureSizeView : public LLView
+class LLTextureSizeView : public LLContainerView
{
-public:
+protected:
LLTextureSizeView(const Params&);
+ friend class LLUICtrlFactory;
+public:
~LLTextureSizeView();
/*virtual*/ void draw();
/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) ;
+ void setType(S32 type) {mType = type ;}
+ enum
+ {
+ TEXTURE_MEM_OVER_SIZE,
+ TEXTURE_MEM_OVER_CATEGORY
+ };
private:
+ //draw background for TEXTURE_MEM_OVER_SIZE
F32 drawTextureSizeDistributionGraph() ;
-
+ //draw real-time texture mem bar over size
+ void drawTextureSizeGraph();
+
+ //draw background for TEXTURE_MEM_OVER_CATEGORY
+ F32 drawTextureCategoryDistributionGraph() ;
+ //draw real-time texture mem bar over category
+ void drawTextureCategoryGraph();
+
private:
std::vector<LLGLTexSizeBar*> mTextureSizeBar ;
LLRect mTextureSizeBarRect ;
- S32 mTextureSizeBarWidth ;
+ S32 mTextureSizeBarWidth ;
+ S32 mType ;
};
extern LLTextureView *gTextureView;
extern LLTextureSizeView *gTextureSizeView;
+extern LLTextureSizeView *gTextureCategoryView;
#endif // LL_TEXTURE_VIEW_H
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
index 24017202cc..93da32b115 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -494,6 +494,8 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)
}
else if (handleMediaHover(mHoverPick))
{
+ // *NOTE: If you think the hover glow conflicts with the media outline, you
+ // could disable it here.
show_highlight = true;
// cursor set by media object
lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl;
diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp
index 841902f683..a3daca6fa4 100644
--- a/indra/newview/llurldispatcher.cpp
+++ b/indra/newview/llurldispatcher.cpp
@@ -46,7 +46,7 @@
#include "llstartup.h" // gStartupState
#include "llurlsimstring.h"
#include "llweb.h"
-#include "llworldmap.h"
+#include "llworldmapmessage.h"
// library includes
#include "llsd.h"
@@ -201,7 +201,7 @@ bool LLURLDispatcherImpl::dispatchRegion(const std::string& url, bool right_mous
//if(url_displayp) url_displayp->setName(region_name);
// Request a region handle by name
- LLWorldMap::getInstance()->sendNamedRegionRequest(region_name,
+ LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name,
LLURLDispatcherImpl::regionNameCallback,
url,
false); // don't teleport
@@ -240,7 +240,7 @@ void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const std::strin
LLVector3d global_pos = from_region_handle(region_handle) + LLVector3d(local_pos);
U64 new_region_handle = to_region_handle(global_pos);
- LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle,
+ LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle,
LLURLDispatcherImpl::regionHandleCallback,
url, teleport);
}
@@ -335,7 +335,7 @@ public:
{
url += tokens[i].asString() + "/";
}
- LLWorldMap::getInstance()->sendNamedRegionRequest(region_name,
+ LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name,
LLURLDispatcherImpl::regionHandleCallback,
url,
true); // teleport
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
index f65baea6ca..b5709fa102 100644
--- a/indra/newview/llviewercamera.cpp
+++ b/indra/newview/llviewercamera.cpp
@@ -109,10 +109,13 @@ LLViewerCamera::LLViewerCamera() : LLCamera()
{
calcProjection(getFar());
mCameraFOVDefault = DEFAULT_FIELD_OF_VIEW;
+ mCosHalfCameraFOV = cosf(mCameraFOVDefault * 0.5f);
mPixelMeterRatio = 0.f;
mScreenPixelArea = 0;
mZoomFactor = 1.f;
mZoomSubregion = 1;
+ mAverageSpeed = 0.f;
+ mAverageAngularSpeed = 0.f;
gSavedSettings.getControl("CameraAngle")->getCommitSignal()->connect(boost::bind(&LLViewerCamera::updateCameraAngle, this, _2));
}
@@ -151,15 +154,22 @@ void LLViewerCamera::updateCameraLocation(const LLVector3 &center,
setOriginAndLookAt(origin, up_direction, point_of_interest);
- F32 dpos = (center - last_position).magVec();
+ mVelocityDir = center - last_position ;
+ F32 dpos = mVelocityDir.normVec() ;
LLQuaternion rotation;
rotation.shortestArc(last_axis, getAtAxis());
F32 x, y, z;
F32 drot;
rotation.getAngleAxis(&drot, &x, &y, &z);
+
mVelocityStat.addValue(dpos);
mAngularVelocityStat.addValue(drot);
+
+ mAverageSpeed = mVelocityStat.getMeanPerSec() ;
+ mAverageAngularSpeed = mAngularVelocityStat.getMeanPerSec() ;
+ mCosHalfCameraFOV = cosf(0.5f * getView() * llmax(1.0f, getAspect()));
+
// update pixel meter ratio using default fov, not modified one
mPixelMeterRatio = getViewHeightInPixels()/ (2.f*tanf(mCameraFOVDefault*0.5));
// update screen pixel area
@@ -818,10 +828,12 @@ BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts)
LLCamera::setView(vertical_fov_rads); // call base implementation
}
-void LLViewerCamera::setDefaultFOV(F32 vertical_fov_rads) {
+void LLViewerCamera::setDefaultFOV(F32 vertical_fov_rads)
+{
vertical_fov_rads = llclamp(vertical_fov_rads, getMinView(), getMaxView());
setView(vertical_fov_rads);
mCameraFOVDefault = vertical_fov_rads;
+ mCosHalfCameraFOV = cosf(mCameraFOVDefault * 0.5f);
}
diff --git a/indra/newview/llviewercamera.h b/indra/newview/llviewercamera.h
index 90b77f771f..2b8a0892bf 100644
--- a/indra/newview/llviewercamera.h
+++ b/indra/newview/llviewercamera.h
@@ -91,17 +91,20 @@ public:
BOOL projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp = TRUE) const;
BOOL projectPosAgentToScreenEdge(const LLVector3 &pos_agent, LLCoordGL &out_point) const;
-
+ const LLVector3* getVelocityDir() const {return &mVelocityDir;}
LLStat *getVelocityStat() { return &mVelocityStat; }
LLStat *getAngularVelocityStat() { return &mAngularVelocityStat; }
+ F32 getCosHalfFov() {return mCosHalfCameraFOV;}
+ F32 getAverageSpeed() {return mAverageSpeed ;}
+ F32 getAverageAngularSpeed() {return mAverageAngularSpeed;}
void getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right);
LLVector3 roundToPixel(const LLVector3 &pos_agent);
// Sets the current matrix
/* virtual */ void setView(F32 vertical_fov_rads);
- // Sets the current matrix AND remembers result as default view
- void setDefaultFOV(F32 vertical_fov_rads);
+
+ void setDefaultFOV(F32 fov) ;
F32 getDefaultFOV() { return mCameraFOVDefault; }
BOOL cameraUnderWater() const;
@@ -120,9 +123,14 @@ protected:
LLStat mVelocityStat;
LLStat mAngularVelocityStat;
+ LLVector3 mVelocityDir ;
+ F32 mAverageSpeed ;
+ F32 mAverageAngularSpeed ;
+
mutable LLMatrix4 mProjectionMatrix; // Cache of perspective matrix
mutable LLMatrix4 mModelviewMatrix;
F32 mCameraFOVDefault;
+ F32 mCosHalfCameraFOV;
LLVector3 mLastPointOfInterest;
F32 mPixelMeterRatio; // Divide by distance from camera to get pixels per meter at that distance.
S32 mScreenPixelArea; // Pixel area of entire window
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 35226a1632..5e23a7e114 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -90,7 +90,7 @@ std::string gCurrentVersion;
extern BOOL gResizeScreenTexture;
extern BOOL gDebugGL;
-
+extern BOOL gAuditTexture;
////////////////////////////////////////////////////////////////////////////
// Listeners
@@ -378,6 +378,12 @@ static bool handleRenderUseImpostorsChanged(const LLSD& newvalue)
return true;
}
+static bool handleAuditTextureChanged(const LLSD& newvalue)
+{
+ gAuditTexture = newvalue.asBoolean();
+ return true;
+}
+
static bool handleRenderDebugGLChanged(const LLSD& newvalue)
{
gDebugGL = newvalue.asBoolean() || gDebugSession;
@@ -587,6 +593,7 @@ void settings_setup_listeners()
gSavedSettings.getControl("RenderDeferredShadow")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
gSavedSettings.getControl("RenderDeferredGI")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
gSavedSettings.getControl("TextureMemory")->getSignal()->connect(boost::bind(&handleVideoMemoryChanged, _2));
+ gSavedSettings.getControl("AuditTexture")->getSignal()->connect(boost::bind(&handleAuditTextureChanged, _2));
gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&handleChatFontSizeChanged, _2));
gSavedSettings.getControl("ChatPersistTime")->getSignal()->connect(boost::bind(&handleChatPersistTimeChanged, _2));
gSavedSettings.getControl("ConsoleMaxLines")->getSignal()->connect(boost::bind(&handleConsoleMaxLinesChanged, _2));
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index a6a72e9666..e0bb8fedeb 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -712,7 +712,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
gBumpImageList.updateImages(); // must be called before gTextureList version so that it's textures are thrown out first.
- const F32 max_image_decode_time = llmin(0.005f, 0.005f*10.f*gFrameIntervalSeconds); // 50 ms/second decode time (no more than 5ms/frame)
+ F32 max_image_decode_time = 0.050f*gFrameIntervalSeconds; // 50 ms/second decode time
+ max_image_decode_time = llclamp(max_image_decode_time, 0.001f, 0.005f ); // min 1ms/frame, max 5ms/frame)
gTextureList.updateImages(max_image_decode_time);
//remove dead textures from GL
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 55b1fa6b49..07d073c3a9 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -610,6 +610,14 @@ class LLAdvancedToggleConsole : public view_listener_t
{
toggle_visibility( (void*)((LLView*)gDebugView->mDebugConsolep) );
}
+ else if (gTextureSizeView && "texture size" == console_type)
+ {
+ toggle_visibility( (void*)gTextureSizeView );
+ }
+ else if (gTextureCategoryView && "texture category" == console_type)
+ {
+ toggle_visibility( (void*)gTextureCategoryView );
+ }
else if ("fast timers" == console_type)
{
toggle_visibility( (void*)gDebugView->mFastTimerView );
@@ -637,6 +645,14 @@ class LLAdvancedCheckConsole : public view_listener_t
{
new_value = get_visibility( (void*)((LLView*)gDebugView->mDebugConsolep) );
}
+ else if (gTextureSizeView && "texture size" == console_type)
+ {
+ new_value = get_visibility( (void*)gTextureSizeView );
+ }
+ else if (gTextureCategoryView && "texture category" == console_type)
+ {
+ new_value = get_visibility( (void*)gTextureCategoryView );
+ }
else if ("fast timers" == console_type)
{
new_value = get_visibility( (void*)gDebugView->mFastTimerView );
@@ -1161,28 +1177,6 @@ class LLAdvancedCheckWireframe : public view_listener_t
};
//////////////////////
-// DISABLE TEXTURES //
-//////////////////////
-
-class LLAdvancedToggleDisableTextures : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- LLViewerTexture::sDontLoadVolumeTextures = !LLViewerTexture::sDontLoadVolumeTextures;
- return true;
- }
-};
-
-class LLAdvancedCheckDisableTextures : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- bool new_value = LLViewerTexture::sDontLoadVolumeTextures; // <-- make this using LLCacheControl
- return new_value;
- }
-};
-
-//////////////////////
// TEXTURE ATLAS //
//////////////////////
@@ -1885,7 +1879,7 @@ class LLAdvancedRebakeTextures : public view_listener_t
};
-#ifndef LL_RELEASE_FOR_DOWNLOAD
+#if 1 //ndef LL_RELEASE_FOR_DOWNLOAD
///////////////////////////
// DEBUG AVATAR TEXTURES //
///////////////////////////
@@ -2523,24 +2517,12 @@ class LLObjectEnableTouch : public view_listener_t
// label.assign("Touch");
// }
//}
-/*
-bool handle_object_open()
-{
- LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
- if(!obj) return true;
- LLFloaterOpenObject::show();
- return true;
+void handle_object_open()
+{
+ LLFloaterReg::showInstance("openobject");
}
-class LLObjectOpen : public view_listener_t
-{
- bool handleEvent(const LLSD& userdata)
- {
- return handle_object_open();
- }
-};
-*/
bool enable_object_open()
{
// Look for contents in root object, which is all the LLFloaterOpenObject
@@ -2648,8 +2630,22 @@ void handle_object_edit()
// Could be first use
LLFirstUse::useBuild();
return;
-
}
+
+void handle_object_inspect()
+{
+ LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
+ LLViewerObject* selected_objectp = selection->getFirstRootObject();
+ if (selected_objectp)
+ {
+ LLSD key;
+ key["task"] = "task";
+ LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
+ }
+
+ LLFloaterReg::showInstance("inspect", LLSD());
+}
+
//---------------------------------------------------------------------------
// Land pie menu
//---------------------------------------------------------------------------
@@ -3484,9 +3480,8 @@ void set_god_level(U8 god_level)
gAgent.setGodLevel( god_level );
LLViewerParcelMgr::getInstance()->notifyObservers();
- // God mode changes sim visibility
- LLWorldMap::getInstance()->reset();
- LLWorldMap::getInstance()->setCurrentLayer(0);
+ // God mode changes region visibility
+ LLWorldMap::getInstance()->reloadItems(true);
// inventory in items may change in god mode
gObjectList.dirtyAllObjectInventory();
@@ -5198,7 +5193,7 @@ void show_debug_menus()
gMenuBarView->setItemEnabled("Develop", qamode);
// Server ('Admin') menu hidden when not in godmode.
- const bool show_server_menu = debug && (gAgent.getGodLevel() > GOD_NOT);
+ const bool show_server_menu = debug && (gAgent.getGodLevel() > GOD_NOT || gAgent.getAdminOverride());
gMenuBarView->setItemVisible("Admin", show_server_menu);
gMenuBarView->setItemEnabled("Admin", show_server_menu);
}
@@ -7782,8 +7777,6 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedSelectedTextureInfo(), "Advanced.SelectedTextureInfo");
view_listener_t::addMenu(new LLAdvancedToggleWireframe(), "Advanced.ToggleWireframe");
view_listener_t::addMenu(new LLAdvancedCheckWireframe(), "Advanced.CheckWireframe");
- view_listener_t::addMenu(new LLAdvancedToggleDisableTextures(), "Advanced.ToggleDisableTextures");
- view_listener_t::addMenu(new LLAdvancedCheckDisableTextures(), "Advanced.CheckDisableTextures");
view_listener_t::addMenu(new LLAdvancedToggleTextureAtlas(), "Advanced.ToggleTextureAtlas");
view_listener_t::addMenu(new LLAdvancedCheckTextureAtlas(), "Advanced.CheckTextureAtlas");
view_listener_t::addMenu(new LLAdvancedEnableObjectObjectOcclusion(), "Advanced.EnableObjectObjectOcclusion");
@@ -7967,6 +7960,8 @@ void initialize_menus()
commit.add("Object.Buy", boost::bind(&handle_buy));
commit.add("Object.Edit", boost::bind(&handle_object_edit));
+ commit.add("Object.Inspect", boost::bind(&handle_object_inspect));
+ commit.add("Object.Open", boost::bind(&handle_object_open));
commit.add("Object.Take", boost::bind(&handle_take));
diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h
index b65878b5e6..db4eb3be9d 100644
--- a/indra/newview/llviewermenu.h
+++ b/indra/newview/llviewermenu.h
@@ -94,6 +94,7 @@ void handle_sit_down(void*);
void handle_object_build(void*);
void handle_object_touch();
bool enable_object_open();
+void handle_object_open();
// Buy either contents or object itself
void handle_buy();
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index d8e6c52c8c..ea1097c477 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2914,46 +2914,37 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
if (!gLastVersionChannel.empty())
{
- LLSD payload;
- payload["message"] = version_channel;
- LLNotifications::instance().add("ServerVersionChanged", LLSD(), payload, server_version_changed_callback);
- }
-
- gLastVersionChannel = version_channel;
-}
-
-bool server_version_changed_callback(const LLSD& notification, const LLSD& response)
-{
- if(notification["payload"]["message"].asString() =="")
- return false;
- std::string url ="http://wiki.secondlife.com/wiki/Release_Notes/";
- //parse the msg string
- std::string server_version = notification["payload"]["message"].asString();
- std::vector<std::string> s_vect;
- boost::algorithm::split(s_vect, server_version, isspace);
- for(U32 i = 0; i < s_vect.size(); i++)
- {
- if (i != (s_vect.size() - 1))
- {
- if(i != (s_vect.size() - 2))
+ // work out the URL for this server's Release Notes
+ std::string url ="http://wiki.secondlife.com/wiki/Release_Notes/";
+ std::string server_version = version_channel;
+ std::vector<std::string> s_vect;
+ boost::algorithm::split(s_vect, server_version, isspace);
+ for(U32 i = 0; i < s_vect.size(); i++)
+ {
+ if (i != (s_vect.size() - 1))
{
- url += s_vect[i] + "_";
+ if(i != (s_vect.size() - 2))
+ {
+ url += s_vect[i] + "_";
+ }
+ else
+ {
+ url += s_vect[i] + "/";
+ }
}
else
{
- url += s_vect[i] + "/";
+ url += s_vect[i].substr(0,4);
}
}
- else
- {
- url += s_vect[i].substr(0,4);
- }
+
+ LLSD args;
+ args["URL"] = url;
+ LLNotifications::instance().add("ServerVersionChanged", args);
}
-
- LLWeb::loadURL(url);
- return false;
-}
+ gLastVersionChannel = version_channel;
+}
void process_crossed_region(LLMessageSystem* msg, void**)
{
diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h
index c15e5df675..e24da2013d 100644
--- a/indra/newview/llviewermessage.h
+++ b/indra/newview/llviewermessage.h
@@ -132,7 +132,6 @@ void container_inventory_arrived(LLViewerObject* object,
// agent movement
void send_complete_agent_movement(const LLHost& sim_host);
void process_agent_movement_complete(LLMessageSystem* msg, void**);
-bool server_version_changed_callback(const LLSD& notification, const LLSD& response);
void process_crossed_region(LLMessageSystem* msg, void**);
void process_teleport_start(LLMessageSystem* msg, void**);
void process_teleport_progress(LLMessageSystem* msg, void**);
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 26411ce152..e491e11960 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -2906,7 +2906,7 @@ F32 LLViewerObject::getMidScale() const
}
-void LLViewerObject::updateTextures(LLAgent &agent)
+void LLViewerObject::updateTextures()
{
}
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index b8ae31118c..01b213a87d 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -199,7 +199,7 @@ public:
S32 getNumFaces() const { return mNumFaces; }
// Graphical stuff for objects - maybe broken out into render class later?
- virtual void updateTextures(LLAgent &agent);
+ virtual void updateTextures();
virtual void boostTexturePriority(BOOL boost_children = TRUE); // When you just want to boost priority of this object
virtual LLDrawable* createDrawable(LLPipeline *pipeline);
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index 2927ca5292..96828ee1b6 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -642,7 +642,7 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
// Update distance & gpw
objectp->setPixelAreaAndAngle(agent); // Also sets the approx. pixel area
- objectp->updateTextures(agent); // Update the image levels of textures for this object.
+ objectp->updateTextures(); // Update the image levels of textures for this object.
}
}
@@ -1074,6 +1074,7 @@ void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap)
LLColor4 group_own_below_water_color =
LLUIColorTable::instance().getColor( "NetMapGroupOwnBelowWater" );
+ F32 max_radius = gSavedSettings.getF32("MiniMapPrimMaxRadius");
for (S32 i = 0; i < mMapObjects.count(); i++)
{
@@ -1089,6 +1090,11 @@ void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap)
F32 approx_radius = (scale.mV[VX] + scale.mV[VY]) * 0.5f * 0.5f * 1.3f; // 1.3 is a fudge
+ // Limit the size of megaprims so they don't blot out everything on the minimap.
+ // Attempting to draw very large megaprims also causes client lag.
+ // See DEV-17370 and DEV-29869/SNOW-79 for details.
+ approx_radius = llmin(approx_radius, max_radius);
+
LLColor4U color = above_water_color;
if( objectp->permYouOwner() )
{
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 7ea55b49e8..d1c9840a97 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1427,11 +1427,11 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
capabilityNames.append("EstateChangeInfo");
capabilityNames.append("EventQueueGet");
capabilityNames.append("FetchInventory");
- capabilityNames.append("WebFetchInventoryDescendents");
capabilityNames.append("ObjectMedia");
capabilityNames.append("ObjectMediaNavigate");
capabilityNames.append("FetchLib");
capabilityNames.append("FetchLibDescendents");
+ capabilityNames.append("GetTexture");
capabilityNames.append("GroupProposalBallot");
capabilityNames.append("HomeLocation");
capabilityNames.append("MapLayer");
@@ -1452,6 +1452,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
capabilityNames.append("SendUserReportWithScreenshot");
capabilityNames.append("ServerReleaseNotes");
capabilityNames.append("StartGroupProposal");
+ capabilityNames.append("TextureStats");
capabilityNames.append("UntrustedSimulatorMessage");
capabilityNames.append("UpdateAgentInformation");
capabilityNames.append("UpdateAgentLanguage");
@@ -1464,6 +1465,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
capabilityNames.append("UploadBakedTexture");
capabilityNames.append("ViewerStartAuction");
capabilityNames.append("ViewerStats");
+ capabilityNames.append("WebFetchInventoryDescendents");
// Please add new capabilities alphabetically to reduce
// merge conflicts.
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 6e07d8f246..caa94dba38 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -560,12 +560,18 @@ extern U32 gVisCompared;
extern U32 gVisTested;
std::map<S32,LLFrameTimer> gDebugTimers;
+std::map<S32,std::string> gDebugTimerLabel;
+
+void init_statistics()
+{
+ // Label debug timers
+ gDebugTimerLabel[0] = "Texture";
+}
void update_statistics(U32 frame_count)
{
gTotalWorldBytes += gVLManager.getTotalBytes();
gTotalObjectBytes += gObjectBits / 8;
- gTotalTextureBytes += gTextureList.mTextureBits / 8;
// make sure we have a valid time delta for this frame
if (gFrameIntervalSeconds > 0.f)
@@ -617,7 +623,6 @@ void update_statistics(U32 frame_count)
F32 layer_bits = (F32)(gVLManager.getLandBits() + gVLManager.getWindBits() + gVLManager.getCloudBits());
LLViewerStats::getInstance()->mLayersKBitStat.addValue(layer_bits/1024.f);
LLViewerStats::getInstance()->mObjectKBitStat.addValue(gObjectBits/1024.f);
- LLViewerStats::getInstance()->mTextureKBitStat.addValue(gTextureList.mTextureBits/1024.f);
LLViewerStats::getInstance()->mVFSPendingOperations.addValue(LLVFile::getVFSThread()->getPending());
LLViewerStats::getInstance()->mAssetKBitStat.addValue(gTransferManager.getTransferBitsIn(LLTCT_ASSET)/1024.f);
gTransferManager.resetTransferBitsIn(LLTCT_ASSET);
@@ -631,8 +636,6 @@ void update_statistics(U32 frame_count)
gDebugTimers[0].unpause();
}
- LLViewerStats::getInstance()->mTexturePacketsStat.addValue(gTextureList.mTexturePackets);
-
{
static F32 visible_avatar_frames = 0.f;
static F32 avg_visible_avatars = 0;
@@ -652,8 +655,20 @@ void update_statistics(U32 frame_count)
gObjectBits = 0;
// gDecodedBits = 0;
- gTextureList.mTextureBits = 0;
- gTextureList.mTexturePackets = 0;
+ // Only update texture stats ones per second so that they are less noisy
+ {
+ static const F32 texture_stats_freq = 1.f;
+ static LLFrameTimer texture_stats_timer;
+ if (texture_stats_timer.getElapsedTimeF32() >= texture_stats_freq)
+ {
+ LLViewerStats::getInstance()->mTextureKBitStat.addValue(LLViewerTextureList::sTextureBits/1024.f);
+ LLViewerStats::getInstance()->mTexturePacketsStat.addValue(LLViewerTextureList::sTexturePackets);
+ gTotalTextureBytes += LLViewerTextureList::sTextureBits / 8;
+ LLViewerTextureList::sTextureBits = 0;
+ LLViewerTextureList::sTexturePackets = 0;
+ texture_stats_timer.reset();
+ }
+ }
}
@@ -826,3 +841,4 @@ void send_stats()
LLViewerStats::getInstance()->addToMessage(body);
LLHTTPClient::post(url, body, new ViewerStatsResponder());
}
+
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index ba89fbf02a..13d73000d2 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -34,6 +34,7 @@
#define LL_LLVIEWERSTATS_H
#include "llstat.h"
+#include "lltextureinfo.h"
class LLViewerStats : public LLSingleton<LLViewerStats>
{
@@ -205,10 +206,13 @@ private:
static const F32 SEND_STATS_PERIOD = 300.0f;
// The following are from (older?) statistics code found in appviewer.
+void init_statistics();
void reset_statistics();
void output_statistics(void*);
void update_statistics(U32 frame_count);
void send_stats();
extern std::map<S32,LLFrameTimer> gDebugTimers;
+extern std::map<S32,std::string> gDebugTimerLabel;
+
#endif // LL_LLVIEWERSTATS_H
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 6b8c8c01d4..9923c9ac74 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -60,6 +60,8 @@
#include "llviewercontrol.h"
#include "pipeline.h"
#include "llappviewer.h"
+#include "llface.h"
+#include "llviewercamera.h"
#include "lltextureatlas.h"
#include "lltextureatlasmanager.h"
#include "lltextureentry.h"
@@ -88,7 +90,15 @@ S32 LLViewerTexture::sTotalTextureMemoryInBytes = 0;
S32 LLViewerTexture::sMaxBoundTextureMemInMegaBytes = 0;
S32 LLViewerTexture::sMaxTotalTextureMemInMegaBytes = 0;
S32 LLViewerTexture::sMaxDesiredTextureMemInBytes = 0 ;
-BOOL LLViewerTexture::sDontLoadVolumeTextures = FALSE;
+S8 LLViewerTexture::sCameraMovingDiscardBias = 0 ;
+S32 LLViewerTexture::sMaxSculptRez = 128 ; //max sculpt image size
+const S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64 ;
+const S32 MAX_CACHED_RAW_SCULPT_IMAGE_AREA = LLViewerTexture::sMaxSculptRez * LLViewerTexture::sMaxSculptRez ;
+const S32 MAX_CACHED_RAW_TERRAIN_IMAGE_AREA = 128 * 128 ;
+S32 LLViewerTexture::sMinLargeImageSize = 65536 ; //256 * 256.
+S32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA ;
+BOOL LLViewerTexture::sFreezeImageScalingDown = FALSE ;
+F32 LLViewerTexture::sCurrentTime = 0.0f ;
BOOL LLViewerTexture::sUseTextureAtlas = FALSE ;
const F32 desired_discard_bias_min = -2.0f; // -max number of levels to improve image quality by
@@ -162,6 +172,7 @@ LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(BOOL usemipma
if(generate_gl_tex)
{
tex->generateGLTexture() ;
+ tex->setCategory(LLViewerTexture::LOCAL) ;
}
return tex ;
}
@@ -171,12 +182,14 @@ LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLUUID&
if(generate_gl_tex)
{
tex->generateGLTexture() ;
+ tex->setCategory(LLViewerTexture::LOCAL) ;
}
return tex ;
}
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLImageRaw* raw, BOOL usemipmaps)
{
LLPointer<LLViewerTexture> tex = new LLViewerTexture(raw, usemipmaps) ;
+ tex->setCategory(LLViewerTexture::LOCAL) ;
return tex ;
}
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, BOOL generate_gl_tex)
@@ -185,6 +198,7 @@ LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const U32 wid
if(generate_gl_tex)
{
tex->generateGLTexture() ;
+ tex->setCategory(LLViewerTexture::LOCAL) ;
}
return tex ;
}
@@ -215,6 +229,19 @@ LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromFile(
return gTextureList.getImageFromFile(filename, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id) ;
}
+//static
+LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const std::string& url,
+ BOOL usemipmaps,
+ S32 boost_priority,
+ S8 texture_type,
+ LLGLint internal_format,
+ LLGLenum primary_format,
+ const LLUUID& force_id
+ )
+{
+ return gTextureList.getImageFromUrl(url, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id) ;
+}
+
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromHost(const LLUUID& image_id, LLHost host)
{
return gTextureList.getImageFromHost(image_id, host) ;
@@ -256,11 +283,12 @@ void LLViewerTextureManager::init()
}
imagep->createGLTexture(0, image_raw);
image_raw = NULL;
- LLViewerFetchedTexture::sDefaultImagep->dontDiscard();
#else
LLViewerFetchedTexture::sDefaultImagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI);
#endif
-
+ LLViewerFetchedTexture::sDefaultImagep->dontDiscard();
+ LLViewerFetchedTexture::sDefaultImagep->setCategory(LLViewerTexture::OTHER) ;
+
LLViewerFetchedTexture::sSmokeImagep = LLViewerTextureManager::getFetchedTexture(IMG_SMOKE, TRUE, LLViewerTexture::BOOST_UI);
LLViewerFetchedTexture::sSmokeImagep->setNoDelete() ;
@@ -284,6 +312,8 @@ void LLViewerTextureManager::cleanup()
LLViewerFetchedTexture::sWhiteImagep = NULL;
LLViewerMediaTexture::cleanup() ;
+
+ LLViewerTexture::cleanupClass() ;
}
//----------------------------------------------------------------------------------------------
@@ -294,6 +324,11 @@ void LLViewerTextureManager::cleanup()
void LLViewerTexture::initClass()
{
LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture() ;
+
+ if(gAuditTexture)
+ {
+ LLImageGL::setHighlightTexture(LLViewerTexture::OTHER) ;
+ }
}
// static
@@ -301,6 +336,25 @@ void LLViewerTexture::cleanupClass()
{
}
+// static
+S32 LLViewerTexture::getTotalNumOfCategories()
+{
+ return MAX_GL_IMAGE_CATEGORY - (BOOST_HIGH - BOOST_SCULPTED) + 2 ;
+}
+
+// static
+//index starts from zero.
+S32 LLViewerTexture::getIndexFromCategory(S32 category)
+{
+ return (category < BOOST_HIGH) ? category : category - (BOOST_HIGH - BOOST_SCULPTED) + 1 ;
+}
+
+//static
+S32 LLViewerTexture::getCategoryFromIndex(S32 index)
+{
+ return (index < BOOST_HIGH) ? index : index + (BOOST_HIGH - BOOST_SCULPTED) - 1 ;
+}
+
// tuning params
const F32 discard_bias_delta = .05f;
const F32 discard_delta_time = 0.5f;
@@ -312,6 +366,8 @@ F32 texmem_middle_bound_scale = 0.925f;
//static
void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity)
{
+ sCurrentTime = gFrameTimeSeconds ;
+
if(LLViewerTextureManager::sTesterp)
{
LLViewerTextureManager::sTesterp->update() ;
@@ -352,6 +408,13 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity
}
sDesiredDiscardBias = llclamp(sDesiredDiscardBias, desired_discard_bias_min, desired_discard_bias_max);
LLViewerTexture::sUseTextureAtlas = gSavedSettings.getBOOL("EnableTextureAtlas") ;
+
+ F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ;
+ F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed();
+ sCameraMovingDiscardBias = (S8)llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1) ;
+
+ LLViewerTexture::sFreezeImageScalingDown = (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) < 0.75f * sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) &&
+ (BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) < 0.75f * sMaxTotalTextureMemInMegaBytes * texmem_middle_bound_scale) ;
}
//end of static functions
@@ -417,7 +480,9 @@ void LLViewerTexture::init(bool firstinit)
mTextureState = NO_DELETE ;
mDontDiscard = FALSE;
mMaxVirtualSize = 0.f;
+ mNeedsGLTexture = FALSE ;
mNeedsResetMaxVirtualSize = FALSE ;
+ mAdditionalDecodePriority = 0.f ;
mParcelMedia = NULL ;
}
@@ -459,11 +524,15 @@ void LLViewerTexture::setBoostLevel(S32 level)
{
setNoDelete() ;
}
+ if(gAuditTexture)
+ {
+ setCategory(mBoostLevel);
+ }
}
}
-bool LLViewerTexture::bindDefaultImage(S32 stage) const
+bool LLViewerTexture::bindDefaultImage(S32 stage)
{
if (stage < 0) return false;
@@ -482,6 +551,10 @@ bool LLViewerTexture::bindDefaultImage(S32 stage) const
llwarns << "LLViewerTexture::bindDefaultImage failed." << llendl;
}
stop_glerror();
+
+ //check if there is cached raw image and switch to it if possible
+ switchToCachedImage() ;
+
if(LLViewerTextureManager::sTesterp)
{
LLViewerTextureManager::sTesterp->updateGrayTextureBinding() ;
@@ -500,24 +573,32 @@ void LLViewerTexture::forceImmediateUpdate()
{
}
-void LLViewerTexture::addTextureStats(F32 virtual_size) const
+void LLViewerTexture::addTextureStats(F32 virtual_size, BOOL needs_gltexture) const
{
- if (virtual_size > mMaxVirtualSize)
+ if(needs_gltexture)
{
- mMaxVirtualSize = virtual_size;
+ mNeedsGLTexture = TRUE ;
}
-}
-void LLViewerTexture::resetTextureStats(BOOL zero)
-{
- if (zero)
+ if(mNeedsResetMaxVirtualSize)
{
- mMaxVirtualSize = 0.0f;
+ //flag to reset the values because the old values are used.
+ mNeedsResetMaxVirtualSize = FALSE ;
+ mMaxVirtualSize = virtual_size;
+ mAdditionalDecodePriority = 0.f ;
+ mNeedsGLTexture = needs_gltexture ;
}
- else
+ else if (virtual_size > mMaxVirtualSize)
{
- mMaxVirtualSize -= mMaxVirtualSize * .10f; // decay by 5%/update
- }
+ mMaxVirtualSize = virtual_size;
+ }
+}
+
+void LLViewerTexture::resetTextureStats()
+{
+ mMaxVirtualSize = 0.0f;
+ mAdditionalDecodePriority = 0.f ;
+ mNeedsResetMaxVirtualSize = FALSE ;
}
//virtual
@@ -544,6 +625,12 @@ void LLViewerTexture::removeFace(LLFace* facep)
mFaceList.remove(facep) ;
}
+//virtual
+void LLViewerTexture::switchToCachedImage()
+{
+ //nothing here.
+}
+
void LLViewerTexture::forceActive()
{
mTextureState = ACTIVE ;
@@ -588,11 +675,11 @@ BOOL LLViewerTexture::createGLTexture()
return mGLTexturep->createGLTexture() ;
}
-BOOL LLViewerTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename)
+BOOL LLViewerTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename, BOOL to_create, S32 category)
{
llassert_always(mGLTexturep.notNull()) ;
- BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename) ;
+ BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category) ;
if(ret)
{
@@ -704,6 +791,13 @@ void LLViewerTexture::setGLTextureCreated (bool initialized)
mGLTexturep->setGLTextureCreated (initialized) ;
}
+void LLViewerTexture::setCategory(S32 category)
+{
+ llassert_always(mGLTexturep.notNull()) ;
+
+ mGLTexturep->setCategory(category) ;
+}
+
LLTexUnit::eTextureAddressMode LLViewerTexture::getAddressMode(void) const
{
llassert_always(mGLTexturep.notNull()) ;
@@ -752,18 +846,18 @@ BOOL LLViewerTexture::getMissed() const
return mGLTexturep->getMissed() ;
}
-BOOL LLViewerTexture::isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents)
+BOOL LLViewerTexture::isJustBound() const
{
llassert_always(mGLTexturep.notNull()) ;
- return mGLTexturep->isValidForSculpt(discard_level, image_width, image_height, ncomponents) ;
+ return mGLTexturep->isJustBound() ;
}
-BOOL LLViewerTexture::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const
+void LLViewerTexture::forceUpdateBindStats(void) const
{
llassert_always(mGLTexturep.notNull()) ;
- return mGLTexturep->readBackRaw(discard_level, imageraw, compressed_ok) ;
+ return mGLTexturep->forceUpdateBindStats() ;
}
U32 LLViewerTexture::getTexelsInAtlas() const
@@ -803,6 +897,11 @@ void LLViewerTexture::destroyGLTexture()
}
}
+BOOL LLViewerTexture::isLargeImage()
+{
+ return mFullWidth * mFullHeight > LLViewerTexture::sMinLargeImageSize ;
+}
+
//virtual
void LLViewerTexture::updateBindStatsForTester()
{
@@ -823,11 +922,12 @@ void LLViewerTexture::updateBindStatsForTester()
//static
F32 LLViewerFetchedTexture::maxDecodePriority()
{
- return 2000000.f;
+ return 6000000.f;
}
-LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, BOOL usemipmaps)
- : LLViewerTexture(id, usemipmaps)
+LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, const LLHost& host, BOOL usemipmaps)
+ : LLViewerTexture(id, usemipmaps),
+ mTargetHost(host)
{
init(TRUE) ;
generateGLTexture() ;
@@ -839,9 +939,9 @@ LLViewerFetchedTexture::LLViewerFetchedTexture(const LLImageRaw* raw, BOOL usemi
init(TRUE) ;
}
-LLViewerFetchedTexture::LLViewerFetchedTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps)
+LLViewerFetchedTexture::LLViewerFetchedTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps)
: LLViewerTexture(id, usemipmaps),
- mLocalFileName(full_path)
+ mUrl(url)
{
init(TRUE) ;
generateGLTexture() ;
@@ -886,10 +986,18 @@ void LLViewerFetchedTexture::init(bool firstinit)
mFetchPriority = 0;
mDownloadProgress = 0.f;
mFetchDeltaTime = 999999.f;
- mDecodeFrame = 0;
- mVisibleFrame = 0;
mForSculpt = FALSE ;
mIsFetched = FALSE ;
+
+ mCachedRawImage = NULL ;
+ mCachedRawDiscardLevel = -1 ;
+ mCachedRawImageReady = FALSE ;
+
+ mSavedRawImage = NULL ;
+ mForceToSaveRawImage = FALSE ;
+ mSavedRawDiscardLevel = -1 ;
+ mDesiredSavedRawDiscardLevel = -1 ;
+ mLastReferencedSavedRawImageTime = 0.0f ;
}
LLViewerFetchedTexture::~LLViewerFetchedTexture()
@@ -926,11 +1034,25 @@ void LLViewerFetchedTexture::cleanup()
// Clean up image data
destroyRawImage();
+ mCachedRawImage = NULL ;
+ mCachedRawDiscardLevel = -1 ;
+ mCachedRawImageReady = FALSE ;
+ mSavedRawImage = NULL ;
}
void LLViewerFetchedTexture::setForSculpt()
{
mForSculpt = TRUE ;
+ if(isForSculptOnly() && !getBoundRecently())
+ {
+ destroyGLTexture() ; //sculpt image does not need gl texture.
+ }
+ checkCachedRawSculptImage() ;
+}
+
+BOOL LLViewerFetchedTexture::isForSculptOnly() const
+{
+ return mForSculpt && !mNeedsGLTexture ;
}
BOOL LLViewerFetchedTexture::isDeleted()
@@ -965,17 +1087,37 @@ void LLViewerFetchedTexture::setInactive()
}
}
+BOOL LLViewerFetchedTexture::isFullyLoaded() const
+{
+ // Unfortunately, the boolean "mFullyLoaded" is never updated correctly so we use that logic
+ // to check if the texture is there and completely downloaded
+ return (mFullWidth != 0) && (mFullHeight != 0) && !mIsFetching && !mHasFetcher;
+}
+
+
// virtual
void LLViewerFetchedTexture::dump()
{
LLViewerTexture::dump();
- llinfos << "LLViewerFetchedTexture"
- << " mIsMissingAsset " << (S32)mIsMissingAsset
- << " mFullWidth " << mFullWidth
- << " mFullHeight " << mFullHeight
- << " mOrigWidth" << mOrigWidth
- << " mOrigHeight" << mOrigHeight
+ llinfos << "Dump : " << mID
+ << ", mIsMissingAsset = " << (S32)mIsMissingAsset
+ << ", mFullWidth = " << (S32)mFullWidth
+ << ", mFullHeight = " << (S32)mFullHeight
+ << ", mOrigWidth = " << (S32)mOrigWidth
+ << ", mOrigHeight = " << (S32)mOrigHeight
+ << llendl;
+ llinfos << " : "
+ << " mFullyLoaded = " << (S32)mFullyLoaded
+ << ", mFetchState = " << (S32)mFetchState
+ << ", mFetchPriority = " << (S32)mFetchPriority
+ << ", mDownloadProgress = " << (F32)mDownloadProgress
+ << llendl;
+ llinfos << " : "
+ << " mHasFetcher = " << (S32)mHasFetcher
+ << ", mIsFetching = " << (S32)mIsFetching
+ << ", mIsFetched = " << (S32)mIsFetched
+ << ", mBoostLevel = " << (S32)mBoostLevel
<< llendl;
}
@@ -996,6 +1138,75 @@ void LLViewerFetchedTexture::destroyTexture()
mFullyLoaded = FALSE ;
}
+//
+//do not change the discard level of the loaded texture image.
+BOOL LLViewerFetchedTexture::keepReuestedDiscardLevel()
+{
+ if (!mLoadedCallbackList.empty())
+ {
+ return TRUE ;
+ }
+
+ return FALSE ;
+}
+
+void LLViewerFetchedTexture::addToCreateTexture()
+{
+ if(isForSculptOnly())
+ {
+ //just update some variables, not to create a real GL texture.
+ createGLTexture(mRawDiscardLevel, mRawImage, 0, FALSE) ;
+ mNeedsCreateTexture = FALSE ;
+ destroyRawImage();
+ }
+ else
+ {
+#if 1
+ //
+ //if mRequestedDiscardLevel > mDesiredDiscardLevel, we assume the required image res keep going up,
+ //so do not scale down the over qualified image.
+ //Note: scaling down image is expensensive. Do it only when very necessary.
+ //
+ if(mRequestedDiscardLevel <= mDesiredDiscardLevel && !keepReuestedDiscardLevel())
+ {
+ S32 w = mFullWidth >> mRawDiscardLevel;
+ S32 h = mFullHeight >> mRawDiscardLevel;
+
+ //if big image, do not load extra data
+ //scale it down to size >= LLViewerTexture::sMinLargeImageSize
+ if(w * h > LLViewerTexture::sMinLargeImageSize)
+ {
+ S32 d_level = llmin(mRequestedDiscardLevel, (S32)mDesiredDiscardLevel) - mRawDiscardLevel ;
+
+ if(d_level > 0)
+ {
+ S32 i = 0 ;
+ while((d_level > 0) && ((w >> i) * (h >> i) > LLViewerTexture::sMinLargeImageSize))
+ {
+ i++;
+ d_level--;
+ }
+ if(i > 0)
+ {
+ mRawDiscardLevel += i ;
+ if(mRawDiscardLevel >= getDiscardLevel() && getDiscardLevel() > 0)
+ {
+ mNeedsCreateTexture = FALSE ;
+ destroyRawImage();
+ return ;
+ }
+ mRawImage->scale(w >> i, h >> i) ;
+ }
+ }
+ }
+ }
+#endif
+ mNeedsCreateTexture = TRUE;
+ gTextureList.mCreateTextureList.insert(this);
+ }
+ return ;
+}
+
// ONLY called from LLViewerTextureList
BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/)
{
@@ -1017,7 +1228,7 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/)
if (!gNoRender)
{
// store original size only for locally-sourced images
- if (!mLocalFileName.empty())
+ if (mUrl.compare(0, 7, "file://") == 0)
{
mOrigWidth = mRawImage->getWidth();
mOrigHeight = mRawImage->getHeight();
@@ -1063,7 +1274,7 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/)
if(!(res = insertToAtlas()))
{
- res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename);
+ res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel);
resetFaceAtlas() ;
}
setActive() ;
@@ -1117,7 +1328,15 @@ void LLViewerFetchedTexture::processTextureStats()
return ;
}
- if(!mFullWidth || !mFullHeight)
+ updateVirtualSize() ;
+
+ static LLCachedControl<bool> textures_fullres(gSavedSettings,"TextureLoadFullRes");
+
+ if (textures_fullres)
+ {
+ mDesiredDiscardLevel = 0;
+ }
+ else if(!mFullWidth || !mFullHeight)
{
mDesiredDiscardLevel = getMaxDiscardLevel() ;
}
@@ -1149,16 +1368,6 @@ void LLViewerFetchedTexture::processTextureStats()
}
}
-//texture does not have any data, so we don't know the size of the image, treat it like 32 * 32.
-F32 LLViewerFetchedTexture::calcDecodePriorityForUnknownTexture(F32 pixel_priority)
-{
- F32 desired = (F32)(log(32.0/pixel_priority) / log_2);
- S32 ddiscard = MAX_DISCARD_LEVEL - (S32)desired + 1;
- ddiscard = llclamp(ddiscard, 1, 9);
-
- return ddiscard*100000.f;
-}
-
F32 LLViewerFetchedTexture::calcDecodePriority()
{
#ifndef LL_RELEASE_FOR_DOWNLOAD
@@ -1177,22 +1386,28 @@ F32 LLViewerFetchedTexture::calcDecodePriority()
{
return mDecodePriority; // no change while waiting to create
}
-
- F32 priority;
- S32 cur_discard = getDiscardLevel();
- bool have_all_data = (cur_discard >= 0 && (cur_discard <= mDesiredDiscardLevel));
- F32 pixel_priority = fsqrtf(mMaxVirtualSize);
- const S32 MIN_NOT_VISIBLE_FRAMES = 30; // NOTE: this function is not called every frame
- mDecodeFrame++;
- if (pixel_priority > 0.f)
+ if(mForceToSaveRawImage)
{
- mVisibleFrame = mDecodeFrame;
+ return maxDecodePriority() ;
}
+ S32 cur_discard = getDiscardLevel();
+ bool have_all_data = (cur_discard >= 0 && (cur_discard <= mDesiredDiscardLevel));
+ F32 pixel_priority = fsqrtf(mMaxVirtualSize);
+
+ F32 priority;
if (mIsMissingAsset)
{
priority = 0.0f;
}
+ else if(mDesiredDiscardLevel >= cur_discard && cur_discard > -1)
+ {
+ priority = -1.0f ;
+ }
+ else if(mCachedRawDiscardLevel > -1 && mDesiredDiscardLevel >= mCachedRawDiscardLevel)
+ {
+ priority = -1.0f;
+ }
else if (mDesiredDiscardLevel > getMaxDiscardLevel())
{
// Don't decode anything we don't need
@@ -1210,11 +1425,6 @@ F32 LLViewerFetchedTexture::calcDecodePriority()
// Always want high boosted images
priority = 1.f;
}
- else if (mVisibleFrame == 0 || (mDecodeFrame - mVisibleFrame > MIN_NOT_VISIBLE_FRAMES))
- {
- // Don't decode anything that isn't visible unless it's important
- priority = -2.0f;
- }
else
{
// Leave the priority as-is
@@ -1223,7 +1433,13 @@ F32 LLViewerFetchedTexture::calcDecodePriority()
}
else if (cur_discard < 0)
{
- priority = calcDecodePriorityForUnknownTexture(pixel_priority) ;
+ //texture does not have any data, so we don't know the size of the image, treat it like 32 * 32.
+ // priority range = 100,000 - 500,000
+ static const F64 log_2 = log(2.0);
+ F32 desired = (F32)(log(32.0/pixel_priority) / log_2);
+ S32 ddiscard = MAX_DISCARD_LEVEL - (S32)desired;
+ ddiscard = llclamp(ddiscard, 0, 4);
+ priority = (ddiscard+1)*100000.f;
}
else if ((mMinDiscardLevel > 0) && (cur_discard <= mMinDiscardLevel))
{
@@ -1236,29 +1452,47 @@ F32 LLViewerFetchedTexture::calcDecodePriority()
}
else
{
- // priority range = 100000-400000
- S32 ddiscard = cur_discard - mDesiredDiscardLevel;
+ // priority range = 100,000 - 500,000
+ S32 desired_discard = mDesiredDiscardLevel;
if (getDontDiscard())
{
- ddiscard+=2;
+ desired_discard -= 2;
+ }
+ else if (!isJustBound() && mCachedRawImageReady && !mBoostLevel)
+ {
+ // We haven't rendered this in the last half second, and we have a cached raw image, leave the desired discard as-is
+ desired_discard = cur_discard;
}
else if (mGLTexturep.notNull() && !mGLTexturep->getBoundRecently() && mBoostLevel == LLViewerTexture::BOOST_NONE)
{
- ddiscard-=2;
+ // We haven't rendered this in a while, de-prioritize it
+ desired_discard += 2;
}
+ S32 ddiscard = cur_discard - desired_discard;
ddiscard = llclamp(ddiscard, 0, 4);
- priority = ddiscard*100000.f;
+ priority = (ddiscard+1)*100000.f;
}
+
+ // Priority Formula:
+ // BOOST_HIGH + ADDITIONAL PRI + DELTA DISCARD + BOOST LEVEL + PIXELS
+ // [10,000,000] + [1-9,000,000] + [1-400,000] + [1-20,000] + [0-999]
if (priority > 0.0f)
{
- pixel_priority = llclamp(pixel_priority, 0.0f, priority-1.f); // priority range = 100000-900000
+ pixel_priority = llclamp(pixel_priority, 0.0f, 999.f);
+
+ priority = pixel_priority + 1000.f * mBoostLevel;
+
if ( mBoostLevel > BOOST_HIGH)
{
- priority = 1000000.f + pixel_priority + 1000.f * (mBoostLevel - LLViewerTexture::BOOST_NONE);
+ priority += 10000000.f;
}
- else
+
+ if(mAdditionalDecodePriority > 0.0f)
{
- priority += 0.f + pixel_priority + 1000.f * (mBoostLevel - LLViewerTexture::BOOST_NONE);
+ // 1-9
+ S32 additional_priority = (S32)(1.0f + mAdditionalDecodePriority*8.0f + .5f); // round
+ // priority range += 0-9,000,000
+ priority += 1000000.f * (F32)additional_priority;
}
}
return priority;
@@ -1271,6 +1505,36 @@ void LLViewerFetchedTexture::setDecodePriority(F32 priority)
mDecodePriority = priority;
}
+void LLViewerFetchedTexture::setAdditionalDecodePriority(F32 priority)
+{
+ priority = llclamp(priority, 0.f, 1.f);
+ if(mAdditionalDecodePriority < priority)
+ {
+ mAdditionalDecodePriority = priority;
+ }
+}
+
+void LLViewerFetchedTexture::updateVirtualSize()
+{
+ if(mNeedsResetMaxVirtualSize)
+ {
+ addTextureStats(0.f, FALSE) ;//reset
+ }
+ if(mFaceList.size() > 0)
+ {
+ for(std::list<LLFace*>::iterator iter = mFaceList.begin(); iter != mFaceList.end(); ++iter)
+ {
+ LLFace* facep = *iter ;
+ if(facep->getDrawable()->isRecentlyVisible())
+ {
+ addTextureStats(facep->getVirtualSize()) ;
+ setAdditionalDecodePriority(facep->getImportanceToCamera()) ;
+ }
+ }
+ }
+ mNeedsResetMaxVirtualSize = TRUE ;
+}
+
bool LLViewerFetchedTexture::updateFetch()
{
mFetchState = 0;
@@ -1310,6 +1574,15 @@ bool LLViewerFetchedTexture::updateFetch()
{
// Sets mRawDiscardLevel, mRawImage, mAuxRawImage
S32 fetch_discard = current_discard;
+
+ if(mForceToSaveRawImage)
+ {
+ if(fetch_discard >= 0)
+ {
+ fetch_discard = llmax(fetch_discard, mSavedRawDiscardLevel) ;
+ }
+ }
+
if (mRawImage.notNull()) sRawCount--;
if (mAuxRawImage.notNull()) sAuxCount--;
bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage);
@@ -1349,11 +1622,24 @@ bool LLViewerFetchedTexture::updateFetch()
(*iter)->dirtyTexture() ;
}
}
- mIsRawImageValid = TRUE;
- gTextureList.mCreateTextureList.insert(this);
- mNeedsCreateTexture = TRUE;
mFullWidth = mRawImage->getWidth() << mRawDiscardLevel;
mFullHeight = mRawImage->getHeight() << mRawDiscardLevel;
+
+ if(mFullWidth > MAX_IMAGE_SIZE || mFullHeight > MAX_IMAGE_SIZE)
+ {
+ //discard all oversized textures.
+ destroyRawImage();
+ setIsMissingAsset();
+ mRawDiscardLevel = INVALID_DISCARD_LEVEL ;
+ mIsFetching = FALSE ;
+ }
+ else
+ {
+ mIsRawImageValid = TRUE;
+ addToCreateTexture() ;
+ }
+
+ return TRUE ;
}
else
{
@@ -1376,13 +1662,13 @@ bool LLViewerFetchedTexture::updateFetch()
}
else
{
- llwarns << mID << ": Setting min discard to " << current_discard << llendl;
+ //llwarns << mID << ": Setting min discard to " << current_discard << llendl;
mMinDiscardLevel = current_discard;
desired_discard = current_discard;
}
destroyRawImage();
}
- else if (mRawImage.isNull())
+ else if (mRawImage.notNull())
{
// We have data, but our fetch failed to return raw data
// *TODO: FIgure out why this is happening and fix it
@@ -1391,12 +1677,29 @@ bool LLViewerFetchedTexture::updateFetch()
}
else
{
+// // Useful debugging code for undesired deprioritization of textures.
+// if (decode_priority <= 0.0f && desired_discard >= 0 && desired_discard < current_discard)
+// {
+// llinfos << "Calling updateRequestPriority() with decode_priority = 0.0f" << llendl;
+// calcDecodePriority();
+// }
LLAppViewer::getTextureFetch()->updateRequestPriority(mID, decode_priority);
}
}
- bool make_request = true;
-
+ if (!mDontDiscard)
+ {
+ if (mBoostLevel == 0)
+ {
+ desired_discard = llmax(desired_discard, current_discard-1);
+ }
+ else
+ {
+ desired_discard = llmax(desired_discard, current_discard-2);
+ }
+ }
+
+ bool make_request = true;
if (decode_priority <= 0)
{
make_request = false;
@@ -1409,6 +1712,10 @@ bool LLViewerFetchedTexture::updateFetch()
{
make_request = false;
}
+ else if (!isJustBound() && mCachedRawImageReady)
+ {
+ make_request = false;
+ }
else
{
if (mIsFetching)
@@ -1436,33 +1743,12 @@ bool LLViewerFetchedTexture::updateFetch()
h = mGLTexturep->getHeight(0);
c = mComponents;
}
- if (!mDontDiscard)
- {
- if (mBoostLevel == LLViewerTexture::BOOST_NONE)
- {
- desired_discard = llmax(desired_discard, current_discard-1);
- }
- else
- {
- desired_discard = llmax(desired_discard, current_discard-2);
- }
- }
-
+
// bypass texturefetch directly by pulling from LLTextureCache
bool fetch_request_created = false;
- if (mLocalFileName.empty())
- {
- fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(getID(), getTargetHost(), decode_priority,
- w, h, c, desired_discard,
- needsAux());
- }
- else
- {
- fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mLocalFileName, getID(),getTargetHost(), decode_priority,
- w, h, c, desired_discard,
- needsAux());
- }
-
+ fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mUrl, getID(),getTargetHost(), decode_priority,
+ w, h, c, desired_discard, needsAux());
+
if (fetch_request_created)
{
mHasFetcher = TRUE;
@@ -1492,9 +1778,82 @@ bool LLViewerFetchedTexture::updateFetch()
return mIsFetching ? true : false;
}
+//
+//force to fetch a new raw image for this texture
+//
+BOOL LLViewerFetchedTexture::forceFetch()
+{
+ if(!mForceToSaveRawImage)
+ {
+ return false ;
+ }
+ if(mDesiredSavedRawDiscardLevel < getDiscardLevel())
+ {
+ //no need to force fetching. normal fetching flow will do the work.
+ //return false ;
+ }
+ if (mNeedsCreateTexture)
+ {
+ // We may be fetching still (e.g. waiting on write)
+ // but don't check until we've processed the raw data we have
+ //return false;
+ }
+ if(mIsFetching)
+ {
+ return false ;
+ }
+ if (mIsMissingAsset)
+ {
+ mForceToSaveRawImage = false ;
+ llassert_always(!mHasFetcher);
+ return false; // skip
+ }
+ if (!mLoadedCallbackList.empty() && mRawImage.notNull())
+ {
+ return false; // process any raw image data in callbacks before replacing
+ }
+ if(mRawImage.notNull() && mRawDiscardLevel <= mDesiredSavedRawDiscardLevel)
+ {
+ return false ; // mRawImage is enough
+ }
+
+ S32 desired_discard = mDesiredSavedRawDiscardLevel ;
+ S32 current_discard = getDiscardLevel();
+
+ bool fetch_request_created = false;
+ S32 w=0, h=0, c=0;
+ if (current_discard >= 0)
+ {
+ w = getWidth(0);
+ h = getHeight(0);
+ c = getComponents();
+ }
+ fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mUrl, getID(),getTargetHost(), maxDecodePriority(),
+ w, h, c, desired_discard, needsAux());
+
+ if (fetch_request_created)
+ {
+ mHasFetcher = TRUE;
+ mIsFetching = TRUE;
+ mRequestedDiscardLevel = desired_discard ;
+
+ mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority,
+ mFetchPriority, mFetchDeltaTime, mRequestDeltaTime);
+ }
+
+ return mIsFetching ? true : false;
+}
+
void LLViewerFetchedTexture::setIsMissingAsset()
{
- llwarns << mLocalFileName << " " << mID << ": Marking image as missing" << llendl;
+ if (mUrl.empty())
+ {
+ llwarns << mID << ": Marking image as missing" << llendl;
+ }
+ else
+ {
+ llwarns << mUrl << ": Marking image as missing" << llendl;
+ }
if (mHasFetcher)
{
LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
@@ -1516,7 +1875,13 @@ void LLViewerFetchedTexture::setLoadedCallback( loaded_callback_func loaded_call
{
// Put in list to call this->doLoadedCallbacks() periodically
gTextureList.mCallbackList.insert(this);
+ mLoadedCallbackDesiredDiscardLevel = (S8)discard_level;
+ }
+ else
+ {
+ mLoadedCallbackDesiredDiscardLevel = llmin(mLoadedCallbackDesiredDiscardLevel, (S8)discard_level) ;
}
+
LLLoadedCallbackEntry* entryp = new LLLoadedCallbackEntry(loaded_callback, discard_level, keep_imageraw, userdata);
mLoadedCallbackList.push_back(entryp);
mNeedsAux |= needs_aux;
@@ -1645,7 +2010,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks()
// We have GL data.
destroyRawImage();
- readBackRawImage(gl_discard);
+ reloadRawImage(mLoadedCallbackDesiredDiscardLevel);
llassert_always(mRawImage.notNull());
llassert_always(!mNeedsAux || mAuxRawImage.notNull());
}
@@ -1754,8 +2119,7 @@ void LLViewerFetchedTexture::forceImmediateUpdate()
return ;
}
-// Was in LLImageGL
-LLImageRaw* LLViewerFetchedTexture::readBackRawImage(S8 discard_level)
+LLImageRaw* LLViewerFetchedTexture::reloadRawImage(S8 discard_level)
{
llassert_always(mGLTexturep.notNull()) ;
llassert_always(discard_level >= 0);
@@ -1765,25 +2129,216 @@ LLImageRaw* LLViewerFetchedTexture::readBackRawImage(S8 discard_level)
llerrs << "called with existing mRawImage" << llendl;
mRawImage = NULL;
}
- mRawImage = new LLImageRaw(mGLTexturep->getWidth(discard_level), mGLTexturep->getHeight(discard_level), mComponents);
- sRawCount++;
- mRawDiscardLevel = discard_level;
- mGLTexturep->readBackRaw(mRawDiscardLevel, mRawImage, false);
- mIsRawImageValid = TRUE;
+
+ if(mSavedRawDiscardLevel >= 0 && mSavedRawDiscardLevel <= discard_level)
+ {
+ mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), getComponents()) ;
+ mRawImage->copy(getSavedRawImage()) ;
+ mRawDiscardLevel = discard_level ;
+ }
+ else
+ {
+ //force to fetch raw image again if cached raw image is not good enough.
+ if(mCachedRawDiscardLevel > discard_level)
+ {
+ mRawImage = mCachedRawImage ;
+ mRawDiscardLevel = mCachedRawDiscardLevel;
+
+ forceToSaveRawImage(discard_level) ;
+ }
+ else //cached raw image is good enough, copy it.
+ {
+ mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), getComponents()) ;
+ mRawImage->copy(mCachedRawImage) ;
+ mRawDiscardLevel = discard_level ;
+ }
+ }
+ mIsRawImageValid = TRUE ;
+ sRawCount++;
return mRawImage;
}
void LLViewerFetchedTexture::destroyRawImage()
-{
- if (mRawImage.notNull()) sRawCount--;
+{
if (mAuxRawImage.notNull()) sAuxCount--;
+
+ if (mRawImage.notNull())
+ {
+ sRawCount--;
+ setCachedRawImage() ;
+
+ if(mForceToSaveRawImage)
+ {
+ saveRawImage() ;
+ }
+ }
+
mRawImage = NULL;
mAuxRawImage = NULL;
mIsRawImageValid = FALSE;
mRawDiscardLevel = INVALID_DISCARD_LEVEL;
+
+ if(mForceToSaveRawImage)
+ {
+ forceFetch() ;
+ }
+}
+
+//use the mCachedRawImage to (re)generate the gl texture.
+//virtual
+void LLViewerFetchedTexture::switchToCachedImage()
+{
+ if(mCachedRawImage.notNull())
+ {
+ mRawImage = mCachedRawImage ;
+
+ if (getComponents() != mRawImage->getComponents())
+ {
+ // We've changed the number of components, so we need to move any
+ // objects using this pool to a different pool.
+ mComponents = mRawImage->getComponents();
+ mGLTexturep->setComponents(mComponents) ;
+ gTextureList.dirtyImage(this);
+ }
+
+ mIsRawImageValid = TRUE;
+ mRawDiscardLevel = mCachedRawDiscardLevel ;
+ gTextureList.mCreateTextureList.insert(this);
+ mNeedsCreateTexture = TRUE;
+ }
+}
+
+void LLViewerFetchedTexture::setCachedRawImage()
+{
+ if(mRawImage == mCachedRawImage)
+ {
+ return ;
+ }
+ if(!mIsRawImageValid)
+ {
+ return ;
+ }
+
+ if(mCachedRawImageReady)
+ {
+ return ;
+ }
+
+ if(mCachedRawDiscardLevel < 0 || mCachedRawDiscardLevel > mRawDiscardLevel)
+ {
+ S32 i = 0 ;
+ S32 w = mRawImage->getWidth() ;
+ S32 h = mRawImage->getHeight() ;
+
+ S32 max_size = MAX_CACHED_RAW_IMAGE_AREA ;
+ if(LLViewerTexture::BOOST_TERRAIN == mBoostLevel)
+ {
+ max_size = MAX_CACHED_RAW_TERRAIN_IMAGE_AREA ;
+ }
+ if(mForSculpt)
+ {
+ max_size = MAX_CACHED_RAW_SCULPT_IMAGE_AREA ;
+ mCachedRawImageReady = !mRawDiscardLevel ;
+ }
+ else
+ {
+ mCachedRawImageReady = (!mRawDiscardLevel || ((w * h) >= max_size)) ;
+ }
+
+ while(((w >> i) * (h >> i)) > max_size)
+ {
+ ++i ;
+ }
+
+ if(i)
+ {
+ if(!(w >> i) || !(h >> i))
+ {
+ --i ;
+ }
+ //if(mForSculpt)
+ //{
+ // mRawImage->scaleDownWithoutBlending(w >> i, h >> i) ;
+ //}
+ //else
+ {
+ mRawImage->scale(w >> i, h >> i) ;
+ }
+ }
+ mCachedRawImage = mRawImage ;
+ mCachedRawDiscardLevel = mRawDiscardLevel + i ;
+ }
+}
+
+void LLViewerFetchedTexture::checkCachedRawSculptImage()
+{
+ if(mCachedRawImageReady && mCachedRawDiscardLevel > 0)
+ {
+ if(getDiscardLevel() != 0)
+ {
+ mCachedRawImageReady = FALSE ;
+ }
+ else if(isForSculptOnly())
+ {
+ resetTextureStats() ; //do not update this image any more.
+ }
+ }
+}
+
+void LLViewerFetchedTexture::saveRawImage()
+{
+ if(mRawImage.isNull() || mSavedRawDiscardLevel == mRawDiscardLevel)
+ {
+ return ;
+ }
+
+ mSavedRawDiscardLevel = mRawDiscardLevel ;
+ mSavedRawImage = new LLImageRaw(mRawImage->getData(), mRawImage->getWidth(), mRawImage->getHeight(), mRawImage->getComponents()) ;
+
+ if(mSavedRawDiscardLevel <= mDesiredSavedRawDiscardLevel)
+ {
+ mForceToSaveRawImage = FALSE ;
+ }
+
+ mLastReferencedSavedRawImageTime = sCurrentTime ;
}
+void LLViewerFetchedTexture::forceToSaveRawImage(S32 desired_discard)
+{
+ if(!mForceToSaveRawImage && (mDesiredSavedRawDiscardLevel < 0 || mDesiredSavedRawDiscardLevel > desired_discard))
+ {
+ mForceToSaveRawImage = TRUE ;
+ mDesiredSavedRawDiscardLevel = desired_discard ;
+
+ forceFetch() ;
+ }
+}
+void LLViewerFetchedTexture::destroySavedRawImage()
+{
+ mSavedRawImage = NULL ;
+ mForceToSaveRawImage = FALSE ;
+ mSavedRawDiscardLevel = -1 ;
+ mDesiredSavedRawDiscardLevel = -1 ;
+ mLastReferencedSavedRawImageTime = 0.0f ;
+}
+
+LLImageRaw* LLViewerFetchedTexture::getSavedRawImage()
+{
+ mLastReferencedSavedRawImageTime = sCurrentTime ;
+
+ return mSavedRawImage ;
+}
+
+BOOL LLViewerFetchedTexture::hasSavedRawImage() const
+{
+ return mSavedRawImage.notNull() ;
+}
+
+F32 LLViewerFetchedTexture::getElapsedLastReferencedSavedRawImageTime() const
+{
+ return mLastReferencedSavedRawImageTime - sCurrentTime ;
+}
//----------------------------------------------------------------------------------------------
//atlasing
//----------------------------------------------------------------------------------------------
@@ -1981,14 +2536,14 @@ BOOL LLViewerFetchedTexture::insertToAtlas()
//----------------------------------------------------------------------------------------------
//start of LLViewerLODTexture
//----------------------------------------------------------------------------------------------
-LLViewerLODTexture::LLViewerLODTexture(const LLUUID& id, BOOL usemipmaps)
- : LLViewerFetchedTexture(id, usemipmaps)
+LLViewerLODTexture::LLViewerLODTexture(const LLUUID& id, const LLHost& host, BOOL usemipmaps)
+ : LLViewerFetchedTexture(id, host, usemipmaps)
{
init(TRUE) ;
}
-LLViewerLODTexture::LLViewerLODTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps)
- : LLViewerFetchedTexture(full_path, id, usemipmaps)
+LLViewerLODTexture::LLViewerLODTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps)
+ : LLViewerFetchedTexture(url, id, usemipmaps)
{
init(TRUE) ;
}
@@ -2006,12 +2561,25 @@ S8 LLViewerLODTexture::getType() const
return LLViewerTexture::LOD_TEXTURE ;
}
+BOOL LLViewerLODTexture::isUpdateFrozen()
+{
+ return LLViewerTexture::sFreezeImageScalingDown && !getDiscardLevel() ;
+}
+
// This is gauranteed to get called periodically for every texture
//virtual
void LLViewerLODTexture::processTextureStats()
{
+ updateVirtualSize() ;
+
+ static LLCachedControl<bool> textures_fullres(gSavedSettings,"TextureLoadFullRes");
+
+ if (textures_fullres)
+ {
+ mDesiredDiscardLevel = 0;
+ }
// Generate the request priority and render priority
- if (mDontDiscard || !mUseMipMaps)
+ else if (mDontDiscard || !mUseMipMaps)
{
mDesiredDiscardLevel = 0;
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
@@ -2040,13 +2608,7 @@ void LLViewerLODTexture::processTextureStats()
// If we know the output width and height, we can force the discard
// level to the correct value, and thus not decode more texture
// data than we need to.
- /*if (mBoostLevel == LLViewerTexture::BOOST_UI ||
- mBoostLevel == LLViewerTexture::BOOST_PREVIEW ||
- mBoostLevel == LLViewerTexture::BOOST_AVATAR_SELF) // what about AVATAR_BAKED_SELF?
- {
- discard_level = 0; // full res
- }
- else*/ if (mKnownDrawWidth && mKnownDrawHeight)
+ if (mKnownDrawWidth && mKnownDrawHeight)
{
S32 draw_texels = mKnownDrawWidth * mKnownDrawHeight;
@@ -2057,6 +2619,12 @@ void LLViewerLODTexture::processTextureStats()
}
else
{
+ if(isLargeImage() && !isJustBound() && mAdditionalDecodePriority < 1.0f)
+ {
+ //if is a big image and not being used recently, nor close to the view point, do not load hi-res data.
+ mMaxVirtualSize = llmin(mMaxVirtualSize, (F32)LLViewerTexture::sMinLargeImageSize) ;
+ }
+
if ((mCalculatedDiscardLevel >= 0.f) &&
(llabs(mMaxVirtualSize - mDiscardVirtualSize) < mMaxVirtualSize*.20f))
{
@@ -2073,13 +2641,11 @@ void LLViewerLODTexture::processTextureStats()
}
if (mBoostLevel < LLViewerTexture::BOOST_HIGH)
{
- static const F32 discard_bias = -.5f; // Must be < 1 or highest discard will never load!
- discard_level += discard_bias;
discard_level += sDesiredDiscardBias;
discard_level *= sDesiredDiscardScale; // scale
+ discard_level += sCameraMovingDiscardBias ;
}
discard_level = floorf(discard_level);
-// discard_level -= (gTextureList.mVideoMemorySetting>>1); // more video ram = higher detail
F32 min_discard = 0.f;
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
@@ -2098,46 +2664,39 @@ void LLViewerLODTexture::processTextureStats()
// proper action if we don't.
//
- BOOL increase_discard = FALSE;
S32 current_discard = getDiscardLevel();
if ((sDesiredDiscardBias > 0.0f) &&
(current_discard >= 0 && mDesiredDiscardLevel >= current_discard))
{
- if ( BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) > sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale)
+ // Limit the amount of GL memory bound each frame
+ if ( BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) > sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale &&
+ (!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel))
{
- // Limit the amount of GL memory bound each frame
- if (mDesiredDiscardLevel > current_discard)
- {
- increase_discard = TRUE;
- }
+ scaleDown() ;
}
- if ( BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) > sMaxTotalTextureMemInMegaBytes*texmem_middle_bound_scale)
+ // Only allow GL to have 2x the video card memory
+ else if ( BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) > sMaxTotalTextureMemInMegaBytes*texmem_middle_bound_scale &&
+ (!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel))
{
- // Only allow GL to have 2x the video card memory
- if (!mGLTexturep->getBoundRecently())
- {
- increase_discard = TRUE;
- }
- }
- if (increase_discard)
- {
- // llinfos << "DISCARDED: " << mID << " Discard: " << current_discard << llendl;
- sBoundTextureMemoryInBytes -= mGLTexturep->mTextureMemory;
- sTotalTextureMemoryInBytes -= mGLTexturep->mTextureMemory;
- // Increase the discard level (reduce the texture res)
- S32 new_discard = current_discard+1;
- mGLTexturep->setDiscardLevel(new_discard);
- sBoundTextureMemoryInBytes += mGLTexturep->mTextureMemory;
- sTotalTextureMemoryInBytes += mGLTexturep->mTextureMemory;
- if(LLViewerTextureManager::sTesterp)
- {
- LLViewerTextureManager::sTesterp->setStablizingTime() ;
- }
+ scaleDown() ;
+
}
}
}
}
+void LLViewerLODTexture::scaleDown()
+{
+ if(hasGLTexture() && mCachedRawDiscardLevel > getDiscardLevel())
+ {
+ switchToCachedImage() ;
+
+ if(LLViewerTextureManager::sTesterp)
+ {
+ LLViewerTextureManager::sTesterp->setStablizingTime() ;
+ }
+ }
+}
//----------------------------------------------------------------------------------------------
//end of LLViewerLODTexture
//----------------------------------------------------------------------------------------------
@@ -2226,6 +2785,8 @@ LLViewerMediaTexture::LLViewerMediaTexture(const LLUUID& id, BOOL usemipmaps, LL
setMediaImpl() ;
+ setCategory(LLViewerTexture::MEDIA) ;
+
LLViewerTexture* tex = gTextureList.findImage(mID) ;
if(tex) //this media is a parcel media for tex.
{
@@ -2770,7 +3331,7 @@ void LLTexturePipelineTester::updateTextureLoadingStats(const LLViewerFetchedTex
mTotalBytesLoadedForLargeImage += data_size ;
}
- if(imagep->isForSculpt())
+ if(imagep->forSculpt())
{
mTotalBytesLoadedForSculpties += data_size ;
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index ff8f14e879..bde87d1dd5 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -123,10 +123,23 @@ public:
BOOST_UI = 16,
BOOST_PREVIEW = 17,
BOOST_MAP = 18,
- BOOST_AVATAR_SELF = 19, // needed for baking avatar
- BOOST_MAX_LEVEL
+ BOOST_MAP_VISIBLE = 19,
+ BOOST_AVATAR_SELF = 20, // needed for baking avatar
+ BOOST_MAX_LEVEL,
+
+ //other texture Categories
+ LOCAL = BOOST_MAX_LEVEL,
+ AVATAR_SCRATCH_TEX,
+ DYNAMIC_TEX,
+ MEDIA,
+ ATLAS,
+ OTHER,
+ MAX_GL_IMAGE_CATEGORY
};
-
+ static S32 getTotalNumOfCategories() ;
+ static S32 getIndexFromCategory(S32 category) ;
+ static S32 getCategoryFromIndex(S32 index) ;
+
typedef std::list<LLFace*> ll_face_list_t ;
protected:
@@ -147,7 +160,7 @@ public:
virtual BOOL isMissingAsset()const ;
virtual void dump(); // debug info to llinfos
- /*virtual*/ bool bindDefaultImage(const S32 stage = 0) const ;
+ /*virtual*/ bool bindDefaultImage(const S32 stage = 0) ;
/*virtual*/ void forceImmediateUpdate() ;
const LLUUID& getID() const { return mID; }
@@ -155,9 +168,9 @@ public:
void setBoostLevel(S32 level);
S32 getBoostLevel() { return mBoostLevel; }
- //maxVirtualSize of the texture
- void addTextureStats(F32 virtual_size) const ;
- void resetTextureStats(BOOL zero = FALSE);
+ void addTextureStats(F32 virtual_size, BOOL needs_gltexture = TRUE) const;
+ void resetTextureStats();
+
virtual F32 getMaxVirtualSize() ;
LLFrameTimer* getLastReferencedTimer() {return &mLastReferencedTimer ;}
@@ -182,7 +195,7 @@ public:
BOOL hasGLTexture() const ;
LLGLuint getTexName() const ;
BOOL createGLTexture() ;
- BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0);
+ BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, S32 category = LLViewerTexture::OTHER);
void setFilteringOption(LLTexUnit::eTextureFilterOptions option);
void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, BOOL swap_bytes = FALSE);
@@ -190,7 +203,8 @@ public:
BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height);
BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height);
void setGLTextureCreated (bool initialized);
-
+ void setCategory(S32 category) ;
+
LLTexUnit::eTextureAddressMode getAddressMode(void) const ;
S32 getMaxDiscardLevel() const;
S32 getDiscardLevel() const;
@@ -203,8 +217,8 @@ public:
BOOL getMask(const LLVector2 &tc);
F32 getTimePassedSinceLastBound();
BOOL getMissed() const ;
- BOOL isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents) ;
- BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const;
+ BOOL isJustBound()const ;
+ void forceUpdateBindStats(void) const;
U32 getTexelsInAtlas() const ;
U32 getTexelsInGLTexture() const ;
@@ -222,6 +236,8 @@ public:
BOOL getDontDiscard() const { return mDontDiscard; }
//-----------------
+ BOOL isLargeImage() ;
+
void setParcelMedia(LLViewerMediaTexture* media) {mParcelMedia = media;}
BOOL hasParcelMedia() const { return mParcelMedia != NULL;}
LLViewerMediaTexture* getParcelMedia() const { return mParcelMedia;}
@@ -234,6 +250,7 @@ protected:
private:
//note: do not make this function public.
/*virtual*/ LLImageGL* getGLTexture() const ;
+ virtual void switchToCachedImage();
protected:
LLUUID mID;
@@ -243,7 +260,9 @@ protected:
BOOL mUseMipMaps ;
S8 mComponents;
mutable F32 mMaxVirtualSize; // The largest virtual size of the image, in pixels - how much data to we need?
+ mutable S8 mNeedsGLTexture;
mutable BOOL mNeedsResetMaxVirtualSize ;
+ mutable F32 mAdditionalDecodePriority; // priority add to mDecodePriority.
LLFrameTimer mLastReferencedTimer;
ll_face_list_t mFaceList ; //reverse pointer pointing to the faces using this image as texture
@@ -279,7 +298,12 @@ public:
static S32 sMaxBoundTextureMemInMegaBytes;
static S32 sMaxTotalTextureMemInMegaBytes;
static S32 sMaxDesiredTextureMemInBytes ;
- static BOOL sDontLoadVolumeTextures;
+ static S8 sCameraMovingDiscardBias;
+ static S32 sMaxSculptRez ;
+ static S32 sMinLargeImageSize ;
+ static S32 sMaxSmallImageSize ;
+ static BOOL sFreezeImageScalingDown ;//do not scale down image res if set.
+ static F32 sCurrentTime ;
static BOOL sUseTextureAtlas ;
static LLPointer<LLViewerTexture> sNullImagep; // Null texture for non-textured objects.
@@ -299,9 +323,9 @@ class LLViewerFetchedTexture : public LLViewerTexture
protected:
/*virtual*/ ~LLViewerFetchedTexture();
public:
- LLViewerFetchedTexture(const LLUUID& id, BOOL usemipmaps = TRUE);
+ LLViewerFetchedTexture(const LLUUID& id, const LLHost& host = LLHost::invalid, BOOL usemipmaps = TRUE);
LLViewerFetchedTexture(const LLImageRaw* raw, BOOL usemipmaps);
- LLViewerFetchedTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps = TRUE);
+ LLViewerFetchedTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps = TRUE);
public:
static F32 maxDecodePriority();
@@ -337,6 +361,8 @@ public:
bool hasCallbacks() { return mLoadedCallbackList.empty() ? false : true; }
bool doLoadedCallbacks();
+ void addToCreateTexture();
+
// ONLY call from LLViewerTextureList
BOOL createTexture(S32 usename = 0);
void destroyTexture() ;
@@ -355,7 +381,11 @@ public:
// the priority list, and cause horrible things to happen.
void setDecodePriority(F32 priority = -1.0f);
F32 getDecodePriority() const { return mDecodePriority; };
+
+ void setAdditionalDecodePriority(F32 priority) ;
+ void updateVirtualSize() ;
+
// setDesiredDiscardLevel is only used by LLViewerTextureList
void setDesiredDiscardLevel(S32 discard) { mDesiredDiscardLevel = discard; }
S32 getDesiredDiscardLevel() { return mDesiredDiscardLevel; }
@@ -379,15 +409,15 @@ public:
BOOL isInImageList() const {return mInImageList ;}
void setInImageList(BOOL flag) {mInImageList = flag ;}
- const std::string& getLocalFileName() const {return mLocalFileName ;}
LLFrameTimer* getLastPacketTimer() {return &mLastPacketTimer;}
U32 getFetchPriority() const { return mFetchPriority ;}
F32 getDownloadProgress() const {return mDownloadProgress ;}
- LLImageRaw* readBackRawImage(S8 discard_level) ;
+ LLImageRaw* reloadRawImage(S8 discard_level) ;
void destroyRawImage();
+ const std::string& getUrl() const {return mUrl;}
//---------------
BOOL isDeleted() ;
BOOL isInactive() ;
@@ -398,13 +428,35 @@ public:
//---------------
void setForSculpt();
- BOOL isForSculpt() const {return mForSculpt;}
+ BOOL forSculpt() const {return mForSculpt;}
+ BOOL isForSculptOnly() const;
+
+ //raw image management
+ void checkCachedRawSculptImage() ;
+ LLImageRaw* getRawImage()const { return mRawImage ;}
+ S32 getRawImageLevel() const {return mRawDiscardLevel;}
+ LLImageRaw* getCachedRawImage() const { return mCachedRawImage ;}
+ S32 getCachedRawImageLevel() const {return mCachedRawDiscardLevel;}
+ BOOL isCachedRawImageReady() const {return mCachedRawImageReady ;}
+ BOOL isRawImageValid()const { return mIsRawImageValid ; }
+ void forceToSaveRawImage(S32 desired_discard = 0) ;
+ void destroySavedRawImage() ;
+ LLImageRaw* getSavedRawImage() ;
+ BOOL hasSavedRawImage() const ;
+ F32 getElapsedLastReferencedSavedRawImageTime() const ;
+ BOOL isFullyLoaded() const;
+
+protected:
+ /*virtual*/ void switchToCachedImage();
private:
void init(bool firstinit) ;
void cleanup() ;
- F32 calcDecodePriorityForUnknownTexture(F32 pixel_priority) ;
+ void saveRawImage() ;
+ BOOL forceFetch() ;
+ void setCachedRawImage() ;
+ BOOL keepReuestedDiscardLevel();
//for atlas
void resetFaceAtlas() ;
@@ -425,11 +477,8 @@ protected:
S32 mKnownDrawWidth;
S32 mKnownDrawHeight;
BOOL mKnownDrawSizeChanged ;
-
- S8 mDesiredDiscardLevel; // The discard level we'd LIKE to have - if we have it and there's space
- S8 mMinDesiredDiscardLevel; // The minimum discard level we'd like to have
- S32 mMinDiscardLevel;
-
+ std::string mUrl;
+
S32 mRequestedDiscardLevel;
F32 mRequestedDownloadPriority;
S32 mFetchState;
@@ -437,8 +486,10 @@ protected:
F32 mDownloadProgress;
F32 mFetchDeltaTime;
F32 mRequestDeltaTime;
- S32 mDecodeFrame;
- S32 mVisibleFrame; // decode frame where image was last visible
+ F32 mDecodePriority; // The priority for decoding this image.
+ S32 mMinDiscardLevel;
+ S8 mDesiredDiscardLevel; // The discard level we'd LIKE to have - if we have it and there's space
+ S8 mMinDesiredDiscardLevel; // The minimum discard level we'd like to have
S8 mNeedsAux; // We need to decode the auxiliary channels
S8 mDecodingAux; // Are we decoding high components
@@ -446,10 +497,10 @@ protected:
S8 mHasFetcher; // We've made a fecth request
S8 mIsFetching; // Fetch request is active
- mutable S8 mIsMissingAsset; // True if we know that there is no image asset with this image id in the database.
+ mutable S8 mIsMissingAsset; // True if we know that there is no image asset with this image id in the database.
- F32 mDecodePriority; // The priority for decoding this image.
typedef std::list<LLLoadedCallbackEntry*> callback_list_t;
+ S8 mLoadedCallbackDesiredDiscardLevel;
callback_list_t mLoadedCallbackList;
LLPointer<LLImageRaw> mRawImage;
@@ -459,6 +510,19 @@ protected:
// doing if you use it for anything else! - djs
LLPointer<LLImageRaw> mAuxRawImage;
+ //keep a copy of mRawImage for some special purposes
+ //when mForceToSaveRawImage is set.
+ BOOL mForceToSaveRawImage ;
+ LLPointer<LLImageRaw> mSavedRawImage;
+ S32 mSavedRawDiscardLevel;
+ S32 mDesiredSavedRawDiscardLevel;
+ F32 mLastReferencedSavedRawImageTime ;
+
+ //a small version of the copy of the raw image (<= 64 * 64)
+ LLPointer<LLImageRaw> mCachedRawImage;
+ S32 mCachedRawDiscardLevel;
+ BOOL mCachedRawImageReady; //the rez of the mCachedRawImage reaches the upper limit.
+
LLHost mTargetHost; // if LLHost::invalid, just request from agent's simulator
// Timers
@@ -487,15 +551,17 @@ protected:
/*virtual*/ ~LLViewerLODTexture(){}
public:
- LLViewerLODTexture(const LLUUID& id, BOOL usemipmaps = TRUE);
- LLViewerLODTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps = TRUE);
+ LLViewerLODTexture(const LLUUID& id, const LLHost& host = LLHost::invalid, BOOL usemipmaps = TRUE);
+ LLViewerLODTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps = TRUE);
/*virtual*/ S8 getType() const;
// Process image stats to determine priority/quality requirements.
/*virtual*/ void processTextureStats();
-
+ BOOL isUpdateFrozen() ;
+
private:
void init(bool firstinit) ;
+ void scaleDown() ;
private:
@@ -611,7 +677,16 @@ public:
static LLViewerFetchedTexture* getFetchedTextureFromFile(const std::string& filename,
BOOL usemipmap = TRUE,
- S32 boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation.
+ S32 boost_priority = LLViewerTexture::BOOST_NONE,
+ S8 texture_type = LLViewerTexture::FETCHED_TEXTURE,
+ LLGLint internal_format = 0,
+ LLGLenum primary_format = 0,
+ const LLUUID& force_id = LLUUID::null
+ );
+
+ static LLViewerFetchedTexture* getFetchedTextureFromUrl(const std::string& url,
+ BOOL usemipmap = TRUE,
+ S32 boost_priority = LLViewerTexture::BOOST_NONE,
S8 texture_type = LLViewerTexture::FETCHED_TEXTURE,
LLGLint internal_format = 0,
LLGLenum primary_format = 0,
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index d2be1ac9b5..081b7cc483 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -44,6 +44,7 @@
#include "llimagetga.h"
#include "llimagejpeg.h"
#include "llimagepng.h"
+#include "llimageworker.h"
#include "llsdserialize.h"
#include "llsys.h"
@@ -68,10 +69,15 @@
void (*LLViewerTextureList::sUUIDCallback)(void **, const LLUUID&) = NULL;
-const S32 IMAGES_PER_REQUEST = 42;
-const S32 IMAGES_MIN_UPDATES = 4; // Always update the highest N images each frame
-const S32 IMAGES_MAX_PACKET_UPDATES = 1; // Only send N packets of IMAGES_PER_REQUEST in a frame
-const F32 RESEND_IMAGE_REQUEST_TIME = 15.f; // seconds
+U32 LLViewerTextureList::sTextureBits = 0;
+U32 LLViewerTextureList::sTexturePackets = 0;
+S32 LLViewerTextureList::sNumImages = 0;
+LLStat LLViewerTextureList::sNumImagesStat(32, TRUE);
+LLStat LLViewerTextureList::sNumRawImagesStat(32, TRUE);
+LLStat LLViewerTextureList::sGLTexMemStat(32, TRUE);
+LLStat LLViewerTextureList::sGLBoundMemStat(32, TRUE);
+LLStat LLViewerTextureList::sRawMemStat(32, TRUE);
+LLStat LLViewerTextureList::sFormattedMemStat(32, TRUE);
LLViewerTextureList gTextureList;
static LLFastTimer::DeclareTimer FTM_PROCESS_IMAGES("Process Images");
@@ -88,7 +94,7 @@ LLViewerTextureList::LLViewerTextureList()
void LLViewerTextureList::init()
{
- mNumImages = 0;
+ sNumImages = 0;
mMaxResidentTexMemInMegaBytes = 0;
mMaxTotalTextureMemInMegaBytes = 0 ;
if (gNoRender)
@@ -231,6 +237,10 @@ void LLViewerTextureList::shutdown()
{
continue; // avoid UI, baked, and other special images
}
+ if(!image->getBoundRecently())
+ {
+ continue ;
+ }
S32 desired = image->getDesiredDiscardLevel();
if (desired >= 0 && desired < MAX_DISCARD_LEVEL)
{
@@ -321,17 +331,30 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string&
LLGLenum primary_format,
const LLUUID& force_id)
{
- if (gNoRender)
+ std::string full_path = gDirUtilp->findSkinnedFilename("textures", filename);
+ if (full_path.empty())
{
- // Never mind that this ignores image_set_id;
- // getImage() will handle that later.
+ llwarns << "Failed to find local image file: " << filename << llendl;
return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI);
}
- std::string full_path = gDirUtilp->findSkinnedFilename("textures", filename);
- if (full_path.empty())
+ std::string url = "file://" + full_path;
+
+ return getImageFromUrl(url, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id);
+}
+
+LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& url,
+ BOOL usemipmaps,
+ S32 boost_priority,
+ S8 texture_type,
+ LLGLint internal_format,
+ LLGLenum primary_format,
+ const LLUUID& force_id)
+{
+ if (gNoRender)
{
- llwarns << "Failed to find local image file: " << filename << llendl;
+ // Never mind that this ignores image_set_id;
+ // getImage() will handle that later.
return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI);
}
@@ -343,7 +366,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string&
}
else
{
- new_id.generate(full_path);
+ new_id.generate(url);
}
LLPointer<LLViewerFetchedTexture> imagep = findImage(new_id);
@@ -353,10 +376,10 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string&
switch(texture_type)
{
case LLViewerTexture::FETCHED_TEXTURE:
- imagep = new LLViewerFetchedTexture(full_path, new_id, usemipmaps);
+ imagep = new LLViewerFetchedTexture(url, new_id, usemipmaps);
break ;
case LLViewerTexture::LOD_TEXTURE:
- imagep = new LLViewerLODTexture(full_path, new_id, usemipmaps);
+ imagep = new LLViewerLODTexture(url, new_id, usemipmaps);
break ;
default:
llerrs << "Invalid texture type " << texture_type << llendl ;
@@ -428,18 +451,15 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id,
switch(texture_type)
{
case LLViewerTexture::FETCHED_TEXTURE:
- imagep = new LLViewerFetchedTexture(image_id, usemipmaps);
+ imagep = new LLViewerFetchedTexture(image_id, request_from_host, usemipmaps);
break ;
case LLViewerTexture::LOD_TEXTURE:
- imagep = new LLViewerLODTexture(image_id, usemipmaps);
+ imagep = new LLViewerLODTexture(image_id, request_from_host, usemipmaps);
break ;
default:
llerrs << "Invalid texture type " << texture_type << llendl ;
}
- // Might want to request from host other than where the agent is. JC
- imagep->setTargetHost(request_from_host);
-
if (internal_format && primary_format)
{
imagep->setExplicitFormat(internal_format, primary_format);
@@ -517,7 +537,7 @@ void LLViewerTextureList::addImage(LLViewerFetchedTexture *new_image)
{
llwarns << "Image with ID " << image_id << " already in list" << llendl;
}
- mNumImages++;
+ sNumImages++;
addImageToList(new_image);
mUUIDMap[image_id] = new_image;
@@ -534,7 +554,7 @@ void LLViewerTextureList::deleteImage(LLViewerFetchedTexture *image)
}
llverify(mUUIDMap.erase(image->getID()) == 1);
- mNumImages--;
+ sNumImages--;
removeImageFromList(image);
}
}
@@ -554,7 +574,9 @@ static LLFastTimer::DeclareTimer FTM_IMAGE_MARK_DIRTY("Dirty Images");
void LLViewerTextureList::updateImages(F32 max_time)
{
- LLViewerStats::getInstance()->mNumImagesStat.addValue(mNumImages);
+ LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerStats::getInstance()->mTextureKBitStat.getMeanPerSec());
+
+ LLViewerStats::getInstance()->mNumImagesStat.addValue(sNumImages);
LLViewerStats::getInstance()->mNumRawImagesStat.addValue(LLImageRaw::sRawImageCount);
LLViewerStats::getInstance()->mGLTexMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sGlobalTextureMemoryInBytes));
LLViewerStats::getInstance()->mGLBoundMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sBoundTextureMemoryInBytes));
@@ -562,10 +584,13 @@ void LLViewerTextureList::updateImages(F32 max_time)
LLViewerStats::getInstance()->mFormattedMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageFormatted::sGlobalFormattedMemory));
updateImagesDecodePriorities();
+
+ F32 total_max_time = max_time;
max_time -= updateImagesFetchTextures(max_time);
- max_time = llmin(llmax(max_time, 0.001f*10.f*gFrameIntervalSeconds), 0.001f);
+
+ max_time = llmax(max_time, total_max_time*.25f); // at least 25% of max_time
max_time -= updateImagesCreateTextures(max_time);
- max_time = llmin(llmax(max_time, 0.001f*10.f*gFrameIntervalSeconds), 0.001f);
+
if (!mDirtyTextureList.empty())
{
LLFastTimer t(FTM_IMAGE_MARK_DIRTY);
@@ -578,7 +603,7 @@ void LLViewerTextureList::updateImages(F32 max_time)
{
//trigger loaded callbacks on local textures immediately
LLViewerFetchedTexture* image = *iter++;
- if (!image->getLocalFileName().empty())
+ if (!image->getUrl().empty())
{
// Do stuff to handle callbacks, update priorities, etc.
didone = image->doLoadedCallbacks();
@@ -636,6 +661,14 @@ void LLViewerTextureList::updateImagesDecodePriorities()
}
else
{
+ if(imagep->hasSavedRawImage())
+ {
+ if(imagep->getElapsedLastReferencedSavedRawImageTime() > MAX_INACTIVE_TIME)
+ {
+ imagep->destroySavedRawImage() ;
+ }
+ }
+
if(imagep->isDeleted())
{
continue ;
@@ -766,74 +799,76 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
const size_t max_update_count = llmin((S32) (1024*10.f*gFrameIntervalSeconds)+1, 256);
// 32 high priority entries
- std::set<LLViewerFetchedTexture*> entries;
+ typedef std::vector<LLViewerFetchedTexture*> entries_list_t;
+ entries_list_t entries;
size_t update_counter = llmin(max_priority_count, mImageList.size());
image_priority_list_t::iterator iter1 = mImageList.begin();
while(update_counter > 0)
{
- // added extra granularity and verbosity for crash logging during 1.19.1 RC. -Brad
- if(iter1 == mImageList.end())
- {
- llerrs << "DEV-12002: update_counter not calculated correctly!" << llendl;
- return 0.f;
- }
-
- LLPointer<LLViewerFetchedTexture> const & ptr = *iter1;
-
- LLViewerFetchedTexture * img = ptr.get();
-
- // added extra granularity and verbosity for crash logging during 1.19.1 RC. -Brad
- if(img == NULL)
- {
- llwarns << "DEV-12002: image is NULL!" << llendl;
- }
-
- entries.insert(img);
-
+ entries.push_back(*iter1);
+
++iter1;
update_counter--;
}
// 256 cycled entries
- update_counter = llmin(max_update_count, mUUIDMap.size());
- uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID);
- while(update_counter > 0)
+ update_counter = llmin(max_update_count, mUUIDMap.size());
+ if(update_counter > 0)
{
- if (iter2 == mUUIDMap.end())
+ uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID);
+ uuid_map_t::iterator iter2p = iter2;
+ while(update_counter > 0)
{
- iter2 = mUUIDMap.begin();
+ if (iter2 == mUUIDMap.end())
+ {
+ iter2 = mUUIDMap.begin();
+ }
+ entries.push_back(iter2->second);
+ iter2p = iter2++;
+ update_counter--;
}
- mLastFetchUUID = iter2->first;
- entries.insert(iter2->second);
- ++iter2;
- update_counter--;
+
+ mLastFetchUUID = iter2p->first;
}
+ S32 fetch_count = 0;
S32 min_count = max_priority_count + max_update_count/4;
- for (std::set<LLViewerFetchedTexture*>::iterator iter3 = entries.begin();
+ for (entries_list_t::iterator iter3 = entries.begin();
iter3 != entries.end(); )
{
LLPointer<LLViewerFetchedTexture> imagep = *iter3++;
- imagep->updateFetch();
+ bool fetching = imagep->updateFetch();
+ if (fetching)
+ {
+ fetch_count++;
+ }
if (min_count <= 0 && image_op_timer.getElapsedTimeF32() > max_time)
{
break;
}
min_count--;
}
+ if (fetch_count == 0)
+ {
+ gDebugTimers[0].pause();
+ }
+ else
+ {
+ gDebugTimers[0].unpause();
+ }
return image_op_timer.getElapsedTimeF32();
}
void LLViewerTextureList::updateImagesUpdateStats()
{
- if (mUpdateStats)
+ if (mUpdateStats && mForceResetTextureStats)
{
for (image_priority_list_t::iterator iter = mImageList.begin();
iter != mImageList.end(); )
{
LLViewerFetchedTexture* imagep = *iter++;
- imagep->resetTextureStats(mForceResetTextureStats);
+ imagep->resetTextureStats();
}
mUpdateStats = FALSE;
mForceResetTextureStats = FALSE;
@@ -1019,6 +1054,9 @@ LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImage
return compressedImage;
}
+const S32 MIN_VIDEO_RAM = 32;
+const S32 MAX_VIDEO_RAM = 512; // 512MB max for performance reasons.
+
// Returns min setting for TextureMemory (in MB)
S32 LLViewerTextureList::getMinVideoRamSetting()
{
@@ -1137,13 +1175,13 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d
if (msg->getReceiveCompressedSize())
{
- gTextureList.mTextureBits += msg->getReceiveCompressedSize() * 8;
+ gTextureList.sTextureBits += msg->getReceiveCompressedSize() * 8;
}
else
{
- gTextureList.mTextureBits += msg->getReceiveSize() * 8;
+ gTextureList.sTextureBits += msg->getReceiveSize() * 8;
}
- gTextureList.mTexturePackets++;
+ gTextureList.sTexturePackets++;
U8 codec;
U16 packets;
@@ -1202,13 +1240,13 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d
if (msg->getReceiveCompressedSize())
{
- gTextureList.mTextureBits += msg->getReceiveCompressedSize() * 8;
+ gTextureList.sTextureBits += msg->getReceiveCompressedSize() * 8;
}
else
{
- gTextureList.mTextureBits += msg->getReceiveSize() * 8;
+ gTextureList.sTextureBits += msg->getReceiveSize() * 8;
}
- gTextureList.mTexturePackets++;
+ gTextureList.sTexturePackets++;
//llprintline("Start decode, image header...");
msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
@@ -1401,7 +1439,7 @@ void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerFetchedTexture *src_v
// for images grabbed from local files, apply clipping rectangle to restore original dimensions
// from power-of-2 gl image
- if (success && imagep.notNull() && src_vi && !src_vi->getLocalFileName().empty())
+ if (success && imagep.notNull() && src_vi && (src_vi->getUrl().compare(0, 7, "file://")==0))
{
F32 clip_x = (F32)src_vi->getOriginalWidth() / (F32)src_vi->getFullWidth();
F32 clip_y = (F32)src_vi->getOriginalHeight() / (F32)src_vi->getFullHeight();
@@ -1458,6 +1496,11 @@ bool LLUIImageList::initFromFile()
llwarns << "Unable to parse UI image list file " << base_file_path << llendl;
return false;
}
+ if (!root->hasAttribute("version"))
+ {
+ llwarns << "No valid version number in UI image list file " << base_file_path << llendl;
+ return false;
+ }
std::vector<std::string> paths;
// path to current selected skin
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index 6948db70f8..3c9c81a689 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -53,7 +53,6 @@ const BOOL GL_TEXTURE_NO = FALSE;
const BOOL IMMEDIATE_YES = TRUE;
const BOOL IMMEDIATE_NO = FALSE;
-class LLImageJ2C;
class LLMessageSystem;
class LLTextureView;
@@ -146,6 +145,15 @@ private:
LLGLenum primary_format = 0,
const LLUUID& force_id = LLUUID::null
);
+
+ LLViewerFetchedTexture* getImageFromUrl(const std::string& url,
+ BOOL usemipmap = TRUE,
+ BOOL level_immediate = FALSE, // Get the requested level immediately upon creation.
+ S8 texture_type = LLViewerTexture::FETCHED_TEXTURE,
+ LLGLint internal_format = 0,
+ LLGLenum primary_format = 0,
+ const LLUUID& force_id = LLUUID::null
+ );
LLViewerFetchedTexture* createImage(const LLUUID &image_id,
BOOL usemipmap = TRUE,
@@ -190,11 +198,18 @@ private:
LLFrameTimer mForceDecodeTimer;
public:
- U32 mTextureBits;
- U32 mTexturePackets;
+ static U32 sTextureBits;
+ static U32 sTexturePackets;
+
+ static LLStat sNumImagesStat;
+ static LLStat sNumRawImagesStat;
+ static LLStat sGLTexMemStat;
+ static LLStat sGLBoundMemStat;
+ static LLStat sRawMemStat;
+ static LLStat sFormattedMemStat;
private:
- S32 mNumImages;
+ static S32 sNumImages;
static void (*sUUIDCallback)(void**, const LLUUID &);
};
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 9b3dbcd2f6..fc09c946af 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -121,6 +121,7 @@
#include "llhudview.h"
#include "llimagebmp.h"
#include "llimagej2c.h"
+#include "llimageworker.h"
#include "llkeyboard.h"
#include "lllineeditor.h"
#include "llmenugl.h"
@@ -160,7 +161,6 @@
#include "lltoolselectland.h"
#include "lltrans.h"
#include "lluictrlfactory.h"
-#include "lluploaddialog.h"
#include "llurldispatcher.h" // SLURL from other app instance
#include "llvieweraudio.h"
#include "llviewercamera.h"
@@ -192,6 +192,7 @@
#include "llbottomtray.h"
#include "llnearbychatbar.h"
#include "llagentui.h"
+#include "llwearablelist.h"
#include "llnotificationmanager.h"
@@ -330,7 +331,9 @@ public:
S32 hours = (S32)(time / (60*60));
S32 mins = (S32)((time - hours*(60*60)) / 60);
S32 secs = (S32)((time - hours*(60*60) - mins*60));
- addText(xpos, ypos, llformat(" Debug %d: %d:%02d:%02d", idx, hours,mins,secs)); ypos += y_inc2;
+ std::string label = gDebugTimerLabel[idx];
+ if (label.empty()) label = llformat("Debug: %d", idx);
+ addText(xpos, ypos, llformat(" %s: %d:%02d:%02d", label.c_str(), hours,mins,secs)); ypos += y_inc2;
}
F32 time = gFrameTimeSeconds;
@@ -1304,6 +1307,7 @@ LLViewerWindow::LLViewerWindow(
// Init the image list. Must happen after GL is initialized and before the images that
// LLViewerWindow needs are requested.
+ LLImageGL::initClass(LLViewerTexture::MAX_GL_IMAGE_CATEGORY) ;
gTextureList.init();
LLViewerTextureManager::init() ;
gBumpImageList.init();
@@ -1670,6 +1674,8 @@ void LLViewerWindow::shutdownGL()
gSky.cleanup();
stop_glerror();
+ LLWearableList::instance().cleanup() ;
+
gTextureList.shutdown();
stop_glerror();
@@ -1683,7 +1689,10 @@ void LLViewerWindow::shutdownGL()
stop_glerror();
LLViewerTextureManager::cleanup() ;
-
+ LLImageGL::cleanupClass() ;
+
+ llinfos << "All texturs and llimagegl images are destroyed!" << llendl ;
+
llinfos << "Cleaning up select manager" << llendl;
LLSelectMgr::getInstance()->cleanup();
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index d7c403739e..c2906b1718 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -324,7 +324,7 @@ public:
} ESnapshotType;
BOOL saveSnapshot(const std::string& filename, S32 image_width, S32 image_height, BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR);
BOOL rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, BOOL keep_window_aspect = TRUE, BOOL is_texture = FALSE,
- BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR, S32 max_size = MAX_IMAGE_SIZE );
+ BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR, S32 max_size = MAX_SNAPSHOT_IMAGE_SIZE );
BOOL thumbnailSnapshot(LLImageRaw *raw, S32 preview_width, S32 preview_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) ;
BOOL isSnapshotLocSet() const { return ! sSnapshotDir.empty(); }
void resetSnapshotLoc() const { sSnapshotDir.clear(); }
diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp
index d124cbcdce..999701ece1 100644
--- a/indra/newview/llvlcomposition.cpp
+++ b/indra/newview/llvlcomposition.cpp
@@ -279,7 +279,6 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
if (mRawImages[i].isNull())
{
// Read back a raw image for this discard level, if it exists
- mRawImages[i] = new LLImageRaw;
S32 min_dim = llmin(mDetailTextures[i]->getFullWidth(), mDetailTextures[i]->getFullHeight());
S32 ddiscard = 0;
while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
@@ -287,12 +286,18 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
ddiscard++;
min_dim /= 2;
}
- if (!mDetailTextures[i]->readBackRaw(ddiscard, mRawImages[i], false))
+
+ mDetailTextures[i]->reloadRawImage(ddiscard) ;
+ if(mDetailTextures[i]->getRawImageLevel() != ddiscard)//raw iamge is not ready, will enter here again later.
{
- llwarns << "Unable to read raw data for terrain detail texture: " << mDetailTextures[i]->getID() << llendl;
- mRawImages[i] = NULL;
+ mDetailTextures[i]->destroyRawImage() ;
+ lldebugs << "cached raw data for terrain detail texture is not ready yet: " << mDetailTextures[i]->getID() << llendl;
return FALSE;
}
+
+ mRawImages[i] = mDetailTextures[i]->getRawImage() ;
+ mDetailTextures[i]->destroyRawImage() ;
+
if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE ||
mDetailTextures[i]->getHeight(ddiscard) != BASE_SIZE ||
mDetailTextures[i]->getComponents() != 3)
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 60ab6f8075..a6f20922d9 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -689,7 +689,6 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mBakedTextureDatas[i].mIsUsed = false;
mBakedTextureDatas[i].mMaskTexName = 0;
mBakedTextureDatas[i].mTextureIndex = LLVOAvatarDictionary::bakedToLocalTextureIndex((EBakedTextureIndex)i);
- mBakedTextureDatas[i].mMorphMasksValid = FALSE;
}
mDirtyMesh = TRUE; // Dirty geometry, need to regenerate.
@@ -999,7 +998,7 @@ void LLVOAvatar::resetImpostors()
// static
void LLVOAvatar::deleteCachedImages(bool clearAll)
-{
+{
if (LLTexLayerSet::sHasCaches)
{
lldebugs << "Deleting layer set caches" << llendl;
@@ -3919,7 +3918,7 @@ U32 LLVOAvatar::renderFootShadows()
LLGLDepthTest test(GL_TRUE, GL_FALSE);
//render foot shadows
LLGLEnable blend(GL_BLEND);
- gGL.getTexUnit(0)->bind(mShadowImagep.get());
+ gGL.getTexUnit(0)->bind(mShadowImagep, TRUE);
glColor4fv(mShadow0Facep->getRenderColor().mV);
mShadow0Facep->renderIndexed(foot_mask);
glColor4fv(mShadow1Facep->getRenderColor().mV);
@@ -3967,7 +3966,7 @@ U32 LLVOAvatar::renderImpostor(LLColor4U color, S32 diffuse_channel)
//------------------------------------------------------------------------
// LLVOAvatar::updateTextures()
//------------------------------------------------------------------------
-void LLVOAvatar::updateTextures(LLAgent &agent)
+void LLVOAvatar::updateTextures()
{
BOOL render_avatar = TRUE;
@@ -6091,28 +6090,6 @@ void LLVOAvatar::addMaskedMorph(EBakedTextureIndex index, LLPolyMorphTarget* mor
}
}
-// invalidates morph masks for a given layer. Don't pass a parameter to invalidate all morph masks.
-void LLVOAvatar::invalidateMorphMasks(LLVOAvatarDefines::EBakedTextureIndex index)
-{
- setMorphMasksValid(FALSE, index);
-}
-
-// updates morph masks to be a value for a given layer. Don't pass an argument to set value for all morph masks
-void LLVOAvatar::setMorphMasksValid(BOOL new_status, LLVOAvatarDefines::EBakedTextureIndex index)
-{
- if (index == BAKED_NUM_INDICES)
- {
- for (U8 tex = 0; tex < (U8)BAKED_NUM_INDICES; tex++)
- {
- mBakedTextureDatas[tex].mMorphMasksValid = new_status;
- }
- }
- else if (index < BAKED_NUM_INDICES)
- {
- mBakedTextureDatas[index].mMorphMasksValid = new_status;
- }
-}
-
// returns TRUE if morph masks are present and not valid for a given baked texture, FALSE otherwise
BOOL LLVOAvatar::morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex index)
{
@@ -6121,9 +6098,20 @@ BOOL LLVOAvatar::morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex inde
return FALSE;
}
- if (!mBakedTextureDatas[index].mMaskedMorphs.empty() && !mBakedTextureDatas[index].mMorphMasksValid)
+ if (!mBakedTextureDatas[index].mMaskedMorphs.empty())
{
- return TRUE;
+ if (isSelf())
+ {
+ LLTexLayerSet *layer_set = mBakedTextureDatas[index].mTexLayerSet;
+ if (layer_set)
+ {
+ return !layer_set->isMorphValid();
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
}
return FALSE;
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index f7c794defe..173ad02808 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -125,7 +125,7 @@ public:
virtual BOOL updateLOD();
BOOL updateJointLODs();
virtual BOOL isActive() const; // Whether this object needs to do an idleUpdate.
- virtual void updateTextures(LLAgent &agent);
+ virtual void updateTextures();
virtual S32 setTETexture(const U8 te, const LLUUID& uuid); // If setting a baked texture, need to request it from a non-local sim.
virtual void onShift(const LLVector3& shift_vector);
virtual U32 getPartitionType() const;
@@ -355,10 +355,8 @@ private:
// Morph masks
//--------------------------------------------------------------------
public:
- void invalidateMorphMasks(LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
BOOL morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
void addMaskedMorph(LLVOAvatarDefines::EBakedTextureIndex index, LLPolyMorphTarget* morph_target, BOOL invert, std::string layer);
- void setMorphMasksValid(BOOL new_status, LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components, LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
//--------------------------------------------------------------------
@@ -489,7 +487,6 @@ protected:
// Stores pointers to the joint meshes that this baked texture deals with
std::vector< LLViewerJointMesh * > mMeshes; // std::vector<LLViewerJointMesh> mJoints[i]->mMeshParts
morph_list_t mMaskedMorphs;
- BOOL mMorphMasksValid;
};
typedef std::vector<BakedTextureData> bakedtexturedata_vec_t;
bakedtexturedata_vec_t mBakedTextureDatas;
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 85a68fdd3f..7a83070770 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -1171,26 +1171,6 @@ void LLVOAvatarSelf::localTextureLoaded(BOOL success, LLViewerFetchedTexture *sr
}
// virtual
-/* //unused
-BOOL LLVOAvatarSelf::getLocalTextureRaw(ETextureIndex index, LLImageRaw* image_raw) const
-{
- if (!isIndexLocalTexture(index)) return FALSE;
- if (getLocalTextureID(index) == IMG_DEFAULT_AVATAR) return TRUE;
-
- const LocalTextureData *local_tex_data = getLocalTextureData(index)[0];
- if (local_tex_data->mImage->readBackRaw(-1, image_raw, false))
- {
-
- return TRUE;
- }
-
- // No data loaded yet
- setLocalTexture((ETextureIndex)index, getTEImage(index), FALSE); // <-- non-const, move this elsewhere
- return FALSE;
-}
-*/
-
-// virtual
BOOL LLVOAvatarSelf::getLocalTextureGL(ETextureIndex type, LLViewerTexture** tex_pp, U32 index) const
{
*tex_pp = NULL;
@@ -1387,8 +1367,8 @@ void LLVOAvatarSelf::invalidateComposite( LLTexLayerSet* layerset, BOOL set_by_u
}
// llinfos << "LLVOAvatar::invalidComposite() " << layerset->getBodyRegion() << llendl;
- invalidateMorphMasks(layerset->getBakedTexIndex());
layerset->requestUpdate();
+ layerset->invalidateMorphMasks();
if( set_by_user )
{
@@ -1841,12 +1821,13 @@ void LLVOAvatarSelf::addLocalTextureStats( ETextureIndex type, LLViewerFetchedTe
if (!covered_by_baked)
{
- if (getLocalTextureID(type, index) != IMG_DEFAULT_AVATAR)
+ if (getLocalTextureID(type, index) != IMG_DEFAULT_AVATAR && imagep->getDiscardLevel() != 0)
{
F32 desired_pixels;
desired_pixels = llmin(mPixelArea, (F32)getTexImageArea());
imagep->setBoostLevel(getAvatarBoostLevel());
imagep->addTextureStats( desired_pixels / texel_area_ratio );
+ imagep->forceUpdateBindStats() ;
if (imagep->getDiscardLevel() < 0)
{
mHasGrey = TRUE; // for statistics gathering
@@ -2144,6 +2125,49 @@ BOOL LLVOAvatarSelf::needsRenderBeam()
// static
void LLVOAvatarSelf::deleteScratchTextures()
{
+ if(gAuditTexture)
+ {
+ S32 total_tex_size = sScratchTexBytes ;
+ S32 tex_size = SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT ;
+
+ if( sScratchTexNames.checkData( GL_LUMINANCE ) )
+ {
+ LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
+ total_tex_size -= tex_size ;
+ }
+ if( sScratchTexNames.checkData( GL_ALPHA ) )
+ {
+ LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
+ total_tex_size -= tex_size ;
+ }
+ if( sScratchTexNames.checkData( GL_COLOR_INDEX ) )
+ {
+ LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
+ total_tex_size -= tex_size ;
+ }
+ if( sScratchTexNames.checkData( GL_LUMINANCE_ALPHA ) )
+ {
+ LLImageGL::decTextureCounter(tex_size, 2, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
+ total_tex_size -= 2 * tex_size ;
+ }
+ if( sScratchTexNames.checkData( GL_RGB ) )
+ {
+ LLImageGL::decTextureCounter(tex_size, 3, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
+ total_tex_size -= 3 * tex_size ;
+ }
+ if( sScratchTexNames.checkData( GL_RGBA ) )
+ {
+ LLImageGL::decTextureCounter(tex_size, 4, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
+ total_tex_size -= 4 * tex_size ;
+ }
+ //others
+ while(total_tex_size > 0)
+ {
+ LLImageGL::decTextureCounter(tex_size, 4, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
+ total_tex_size -= 4 * tex_size ;
+ }
+ }
+
for( LLGLuint* namep = sScratchTexNames.getFirstData();
namep;
namep = sScratchTexNames.getNextData() )
@@ -2166,7 +2190,8 @@ void LLVOAvatarSelf::deleteScratchTextures()
BOOL LLVOAvatarSelf::bindScratchTexture( LLGLenum format )
{
U32 texture_bytes = 0;
- GLuint gl_name = getScratchTexName( format, &texture_bytes );
+ S32 components = 0;
+ GLuint gl_name = getScratchTexName( format, components, &texture_bytes );
if( gl_name )
{
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, gl_name);
@@ -2178,12 +2203,12 @@ BOOL LLVOAvatarSelf::bindScratchTexture( LLGLenum format )
if( *last_bind_time != LLImageGL::sLastFrameTime )
{
*last_bind_time = LLImageGL::sLastFrameTime;
- LLImageGL::updateBoundTexMem(texture_bytes);
+ LLImageGL::updateBoundTexMem(texture_bytes, components, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
}
}
else
{
- LLImageGL::updateBoundTexMem(texture_bytes);
+ LLImageGL::updateBoundTexMem(texture_bytes, components, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
sScratchTexLastBindTime.addData( format, new F32(LLImageGL::sLastFrameTime) );
}
return TRUE;
@@ -2191,9 +2216,8 @@ BOOL LLVOAvatarSelf::bindScratchTexture( LLGLenum format )
return FALSE;
}
-LLGLuint LLVOAvatarSelf::getScratchTexName( LLGLenum format, U32* texture_bytes )
-{
- S32 components;
+LLGLuint LLVOAvatarSelf::getScratchTexName( LLGLenum format, S32& components, U32* texture_bytes )
+{
GLenum internal_format;
switch( format )
{
@@ -2239,6 +2263,11 @@ LLGLuint LLVOAvatarSelf::getScratchTexName( LLGLenum format, U32* texture_bytes
sScratchTexBytes += *texture_bytes;
LLImageGL::sGlobalTextureMemoryInBytes += *texture_bytes;
+
+ if(gAuditTexture)
+ {
+ LLImageGL::incTextureCounter(SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT, components, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
+ }
return name;
}
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index 6e52b33634..8a66422c44 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -241,7 +241,7 @@ public:
BOOL bindScratchTexture(LLGLenum format);
static void deleteScratchTextures();
protected:
- LLGLuint getScratchTexName(LLGLenum format, U32* texture_bytes);
+ LLGLuint getScratchTexName(LLGLenum format, S32& components, U32* texture_bytes);
private:
static S32 sScratchTexBytes;
static LLMap< LLGLenum, LLGLuint*> sScratchTexNames;
diff --git a/indra/newview/llvoclouds.cpp b/indra/newview/llvoclouds.cpp
index 8d3c8b6f1a..177cb16c50 100644
--- a/indra/newview/llvoclouds.cpp
+++ b/indra/newview/llvoclouds.cpp
@@ -101,7 +101,7 @@ void LLVOClouds::setPixelAreaAndAngle(LLAgent &agent)
mPixelArea = 1500*100;
}
-void LLVOClouds::updateTextures(LLAgent &agent)
+void LLVOClouds::updateTextures()
{
getTEImage(0)->addTextureStats(mPixelArea);
}
diff --git a/indra/newview/llvoclouds.h b/indra/newview/llvoclouds.h
index 95e6b96e4e..c4a75f5b5e 100644
--- a/indra/newview/llvoclouds.h
+++ b/indra/newview/llvoclouds.h
@@ -65,7 +65,7 @@ public:
/*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
F32 getPartSize(S32 idx);
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
void updateFaceSize(S32 idx) { }
diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp
index 110433a27d..f33a5cc818 100644
--- a/indra/newview/llvograss.cpp
+++ b/indra/newview/llvograss.cpp
@@ -322,7 +322,7 @@ void LLVOGrass::setPixelAreaAndAngle(LLAgent &agent)
// BUG could speed this up by caching the relative_position and range calculations
-void LLVOGrass::updateTextures(LLAgent &agent)
+void LLVOGrass::updateTextures()
{
if (getTEImage(0))
{
diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h
index 124400d356..6a6fcc31c3 100644
--- a/indra/newview/llvograss.h
+++ b/indra/newview/llvograss.h
@@ -72,7 +72,7 @@ public:
LLStrider<U16>& indicesp);
void updateFaceSize(S32 idx) { }
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ BOOL updateLOD();
/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp
index ac2484ddfd..221c6b61ec 100644
--- a/indra/newview/llvoground.cpp
+++ b/indra/newview/llvoground.cpp
@@ -70,7 +70,7 @@ BOOL LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
}
-void LLVOGround::updateTextures(LLAgent &agent)
+void LLVOGround::updateTextures()
{
}
diff --git a/indra/newview/llvoground.h b/indra/newview/llvoground.h
index af3fcd65d4..0ccb0834a2 100644
--- a/indra/newview/llvoground.h
+++ b/indra/newview/llvoground.h
@@ -51,7 +51,7 @@ public:
// Graphical stuff for objects - maybe broken out into render class
// later?
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
/*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp
index 143cd2d9c6..9dd0b598dc 100644
--- a/indra/newview/llvopartgroup.cpp
+++ b/indra/newview/llvopartgroup.cpp
@@ -108,7 +108,7 @@ void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent)
}
}
-void LLVOPartGroup::updateTextures(LLAgent &agent)
+void LLVOPartGroup::updateTextures()
{
// Texture stats for particles need to be updated in a different way...
}
diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h
index 3dc3292992..18583b4be9 100644
--- a/indra/newview/llvopartgroup.h
+++ b/indra/newview/llvopartgroup.h
@@ -61,7 +61,7 @@ public:
virtual U32 getPartitionType() const;
/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
/*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp
index d37deaf53d..5ff8f0d267 100644
--- a/indra/newview/llvosky.cpp
+++ b/indra/newview/llvosky.cpp
@@ -289,7 +289,7 @@ void LLSkyTex::create(const F32 brightness)
void LLSkyTex::createGLImage(S32 which)
{
- mTexture[which]->createGLTexture(0, mImageRaw[which]);
+ mTexture[which]->createGLTexture(0, mImageRaw[which], 0, TRUE, LLViewerTexture::LOCAL);
mTexture[which]->setAddressMode(LLTexUnit::TAM_CLAMP);
}
@@ -1180,7 +1180,7 @@ BOOL LLVOSky::updateSky()
return TRUE;
}
-void LLVOSky::updateTextures(LLAgent &agent)
+void LLVOSky::updateTextures()
{
if (mSunTexturep)
{
diff --git a/indra/newview/llvosky.h b/indra/newview/llvosky.h
index 62c934fb41..ef74324e58 100644
--- a/indra/newview/llvosky.h
+++ b/indra/newview/llvosky.h
@@ -492,7 +492,7 @@ public:
// Graphical stuff for objects - maybe broken out into render class
// later?
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
/*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
index 164f0f0cad..ef7b161003 100644
--- a/indra/newview/llvosurfacepatch.cpp
+++ b/indra/newview/llvosurfacepatch.cpp
@@ -134,7 +134,7 @@ void LLVOSurfacePatch::setPixelAreaAndAngle(LLAgent &agent)
}
-void LLVOSurfacePatch::updateTextures(LLAgent &agent)
+void LLVOSurfacePatch::updateTextures()
{
}
diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h
index aaf4d41fa1..10a5888526 100644
--- a/indra/newview/llvosurfacepatch.h
+++ b/indra/newview/llvosurfacepatch.h
@@ -75,7 +75,7 @@ public:
LLStrider<LLVector2> &texCoords1p,
LLStrider<U16> &indicesp);
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
/*virtual*/ void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax);
diff --git a/indra/newview/llvotextbubble.cpp b/indra/newview/llvotextbubble.cpp
index f5094c025e..75beab519e 100644
--- a/indra/newview/llvotextbubble.cpp
+++ b/indra/newview/llvotextbubble.cpp
@@ -115,7 +115,7 @@ BOOL LLVOTextBubble::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
}
-void LLVOTextBubble::updateTextures(LLAgent &agent)
+void LLVOTextBubble::updateTextures()
{
// Update the image levels of all textures...
diff --git a/indra/newview/llvotextbubble.h b/indra/newview/llvotextbubble.h
index 45d4df2a7e..7f84dbf631 100644
--- a/indra/newview/llvotextbubble.h
+++ b/indra/newview/llvotextbubble.h
@@ -44,7 +44,7 @@ public:
/*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
/*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
/*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
/*virtual*/ BOOL updateLOD();
diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp
index ec118d89bc..235e10f716 100644
--- a/indra/newview/llvotree.cpp
+++ b/indra/newview/llvotree.cpp
@@ -463,7 +463,7 @@ void LLVOTree::setPixelAreaAndAngle(LLAgent &agent)
#endif
}
-void LLVOTree::updateTextures(LLAgent &agent)
+void LLVOTree::updateTextures()
{
if (mTreeImagep)
{
diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h
index 13817fa111..feac9e0675 100644
--- a/indra/newview/llvotree.h
+++ b/indra/newview/llvotree.h
@@ -69,7 +69,7 @@ public:
// Graphical stuff for objects - maybe broken out into render class later?
/*virtual*/ void render(LLAgent &agent);
/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
/*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
diff --git a/indra/newview/llvotreenew.h b/indra/newview/llvotreenew.h
index 3fec5855ef..426470101d 100644
--- a/indra/newview/llvotreenew.h
+++ b/indra/newview/llvotreenew.h
@@ -156,7 +156,7 @@ public:
/*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
/*virtual*/ void render(LLAgent &agent);
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
/*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index f6343b4134..78f534bacd 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -53,12 +53,10 @@
#include "lldrawpoolbump.h"
#include "llface.h"
#include "llspatialpartition.h"
-
-// TEMP HACK ventrella
#include "llhudmanager.h"
#include "llflexibleobject.h"
-
#include "llsky.h"
+#include "lltexturefetch.h"
#include "llviewercamera.h"
#include "llviewertexturelist.h"
#include "llviewerregion.h"
@@ -75,7 +73,6 @@ const S32 MIN_QUIET_FRAMES_COALESCE = 30;
const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
const F32 FORCE_CULL_AREA = 8.f;
const F32 MAX_LOD_DISTANCE = 24.f;
-const S32 MAX_SCULPT_REZ = 128;
BOOL gAnimateTextures = TRUE;
@@ -151,7 +148,6 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
mRelativeXformInvTrans.setIdentity();
mLOD = MIN_LOD;
- mSculptLevel = -2;
mTextureAnimp = NULL;
mVObjRadius = LLVector3(1,1,0.5f).length();
mNumFaces = 0;
@@ -564,28 +560,32 @@ BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
return TRUE;
}
-void LLVOVolume::updateTextures(LLAgent &agent)
+void LLVOVolume::updateTextures()
{
const F32 TEXTURE_AREA_REFRESH_TIME = 5.f; // seconds
- if (mDrawable.notNull() && mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME)
+ if (mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME)
{
- if (mDrawable->isVisible())
- {
- updateTextures();
- }
+ updateTextureVirtualSize();
}
}
-void LLVOVolume::updateTextures()
+void LLVOVolume::updateTextureVirtualSize()
{
// Update the pixel area of all faces
+ if(mDrawable.isNull() || !mDrawable->isVisible())
+ {
+ return ;
+ }
+
if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SIMPLE))
{
return;
}
-
- if (LLViewerTexture::sDontLoadVolumeTextures || mDrawable.isNull()) // || !mDrawable->isVisible())
+
+ static LLCachedControl<bool> dont_load_textures(gSavedSettings,"TextureDisable");
+
+ if (dont_load_textures || LLAppViewer::getTextureFetch()->mDebugPause) // || !mDrawable->isVisible())
{
return;
}
@@ -602,14 +602,15 @@ void LLVOVolume::updateTextures()
LLFace* face = mDrawable->getFace(i);
const LLTextureEntry *te = face->getTextureEntry();
LLViewerTexture *imagep = face->getTexture();
- if (!imagep || !te ||
+ if (!imagep || !te ||
face->mExtents[0] == face->mExtents[1])
{
continue;
}
F32 vsize;
-
+ F32 old_size = face->getVirtualSize();
+
if (isHUDAttachment())
{
F32 area = (F32) LLViewerCamera::getInstance()->getScreenPixelArea();
@@ -619,12 +620,10 @@ void LLVOVolume::updateTextures()
}
else
{
- vsize = getTextureVirtualSize(face);
+ vsize = face->getTextureVirtualSize();
}
- mPixelArea = llmax(mPixelArea, face->getPixelArea());
-
- F32 old_size = face->getVirtualSize();
+ mPixelArea = llmax(mPixelArea, face->getPixelArea());
if (face->mTextureMatrix != NULL)
{
@@ -636,7 +635,6 @@ void LLVOVolume::updateTextures()
}
face->setVirtualSize(vsize);
- imagep->addTextureStats(vsize);
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
{
if (vsize < min_vsize) min_vsize = vsize;
@@ -668,32 +666,44 @@ void LLVOVolume::updateTextures()
mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
if (mSculptTexture.notNull())
{
- S32 lod = llmin(mLOD, 3);
- F32 lodf = ((F32)(lod + 1.0f)/4.f);
- F32 tex_size = lodf * MAX_SCULPT_REZ;
- mSculptTexture->addTextureStats(2.f * tex_size * tex_size);
mSculptTexture->setBoostLevel(llmax((S32)mSculptTexture->getBoostLevel(),
(S32)LLViewerTexture::BOOST_SCULPTED));
mSculptTexture->setForSculpt() ;
- }
-
- S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture
- S32 current_discard = mSculptLevel;
+
+ if(!mSculptTexture->isCachedRawImageReady())
+ {
+ S32 lod = llmin(mLOD, 3);
+ F32 lodf = ((F32)(lod + 1.0f)/4.f);
+ F32 tex_size = lodf * LLViewerTexture::sMaxSculptRez ;
+ mSculptTexture->addTextureStats(2.f * tex_size * tex_size, FALSE);
+
+ //if the sculpty very close to the view point, load first
+ {
+ LLVector3 lookAt = getPositionAgent() - LLViewerCamera::getInstance()->getOrigin();
+ F32 dist = lookAt.normVec() ;
+ F32 cos_angle_to_view_dir = lookAt * LLViewerCamera::getInstance()->getXAxis() ;
+ mSculptTexture->setAdditionalDecodePriority(0.8f * LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist)) ;
+ }
+ }
+
+ S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture
+ S32 current_discard = getVolume() ? getVolume()->getSculptLevel() : -2 ;
- if (texture_discard >= 0 && //texture has some data available
- (texture_discard < current_discard || //texture has more data than last rebuild
- current_discard < 0)) //no previous rebuild
- {
- gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
- mSculptChanged = TRUE;
- }
+ if (texture_discard >= 0 && //texture has some data available
+ (texture_discard < current_discard || //texture has more data than last rebuild
+ current_discard < 0)) //no previous rebuild
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
+ mSculptChanged = TRUE;
+ }
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SCULPTED))
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SCULPTED))
{
setDebugText(llformat("T%d C%d V%d\n%dx%d",
- texture_discard, current_discard, getVolume()->getSculptLevel(),
- mSculptTexture->getHeight(), mSculptTexture->getWidth()));
+ texture_discard, current_discard, getVolume()->getSculptLevel(),
+ mSculptTexture->getHeight(), mSculptTexture->getWidth()));
}
+ }
}
if (getLightTextureID().notNull())
@@ -714,10 +724,10 @@ void LLVOVolume::updateTextures()
{
setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize)));
}
- else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
- {
- setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize)));
- }
+ else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize)));
+ }
else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA))
{
setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize)));
@@ -729,36 +739,6 @@ void LLVOVolume::updateTextures()
}
}
-F32 LLVOVolume::getTextureVirtualSize(LLFace* face)
-{
- //get area of circle around face
- LLVector3 center = face->getPositionAgent();
- LLVector3 size = (face->mExtents[1] - face->mExtents[0]) * 0.5f;
-
- F32 face_area = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance());
-
- face->setPixelArea(face_area);
-
- if (face_area <= 0.f)
- {
- return 0.f;
- }
-
- //get area of circle in texture space
- LLVector2 tdim = face->mTexExtents[1] - face->mTexExtents[0];
- F32 texel_area = (tdim * 0.5f).lengthSquared()*F_PI;
- if (texel_area <= 0.f)
- {
- // Probably animated, use default
- texel_area = 1.f;
- }
-
- //apply texel area to face area to get accurate ratio
- face_area /= llclamp(texel_area, 1.f/64.f, 16.f);
-
- return face_area;
-}
-
BOOL LLVOVolume::isActive() const
{
return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive());
@@ -835,7 +815,6 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
return mDrawable;
}
-
BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume)
{
// Check if we need to change implementations
@@ -890,7 +869,6 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail
{
sculpt();
}
- mSculptLevel = getVolume()->getSculptLevel();
}
}
else
@@ -905,32 +883,22 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail
// sculpt replaces generate() for sculpted surfaces
void LLVOVolume::sculpt()
-{
- U16 sculpt_height = 0;
- U16 sculpt_width = 0;
- S8 sculpt_components = 0;
- const U8* sculpt_data = NULL;
-
+{
if (mSculptTexture.notNull())
- {
- S32 discard_level;
- S32 desired_discard = 0; // lower discard levels have MUCH less resolution
-
- discard_level = desired_discard;
+ {
+ U16 sculpt_height = 0;
+ U16 sculpt_width = 0;
+ S8 sculpt_components = 0;
+ const U8* sculpt_data = NULL;
+
+ S32 discard_level = mSculptTexture->getDiscardLevel() ;
+ LLImageRaw* raw_image = mSculptTexture->getCachedRawImage() ;
S32 max_discard = mSculptTexture->getMaxDiscardLevel();
if (discard_level > max_discard)
discard_level = max_discard; // clamp to the best we can do
- S32 best_discard = mSculptTexture->getDiscardLevel();
- if (discard_level < best_discard)
- discard_level = best_discard; // clamp to what we have
-
- if (best_discard == -1)
- discard_level = -1; // and if we have nothing, set to nothing
-
-
- S32 current_discard = getVolume()->getSculptLevel();
+ S32 current_discard = getVolume()->getSculptLevel() ;
if(current_discard < -2)
{
llwarns << "WARNING!!: Current discard of sculpty at " << current_discard
@@ -951,18 +919,7 @@ void LLVOVolume::sculpt()
if (current_discard == discard_level) // no work to do here
return;
- LLPointer<LLImageRaw> raw_image = new LLImageRaw();
- BOOL is_valid = mSculptTexture->readBackRaw(discard_level, raw_image, FALSE);
-
- sculpt_height = raw_image->getHeight();
- sculpt_width = raw_image->getWidth();
- sculpt_components = raw_image->getComponents();
-
- if(is_valid)
- {
- is_valid = mSculptTexture->isValidForSculpt(discard_level, sculpt_width, sculpt_height, sculpt_components) ;
- }
- if(!is_valid)
+ if(!raw_image)
{
sculpt_width = 0;
sculpt_height = 0;
@@ -974,10 +931,10 @@ void LLVOVolume::sculpt()
}
}
else
- {
- if (raw_image->getDataSize() < sculpt_height * sculpt_width * sculpt_components)
- llerrs << "Sculpt: image data size = " << raw_image->getDataSize()
- << " < " << sculpt_height << " x " << sculpt_width << " x " <<sculpt_components << llendl;
+ {
+ sculpt_height = raw_image->getHeight();
+ sculpt_width = raw_image->getWidth();
+ sculpt_components = raw_image->getComponents();
sculpt_data = raw_image->getData();
@@ -1013,12 +970,6 @@ BOOL LLVOVolume::calcLOD()
return FALSE;
}
- //update face texture sizes on lod calculation
- //if (mDrawable->isVisible())
- //{
- // updateTextures();
- //}
-
S32 cur_detail = 0;
F32 radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length();
@@ -2783,7 +2734,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
if (face == -1)
{
start_face = 0;
- end_face = volume->getNumFaces();
+ end_face = volume->getNumVolumeFaces();
}
else
{
@@ -2798,8 +2749,8 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
if (face_hit >= 0 && mDrawable->getNumFaces() > face_hit)
{
- LLFace* face = mDrawable->getFace(face_hit);
-
+ LLFace* face = mDrawable->getFace(face_hit);
+
if (pick_transparent || !face->getTexture() || !face->getTexture()->hasGLTexture() || face->getTexture()->getMask(face->surfaceToTexture(tc, p, n)))
{
v_end = p;
@@ -3046,7 +2997,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
LLVOVolume* vobj = drawablep->getVOVolume();
llassert_always(vobj);
- vobj->updateTextures();
+ vobj->updateTextureVirtualSize();
vobj->preRebuild();
drawablep->clearState(LLDrawable::HAS_ALPHA);
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 00810b22c4..0574d3e385 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -139,7 +139,7 @@ public:
BOOL getVolumeChanged() const { return mVolumeChanged; }
- F32 getTextureVirtualSize(LLFace* face);
+
/*virtual*/ F32 getRadius() const { return mVObjRadius; };
const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const;
@@ -187,8 +187,8 @@ public:
/*virtual*/ void updateFaceSize(S32 idx);
/*virtual*/ BOOL updateLOD();
void updateRadius();
- /*virtual*/ void updateTextures(LLAgent &agent);
- void updateTextures();
+ /*virtual*/ void updateTextures();
+ void updateTextureVirtualSize();
void updateFaceFlags();
void regenFaces();
@@ -284,7 +284,6 @@ private:
LLFrameTimer mTextureUpdateTimer;
S32 mLOD;
BOOL mLODChanged;
- S32 mSculptLevel;
BOOL mSculptChanged;
F32 mSpotLightPriority;
LLMatrix4 mRelativeXform;
diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp
index 0c967f9020..e5ff62746e 100644
--- a/indra/newview/llvowater.cpp
+++ b/indra/newview/llvowater.cpp
@@ -100,7 +100,7 @@ void LLVOWater::setPixelAreaAndAngle(LLAgent &agent)
// virtual
-void LLVOWater::updateTextures(LLAgent &agent)
+void LLVOWater::updateTextures()
{
}
diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h
index 28a5633c58..3cc031e589 100644
--- a/indra/newview/llvowater.h
+++ b/indra/newview/llvowater.h
@@ -68,7 +68,7 @@ public:
/*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
/*virtual*/ void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax);
- /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void updateTextures();
/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
virtual U32 getPartitionType() const;
diff --git a/indra/newview/llwearablelist.cpp b/indra/newview/llwearablelist.cpp
index da62223aac..9bde85dcaf 100644
--- a/indra/newview/llwearablelist.cpp
+++ b/indra/newview/llwearablelist.cpp
@@ -70,6 +70,11 @@ struct LLWearableArrivedData
LLWearableList::~LLWearableList()
{
+ llassert_always(mList.empty()) ;
+}
+
+void LLWearableList::cleanup()
+{
for_each(mList.begin(), mList.end(), DeletePairedPointer());
mList.clear();
}
diff --git a/indra/newview/llwearablelist.h b/indra/newview/llwearablelist.h
index e5155c66a4..cf1a9bddff 100644
--- a/indra/newview/llwearablelist.h
+++ b/indra/newview/llwearablelist.h
@@ -44,6 +44,7 @@ class LLWearableList : public LLSingleton<LLWearableList>
public:
LLWearableList() {}
~LLWearableList();
+ void cleanup() ;
S32 getLength() const { return mList.size(); }
diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp
index 355d4141c3..66cb02ce99 100644
--- a/indra/newview/llworldmap.cpp
+++ b/indra/newview/llworldmap.cpp
@@ -34,57 +34,81 @@
#include "llworldmap.h"
-#include "llregionhandle.h"
+#include "llworldmapmessage.h"
#include "message.h"
-
-#include "llagent.h"
-#include "llmapresponders.h"
-#include "llviewercontrol.h"
-#include "llfloaterworldmap.h"
#include "lltracker.h"
#include "llviewertexturelist.h"
-#include "llviewerregion.h"
-#include "llregionflags.h"
#include "lltrans.h"
-const F32 REQUEST_ITEMS_TIMER = 10.f * 60.f; // 10 minutes
+// Timers to temporise database requests
+const F32 AGENTS_UPDATE_TIMER = 60.0; // Seconds between 2 agent requests for a region
+const F32 REQUEST_ITEMS_TIMER = 10.f * 60.f; // Seconds before we consider re-requesting item data for the grid
+
+//---------------------------------------------------------------------------
+// LLItemInfo
+//---------------------------------------------------------------------------
-// For DEV-17507, do lazy image loading in llworldmapview.cpp instead,
-// limiting requests to currently visible regions and minimizing the
-// number of textures being requested simultaneously.
-//
-// Uncomment IMMEDIATE_IMAGE_LOAD to restore the old behavior
-//
-//#define IMMEDIATE_IMAGE_LOAD
LLItemInfo::LLItemInfo(F32 global_x, F32 global_y,
const std::string& name,
- LLUUID id,
- S32 extra, S32 extra2)
+ LLUUID id)
: mName(name),
mToolTip(""),
mPosGlobal(global_x, global_y, 40.0),
mID(id),
- mSelected(FALSE),
- mExtra(extra),
- mExtra2(extra2)
+ mCount(1)
+// mSelected(false)
+// mColor()
{
- mRegionHandle = to_region_handle(mPosGlobal);
}
-LLSimInfo::LLSimInfo()
-: mHandle(0),
+//---------------------------------------------------------------------------
+// LLSimInfo
+//---------------------------------------------------------------------------
+
+LLSimInfo::LLSimInfo(U64 handle)
+: mHandle(handle),
mName(),
mAgentsUpdateTime(0),
- mShowAgentLocations(FALSE),
mAccess(0x0),
mRegionFlags(0x0),
- mWaterHeight(0.f),
- mAlpha(-1.f)
+ mFirstAgentRequest(true)
+// mWaterHeight(0.f)
+{
+}
+
+void LLSimInfo::setLandForSaleImage (LLUUID image_id)
{
+ mMapImageID = image_id;
+
+ // Fetch the image
+ if (mMapImageID.notNull())
+ {
+ mOverlayImage = LLViewerTextureManager::getFetchedTexture(mMapImageID, MIPMAP_TRUE, LLViewerTexture::BOOST_MAP, LLViewerTexture::LOD_TEXTURE);
+ mOverlayImage->setAddressMode(LLTexUnit::TAM_CLAMP);
+ }
+ else
+ {
+ mOverlayImage = NULL;
+ }
}
+LLPointer<LLViewerFetchedTexture> LLSimInfo::getLandForSaleImage ()
+{
+ if (mOverlayImage.isNull() && mMapImageID.notNull())
+ {
+ // Fetch the image if it hasn't been done yet (unlikely but...)
+ mOverlayImage = LLViewerTextureManager::getFetchedTexture(mMapImageID, MIPMAP_TRUE, LLViewerTexture::BOOST_MAP, LLViewerTexture::LOD_TEXTURE);
+ mOverlayImage->setAddressMode(LLTexUnit::TAM_CLAMP);
+ }
+ if (!mOverlayImage.isNull())
+ {
+ // Boost the fetch level when we try to access that image
+ mOverlayImage->setBoostLevel(LLViewerTexture::BOOST_MAP);
+ }
+ return mOverlayImage;
+}
-LLVector3d LLSimInfo::getGlobalPos(LLVector3 local_pos) const
+LLVector3d LLSimInfo::getGlobalPos(const LLVector3& local_pos) const
{
LLVector3d pos = from_region_handle(mHandle);
pos.mdV[VX] += local_pos.mV[VX];
@@ -93,128 +117,184 @@ LLVector3d LLSimInfo::getGlobalPos(LLVector3 local_pos) const
return pos;
}
+LLVector3d LLSimInfo::getGlobalOrigin() const
+{
+ return from_region_handle(mHandle);
+}
LLVector3 LLSimInfo::getLocalPos(LLVector3d global_pos) const
{
LLVector3d sim_origin = from_region_handle(mHandle);
return LLVector3(global_pos - sim_origin);
}
+void LLSimInfo::clearImage()
+{
+ if (!mOverlayImage.isNull())
+ {
+ mOverlayImage->setBoostLevel(0);
+ mOverlayImage = NULL;
+ }
+}
-
-//---------------------------------------------------------------------------
-// World Map
-//---------------------------------------------------------------------------
-
-LLWorldMap::LLWorldMap() :
- mIsTrackingUnknownLocation( FALSE ),
- mInvalidLocation( FALSE ),
- mIsTrackingDoubleClick( FALSE ),
- mIsTrackingCommit( FALSE ),
- mUnknownLocation( 0, 0, 0 ),
- mRequestLandForSale(true),
- mCurrentMap(0),
- mMinX(U32_MAX),
- mMaxX(U32_MIN),
- mMinY(U32_MAX),
- mMaxY(U32_MIN),
- mNeighborMap(NULL),
- mTelehubCoverageMap(NULL),
- mNeighborMapWidth(0),
- mNeighborMapHeight(0),
- mSLURLRegionName(),
- mSLURLRegionHandle(0),
- mSLURL(),
- mSLURLCallback(0),
- mSLURLTeleport(false)
-{
- for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map)
+void LLSimInfo::dropImagePriority()
+{
+ if (!mOverlayImage.isNull())
{
- mMapLoaded[map] = FALSE;
- mMapBlockLoaded[map] = new BOOL[MAP_BLOCK_RES*MAP_BLOCK_RES];
- for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx)
- {
- mMapBlockLoaded[map][idx] = FALSE;
- }
+ mOverlayImage->setBoostLevel(0);
}
}
+// Update the agent count for that region
+void LLSimInfo::updateAgentCount(F64 time)
+{
+ if ((time - mAgentsUpdateTime > AGENTS_UPDATE_TIMER) || mFirstAgentRequest)
+ {
+ LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_AGENT_LOCATIONS, mHandle);
+ mAgentsUpdateTime = time;
+ mFirstAgentRequest = false;
+ }
+}
-LLWorldMap::~LLWorldMap()
+// Get the total agents count
+const S32 LLSimInfo::getAgentCount() const
{
- reset();
- for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map)
+ S32 total_agent_count = 0;
+ for (LLSimInfo::item_info_list_t::const_iterator it = mAgentLocations.begin(); it != mAgentLocations.end(); ++it)
{
- delete[] mMapBlockLoaded[map];
+ total_agent_count += it->getCount();
}
+ return total_agent_count;
}
+bool LLSimInfo::isName(const std::string& name) const
+{
+ return (LLStringUtil::compareInsensitive(name, mName) == 0);
+}
-void LLWorldMap::reset()
+void LLSimInfo::dump() const
{
- for_each(mSimInfoMap.begin(), mSimInfoMap.end(), DeletePairedPointer());
- mSimInfoMap.clear();
+ U32 x_pos, y_pos;
+ from_region_handle(mHandle, &x_pos, &y_pos);
+
+ LL_INFOS("World Map") << x_pos << "," << y_pos
+ << " " << mName
+ << " " << (S32)mAccess
+ << " " << std::hex << mRegionFlags << std::dec
+// << " " << mWaterHeight
+ << LL_ENDL;
+}
+
+void LLSimInfo::clearItems()
+{
+ mTelehubs.clear();
+ mInfohubs.clear();
+ mPGEvents.clear();
+ mMatureEvents.clear();
+ mAdultEvents.clear();
+ mLandForSale.clear();
+ mLandForSaleAdult.clear();
+// We persist the agent count though as it is updated on a frequent basis
+// mAgentLocations.clear();
+}
+
+void LLSimInfo::insertAgentLocation(const LLItemInfo& item)
+{
+ std::string name = item.getName();
- for (S32 m=0; m<MAP_SIM_IMAGE_TYPES; ++m)
+ // Find the last item in the list with a different name and erase them
+ item_info_list_t::iterator lastiter;
+ for (lastiter = mAgentLocations.begin(); lastiter != mAgentLocations.end(); ++lastiter)
+ {
+ LLItemInfo& info = *lastiter;
+ if (info.isName(name))
+ {
+ break;
+ }
+ }
+ if (lastiter != mAgentLocations.begin())
{
- mMapLoaded[m] = FALSE;
+ mAgentLocations.erase(mAgentLocations.begin(), lastiter);
}
+ // Now append the new location
+ mAgentLocations.push_back(item);
+}
+
+//---------------------------------------------------------------------------
+// World Map
+//---------------------------------------------------------------------------
+
+LLWorldMap::LLWorldMap() :
+ mIsTrackingLocation( false ),
+ mIsTrackingFound( false ),
+ mIsInvalidLocation( false ),
+ mIsTrackingDoubleClick( false ),
+ mIsTrackingCommit( false ),
+ mTrackingLocation( 0, 0, 0 ),
+ mFirstRequest(true)
+{
+ //LL_INFOS("World Map") << "Creating the World Map -> LLWorldMap::LLWorldMap()" << LL_ENDL;
+ mMapBlockLoaded = new bool[MAP_BLOCK_RES*MAP_BLOCK_RES];
clearSimFlags();
-
- eraseItems();
+}
- mMinX = U32_MAX;
- mMaxX = U32_MIN;
- mMinY = U32_MAX;
- mMaxY = U32_MIN;
+LLWorldMap::~LLWorldMap()
+{
+ //LL_INFOS("World Map") << "Destroying the World Map -> LLWorldMap::~LLWorldMap()" << LL_ENDL;
+ reset();
+ delete[] mMapBlockLoaded;
+}
- delete [] mNeighborMap;
- mNeighborMap = NULL;
- delete [] mTelehubCoverageMap;
- mTelehubCoverageMap = NULL;
- mNeighborMapWidth = 0;
- mNeighborMapHeight = 0;
+void LLWorldMap::reset()
+{
+ clearItems(true); // Clear the items lists
+ clearImageRefs(); // Clear the world mipmap and the land for sale tiles
+ clearSimFlags(); // Clear the block info flags array
- for (S32 i=0; i<MAP_SIM_IMAGE_TYPES; i++)
- {
- mMapLayers[i].clear();
- }
+ // Finally, clear the region map itself
+ for_each(mSimInfoMap.begin(), mSimInfoMap.end(), DeletePairedPointer());
+ mSimInfoMap.clear();
}
-void LLWorldMap::eraseItems()
+// Returns true if the items have been cleared
+bool LLWorldMap::clearItems(bool force)
{
- if (mRequestTimer.getElapsedTimeF32() > REQUEST_ITEMS_TIMER)
+ bool clear = false;
+ if ((mRequestTimer.getElapsedTimeF32() > REQUEST_ITEMS_TIMER) || mFirstRequest || force)
{
mRequestTimer.reset();
- mTelehubs.clear();
- mInfohubs.clear();
- mPGEvents.clear();
- mMatureEvents.clear();
- mAdultEvents.clear();
- mLandForSale.clear();
+ LLSimInfo* sim_info = NULL;
+ for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ sim_info = it->second;
+ if (sim_info)
+ {
+ sim_info->clearItems();
+ }
+ }
+ clear = true;
+ mFirstRequest = false;
}
-// mAgentLocationsMap.clear(); // persists
-// mNumAgents.clear(); // persists
+ return clear;
}
-
void LLWorldMap::clearImageRefs()
{
+ // We clear the reference to the images we're holding.
+ // Images hold by the world mipmap first
+ mWorldMipmap.reset();
+
+ // Images hold by the region map
+ LLSimInfo* sim_info = NULL;
for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
{
- LLSimInfo* info = (*it).second;
- if (info->mCurrentImage)
- {
- info->mCurrentImage->setBoostLevel(0);
- info->mCurrentImage = NULL;
- }
- if (info->mOverlayImage)
+ sim_info = it->second;
+ if (sim_info)
{
- info->mOverlayImage->setBoostLevel(0);
- info->mOverlayImage = NULL;
+ sim_info->clearImage();
}
}
}
@@ -222,15 +302,25 @@ void LLWorldMap::clearImageRefs()
// Doesn't clear the already-loaded sim infos, just re-requests them
void LLWorldMap::clearSimFlags()
{
- for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map)
+ for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx)
{
- for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx)
- {
- mMapBlockLoaded[map][idx] = FALSE;
- }
+ mMapBlockLoaded[idx] = false;
}
}
+LLSimInfo* LLWorldMap::createSimInfoFromHandle(const U64 handle)
+{
+ LLSimInfo* sim_info = new LLSimInfo(handle);
+ mSimInfoMap[handle] = sim_info;
+ return sim_info;
+}
+
+void LLWorldMap::equalizeBoostLevels()
+{
+ mWorldMipmap.equalizeBoostLevels();
+ return;
+}
+
LLSimInfo* LLWorldMap::simInfoFromPosGlobal(const LLVector3d& pos_global)
{
U64 handle = to_region_handle(pos_global);
@@ -242,11 +332,7 @@ LLSimInfo* LLWorldMap::simInfoFromHandle(const U64 handle)
sim_info_map_t::iterator it = mSimInfoMap.find(handle);
if (it != mSimInfoMap.end())
{
- LLSimInfo* sim_info = (*it).second;
- if (sim_info)
- {
- return sim_info;
- }
+ return it->second;
}
return NULL;
}
@@ -257,763 +343,273 @@ LLSimInfo* LLWorldMap::simInfoFromName(const std::string& sim_name)
LLSimInfo* sim_info = NULL;
if (!sim_name.empty())
{
- for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ // Iterate through the entire sim info map and compare the name
+ sim_info_map_t::iterator it;
+ for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
{
- sim_info = (*it).second;
- if (sim_info
- && (0 == LLStringUtil::compareInsensitive(sim_name, sim_info->mName)) )
+ sim_info = it->second;
+ if (sim_info && sim_info->isName(sim_name) )
{
+ // break out of loop if success
break;
}
- sim_info = NULL;
}
+ // If we got to the end, we haven't found the sim. Reset the ouput value to NULL.
+ if (it == mSimInfoMap.end())
+ sim_info = NULL;
}
return sim_info;
}
bool LLWorldMap::simNameFromPosGlobal(const LLVector3d& pos_global, std::string & outSimName )
{
- bool gotSimName = true;
+ LLSimInfo* sim_info = simInfoFromPosGlobal(pos_global);
- U64 handle = to_region_handle(pos_global);
-
- sim_info_map_t::iterator it = mSimInfoMap.find(handle);
- if (it != mSimInfoMap.end())
+ if (sim_info)
{
- LLSimInfo* info = (*it).second;
- outSimName = info->mName;
+ outSimName = sim_info->getName();
}
else
{
- gotSimName = false;
outSimName = "(unknown region)";
}
- return gotSimName;
+ return (sim_info != NULL);
}
-void LLWorldMap::setCurrentLayer(S32 layer, bool request_layer)
+void LLWorldMap::reloadItems(bool force)
{
- mCurrentMap = layer;
- if (!mMapLoaded[layer] || request_layer)
+ //LL_INFOS("World Map") << "LLWorldMap::reloadItems()" << LL_ENDL;
+ if (clearItems(force))
{
- sendMapLayerRequest();
+ LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_TELEHUB);
+ LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_PG_EVENT);
+ LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_MATURE_EVENT);
+ LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_ADULT_EVENT);
+ LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_LAND_FOR_SALE);
}
-
- if (mTelehubs.size() == 0 ||
- mInfohubs.size() == 0)
- {
- // Request for telehubs
- sendItemRequest(MAP_ITEM_TELEHUB);
- }
-
- if (mPGEvents.size() == 0)
- {
- // Request for events
- sendItemRequest(MAP_ITEM_PG_EVENT);
- }
-
- if (mMatureEvents.size() == 0)
- {
- // Request for events (mature)
- sendItemRequest(MAP_ITEM_MATURE_EVENT);
- }
-
- if (mAdultEvents.size() == 0)
- {
- // Request for events (adult)
- sendItemRequest(MAP_ITEM_ADULT_EVENT);
- }
-
- if (mLandForSale.size() == 0)
- {
- // Request for Land For Sale
- sendItemRequest(MAP_ITEM_LAND_FOR_SALE);
- }
-
- if (mLandForSaleAdult.size() == 0)
- {
- // Request for Land For Sale
- sendItemRequest(MAP_ITEM_LAND_FOR_SALE_ADULT);
- }
-
- clearImageRefs();
- clearSimFlags();
}
-void LLWorldMap::sendItemRequest(U32 type, U64 handle)
-{
- LLMessageSystem* msg = gMessageSystem;
- S32 layer = mCurrentMap;
-
- msg->newMessageFast(_PREHASH_MapItemRequest);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->addU32Fast(_PREHASH_Flags, layer);
- msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
- msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
-
- msg->nextBlockFast(_PREHASH_RequestData);
- msg->addU32Fast(_PREHASH_ItemType, type);
- msg->addU64Fast(_PREHASH_RegionHandle, handle); // If zero, filled in on sim
-
- gAgent.sendReliableMessage();
-}
-// public
-void LLWorldMap::sendMapLayerRequest()
+// static public
+// Insert a region in the region map
+// returns true if region inserted, false otherwise
+bool LLWorldMap::insertRegion(U32 x_world, U32 y_world, std::string& name, LLUUID& image_id, U32 accesscode, U32 region_flags)
{
- if (!gAgent.getRegion()) return;
-
- LLSD body;
- body["Flags"] = mCurrentMap;
- std::string url = gAgent.getRegion()->getCapability(
- gAgent.isGodlike() ? "MapLayerGod" : "MapLayer");
-
- if (!url.empty())
+ // This region doesn't exist
+ if (accesscode == 255)
{
- llinfos << "LLWorldMap::sendMapLayerRequest via capability" << llendl;
- LLHTTPClient::post(url, body, new LLMapLayerResponder());
+ // Checks if the track point is in it and invalidates it if it is
+ if (LLWorldMap::getInstance()->isTrackingInRectangle( x_world, y_world, x_world + REGION_WIDTH_UNITS, y_world + REGION_WIDTH_UNITS))
+ {
+ LLWorldMap::getInstance()->setTrackingInvalid();
+ }
+ // return failure to insert
+ return false;
}
else
{
- llinfos << "LLWorldMap::sendMapLayerRequest via message system" << llendl;
- LLMessageSystem* msg = gMessageSystem;
- S32 layer = mCurrentMap;
-
- // Request for layer
- msg->newMessageFast(_PREHASH_MapLayerRequest);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->addU32Fast(_PREHASH_Flags, layer);
- msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
- msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
- gAgent.sendReliableMessage();
-
- if (mRequestLandForSale)
+ U64 handle = to_region_handle(x_world, y_world);
+ //LL_INFOS("World Map") << "Map sim : " << name << ", ID : " << image_id.getString() << LL_ENDL;
+ // Insert the region in the region map of the world map
+ // Loading the LLSimInfo object with what we got and insert it in the map
+ LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle);
+ if (siminfo == NULL)
{
- msg->newMessageFast(_PREHASH_MapLayerRequest);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->addU32Fast(_PREHASH_Flags, 2);
- msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
- msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
- gAgent.sendReliableMessage();
+ siminfo = LLWorldMap::getInstance()->createSimInfoFromHandle(handle);
}
+ siminfo->setName(name);
+ siminfo->setAccess(accesscode);
+ siminfo->setRegionFlags(region_flags);
+ // siminfo->setWaterHeight((F32) water_height);
+ siminfo->setLandForSaleImage(image_id);
+
+ // Handle the location tracking (for teleport, UI feedback and info display)
+ if (LLWorldMap::getInstance()->isTrackingInRectangle( x_world, y_world, x_world + REGION_WIDTH_UNITS, y_world + REGION_WIDTH_UNITS))
+ {
+ if (siminfo->isDown())
+ {
+ // We were tracking this location, but it's no available
+ LLWorldMap::getInstance()->setTrackingInvalid();
+ }
+ else
+ {
+ // We were tracking this location, and it does exist and is available
+ LLWorldMap::getInstance()->setTrackingValid();
+ }
+ }
+ // return insert region success
+ return true;
}
}
-// public
-void LLWorldMap::sendNamedRegionRequest(std::string region_name)
-{
- LLMessageSystem* msg = gMessageSystem;
- S32 layer = mCurrentMap;
-
- // Request for layer
- msg->newMessageFast(_PREHASH_MapNameRequest);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->addU32Fast(_PREHASH_Flags, layer);
- msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
- msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
- msg->nextBlockFast(_PREHASH_NameData);
- msg->addStringFast(_PREHASH_Name, region_name);
- gAgent.sendReliableMessage();
-}
-// public
-void LLWorldMap::sendNamedRegionRequest(std::string region_name,
- url_callback_t callback,
- const std::string& callback_url,
- bool teleport) // immediately teleport when result returned
-{
- mSLURLRegionName = region_name;
- mSLURLRegionHandle = 0;
- mSLURL = callback_url;
- mSLURLCallback = callback;
- mSLURLTeleport = teleport;
-
- sendNamedRegionRequest(region_name);
-}
-
-void LLWorldMap::sendHandleRegionRequest(U64 region_handle,
- url_callback_t callback,
- const std::string& callback_url,
- bool teleport) // immediately teleport when result returned
-{
- mSLURLRegionName.clear();
- mSLURLRegionHandle = region_handle;
- mSLURL = callback_url;
- mSLURLCallback = callback;
- mSLURLTeleport = teleport;
-
- U32 global_x;
- U32 global_y;
- from_region_handle(region_handle, &global_x, &global_y);
- U16 grid_x = (U16)(global_x / REGION_WIDTH_UNITS);
- U16 grid_y = (U16)(global_y / REGION_WIDTH_UNITS);
-
- sendMapBlockRequest(grid_x, grid_y, grid_x, grid_y, true);
-}
-
-// public
-void LLWorldMap::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent)
-{
- S32 layer = mCurrentMap;
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_MapBlockRequest);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- U32 flags = layer;
- flags |= (return_nonexistent ? 0x10000 : 0);
- msg->addU32Fast(_PREHASH_Flags, flags);
- msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
- msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
- msg->nextBlockFast(_PREHASH_PositionData);
- msg->addU16Fast(_PREHASH_MinX, min_x);
- msg->addU16Fast(_PREHASH_MinY, min_y);
- msg->addU16Fast(_PREHASH_MaxX, max_x);
- msg->addU16Fast(_PREHASH_MaxY, max_y);
- gAgent.sendReliableMessage();
-
- if (mRequestLandForSale)
- {
- msg->newMessageFast(_PREHASH_MapBlockRequest);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->addU32Fast(_PREHASH_Flags, 2);
- msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
- msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
- msg->nextBlockFast(_PREHASH_PositionData);
- msg->addU16Fast(_PREHASH_MinX, min_x);
- msg->addU16Fast(_PREHASH_MinY, min_y);
- msg->addU16Fast(_PREHASH_MaxX, max_x);
- msg->addU16Fast(_PREHASH_MaxY, max_y);
- gAgent.sendReliableMessage();
- }
-}
-
-// public static
-void LLWorldMap::processMapLayerReply(LLMessageSystem* msg, void**)
+// static public
+// Insert an item in the relevant region map
+// returns true if item inserted, false otherwise
+bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID& uuid, U32 type, S32 extra, S32 extra2)
{
- llinfos << "LLWorldMap::processMapLayerReply from message system" << llendl;
-
- U32 agent_flags;
- msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags);
-
- if (agent_flags != (U32)LLWorldMap::getInstance()->mCurrentMap)
- {
- llwarns << "Invalid or out of date map image type returned!" << llendl;
- return;
- }
+ // Create an item record for the received object
+ LLItemInfo new_item((F32)x_world, (F32)y_world, name, uuid);
- LLUUID image_id;
- //U32 left, right, top, bottom;
+ // Compute a region handle based on the objects coordinates
+ LLVector3d pos((F32)x_world, (F32)y_world, 40.0);
+ U64 handle = to_region_handle(pos);
- S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_LayerData);
-
- LLWorldMap::getInstance()->mMapLayers[agent_flags].clear();
-
- BOOL adjust = FALSE;
- for (S32 block=0; block<num_blocks; ++block)
+ // Get the region record for that handle or NULL if we haven't browsed it yet
+ LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle);
+ if (siminfo == NULL)
{
- LLWorldMapLayer new_layer;
- new_layer.LayerDefined = TRUE;
- msg->getUUIDFast(_PREHASH_LayerData, _PREHASH_ImageID, new_layer.LayerImageID, block);
- new_layer.LayerImage = LLViewerTextureManager::getFetchedTexture(new_layer.LayerImageID, MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
-
- gGL.getTexUnit(0)->bind(new_layer.LayerImage);
- new_layer.LayerImage->setAddressMode(LLTexUnit::TAM_CLAMP);
-
- U32 left, right, top, bottom;
- msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Left, left, block);
- msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Right, right, block);
- msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Top, top, block);
- msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Bottom, bottom, block);
-
- new_layer.LayerExtents.mLeft = left;
- new_layer.LayerExtents.mRight = right;
- new_layer.LayerExtents.mBottom = bottom;
- new_layer.LayerExtents.mTop = top;
-
- F32 x_meters = F32(left*REGION_WIDTH_UNITS);
- F32 y_meters = F32(bottom*REGION_WIDTH_UNITS);
- adjust = LLWorldMap::getInstance()->extendAABB(U32(x_meters), U32(y_meters),
- U32(x_meters+REGION_WIDTH_UNITS*new_layer.LayerExtents.getWidth()),
- U32(y_meters+REGION_WIDTH_UNITS*new_layer.LayerExtents.getHeight())) || adjust;
-
- LLWorldMap::getInstance()->mMapLayers[agent_flags].push_back(new_layer);
+ siminfo = LLWorldMap::getInstance()->createSimInfoFromHandle(handle);
}
- LLWorldMap::getInstance()->mMapLoaded[agent_flags] = TRUE;
- if(adjust) gFloaterWorldMap->adjustZoomSliderBounds();
-}
-
-// public static
-void LLWorldMap::processMapBlockReply(LLMessageSystem* msg, void**)
-{
- U32 agent_flags;
- msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags);
-
- if ((S32)agent_flags < 0 || agent_flags >= MAP_SIM_IMAGE_TYPES)
+ //LL_INFOS("World Map") << "Process item : type = " << type << LL_ENDL;
+ switch (type)
{
- llwarns << "Invalid map image type returned! " << agent_flags << llendl;
- return;
- }
-
- S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_Data);
-
- bool found_null_sim = false;
-
- BOOL adjust = FALSE;
- for (S32 block=0; block<num_blocks; ++block)
- {
- U16 x_regions;
- U16 y_regions;
- std::string name;
- U8 accesscode;
- U32 region_flags;
- U8 water_height;
- U8 agents;
- LLUUID image_id;
- msg->getU16Fast(_PREHASH_Data, _PREHASH_X, x_regions, block);
- msg->getU16Fast(_PREHASH_Data, _PREHASH_Y, y_regions, block);
- msg->getStringFast(_PREHASH_Data, _PREHASH_Name, name, block);
- msg->getU8Fast(_PREHASH_Data, _PREHASH_Access, accesscode, block);
- msg->getU32Fast(_PREHASH_Data, _PREHASH_RegionFlags, region_flags, block);
- msg->getU8Fast(_PREHASH_Data, _PREHASH_WaterHeight, water_height, block);
- msg->getU8Fast(_PREHASH_Data, _PREHASH_Agents, agents, block);
- msg->getUUIDFast(_PREHASH_Data, _PREHASH_MapImageID, image_id, block);
-
- U32 x_meters = x_regions * REGION_WIDTH_UNITS;
- U32 y_meters = y_regions * REGION_WIDTH_UNITS;
-
- U64 handle = to_region_handle(x_meters, y_meters);
-
- if (accesscode == 255)
- {
- // This region doesn't exist
- if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation &&
- LLWorldMap::getInstance()->mUnknownLocation.mdV[0] >= x_meters &&
- LLWorldMap::getInstance()->mUnknownLocation.mdV[0] < x_meters + 256 &&
- LLWorldMap::getInstance()->mUnknownLocation.mdV[1] >= y_meters &&
- LLWorldMap::getInstance()->mUnknownLocation.mdV[1] < y_meters + 256)
- {
- // We were tracking this location, but it doesn't exist
- LLWorldMap::getInstance()->mInvalidLocation = TRUE;
- }
-
- found_null_sim = true;
- }
- else
+ case MAP_ITEM_TELEHUB: // telehubs
{
- adjust = LLWorldMap::getInstance()->extendAABB(x_meters,
- y_meters,
- x_meters+REGION_WIDTH_UNITS,
- y_meters+REGION_WIDTH_UNITS) || adjust;
-
-// llinfos << "Map sim " << name << " image layer " << agent_flags << " ID " << image_id.getString() << llendl;
-
- LLSimInfo* siminfo = new LLSimInfo();
- sim_info_map_t::iterator iter = LLWorldMap::getInstance()->mSimInfoMap.find(handle);
- if (iter != LLWorldMap::getInstance()->mSimInfoMap.end())
- {
- LLSimInfo* oldinfo = iter->second;
- for (S32 image=0; image<MAP_SIM_IMAGE_TYPES; ++image)
- {
- siminfo->mMapImageID[image] = oldinfo->mMapImageID[image];
- }
- delete oldinfo;
- }
- LLWorldMap::getInstance()->mSimInfoMap[handle] = siminfo;
-
- siminfo->mHandle = handle;
- siminfo->mName.assign( name );
- siminfo->mAccess = accesscode;
- siminfo->mRegionFlags = region_flags;
- siminfo->mWaterHeight = (F32) water_height;
- siminfo->mMapImageID[agent_flags] = image_id;
-
-#ifdef IMMEDIATE_IMAGE_LOAD
- siminfo->mCurrentImage = LLViewerTextureManager::getFetchedTexture(siminfo->mMapImageID[LLWorldMap::getInstance()->mCurrentMap],
- MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- siminfo->mCurrentImage->setAddressMode(LLTexUnit::TAM_CLAMP);
-#endif
+ /* Merov: we are not using the hub color anymore for display so commenting that out
+ // Telehub color
+ U32 X = x_world / REGION_WIDTH_UNITS;
+ U32 Y = y_world / REGION_WIDTH_UNITS;
+ F32 red = fmod((F32)X * 0.11f, 1.f) * 0.8f;
+ F32 green = fmod((F32)Y * 0.11f, 1.f) * 0.8f;
+ F32 blue = fmod(1.5f * (F32)(X + Y) * 0.11f, 1.f) * 0.8f;
+ F32 add_amt = (X % 2) ? 0.15f : -0.15f;
+ add_amt += (Y % 2) ? -0.15f : 0.15f;
+ LLColor4 color(red + add_amt, green + add_amt, blue + add_amt);
+ new_item.setColor(color);
+ */
- if (siminfo->mMapImageID[2].notNull())
+ // extra2 specifies whether this is an infohub or a telehub.
+ if (extra2)
{
-#ifdef IMMEDIATE_IMAGE_LOAD
- siminfo->mOverlayImage = LLViewerTextureManager::getFetchedTexture(siminfo->mMapImageID[2], MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
-#endif
+ siminfo->insertInfoHub(new_item);
}
else
{
- siminfo->mOverlayImage = NULL;
- }
-
- if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation &&
- LLWorldMap::getInstance()->mUnknownLocation.mdV[0] >= x_meters &&
- LLWorldMap::getInstance()->mUnknownLocation.mdV[0] < x_meters + 256 &&
- LLWorldMap::getInstance()->mUnknownLocation.mdV[1] >= y_meters &&
- LLWorldMap::getInstance()->mUnknownLocation.mdV[1] < y_meters + 256)
- {
- if (siminfo->mAccess == SIM_ACCESS_DOWN)
- {
- // We were tracking this location, but it doesn't exist
- LLWorldMap::getInstance()->mInvalidLocation = true;
- }
- else
- {
- // We were tracking this location, and it does exist
- bool is_tracking_dbl = LLWorldMap::getInstance()->mIsTrackingDoubleClick == TRUE;
- gFloaterWorldMap->trackLocation(LLWorldMap::getInstance()->mUnknownLocation);
- if (is_tracking_dbl)
- {
- LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
- gAgent.teleportViaLocation( pos_global );
- }
- }
+ siminfo->insertTeleHub(new_item);
}
+ break;
}
-
- if(LLWorldMap::getInstance()->mSLURLCallback != NULL)
+ case MAP_ITEM_PG_EVENT: // events
+ case MAP_ITEM_MATURE_EVENT:
+ case MAP_ITEM_ADULT_EVENT:
{
- // Server returns definitive capitalization, SLURL might not have that.
- if ((LLStringUtil::compareInsensitive(LLWorldMap::getInstance()->mSLURLRegionName, name)==0)
- || (LLWorldMap::getInstance()->mSLURLRegionHandle == handle))
+ std::string timeStr = "["+ LLTrans::getString ("TimeHour")+"]:["
+ +LLTrans::getString ("TimeMin")+"] ["
+ +LLTrans::getString ("TimeAMPM")+"]";
+ LLSD substitution;
+ substitution["datetime"] = (S32) extra;
+ LLStringUtil::format (timeStr, substitution);
+ new_item.setTooltip(timeStr);
+
+ // HACK: store Z in extra2
+ new_item.setElevation((F64)extra2);
+ if (type == MAP_ITEM_PG_EVENT)
{
- url_callback_t callback = LLWorldMap::getInstance()->mSLURLCallback;
-
- LLWorldMap::getInstance()->mSLURLCallback = NULL;
- LLWorldMap::getInstance()->mSLURLRegionName.clear();
- LLWorldMap::getInstance()->mSLURLRegionHandle = 0;
-
- callback(handle, LLWorldMap::getInstance()->mSLURL, image_id, LLWorldMap::getInstance()->mSLURLTeleport);
+ siminfo->insertPGEvent(new_item);
}
- }
- }
-
- if(adjust) gFloaterWorldMap->adjustZoomSliderBounds();
- gFloaterWorldMap->updateSims(found_null_sim);
-}
-
-// public static
-void LLWorldMap::processMapItemReply(LLMessageSystem* msg, void**)
-{
- U32 type;
- msg->getU32Fast(_PREHASH_RequestData, _PREHASH_ItemType, type);
-
- S32 num_blocks = msg->getNumberOfBlocks("Data");
-
- for (S32 block=0; block<num_blocks; ++block)
- {
- U32 X, Y;
- std::string name;
- S32 extra, extra2;
- LLUUID uuid;
- msg->getU32Fast(_PREHASH_Data, _PREHASH_X, X, block);
- msg->getU32Fast(_PREHASH_Data, _PREHASH_Y, Y, block);
- msg->getStringFast(_PREHASH_Data, _PREHASH_Name, name, block);
- msg->getUUIDFast(_PREHASH_Data, _PREHASH_ID, uuid, block);
- msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra, extra, block);
- msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra2, extra2, block);
-
- F32 world_x = (F32)X;
- X /= REGION_WIDTH_UNITS;
- F32 world_y = (F32)Y;
- Y /= REGION_WIDTH_UNITS;
-
- LLItemInfo new_item(world_x, world_y, name, uuid, extra, extra2);
- LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(new_item.mRegionHandle);
-
- switch (type)
- {
- case MAP_ITEM_TELEHUB: // telehubs
+ else if (type == MAP_ITEM_MATURE_EVENT)
{
- // Telehub color, store in extra as 4 U8's
- U8 *color = (U8 *)&new_item.mExtra;
-
- F32 red = fmod((F32)X * 0.11f, 1.f) * 0.8f;
- F32 green = fmod((F32)Y * 0.11f, 1.f) * 0.8f;
- F32 blue = fmod(1.5f * (F32)(X + Y) * 0.11f, 1.f) * 0.8f;
- F32 add_amt = (X % 2) ? 0.15f : -0.15f;
- add_amt += (Y % 2) ? -0.15f : 0.15f;
- color[0] = U8((red + add_amt) * 255);
- color[1] = U8((green + add_amt) * 255);
- color[2] = U8((blue + add_amt) * 255);
- color[3] = 255;
-
- // extra2 specifies whether this is an infohub or a telehub.
- if (extra2)
- {
- LLWorldMap::getInstance()->mInfohubs.push_back(new_item);
- }
- else
- {
- LLWorldMap::getInstance()->mTelehubs.push_back(new_item);
- }
-
- break;
+ siminfo->insertMatureEvent(new_item);
}
- case MAP_ITEM_PG_EVENT: // events
- case MAP_ITEM_MATURE_EVENT:
- case MAP_ITEM_ADULT_EVENT:
+ else if (type == MAP_ITEM_ADULT_EVENT)
{
- std::string timeStr = "["+ LLTrans::getString ("TimeHour")+"]:["
- +LLTrans::getString ("TimeMin")+"] ["
- +LLTrans::getString ("TimeAMPM")+"]";
- LLSD substitution;
- substitution["datetime"] = (S32) extra;
- LLStringUtil::format (timeStr, substitution);
- new_item.mToolTip = timeStr;
-
- // HACK: store Z in extra2
- new_item.mPosGlobal.mdV[VZ] = (F64)extra2;
- if (type == MAP_ITEM_PG_EVENT)
- {
- LLWorldMap::getInstance()->mPGEvents.push_back(new_item);
- }
- else if (type == MAP_ITEM_MATURE_EVENT)
- {
- LLWorldMap::getInstance()->mMatureEvents.push_back(new_item);
- }
- else if (type == MAP_ITEM_ADULT_EVENT)
- {
- LLWorldMap::getInstance()->mAdultEvents.push_back(new_item);
- }
-
- break;
+ siminfo->insertAdultEvent(new_item);
}
- case MAP_ITEM_LAND_FOR_SALE: // land for sale
- case MAP_ITEM_LAND_FOR_SALE_ADULT: // adult land for sale
+ break;
+ }
+ case MAP_ITEM_LAND_FOR_SALE: // land for sale
+ case MAP_ITEM_LAND_FOR_SALE_ADULT: // adult land for sale
+ {
+ std::string tooltip = llformat("%d sq. m. L$%d", extra, extra2);
+ new_item.setTooltip(tooltip);
+ if (type == MAP_ITEM_LAND_FOR_SALE)
{
- new_item.mToolTip = llformat("%d sq. m. L$%d", new_item.mExtra, new_item.mExtra2);
- if (type == MAP_ITEM_LAND_FOR_SALE)
- {
- LLWorldMap::getInstance()->mLandForSale.push_back(new_item);
- }
- else if (type == MAP_ITEM_LAND_FOR_SALE_ADULT)
- {
- LLWorldMap::getInstance()->mLandForSaleAdult.push_back(new_item);
- }
- break;
+ siminfo->insertLandForSale(new_item);
}
- case MAP_ITEM_CLASSIFIED: // classifieds
+ else if (type == MAP_ITEM_LAND_FOR_SALE_ADULT)
{
- //DEPRECATED: no longer used
- break;
+ siminfo->insertLandForSaleAdult(new_item);
}
- case MAP_ITEM_AGENT_LOCATIONS: // agent locations
+ break;
+ }
+ case MAP_ITEM_CLASSIFIED: // classifieds
+ {
+ //DEPRECATED: no longer used
+ break;
+ }
+ case MAP_ITEM_AGENT_LOCATIONS: // agent locations
+ {
+// LL_INFOS("World Map") << "New Location " << new_item.mName << LL_ENDL;
+ if (extra > 0)
{
- if (!siminfo)
- {
- llinfos << "siminfo missing for " << new_item.mPosGlobal.mdV[0] << ", " << new_item.mPosGlobal.mdV[1] << llendl;
- break;
- }
-// llinfos << "New Location " << new_item.mName << llendl;
-
- item_info_list_t& agentcounts = LLWorldMap::getInstance()->mAgentLocationsMap[new_item.mRegionHandle];
-
- // Find the last item in the list with a different name and erase them
- item_info_list_t::iterator lastiter;
- for (lastiter = agentcounts.begin(); lastiter!=agentcounts.end(); ++lastiter)
- {
- const LLItemInfo& info = *lastiter;
- if (info.mName == new_item.mName)
- {
- break;
- }
- }
- if (lastiter != agentcounts.begin())
- {
- agentcounts.erase(agentcounts.begin(), lastiter);
- }
- // Now append the new location
- if (new_item.mExtra > 0)
- {
- agentcounts.push_back(new_item);
- }
- break;
+ new_item.setCount(extra);
+ siminfo->insertAgentLocation(new_item);
}
- default:
- break;
- };
- }
-}
-
-void LLWorldMap::dump()
-{
- for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
- {
- U64 handle = (*it).first;
- LLSimInfo* info = (*it).second;
-
- U32 x_pos, y_pos;
- from_region_handle(handle, &x_pos, &y_pos);
-
- llinfos << x_pos << "," << y_pos
- << " " << info->mName
- << " " << (S32)info->mAccess
- << " " << std::hex << info->mRegionFlags << std::dec
- << " " << info->mWaterHeight
- //<< " " << info->mTelehubName
- //<< " " << info->mTelehubPosition
- << llendl;
-
- if (info->mCurrentImage)
- {
- llinfos << "image discard " << (S32)info->mCurrentImage->getDiscardLevel()
- << " fullwidth " << info->mCurrentImage->getFullWidth()
- << " fullheight " << info->mCurrentImage->getFullHeight()
- << " maxvirt " << info->mCurrentImage->getMaxVirtualSize()
- //<< " maxdisc " << (S32)info->mCurrentImage->getMaxDiscardLevel()
- << llendl;
+ break;
}
+ default:
+ break;
}
+ return true;
}
-
-BOOL LLWorldMap::extendAABB(U32 min_x, U32 min_y, U32 max_x, U32 max_y)
+bool LLWorldMap::isTrackingInRectangle(F64 x0, F64 y0, F64 x1, F64 y1)
{
- BOOL rv = FALSE;
- if (min_x < mMinX)
- {
- rv = TRUE;
- mMinX = min_x;
- }
- if (min_y < mMinY)
- {
- rv = TRUE;
- mMinY = min_y;
- }
- if (max_x > mMaxX)
- {
- rv = TRUE;
- mMaxX = max_x;
- }
- if (max_y > mMaxY)
- {
- rv = TRUE;
- mMaxY = max_y;
- }
- lldebugs << "World map aabb: (" << mMinX << ", " << mMinY << "), ("
- << mMaxX << ", " << mMaxY << ")" << llendl;
- return rv;
+ if (!mIsTrackingLocation)
+ return false;
+ return ((mTrackingLocation[0] >= x0) && (mTrackingLocation[0] < x1) && (mTrackingLocation[1] >= y0) && (mTrackingLocation[1] < y1));
}
-
-U32 LLWorldMap::getWorldWidth() const
+// Drop priority of all images being fetched by the map
+void LLWorldMap::dropImagePriorities()
{
- return mMaxX - mMinX;
-}
-
-
-U32 LLWorldMap::getWorldHeight() const
-{
- return mMaxY - mMinY;
-}
-
-BOOL LLWorldMap::coveredByTelehub(LLSimInfo* infop)
-{
- /*if (!mTelehubCoverageMap)
+ // Drop the download of tiles priority to nil
+ mWorldMipmap.dropBoostLevels();
+ // Same for the "land for sale" tiles per region
+ for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
{
- return FALSE;
+ LLSimInfo* info = it->second;
+ info->dropImagePriority();
}
- U32 x_pos, y_pos;
- from_region_handle(infop->mHandle, &x_pos, &y_pos);
- x_pos /= REGION_WIDTH_UNITS;
- y_pos /= REGION_WIDTH_UNITS;
-
- S32 index = x_pos - (mMinX / REGION_WIDTH_UNITS - 1) + (mNeighborMapWidth * (y_pos - (mMinY / REGION_WIDTH_UNITS - 1)));
- return mTelehubCoverageMap[index] != 0; */
- return FALSE;
}
-void LLWorldMap::updateTelehubCoverage()
+// Load all regions in a given rectangle (in region grid coordinates, i.e. world / 256 meters)
+void LLWorldMap::updateRegions(S32 x0, S32 y0, S32 x1, S32 y1)
{
- /*S32 neighbor_width = getWorldWidth() / REGION_WIDTH_UNITS + 2;
- S32 neighbor_height = getWorldHeight() / REGION_WIDTH_UNITS + 2;
- if (neighbor_width > mNeighborMapWidth || neighbor_height > mNeighborMapHeight)
- {
- mNeighborMapWidth = neighbor_width;
- mNeighborMapHeight = neighbor_height;
- delete mNeighborMap;
- delete mTelehubCoverageMap;
-
- mNeighborMap = new U8[mNeighborMapWidth * mNeighborMapHeight];
- mTelehubCoverageMap = new U8[mNeighborMapWidth * mNeighborMapHeight];
- }
-
- memset(mNeighborMap, 0, mNeighborMapWidth * mNeighborMapHeight * sizeof(U8));
- memset(mTelehubCoverageMap, 0, mNeighborMapWidth * mNeighborMapHeight * sizeof(U8));
+ // Convert those boundaries to the corresponding (MAP_BLOCK_SIZE x MAP_BLOCK_SIZE) block coordinates
+ x0 = x0 / MAP_BLOCK_SIZE;
+ x1 = x1 / MAP_BLOCK_SIZE;
+ y0 = y0 / MAP_BLOCK_SIZE;
+ y1 = y1 / MAP_BLOCK_SIZE;
- // leave 1 sim border
- S32 min_x = (mMinX / REGION_WIDTH_UNITS) - 1;
- S32 min_y = (mMinY / REGION_WIDTH_UNITS) - 1;
-
- std::map<U64, LLSimInfo*>::const_iterator it;
- for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ // Load the region info those blocks
+ for (S32 block_x = llmax(x0, 0); block_x <= llmin(x1, MAP_BLOCK_RES-1); ++block_x)
{
- U64 handle = (*it).first;
- //LLSimInfo* info = (*it).second;
-
- U32 x_pos, y_pos;
- from_region_handle(handle, &x_pos, &y_pos);
- x_pos /= REGION_WIDTH_UNITS;
- y_pos /= REGION_WIDTH_UNITS;
- x_pos -= min_x;
- y_pos -= min_y;
-
- S32 index = x_pos + (mNeighborMapWidth * y_pos);
- mNeighborMap[index - 1]++;
- mNeighborMap[index + 1]++;
- mNeighborMap[index - mNeighborMapWidth]++;
- mNeighborMap[index + mNeighborMapWidth]++;
- }
-
- for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
- {
- U64 handle = (*it).first;
- LLSimInfo* info = (*it).second;
-
- U32 x_pos, y_pos;
- from_region_handle(handle, &x_pos, &y_pos);
- x_pos /= REGION_WIDTH_UNITS;
- y_pos /= REGION_WIDTH_UNITS;
- x_pos -= min_x;
- y_pos -= min_y;
-
- S32 index = x_pos + (mNeighborMapWidth * y_pos);
-
- if (!info->mTelehubName.empty() && mNeighborMap[index])
+ for (S32 block_y = llmax(y0, 0); block_y <= llmin(y1, MAP_BLOCK_RES-1); ++block_y)
{
- S32 x_start = llmax(0, S32(x_pos - 5));
- S32 x_span = llmin(mNeighborMapWidth - 1, (S32)(x_pos + 5)) - x_start + 1;
- S32 y_start = llmax(0, (S32)y_pos - 5);
- S32 y_end = llmin(mNeighborMapHeight - 1, (S32)(y_pos + 5));
- for (S32 y_index = y_start; y_index <= y_end; y_index++)
+ S32 offset = block_x | (block_y * MAP_BLOCK_RES);
+ if (!mMapBlockLoaded[offset])
{
- memset(&mTelehubCoverageMap[x_start + y_index * mNeighborMapWidth], 0xff, sizeof(U8) * x_span);
+ //LL_INFOS("World Map") << "Loading Block (" << block_x << "," << block_y << ")" << LL_ENDL;
+ LLWorldMapMessage::getInstance()->sendMapBlockRequest(block_x * MAP_BLOCK_SIZE, block_y * MAP_BLOCK_SIZE, (block_x * MAP_BLOCK_SIZE) + MAP_BLOCK_SIZE - 1, (block_y * MAP_BLOCK_SIZE) + MAP_BLOCK_SIZE - 1);
+ mMapBlockLoaded[offset] = true;
}
}
}
+}
- for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+void LLWorldMap::dump()
+{
+ LL_INFOS("World Map") << "LLWorldMap::dump()" << LL_ENDL;
+ for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
{
- U64 handle = (*it).first;
- //LLSimInfo* info = (*it).second;
-
- U32 x_pos, y_pos;
- from_region_handle(handle, &x_pos, &y_pos);
- x_pos /= REGION_WIDTH_UNITS;
- y_pos /= REGION_WIDTH_UNITS;
-
- S32 index = x_pos - min_x + (mNeighborMapWidth * (y_pos - min_y));
- mTelehubCoverageMap[index] *= mNeighborMap[index];
- }*/
+ LLSimInfo* info = it->second;
+ if (info)
+ {
+ info->dump();
+ }
+ }
}
+
diff --git a/indra/newview/llworldmap.h b/indra/newview/llworldmap.h
index 366de8f071..7e37727b86 100644
--- a/indra/newview/llworldmap.h
+++ b/indra/newview/llworldmap.h
@@ -33,203 +33,243 @@
#ifndef LL_LLWORLDMAP_H
#define LL_LLWORLDMAP_H
-#include <map>
-#include <string>
-#include <vector>
+#include "llworldmipmap.h"
#include <boost/function.hpp>
-#include "v3math.h"
#include "v3dmath.h"
-#include "llframetimer.h"
-#include "llmapimagetype.h"
#include "lluuid.h"
#include "llpointer.h"
#include "llsingleton.h"
+#include "llviewerregion.h"
#include "llviewertexture.h"
-#include "lleventinfo.h"
-#include "v3color.h"
-
-class LLMessageSystem;
-
+// Description of objects like hubs, events, land for sale, people and more (TBD).
+// Note: we don't store a "type" in there so we need to store instances of this class in
+// well known objects (i.e. list of objects which type is "well known").
class LLItemInfo
{
public:
- LLItemInfo(F32 global_x, F32 global_y, const std::string& name, LLUUID id, S32 extra = 0, S32 extra2 = 0);
-
- std::string mName;
- std::string mToolTip;
- LLVector3d mPosGlobal;
- LLUUID mID;
- BOOL mSelected;
- S32 mExtra;
- S32 mExtra2;
- U64 mRegionHandle;
-};
+ LLItemInfo(F32 global_x, F32 global_y, const std::string& name, LLUUID id);
+
+ // Setters
+ void setTooltip(std::string& tooltip) { mToolTip = tooltip; }
+ void setElevation(F64 z) { mPosGlobal.mdV[VZ] = z; }
+ void setCount(S32 count) { mCount = count; }
+// void setSelected(bool selected) { mSelected = selected; }
+// void setColor(LLColor4 color) { mColor = color; }
+
+ // Accessors
+ const LLVector3d& getGlobalPosition() const { return mPosGlobal; }
+ const std::string& getName() const { return mName; }
+ const std::string& getToolTip() const { return mToolTip; }
+ const LLUUID& getUUID() const { return mID; }
+ S32 getCount() const { return mCount; }
+
+ U64 getRegionHandle() const { return to_region_handle(mPosGlobal); } // Build the handle on the fly
-// Map layers, see indra_constants.h
-// 0 - Prim
-// 1 - Terrain Only
-// 2 - Overlay: Land For Sale
+ bool isName(const std::string& name) const { return (mName == name); } // True if name same as item's name
+// bool isSelected() const { return mSelected; }
+private:
+ std::string mName; // Name of the individual item
+ std::string mToolTip; // Tooltip : typically, something to be displayed to the user when selecting this item
+ LLVector3d mPosGlobal; // Global world position
+ LLUUID mID; // UUID of the item
+ S32 mCount; // Number of elements in item (e.g. people count)
+ // Currently not used but might prove useful one day so we comment out
+// bool mSelected; // Selected or not: updated by the viewer UI, not the simulator or asset DB
+// LLColor4 mColor; // Color of the item
+};
+
+// Info per region
+// Such records are stored in a global map hold by the LLWorldMap and indexed by region handles.
+// To avoid creating too many of them, they are requested in "blocks" corresponding to areas covered by the screen.
+// Unfortunately, when the screen covers the whole world (zoomed out), that can translate in requesting info for
+// every sim on the grid... Not good...
+// To avoid this, the code implements a cut-off threshold for overlay graphics and, therefore, all LLSimInfo.
+// In other words, when zooming out too much, we simply stop requesting LLSimInfo and
+// LLItemInfo and just display the map tiles.
+// As they are stored in different structures (LLSimInfo and LLWorldMipmap), this strategy is now workable.
class LLSimInfo
{
public:
- LLSimInfo();
+ LLSimInfo(U64 handle);
- LLVector3d getGlobalPos(LLVector3 local_pos) const;
+ // Convert local region coordinates into world coordinates
+ LLVector3d getGlobalPos(const LLVector3& local_pos) const;
+ // Get the world coordinates of the SW corner of that region
+ LLVector3d getGlobalOrigin() const;
LLVector3 getLocalPos(LLVector3d global_pos) const;
-public:
- U64 mHandle;
- std::string mName;
-
- F64 mAgentsUpdateTime;
- BOOL mShowAgentLocations; // are agents visible?
+ void clearImage(); // Clears the reference to the Land for sale image for that region
+ void dropImagePriority(); // Drops the boost level of the Land for sale image for that region
+ void updateAgentCount(F64 time); // Send an item request for agent count on that region if time's up
- U8 mAccess;
- U32 mRegionFlags;
- F32 mWaterHeight;
+ // Setters
+ void setName(std::string& name) { mName = name; }
+ void setAccess (U32 accesscode) { mAccess = accesscode; }
+ void setRegionFlags (U32 region_flags) { mRegionFlags = region_flags; }
+ void setLandForSaleImage (LLUUID image_id);
+// void setWaterHeight (F32 water_height) { mWaterHeight = water_height; }
- F32 mAlpha;
+ // Accessors
+ std::string getName() const { return mName; }
+ const std::string getFlagsString() const { return LLViewerRegion::regionFlagsToString(mRegionFlags); }
+ const std::string getAccessString() const { return LLViewerRegion::accessToString((U8)mAccess); }
- // Image ID for the current overlay mode.
- LLUUID mMapImageID[MAP_SIM_IMAGE_TYPES];
+ const S32 getAgentCount() const; // Compute the total agents count
+ LLPointer<LLViewerFetchedTexture> getLandForSaleImage(); // Get the overlay image, fetch it if necessary
- // Hold a reference to the currently displayed image.
- LLPointer<LLViewerFetchedTexture> mCurrentImage;
- LLPointer<LLViewerFetchedTexture> mOverlayImage;
-};
+ bool isName(const std::string& name) const;
+ bool isDown() { return (mAccess == SIM_ACCESS_DOWN); }
+ bool isPG() { return (mAccess <= SIM_ACCESS_PG); }
-#define MAP_BLOCK_RES 256
+ // Debug only
+ void dump() const; // Print the region info to the standard output
-struct LLWorldMapLayer
-{
- BOOL LayerDefined;
- LLPointer<LLViewerFetchedTexture> LayerImage;
- LLUUID LayerImageID;
- LLRect LayerExtents;
+ // Items lists handling
+ typedef std::vector<LLItemInfo> item_info_list_t;
+ void clearItems();
+
+ void insertTeleHub(const LLItemInfo& item) { mTelehubs.push_back(item); }
+ void insertInfoHub(const LLItemInfo& item) { mInfohubs.push_back(item); }
+ void insertPGEvent(const LLItemInfo& item) { mPGEvents.push_back(item); }
+ void insertMatureEvent(const LLItemInfo& item) { mMatureEvents.push_back(item); }
+ void insertAdultEvent(const LLItemInfo& item) { mAdultEvents.push_back(item); }
+ void insertLandForSale(const LLItemInfo& item) { mLandForSale.push_back(item); }
+ void insertLandForSaleAdult(const LLItemInfo& item) { mLandForSaleAdult.push_back(item); }
+ void insertAgentLocation(const LLItemInfo& item);
+
+ const LLSimInfo::item_info_list_t& getTeleHub() const { return mTelehubs; }
+ const LLSimInfo::item_info_list_t& getInfoHub() const { return mInfohubs; }
+ const LLSimInfo::item_info_list_t& getPGEvent() const { return mPGEvents; }
+ const LLSimInfo::item_info_list_t& getMatureEvent() const { return mMatureEvents; }
+ const LLSimInfo::item_info_list_t& getAdultEvent() const { return mAdultEvents; }
+ const LLSimInfo::item_info_list_t& getLandForSale() const { return mLandForSale; }
+ const LLSimInfo::item_info_list_t& getLandForSaleAdult() const { return mLandForSaleAdult; }
+ const LLSimInfo::item_info_list_t& getAgentLocation() const { return mAgentLocations; }
- LLWorldMapLayer() : LayerDefined(FALSE) { }
+private:
+ U64 mHandle; // This is a hash of the X and Y world coordinates of the SW corner of the sim
+ std::string mName; // Region name
+
+ F64 mAgentsUpdateTime; // Time stamp giving the last time the agents information was requested for that region
+ bool mFirstAgentRequest; // Init agent request flag
+
+ U32 mAccess; // Down/up and maturity rating of the region
+ U32 mRegionFlags; // Tell us if the siminfo has been received (if non 0) and what kind of region it is (Sandbox, allow damage)
+ // Currently not used but might prove useful one day so we comment out
+// F32 mWaterHeight; // Water height on the region (not actively used)
+
+ // Handling the "land for sale / land for auction" overlay image
+ LLUUID mMapImageID; // Image ID of the overlay image
+ LLPointer<LLViewerFetchedTexture> mOverlayImage; // Reference to the overlay image
+
+ // Items for this region
+ // Those are data received through item requests (as opposed to block requests for the rest of the data)
+ item_info_list_t mTelehubs; // List of tele hubs in the region
+ item_info_list_t mInfohubs; // List of info hubs in the region
+ item_info_list_t mPGEvents; // List of PG events in the region
+ item_info_list_t mMatureEvents; // List of Mature events in the region
+ item_info_list_t mAdultEvents; // List of Adult events in the region (AO)
+ item_info_list_t mLandForSale; // List of Land for sales in the region
+ item_info_list_t mLandForSaleAdult; // List of Adult Land for sales in the region (AO)
+ item_info_list_t mAgentLocations; // List of agents in the region
};
+// We request region data on the world by "blocks" of (MAP_BLOCK_SIZE x MAP_BLOCK_SIZE) regions
+// This is to reduce the number of requests to the asset DB and get things in big "blocks"
+const S32 MAP_MAX_SIZE = 2048;
+const S32 MAP_BLOCK_SIZE = 4;
+const S32 MAP_BLOCK_RES = (MAP_MAX_SIZE / MAP_BLOCK_SIZE);
class LLWorldMap : public LLSingleton<LLWorldMap>
{
public:
- typedef boost::function<void(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)>
- url_callback_t;
-
LLWorldMap();
~LLWorldMap();
- // clears the list
+ // Clear all: list of region info, tiles, blocks and items
void reset();
- // clear the visible items
- void eraseItems();
+ void clearImageRefs(); // Clears the image references
+ void dropImagePriorities(); // Drops the priority of the images being fetched
+ void reloadItems(bool force = false); // Reload the items (people, hub, etc...)
- // Removes references to cached images
- void clearImageRefs();
+ // Region Map access
+ typedef std::map<U64, LLSimInfo*> sim_info_map_t;
+ const LLWorldMap::sim_info_map_t& getRegionMap() const { return mSimInfoMap; }
+ void updateRegions(S32 x0, S32 y0, S32 x1, S32 y1); // Requests region info for a rectangle of regions (in grid coordinates)
- // Clears the flags indicating that we've received sim infos
- // Causes a re-request of the sim info without erasing extisting info
- void clearSimFlags();
+ // Insert a region and items in the map global instance
+ // Note: x_world and y_world in world coordinates (meters)
+ static bool insertRegion(U32 x_world, U32 y_world, std::string& name, LLUUID& uuid, U32 accesscode, U32 region_flags);
+ static bool insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID& uuid, U32 type, S32 extra, S32 extra2);
- // Returns simulator information, or NULL if out of range
+ // Get info on sims (region) : note that those methods only search the range of loaded sims (the one that are being browsed)
+ // *not* the entire world. So a NULL return does not mean a down or unexisting region, just an out of range region.
LLSimInfo* simInfoFromHandle(const U64 handle);
-
- // Returns simulator information, or NULL if out of range
LLSimInfo* simInfoFromPosGlobal(const LLVector3d& pos_global);
-
- // Returns simulator information for named sim, or NULL if non-existent
LLSimInfo* simInfoFromName(const std::string& sim_name);
- // Gets simulator name for a global position, returns true if it was found
+ // Gets simulator name from a global position, returns true if found
bool simNameFromPosGlobal(const LLVector3d& pos_global, std::string& outSimName );
- // Sets the current layer
- void setCurrentLayer(S32 layer, bool request_layer = false);
-
- void sendMapLayerRequest();
- void sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent = false);
- void sendNamedRegionRequest(std::string region_name);
- void sendNamedRegionRequest(std::string region_name,
- url_callback_t callback,
- const std::string& callback_url,
- bool teleport);
- void sendHandleRegionRequest(U64 region_handle,
- url_callback_t callback,
- const std::string& callback_url,
- bool teleport);
- void sendItemRequest(U32 type, U64 handle = 0);
-
- static void processMapLayerReply(LLMessageSystem*, void**);
- static void processMapBlockReply(LLMessageSystem*, void**);
- static void processMapItemReply(LLMessageSystem*, void**);
-
- void dump();
-
- // Extend the bounding box of the list of simulators. Returns true
- // if the extents changed.
- BOOL extendAABB(U32 x_min, U32 y_min, U32 x_max, U32 y_max);
-
- // build coverage maps for telehub region visualization
- void updateTelehubCoverage();
- BOOL coveredByTelehub(LLSimInfo* infop);
-
- // Bounds of the world, in meters
- U32 getWorldWidth() const;
- U32 getWorldHeight() const;
-public:
- // Map from region-handle to simulator info
- typedef std::map<U64, LLSimInfo*> sim_info_map_t;
- sim_info_map_t mSimInfoMap;
+ // Debug only
+ void dump(); // Print the world info to the standard output
- BOOL mIsTrackingUnknownLocation, mInvalidLocation, mIsTrackingDoubleClick, mIsTrackingCommit;
- LLVector3d mUnknownLocation;
+ // Track handling
+ void cancelTracking() { mIsTrackingLocation = false; mIsTrackingFound = false; mIsInvalidLocation = false; mIsTrackingDoubleClick = false; mIsTrackingCommit = false; }
- bool mRequestLandForSale;
+ void setTracking(const LLVector3d& loc) { mIsTrackingLocation = true; mTrackingLocation = loc; mIsTrackingFound = false; mIsInvalidLocation = false; mIsTrackingDoubleClick = false; mIsTrackingCommit = false;}
+ void setTrackingInvalid() { mIsTrackingFound = true; mIsInvalidLocation = true; }
+ void setTrackingValid() { mIsTrackingFound = true; mIsInvalidLocation = false; }
+ void setTrackingDoubleClick() { mIsTrackingDoubleClick = true; }
+ void setTrackingCommit() { mIsTrackingCommit = true; }
- typedef std::vector<LLItemInfo> item_info_list_t;
- item_info_list_t mTelehubs;
- item_info_list_t mInfohubs;
- item_info_list_t mPGEvents;
- item_info_list_t mMatureEvents;
- item_info_list_t mAdultEvents;
- item_info_list_t mLandForSale;
- item_info_list_t mLandForSaleAdult;
-
- std::map<U64,S32> mNumAgents;
-
- typedef std::map<U64, item_info_list_t> agent_list_map_t;
- agent_list_map_t mAgentLocationsMap;
-
- std::vector<LLWorldMapLayer> mMapLayers[MAP_SIM_IMAGE_TYPES];
- BOOL mMapLoaded[MAP_SIM_IMAGE_TYPES];
- BOOL * mMapBlockLoaded[MAP_SIM_IMAGE_TYPES];
- S32 mCurrentMap;
-
- // AABB of the list of simulators
- U32 mMinX;
- U32 mMaxX;
- U32 mMinY;
- U32 mMaxY;
-
- U8* mNeighborMap;
- U8* mTelehubCoverageMap;
- S32 mNeighborMapWidth;
- S32 mNeighborMapHeight;
+ bool isTracking() { return mIsTrackingLocation; }
+ bool isTrackingValidLocation() { return mIsTrackingFound && !mIsInvalidLocation; }
+ bool isTrackingInvalidLocation() { return mIsTrackingFound && mIsInvalidLocation; }
+ bool isTrackingDoubleClick() { return mIsTrackingDoubleClick; }
+ bool isTrackingCommit() { return mIsTrackingCommit; }
+ bool isTrackingInRectangle(F64 x0, F64 y0, F64 x1, F64 y1);
+
+ LLVector3d getTrackedPositionGlobal() const { return mTrackingLocation; }
+
+ // World Mipmap delegation: currently used when drawing the mipmap
+ void equalizeBoostLevels();
+ LLPointer<LLViewerFetchedTexture> getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load = true) { return mWorldMipmap.getObjectsTile(grid_x, grid_y, level, load); }
private:
- LLTimer mRequestTimer;
-
- // search for named region for url processing
- std::string mSLURLRegionName;
- U64 mSLURLRegionHandle;
- std::string mSLURL;
- url_callback_t mSLURLCallback;
- bool mSLURLTeleport;
+ bool clearItems(bool force = false); // Clears the item lists
+ void clearSimFlags(); // Clears the block flags indicating that we've already requested region infos
+
+ // Create a region record corresponding to the handle, insert it in the region map and returns a pointer
+ LLSimInfo* createSimInfoFromHandle(const U64 handle);
+
+ // Map from region-handle to region info
+ sim_info_map_t mSimInfoMap;
+
+ // Holds the tiled mipmap of the world. This is the structure that contains the images used for rendering.
+ LLWorldMipmap mWorldMipmap;
+
+ // The World is divided in "blocks" of (MAP_BLOCK_SIZE x MAP_BLOCK_SIZE) regions that get requested at once.
+ // This boolean table avoids "blocks" to be requested multiple times.
+ // Issue: Not sure this scheme is foolproof though as I've seen
+ // cases where a block is never retrieved and, because of this boolean being set, never re-requested
+ bool * mMapBlockLoaded; // Telling us if the block of regions has been requested or not
+
+ // Track location data : used while there's nothing tracked yet by LLTracker
+ bool mIsTrackingLocation; // True when we're tracking a point
+ bool mIsTrackingFound; // True when the tracking position has been found, valid or not
+ bool mIsInvalidLocation; // The region is down or the location does not correspond to an existing region
+ bool mIsTrackingDoubleClick; // User double clicked to set the location (i.e. teleport when found please...)
+ bool mIsTrackingCommit; // User used the search or landmark fields to set the location
+ LLVector3d mTrackingLocation; // World global position being tracked
+
+ // General grid items request timing flags (used for events,hubs and land for sale)
+ LLTimer mRequestTimer;
+ bool mFirstRequest;
};
#endif
diff --git a/indra/newview/llworldmapmessage.cpp b/indra/newview/llworldmapmessage.cpp
new file mode 100644
index 0000000000..6a074d9697
--- /dev/null
+++ b/indra/newview/llworldmapmessage.cpp
@@ -0,0 +1,261 @@
+/**
+ * @file llworldmapmessage.cpp
+ * @brief Handling of the messages to the DB made by and for the world map.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ *
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llworldmapmessage.h"
+
+#include "llworldmap.h"
+#include "llagent.h"
+#include "llfloaterworldmap.h"
+
+const U32 LAYER_FLAG = 2;
+
+//---------------------------------------------------------------------------
+// World Map Message Handling
+//---------------------------------------------------------------------------
+
+LLWorldMapMessage::LLWorldMapMessage() :
+ mSLURLRegionName(),
+ mSLURLRegionHandle(0),
+ mSLURL(),
+ mSLURLCallback(0),
+ mSLURLTeleport(false)
+{
+}
+
+LLWorldMapMessage::~LLWorldMapMessage()
+{
+}
+
+void LLWorldMapMessage::sendItemRequest(U32 type, U64 handle)
+{
+ //LL_INFOS("World Map") << "Send item request : type = " << type << LL_ENDL;
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_MapItemRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, LAYER_FLAG);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+
+ msg->nextBlockFast(_PREHASH_RequestData);
+ msg->addU32Fast(_PREHASH_ItemType, type);
+ msg->addU64Fast(_PREHASH_RegionHandle, handle); // If zero, filled in on sim
+
+ gAgent.sendReliableMessage();
+}
+
+void LLWorldMapMessage::sendNamedRegionRequest(std::string region_name)
+{
+ //LL_INFOS("World Map") << "LLWorldMap::sendNamedRegionRequest()" << LL_ENDL;
+ LLMessageSystem* msg = gMessageSystem;
+
+ // Request for region data
+ msg->newMessageFast(_PREHASH_MapNameRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, LAYER_FLAG);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ msg->nextBlockFast(_PREHASH_NameData);
+ msg->addStringFast(_PREHASH_Name, region_name);
+ gAgent.sendReliableMessage();
+}
+
+void LLWorldMapMessage::sendNamedRegionRequest(std::string region_name,
+ url_callback_t callback,
+ const std::string& callback_url,
+ bool teleport) // immediately teleport when result returned
+{
+ //LL_INFOS("World Map") << "LLWorldMap::sendNamedRegionRequest()" << LL_ENDL;
+ mSLURLRegionName = region_name;
+ mSLURLRegionHandle = 0;
+ mSLURL = callback_url;
+ mSLURLCallback = callback;
+ mSLURLTeleport = teleport;
+
+ sendNamedRegionRequest(region_name);
+}
+
+void LLWorldMapMessage::sendHandleRegionRequest(U64 region_handle,
+ url_callback_t callback,
+ const std::string& callback_url,
+ bool teleport) // immediately teleport when result returned
+{
+ //LL_INFOS("World Map") << "LLWorldMap::sendHandleRegionRequest()" << LL_ENDL;
+ mSLURLRegionName.clear();
+ mSLURLRegionHandle = region_handle;
+ mSLURL = callback_url;
+ mSLURLCallback = callback;
+ mSLURLTeleport = teleport;
+
+ U32 global_x;
+ U32 global_y;
+ from_region_handle(region_handle, &global_x, &global_y);
+ U16 grid_x = (U16)(global_x / REGION_WIDTH_UNITS);
+ U16 grid_y = (U16)(global_y / REGION_WIDTH_UNITS);
+
+ sendMapBlockRequest(grid_x, grid_y, grid_x, grid_y, true);
+}
+
+void LLWorldMapMessage::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent)
+{
+ //LL_INFOS("World Map") << "LLWorldMap::sendMapBlockRequest()" << ", min = (" << min_x << ", " << min_y << "), max = (" << max_x << ", " << max_y << "), nonexistent = " << return_nonexistent << LL_ENDL;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MapBlockRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ U32 flags = LAYER_FLAG;
+ flags |= (return_nonexistent ? 0x10000 : 0);
+ msg->addU32Fast(_PREHASH_Flags, flags);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ msg->nextBlockFast(_PREHASH_PositionData);
+ msg->addU16Fast(_PREHASH_MinX, min_x);
+ msg->addU16Fast(_PREHASH_MinY, min_y);
+ msg->addU16Fast(_PREHASH_MaxX, max_x);
+ msg->addU16Fast(_PREHASH_MaxY, max_y);
+ gAgent.sendReliableMessage();
+}
+
+// public static
+void LLWorldMapMessage::processMapBlockReply(LLMessageSystem* msg, void**)
+{
+ U32 agent_flags;
+ msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags);
+
+ // There's only one flag that we ever use here
+ if (agent_flags != LAYER_FLAG)
+ {
+ llwarns << "Invalid map image type returned! layer = " << agent_flags << llendl;
+ return;
+ }
+
+ S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_Data);
+ //LL_INFOS("World Map") << "LLWorldMap::processMapBlockReply(), num_blocks = " << num_blocks << LL_ENDL;
+
+ bool found_null_sim = false;
+
+ for (S32 block=0; block<num_blocks; ++block)
+ {
+ U16 x_regions;
+ U16 y_regions;
+ std::string name;
+ U8 accesscode;
+ U32 region_flags;
+// U8 water_height;
+// U8 agents;
+ LLUUID image_id;
+ msg->getU16Fast(_PREHASH_Data, _PREHASH_X, x_regions, block);
+ msg->getU16Fast(_PREHASH_Data, _PREHASH_Y, y_regions, block);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, name, block);
+ msg->getU8Fast(_PREHASH_Data, _PREHASH_Access, accesscode, block);
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_RegionFlags, region_flags, block);
+// msg->getU8Fast(_PREHASH_Data, _PREHASH_WaterHeight, water_height, block);
+// msg->getU8Fast(_PREHASH_Data, _PREHASH_Agents, agents, block);
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_MapImageID, image_id, block);
+
+ U32 x_world = (U32)(x_regions) * REGION_WIDTH_UNITS;
+ U32 y_world = (U32)(y_regions) * REGION_WIDTH_UNITS;
+
+ // Insert that region in the world map, if failure, flag it as a "null_sim"
+ if (!(LLWorldMap::getInstance()->insertRegion(x_world, y_world, name, image_id, (U32)accesscode, region_flags)))
+ {
+ found_null_sim = true;
+ }
+
+ // If we hit a valid tracking location, do what needs to be done app level wise
+ if (LLWorldMap::getInstance()->isTrackingValidLocation())
+ {
+ LLVector3d pos_global = LLWorldMap::getInstance()->getTrackedPositionGlobal();
+ if (LLWorldMap::getInstance()->isTrackingDoubleClick())
+ {
+ // Teleport if the user double clicked
+ gAgent.teleportViaLocation(pos_global);
+ }
+ // Update the "real" tracker information
+ gFloaterWorldMap->trackLocation(pos_global);
+ }
+
+ // Handle the SLURL callback if any
+ if(LLWorldMapMessage::getInstance()->mSLURLCallback != NULL)
+ {
+ U64 handle = to_region_handle(x_world, y_world);
+ // Check if we reached the requested region
+ if ((LLStringUtil::compareInsensitive(LLWorldMapMessage::getInstance()->mSLURLRegionName, name)==0)
+ || (LLWorldMapMessage::getInstance()->mSLURLRegionHandle == handle))
+ {
+ url_callback_t callback = LLWorldMapMessage::getInstance()->mSLURLCallback;
+
+ LLWorldMapMessage::getInstance()->mSLURLCallback = NULL;
+ LLWorldMapMessage::getInstance()->mSLURLRegionName.clear();
+ LLWorldMapMessage::getInstance()->mSLURLRegionHandle = 0;
+
+ callback(handle, LLWorldMapMessage::getInstance()->mSLURL, image_id, LLWorldMapMessage::getInstance()->mSLURLTeleport);
+ }
+ }
+ }
+ // Tell the UI to update itself
+ gFloaterWorldMap->updateSims(found_null_sim);
+}
+
+// public static
+void LLWorldMapMessage::processMapItemReply(LLMessageSystem* msg, void**)
+{
+ //LL_INFOS("World Map") << "LLWorldMap::processMapItemReply()" << LL_ENDL;
+ U32 type;
+ msg->getU32Fast(_PREHASH_RequestData, _PREHASH_ItemType, type);
+
+ S32 num_blocks = msg->getNumberOfBlocks("Data");
+
+ for (S32 block=0; block<num_blocks; ++block)
+ {
+ U32 X, Y;
+ std::string name;
+ S32 extra, extra2;
+ LLUUID uuid;
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_X, X, block);
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_Y, Y, block);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, name, block);
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ID, uuid, block);
+ msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra, extra, block);
+ msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra2, extra2, block);
+
+ LLWorldMap::getInstance()->insertItem(X, Y, name, uuid, type, extra, extra2);
+ }
+}
+
diff --git a/indra/newview/llworldmapmessage.h b/indra/newview/llworldmapmessage.h
new file mode 100644
index 0000000000..2c8fedcb10
--- /dev/null
+++ b/indra/newview/llworldmapmessage.h
@@ -0,0 +1,83 @@
+/**
+ * @file llworldmapmessage.h
+ * @brief Handling of the messages to the DB made by and for the world map.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ *
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLWORLDMAPMESSAGE_H
+#define LL_LLWORLDMAPMESSAGE_H
+
+// Handling of messages (send and process) as well as SLURL callback if necessary
+class LLMessageSystem;
+
+class LLWorldMapMessage : public LLSingleton<LLWorldMapMessage>
+{
+public:
+ typedef boost::function<void(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)>
+ url_callback_t;
+
+ LLWorldMapMessage();
+ ~LLWorldMapMessage();
+
+ // Process incoming answers to map stuff requests
+ static void processMapBlockReply(LLMessageSystem*, void**);
+ static void processMapItemReply(LLMessageSystem*, void**);
+
+ // Request data for all regions in a rectangular area. Coordinates in grids (i.e. meters / 256).
+ void sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent = false);
+
+ // Various methods to request LLSimInfo data to the simulator and asset DB
+ void sendNamedRegionRequest(std::string region_name);
+ void sendNamedRegionRequest(std::string region_name,
+ url_callback_t callback,
+ const std::string& callback_url,
+ bool teleport);
+ void sendHandleRegionRequest(U64 region_handle,
+ url_callback_t callback,
+ const std::string& callback_url,
+ bool teleport);
+
+ // Request item data for regions
+ // Note: the handle works *only* when requesting agent count (type = MAP_ITEM_AGENT_LOCATIONS). In that case,
+ // the request will actually be transitting through the spaceserver (all that is done on the sim).
+ // All other values of type do create a global grid request to the asset DB. So no need to try to get, say,
+ // the events for one particular region. For such a request, the handle is ignored.
+ void sendItemRequest(U32 type, U64 handle = 0);
+
+private:
+ // Search for region (by name or handle) for SLURL processing and teleport
+ // None of this relies explicitly on the LLWorldMap instance so better handle it here
+ std::string mSLURLRegionName;
+ U64 mSLURLRegionHandle;
+ std::string mSLURL;
+ url_callback_t mSLURLCallback;
+ bool mSLURLTeleport;
+};
+
+#endif // LL_LLWORLDMAPMESSAGE_H
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
index 0509898f40..60b1e59645 100644
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -46,7 +46,6 @@
#include "llagent.h"
#include "llcallingcard.h"
#include "llviewercontrol.h"
-#include "llcylinder.h"
#include "llfloatermap.h"
#include "llfloaterworldmap.h"
#include "llfocusmgr.h"
@@ -57,25 +56,29 @@
#include "llviewercamera.h"
#include "llviewertexture.h"
#include "llviewertexturelist.h"
-#include "llviewermenu.h"
-#include "llviewerparceloverlay.h"
#include "llviewerregion.h"
#include "llviewerwindow.h"
-#include "llworldmap.h"
-#include "lltexturefetch.h"
-#include "llappviewer.h" // Only for constants!
#include "lltrans.h"
#include "llglheaders.h"
+// Basically a C++ implementation of the OCEAN_COLOR defined in mapstitcher.py
+// Please ensure consistency between those 2 files (TODO: would be better to get that color from an asset source...)
+// # Constants
+// OCEAN_COLOR = "#1D475F"
+const F32 OCEAN_RED = (F32)(0x1D)/255.f;
+const F32 OCEAN_GREEN = (F32)(0x47)/255.f;
+const F32 OCEAN_BLUE = (F32)(0x5F)/255.f;
+
const F32 GODLY_TELEPORT_HEIGHT = 200.f;
const S32 SCROLL_HINT_WIDTH = 65;
const F32 BIG_DOT_RADIUS = 5.f;
BOOL LLWorldMapView::sHandledLastClick = FALSE;
-LLUIImagePtr LLWorldMapView::sAvatarYouSmallImage = NULL;
LLUIImagePtr LLWorldMapView::sAvatarSmallImage = NULL;
-LLUIImagePtr LLWorldMapView::sAvatarLargeImage = NULL;
+LLUIImagePtr LLWorldMapView::sAvatarYouImage = NULL;
+LLUIImagePtr LLWorldMapView::sAvatarYouLargeImage = NULL;
+LLUIImagePtr LLWorldMapView::sAvatarLevelImage = NULL;
LLUIImagePtr LLWorldMapView::sAvatarAboveImage = NULL;
LLUIImagePtr LLWorldMapView::sAvatarBelowImage = NULL;
@@ -93,35 +96,34 @@ LLUIImagePtr LLWorldMapView::sClassifiedsImage = NULL;
LLUIImagePtr LLWorldMapView::sForSaleImage = NULL;
LLUIImagePtr LLWorldMapView::sForSaleAdultImage = NULL;
-F32 LLWorldMapView::sThresholdA = 48.f;
-F32 LLWorldMapView::sThresholdB = 96.f;
F32 LLWorldMapView::sPanX = 0.f;
F32 LLWorldMapView::sPanY = 0.f;
F32 LLWorldMapView::sTargetPanX = 0.f;
F32 LLWorldMapView::sTargetPanY = 0.f;
S32 LLWorldMapView::sTrackingArrowX = 0;
S32 LLWorldMapView::sTrackingArrowY = 0;
-F32 LLWorldMapView::sPixelsPerMeter = 1.f;
-F32 CONE_SIZE = 0.6f;
+bool LLWorldMapView::sVisibleTilesLoaded = false;
+F32 LLWorldMapView::sMapScale = 128.f;
std::map<std::string,std::string> LLWorldMapView::sStringsMap;
-#define SIM_NULL_MAP_SCALE 1 // width in pixels, where we start drawing "null" sims
-#define SIM_MAP_AGENT_SCALE 2 // width in pixels, where we start drawing agents
-#define SIM_MAP_SCALE 1 // width in pixels, where we start drawing sim tiles
-
-// Updates for agent locations.
-#define AGENTS_UPDATE_TIME 60.0 // in seconds
+// Fetch and draw info thresholds
+const F32 DRAW_TEXT_THRESHOLD = 96.f; // Don't draw text under that resolution value (res = width region in meters)
+const S32 DRAW_SIMINFO_THRESHOLD = 3; // Max level for which we load or display sim level information (level in LLWorldMipmap sense)
+const S32 DRAW_LANDFORSALE_THRESHOLD = 2; // Max level for which we load or display land for sale picture data (level in LLWorldMipmap sense)
+// When on, draw an outline for each mipmap tile gotten from S3
+#define DEBUG_DRAW_TILE 0
void LLWorldMapView::initClass()
{
- sAvatarYouSmallImage = LLUI::getUIImage("map_avatar_you_8.tga");
- sAvatarSmallImage = LLUI::getUIImage("map_avatar_8.tga");
- sAvatarLargeImage = LLUI::getUIImage("map_avatar_16.tga");
- sAvatarAboveImage = LLUI::getUIImage("map_avatar_above_8.tga");
- sAvatarBelowImage = LLUI::getUIImage("map_avatar_below_8.tga");
+ sAvatarSmallImage = LLUI::getUIImage("map_avatar_8.tga");
+ sAvatarYouImage = LLUI::getUIImage("map_avatar_16.tga");
+ sAvatarYouLargeImage = LLUI::getUIImage("map_avatar_you_32.tga");
+ sAvatarLevelImage = LLUI::getUIImage("map_avatar_32.tga");
+ sAvatarAboveImage = LLUI::getUIImage("map_avatar_above_32.tga");
+ sAvatarBelowImage = LLUI::getUIImage("map_avatar_below_32.tga");
sHomeImage = LLUI::getUIImage("map_home.tga");
sTelehubImage = LLUI::getUIImage("map_telehub.tga");
@@ -145,9 +147,10 @@ void LLWorldMapView::initClass()
// static
void LLWorldMapView::cleanupClass()
{
- sAvatarYouSmallImage = NULL;
sAvatarSmallImage = NULL;
- sAvatarLargeImage = NULL;
+ sAvatarYouImage = NULL;
+ sAvatarYouLargeImage = NULL;
+ sAvatarLevelImage = NULL;
sAvatarAboveImage = NULL;
sAvatarBelowImage = NULL;
@@ -167,7 +170,7 @@ void LLWorldMapView::cleanupClass()
LLWorldMapView::LLWorldMapView()
: LLPanel(),
- mBackgroundColor( LLColor4( 4.f/255.f, 4.f/255.f, 75.f/255.f, 1.f ) ),
+ mBackgroundColor( LLColor4( OCEAN_RED, OCEAN_GREEN, OCEAN_BLUE, 1.f ) ),
mItemPicked(FALSE),
mPanning( FALSE ),
mMouseDownPanX( 0 ),
@@ -176,7 +179,8 @@ LLWorldMapView::LLWorldMapView()
mMouseDownY( 0 ),
mSelectIDStart(0)
{
- sPixelsPerMeter = gMapScale / REGION_WIDTH_METERS;
+ //LL_INFOS("World Map") << "Creating the Map -> LLWorldMapView::LLWorldMapView()" << LL_ENDL;
+
clearLastClick();
}
@@ -215,6 +219,7 @@ BOOL LLWorldMapView::postBuild()
LLWorldMapView::~LLWorldMapView()
{
+ //LL_INFOS("World Map") << "Destroying the map -> LLWorldMapView::~LLWorldMapView()" << LL_ENDL;
cleanupTextures();
}
@@ -228,14 +233,14 @@ void LLWorldMapView::cleanupTextures()
// static
void LLWorldMapView::setScale( F32 scale )
{
- if (scale != gMapScale)
+ if (scale != sMapScale)
{
- F32 old_scale = gMapScale;
+ F32 old_scale = sMapScale;
- gMapScale = scale;
- if (gMapScale == 0.f)
+ sMapScale = scale;
+ if (sMapScale <= 0.f)
{
- gMapScale = 0.1f;
+ sMapScale = 0.1f;
}
F32 ratio = (scale / old_scale);
@@ -243,8 +248,7 @@ void LLWorldMapView::setScale( F32 scale )
sPanY *= ratio;
sTargetPanX = sPanX;
sTargetPanY = sPanY;
-
- sPixelsPerMeter = gMapScale / REGION_WIDTH_METERS;
+ sVisibleTilesLoaded = false;
}
}
@@ -256,6 +260,7 @@ void LLWorldMapView::translatePan( S32 delta_x, S32 delta_y )
sPanY += delta_y;
sTargetPanX = sPanX;
sTargetPanY = sPanY;
+ sVisibleTilesLoaded = false;
}
@@ -269,18 +274,22 @@ void LLWorldMapView::setPan( S32 x, S32 y, BOOL snap )
sPanX = sTargetPanX;
sPanY = sTargetPanY;
}
+ sVisibleTilesLoaded = false;
}
+bool LLWorldMapView::showRegionInfo()
+{
+ return (LLWorldMipmap::scaleToLevel(sMapScale) <= DRAW_SIMINFO_THRESHOLD ? true : false);
+}
///////////////////////////////////////////////////////////////////////////////////
// HELPERS
BOOL is_agent_in_region(LLViewerRegion* region, LLSimInfo* info)
{
- return ((region && info) && (info->mName == region->getName()));
+ return (region && info && info->isName(region->getName()));
}
-
///////////////////////////////////////////////////////////////////////////////////
void LLWorldMapView::draw()
@@ -292,7 +301,7 @@ void LLWorldMapView::draw()
F64 current_time = LLTimer::getElapsedSeconds();
mVisibleRegions.clear();
-
+
// animate pan if necessary
sPanX = lerp(sPanX, sTargetPanX, LLCriticalDamp::getInterpolant(0.1f));
sPanY = lerp(sPanY, sTargetPanY, LLCriticalDamp::getInterpolant(0.1f));
@@ -303,10 +312,12 @@ void LLWorldMapView::draw()
const F32 half_height = F32(height) / 2.0f;
LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+ S32 level = LLWorldMipmap::scaleToLevel(sMapScale);
+
LLLocalClipRect clip(getLocalRect());
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
+
glMatrixMode(GL_MODELVIEW);
// Clear the background alpha to 0
@@ -319,308 +330,59 @@ void LLWorldMapView::draw()
}
gGL.flush();
+
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
gGL.setColorMask(true, true);
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
-
- F32 layer_alpha = 1.f;
-
- // Draw one image per layer
- for (U32 layer_idx=0; layer_idx<LLWorldMap::getInstance()->mMapLayers[LLWorldMap::getInstance()->mCurrentMap].size(); ++layer_idx)
- {
- if (!LLWorldMap::getInstance()->mMapLayers[LLWorldMap::getInstance()->mCurrentMap][layer_idx].LayerDefined)
- {
- continue;
- }
- LLWorldMapLayer *layer = &LLWorldMap::getInstance()->mMapLayers[LLWorldMap::getInstance()->mCurrentMap][layer_idx];
- LLViewerFetchedTexture *current_image = layer->LayerImage;
-
- if (current_image->isMissingAsset())
- {
- continue; // better to draw nothing than the missing asset image
- }
-
- LLVector3d origin_global((F64)layer->LayerExtents.mLeft * REGION_WIDTH_METERS, (F64)layer->LayerExtents.mBottom * REGION_WIDTH_METERS, 0.f);
-
- // Find x and y position relative to camera's center.
- LLVector3d rel_region_pos = origin_global - camera_global;
- F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale;
- F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale;
-
- F32 pix_width = gMapScale*(layer->LayerExtents.getWidth() + 1);
- F32 pix_height = gMapScale*(layer->LayerExtents.getHeight() + 1);
-
- // When the view isn't panned, 0,0 = center of rectangle
- F32 bottom = sPanY + half_height + relative_y;
- F32 left = sPanX + half_width + relative_x;
- F32 top = bottom + pix_height;
- F32 right = left + pix_width;
- F32 pixel_area = pix_width*pix_height;
- // discard layers that are outside the rectangle
- // and discard small layers
- if (top < 0.f ||
- bottom > height ||
- right < 0.f ||
- left > width ||
- (pixel_area < 4*4))
- {
- current_image->setBoostLevel(0);
- continue;
- }
-
- current_image->setBoostLevel(LLViewerTexture::BOOST_MAP);
- current_image->setKnownDrawSize(llround(pix_width * LLUI::sGLScaleFactor.mV[VX]), llround(pix_height * LLUI::sGLScaleFactor.mV[VY]));
-
- if (!current_image->hasGLTexture())
- {
- continue; // better to draw nothing than the default image
- }
-
-// LLTextureView::addDebugImage(current_image);
-
- // Draw using the texture. If we don't clamp we get artifact at
- // the edge.
- gGL.getTexUnit(0)->bind(current_image);
-
- // Draw map image into RGB
- //gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- gGL.flush();
- gGL.setColorMask(true, false);
- gGL.color4f(1.f, 1.f, 1.f, layer_alpha);
-
- gGL.begin(LLRender::QUADS);
- gGL.texCoord2f(0.0f, 1.0f);
- gGL.vertex3f(left, top, -1.0f);
- gGL.texCoord2f(0.0f, 0.0f);
- gGL.vertex3f(left, bottom, -1.0f);
- gGL.texCoord2f(1.0f, 0.0f);
- gGL.vertex3f(right, bottom, -1.0f);
- gGL.texCoord2f(1.0f, 1.0f);
- gGL.vertex3f(right, top, -1.0f);
- gGL.end();
-
- // draw an alpha of 1 where the sims are visible
- gGL.flush();
- gGL.setColorMask(false, true);
- gGL.color4f(1.f, 1.f, 1.f, 1.f);
-
- gGL.begin(LLRender::QUADS);
- gGL.texCoord2f(0.0f, 1.0f);
- gGL.vertex2f(left, top);
- gGL.texCoord2f(0.0f, 0.0f);
- gGL.vertex2f(left, bottom);
- gGL.texCoord2f(1.0f, 0.0f);
- gGL.vertex2f(right, bottom);
- gGL.texCoord2f(1.0f, 1.0f);
- gGL.vertex2f(right, top);
- gGL.end();
- }
+#if 1
+ // Draw the image tiles
+ drawMipmap(width, height);
gGL.flush();
+
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
gGL.setColorMask(true, true);
- // there used to be an #if 1 here, but it was uncommented; perhaps marking a block of code?
- F32 sim_alpha = 1.f;
-
- // Draw one image per region, centered on the camera position.
- const S32 MAX_SIMULTANEOUS_TEX = 100;
- const S32 MAX_REQUEST_PER_TICK = 5;
- const S32 MIN_REQUEST_PER_TICK = 1;
- S32 textures_requested_this_tick = 0;
-
- for (LLWorldMap::sim_info_map_t::iterator it = LLWorldMap::getInstance()->mSimInfoMap.begin();
- it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it)
+ // Draw per sim overlayed information (names, mature, offline...)
+ for (LLWorldMap::sim_info_map_t::const_iterator it = LLWorldMap::getInstance()->getRegionMap().begin();
+ it != LLWorldMap::getInstance()->getRegionMap().end(); ++it)
{
- U64 handle = (*it).first;
- LLSimInfo* info = (*it).second;
+ U64 handle = it->first;
+ LLSimInfo* info = it->second;
- LLViewerFetchedTexture* simimage = info->mCurrentImage;
- LLViewerFetchedTexture* overlayimage = info->mOverlayImage;
-
- if (gMapScale < SIM_MAP_SCALE)
- {
- if (simimage != NULL) simimage->setBoostLevel(0);
- if (overlayimage != NULL) overlayimage->setBoostLevel(0);
- continue;
- }
-
LLVector3d origin_global = from_region_handle(handle);
- LLVector3d camera_global = gAgent.getCameraPositionGlobal();
// Find x and y position relative to camera's center.
LLVector3d rel_region_pos = origin_global - camera_global;
- F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale;
- F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale;
+ F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * sMapScale;
+ F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * sMapScale;
+ // Coordinates of the sim in pixels in the UI panel
// When the view isn't panned, 0,0 = center of rectangle
- F32 bottom = sPanY + half_height + relative_y;
- F32 left = sPanX + half_width + relative_x;
- F32 top = bottom + gMapScale ;
- F32 right = left + gMapScale ;
-
- // Switch to world map texture (if available for this region) if either:
- // 1. Tiles are zoomed out small enough, or
- // 2. Sim's texture has not been loaded yet
- F32 map_scale_cutoff = SIM_MAP_SCALE;
- if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) > 0)
- {
- map_scale_cutoff = SIM_NULL_MAP_SCALE;
- }
-
- info->mShowAgentLocations = (gMapScale >= SIM_MAP_AGENT_SCALE);
-
- bool sim_visible =
- (gMapScale >= map_scale_cutoff) &&
- (simimage != NULL) &&
- (simimage->hasGLTexture());
-
- if (sim_visible)
- {
- // Fade in
- if (info->mAlpha < 0.0f)
- info->mAlpha = 1.f; // don't fade initially
- else
- info->mAlpha = lerp(info->mAlpha, 1.f, LLCriticalDamp::getInterpolant(0.15f));
- }
- else
- {
- // Fade out
- if (info->mAlpha < 0.0f)
- info->mAlpha = 0.f; // don't fade initially
- else
- info->mAlpha = lerp(info->mAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
- }
-
- // discard regions that are outside the rectangle
- // and discard small regions
- if (top < 0.f ||
- bottom > height ||
- right < 0.f ||
- left > width )
+ F32 bottom = sPanY + half_height + relative_y;
+ F32 left = sPanX + half_width + relative_x;
+ F32 top = bottom + sMapScale ;
+ F32 right = left + sMapScale ;
+
+ // Discard if region is outside the screen rectangle (not visible on screen)
+ if ((top < 0.f) || (bottom > height) ||
+ (right < 0.f) || (left > width) )
{
- if (simimage != NULL) simimage->setBoostLevel(0);
- if (overlayimage != NULL) overlayimage->setBoostLevel(0);
+ // Drop the "land for sale" fetching priority since it's outside the view rectangle
+ info->dropImagePriority();
continue;
}
- if (info->mCurrentImage.isNull())
- {
- if ((textures_requested_this_tick < MIN_REQUEST_PER_TICK) ||
- ((LLAppViewer::getTextureFetch()->getNumRequests() < MAX_SIMULTANEOUS_TEX) &&
- (textures_requested_this_tick < MAX_REQUEST_PER_TICK)))
- {
- textures_requested_this_tick++;
- info->mCurrentImage = LLViewerTextureManager::getFetchedTexture(info->mMapImageID[LLWorldMap::getInstance()->mCurrentMap],
- MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- info->mCurrentImage->setAddressMode(LLTexUnit::TAM_CLAMP);
- simimage = info->mCurrentImage;
- gGL.getTexUnit(0)->bind(simimage);
- }
- }
- if (info->mOverlayImage.isNull() && info->mMapImageID[2].notNull())
- {
- if ((textures_requested_this_tick < MIN_REQUEST_PER_TICK) ||
- ((LLAppViewer::getTextureFetch()->getNumRequests() < MAX_SIMULTANEOUS_TEX) &&
- (textures_requested_this_tick < MAX_REQUEST_PER_TICK)))
- {
- textures_requested_this_tick++;
- info->mOverlayImage = LLViewerTextureManager::getFetchedTexture(info->mMapImageID[2], MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- info->mOverlayImage->setAddressMode(LLTexUnit::TAM_CLAMP);
- overlayimage = info->mOverlayImage;
- gGL.getTexUnit(0)->bind(overlayimage);
- }
- }
+ // This list is used by other methods to know which regions are indeed displayed on screen
mVisibleRegions.push_back(handle);
- // See if the agents need updating
- if (current_time - info->mAgentsUpdateTime > AGENTS_UPDATE_TIME)
- {
- LLWorldMap::getInstance()->sendItemRequest(MAP_ITEM_AGENT_LOCATIONS, info->mHandle);
- info->mAgentsUpdateTime = current_time;
- }
-
- // Bias the priority escalation for images nearer
- LLVector3d center_global = origin_global;
- center_global.mdV[VX] += 128.0;
- center_global.mdV[VY] += 128.0;
-
- S32 draw_size = llround(gMapScale);
- if (simimage != NULL)
- {
- simimage->setBoostLevel(LLViewerTexture::BOOST_MAP);
- simimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY]));
- }
-
- if (overlayimage != NULL)
- {
- overlayimage->setBoostLevel(LLViewerTexture::BOOST_MAP);
- overlayimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY]));
- }
-
-// LLTextureView::addDebugImage(simimage);
- if (sim_visible && info->mAlpha > 0.001f)
+ // Update the agent count for that region if we're not too zoomed out already
+ if (level <= DRAW_SIMINFO_THRESHOLD)
{
- // Draw using the texture. If we don't clamp we get artifact at
- // the edge.
- LLGLSUIDefault gls_ui;
- if (simimage != NULL)
- gGL.getTexUnit(0)->bind(simimage);
-
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
- F32 alpha = sim_alpha * info->mAlpha;
- gGL.color4f(1.f, 1.0f, 1.0f, alpha);
-
- gGL.begin(LLRender::QUADS);
- gGL.texCoord2f(0.f, 1.f);
- gGL.vertex3f(left, top, 0.f);
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex3f(left, bottom, 0.f);
- gGL.texCoord2f(1.f, 0.f);
- gGL.vertex3f(right, bottom, 0.f);
- gGL.texCoord2f(1.f, 1.f);
- gGL.vertex3f(right, top, 0.f);
- gGL.end();
-
- if (gSavedSettings.getBOOL("MapShowLandForSale") && overlayimage && overlayimage->hasGLTexture())
- {
- gGL.getTexUnit(0)->bind(overlayimage);
- gGL.color4f(1.f, 1.f, 1.f, alpha);
- gGL.begin(LLRender::QUADS);
- gGL.texCoord2f(0.f, 1.f);
- gGL.vertex3f(left, top, -0.5f);
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex3f(left, bottom, -0.5f);
- gGL.texCoord2f(1.f, 0.f);
- gGL.vertex3f(right, bottom, -0.5f);
- gGL.texCoord2f(1.f, 1.f);
- gGL.vertex3f(right, top, -0.5f);
- gGL.end();
- }
-
- if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) == 0)
- {
- // draw an alpha of 1 where the sims are visible (except NULL sims)
- gGL.flush();
- gGL.setSceneBlendType(LLRender::BT_REPLACE);
- gGL.setColorMask(false, true);
- gGL.color4f(1.f, 1.f, 1.f, 1.f);
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.begin(LLRender::QUADS);
- gGL.vertex2f(left, top);
- gGL.vertex2f(left, bottom);
- gGL.vertex2f(right, bottom);
- gGL.vertex2f(right, top);
- gGL.end();
-
- gGL.flush();
- gGL.setColorMask(true, true);
- }
+ info->updateAgentCount(current_time);
}
- if (info->mAccess == SIM_ACCESS_DOWN)
+ if (info->isDown())
{
// Draw a transparent red square over down sims
gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_SOURCE_ALPHA);
@@ -634,18 +396,15 @@ void LLWorldMapView::draw()
gGL.vertex2f(right, top);
gGL.end();
}
-
- // As part of the AO project, we no longer want to draw access indicators;
- // it's too complicated to get all the rules straight and will only
+ // As part of the AO project, we no longer want to draw access indicators;
+ // it's too complicated to get all the rules straight and will only
// cause confusion.
/**********************
- // If this is mature, and you are not, draw a line across it
- if (info->mAccess != SIM_ACCESS_DOWN
- && info->mAccess > SIM_ACCESS_PG
- && gAgent.isTeen())
+ else if (!info->isPG() && gAgent.isTeen())
{
+ // If this is a mature region, and you are not, draw a line across it
gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO);
-
+
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color3f(1.f, 0.f, 0.f);
gGL.begin(LLRender::LINES);
@@ -656,68 +415,67 @@ void LLWorldMapView::draw()
gGL.end();
}
**********************/
-
- // Draw the region name in the lower left corner
- LLFontGL* font = LLFontGL::getFontSansSerifSmall();
-
- std::string mesg;
- if (gMapScale < sThresholdA)
+ else if (gSavedSettings.getBOOL("MapShowLandForSale") && (level <= DRAW_LANDFORSALE_THRESHOLD))
{
+ // Draw the overlay image "Land for Sale / Land for Auction"
+ LLViewerFetchedTexture* overlayimage = info->getLandForSaleImage();
+ if (overlayimage)
+ {
+ // Inform the fetch mechanism of the size we need
+ S32 draw_size = llround(sMapScale);
+ overlayimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY]));
+ // Draw something whenever we have enough info
+ if (overlayimage->hasGLTexture())
+ {
+ gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO);
+ gGL.getTexUnit(0)->bind(overlayimage);
+ gGL.color4f(1.f, 1.f, 1.f, 1.f);
+ gGL.begin(LLRender::QUADS);
+ gGL.texCoord2f(0.f, 1.f);
+ gGL.vertex3f(left, top, -0.5f);
+ gGL.texCoord2f(0.f, 0.f);
+ gGL.vertex3f(left, bottom, -0.5f);
+ gGL.texCoord2f(1.f, 0.f);
+ gGL.vertex3f(right, bottom, -0.5f);
+ gGL.texCoord2f(1.f, 1.f);
+ gGL.vertex3f(right, top, -0.5f);
+ gGL.end();
+ }
+ }
}
- else if (gMapScale < sThresholdB)
+ else
{
- // mesg = llformat( info->mAgents);
+ // If we're not displaying the "land for sale", drop its fetching priority
+ info->dropImagePriority();
}
- else
+
+ // Draw the region name in the lower left corner
+ if (sMapScale >= DRAW_TEXT_THRESHOLD)
{
- //mesg = llformat("%d / %s (%s)",
- // info->mAgents,
- // info->mName.c_str(),
- // LLViewerRegion::accessToShortString(info->mAccess).c_str() );
- if (info->mAccess == SIM_ACCESS_DOWN)
+ LLFontGL* font = LLFontGL::getFontSansSerifSmall();
+ std::string mesg;
+ if (info->isDown())
{
- mesg = llformat( "%s (%s)", info->mName.c_str(), sStringsMap["offline"].c_str());
+ mesg = llformat( "%s (%s)", info->getName().c_str(), sStringsMap["offline"].c_str());
}
else
{
- mesg = info->mName;
+ mesg = info->getName();
}
- }
-
- if (!mesg.empty())
- {
- font->renderUTF8(
- mesg, 0,
- llfloor(left + 3),
- llfloor(bottom + 2),
- LLColor4::white,
- LLFontGL::LEFT,
- LLFontGL::BASELINE,
- LLFontGL::NORMAL,
- LLFontGL::DROP_SHADOW);
-
- // If map texture is still loading,
- // display "Loading" placeholder text.
- if ((simimage != NULL) &&
- simimage->getDiscardLevel() != 1 &&
- simimage->getDiscardLevel() != 0)
+ if (!mesg.empty())
{
font->renderUTF8(
- sStringsMap["loading"], 0,
- llfloor(left + 18),
- llfloor(top - 25),
+ mesg, 0,
+ llfloor(left + 3), llfloor(bottom + 2),
LLColor4::white,
- LLFontGL::LEFT,
- LLFontGL::BASELINE,
- LLFontGL::NORMAL,
- LLFontGL::DROP_SHADOW);
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW);
}
}
}
- // #endif used to be here
+ #endif
- // there used to be an #if 1 here, but it was uncommented; perhaps marking a block of code?
+ #if 1
// Draw background rectangle
LLGLSUIDefault gls_ui;
{
@@ -727,69 +485,49 @@ void LLWorldMapView::draw()
gGL.color4fv( mBackgroundColor.mV );
gl_rect_2d(0, height, width, 0);
}
-
+
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
gGL.setSceneBlendType(LLRender::BT_ALPHA);
- // Infohubs
- if (gSavedSettings.getBOOL("MapShowInfohubs")) //(gMapScale >= sThresholdB)
+ // Draw item infos if we're not zoomed out too much and there's something to draw
+ if ((level <= DRAW_SIMINFO_THRESHOLD) && (gSavedSettings.getBOOL("MapShowInfohubs") ||
+ gSavedSettings.getBOOL("MapShowTelehubs") ||
+ gSavedSettings.getBOOL("MapShowLandForSale") ||
+ gSavedSettings.getBOOL("MapShowEvents") ||
+ gSavedSettings.getBOOL("ShowMatureEvents") ||
+ gSavedSettings.getBOOL("ShowAdultEvents")))
{
- drawGenericItems(LLWorldMap::getInstance()->mInfohubs, sInfohubImage);
+ drawItems();
}
- // Telehubs
- if (gSavedSettings.getBOOL("MapShowTelehubs")) //(gMapScale >= sThresholdB)
- {
- drawGenericItems(LLWorldMap::getInstance()->mTelehubs, sTelehubImage);
- }
-
- // Home Sweet Home
+ // Draw the Home location (always)
LLVector3d home;
if (gAgent.getHomePosGlobal(&home))
{
drawImage(home, sHomeImage);
}
- if (gSavedSettings.getBOOL("MapShowLandForSale"))
- {
- drawGenericItems(LLWorldMap::getInstance()->mLandForSale, sForSaleImage);
- // for 1.23, we're showing normal land and adult land in the same UI; you don't
- // get a choice about which ones you want. If you're currently asking for adult
- // content and land you'll get the adult land.
- if (gAgent.canAccessAdult())
- {
- drawGenericItems(LLWorldMap::getInstance()->mLandForSaleAdult, sForSaleAdultImage);
- }
- }
-
- if (gSavedSettings.getBOOL("MapShowEvents") ||
- gSavedSettings.getBOOL("ShowMatureEvents") ||
- gSavedSettings.getBOOL("ShowAdultEvents") )
- {
- drawEvents();
- }
-
- // Now draw your avatar after all that other stuff.
+ // Draw the current agent after all that other stuff.
LLVector3d pos_global = gAgent.getPositionGlobal();
- drawImage(pos_global, sAvatarLargeImage);
+ drawImage(pos_global, sAvatarYouImage);
LLVector3 pos_map = globalPosToView(pos_global);
if (!pointInView(llround(pos_map.mV[VX]), llround(pos_map.mV[VY])))
{
- drawTracking(pos_global,
- lerp(LLColor4::yellow, LLColor4::orange, 0.4f),
- TRUE,
- "You are here",
- "",
- llround(LLFontGL::getFontSansSerifSmall()->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking
+ drawTracking(pos_global,
+ lerp(LLColor4::yellow, LLColor4::orange, 0.4f),
+ TRUE,
+ "You are here",
+ "",
+ llround(LLFontGL::getFontSansSerifSmall()->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking
}
- // Show your viewing angle
+ // Draw the current agent viewing angle
drawFrustum();
// Draw icons for the avatars in each region.
- // Drawn after your avatar so you can see nearby people.
- if (gSavedSettings.getBOOL("MapShowPeople"))
+ // Drawn this after the current agent avatar so one can see nearby people
+ if (gSavedSettings.getBOOL("MapShowPeople") && (level <= DRAW_SIMINFO_THRESHOLD))
{
drawAgents();
}
@@ -799,9 +537,9 @@ void LLWorldMapView::draw()
if ( LLTracker::TRACKING_AVATAR == tracking_status )
{
drawTracking( LLAvatarTracker::instance().getGlobalPos(), map_track_color, TRUE, LLTracker::getLabel(), "" );
- }
- else if ( LLTracker::TRACKING_LANDMARK == tracking_status
- || LLTracker::TRACKING_LOCATION == tracking_status )
+ }
+ else if ( LLTracker::TRACKING_LANDMARK == tracking_status
+ || LLTracker::TRACKING_LOCATION == tracking_status )
{
// While fetching landmarks, will have 0,0,0 location for a while,
// so don't draw. JC
@@ -811,31 +549,33 @@ void LLWorldMapView::draw()
drawTracking( pos_global, map_track_color, TRUE, LLTracker::getLabel(), LLTracker::getToolTip() );
}
}
- else if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation)
+ else if (LLWorldMap::getInstance()->isTracking())
{
- if (LLWorldMap::getInstance()->mInvalidLocation)
+ if (LLWorldMap::getInstance()->isTrackingInvalidLocation())
{
- // We know this location to be invalid
+ // We know this location to be invalid, draw a blue circle
LLColor4 loading_color(0.0, 0.5, 1.0, 1.0);
- drawTracking( LLWorldMap::getInstance()->mUnknownLocation, loading_color, TRUE, getString("InvalidLocation"), "");
+ drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, TRUE, getString("InvalidLocation"), "");
}
else
{
+ // We don't know yet what that location is, draw a throbing blue circle
double value = fmod(current_time, 2);
- value = 0.5 + 0.5*cos(value * M_PI);
+ value = 0.5 + 0.5*cos(value * F_PI);
LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0);
- drawTracking( LLWorldMap::getInstance()->mUnknownLocation, loading_color, TRUE, getString("Loading"), "");
+ drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, TRUE, getString("Loading"), "");
}
}
- // #endif used to be here
-
+ #endif
+
// turn off the scissor
LLGLDisable no_scissor(GL_SCISSOR_TEST);
-
+
updateDirections();
LLView::draw();
+ // Get sim info for all sims in view
updateVisibleBlocks();
} // end draw()
@@ -846,36 +586,182 @@ void LLWorldMapView::setVisible(BOOL visible)
LLPanel::setVisible(visible);
if (!visible)
{
- for (S32 map = 0; map < MAP_SIM_IMAGE_TYPES; map++)
+ // Drop the download of tiles and images priority to nil if we hide the map
+ LLWorldMap::getInstance()->dropImagePriorities();
+ }
+}
+
+void LLWorldMapView::drawMipmap(S32 width, S32 height)
+{
+ // Compute the level of the mipmap to use for the current scale level
+ S32 level = LLWorldMipmap::scaleToLevel(sMapScale);
+ // Set the tile boost level so that unused tiles get to 0
+ LLWorldMap::getInstance()->equalizeBoostLevels();
+
+ // Render whatever we already have loaded if we haven't the current level
+ // complete and use it as a background (scaled up or scaled down)
+ if (!sVisibleTilesLoaded)
+ {
+ // Note: the (load = false) parameter avoids missing tiles to be fetched (i.e. we render what we have, no more)
+ // Check all the lower res levels and render them in reverse order (worse to best)
+ // We need to traverse all the levels as the user can zoom in very fast
+ for (S32 l = LLWorldMipmap::MAP_LEVELS; l > level; l--)
{
- for (U32 layer_idx=0; layer_idx<LLWorldMap::getInstance()->mMapLayers[map].size(); ++layer_idx)
- {
- if (LLWorldMap::getInstance()->mMapLayers[map][layer_idx].LayerDefined)
- {
- LLWorldMapLayer *layer = &LLWorldMap::getInstance()->mMapLayers[map][layer_idx];
- layer->LayerImage->setBoostLevel(0);
- }
- }
+ drawMipmapLevel(width, height, l, false);
}
- for (LLWorldMap::sim_info_map_t::iterator it = LLWorldMap::getInstance()->mSimInfoMap.begin();
- it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it)
+ // Skip the current level, as we'll do it anyway here under...
+
+ // Just go one level down in res as it can really get too much stuff
+ // when zooming out and too small to see anyway...
+ if (level > 1)
{
- LLSimInfo* info = (*it).second;
- if (info->mCurrentImage.notNull())
+ drawMipmapLevel(width, height, level - 1, false);
+ }
+ }
+ else
+ {
+ //LL_INFOS("World Map") << "Render complete, don't draw background..." << LL_ENDL;
+ }
+
+ // Render the current level
+ sVisibleTilesLoaded = drawMipmapLevel(width, height, level);
+
+ return;
+}
+
+// Return true if all the tiles required to render that level have been fetched or are truly missing
+bool LLWorldMapView::drawMipmapLevel(S32 width, S32 height, S32 level, bool load)
+{
+ // Check input level
+ llassert (level > 0);
+ if (level <= 0)
+ return false;
+
+ // Count tiles hit and completed
+ S32 completed_tiles = 0;
+ S32 total_tiles = 0;
+
+ // Size in meters (global) of each tile of that level
+ S32 tile_width = LLWorldMipmap::MAP_TILE_SIZE * (1 << (level - 1));
+ // Dimension of the screen in meter at that scale
+ LLVector3d pos_SW = viewPosToGlobal(0, 0);
+ LLVector3d pos_NE = viewPosToGlobal(width, height);
+ // Add external band of tiles on the outskirt so to hit the partially displayed tiles right and top
+ pos_NE[VX] += tile_width;
+ pos_NE[VY] += tile_width;
+
+ // Iterate through the tiles on screen: we just need to ask for one tile every tile_width meters
+ U32 grid_x, grid_y;
+ for (F64 index_y = pos_SW[VY]; index_y < pos_NE[VY]; index_y += tile_width)
+ {
+ for (F64 index_x = pos_SW[VX]; index_x < pos_NE[VX]; index_x += tile_width)
+ {
+ // Compute the world coordinates of the current point
+ LLVector3d pos_global(index_x, index_y, pos_SW[VZ]);
+ // Convert to the mipmap level coordinates for that point (i.e. which tile to we hit)
+ LLWorldMipmap::globalToMipmap(pos_global[VX], pos_global[VY], level, &grid_x, &grid_y);
+ // Get the tile. Note: NULL means that the image does not exist (so it's considered "complete" as far as fetching is concerned)
+ LLPointer<LLViewerFetchedTexture> simimage = LLWorldMap::getInstance()->getObjectsTile(grid_x, grid_y, level, load);
+ if (simimage)
{
- info->mCurrentImage->setBoostLevel(0);
+ // Checks that the image has a valid texture
+ if (simimage->hasGLTexture())
+ {
+ // Increment the number of completly fetched tiles
+ completed_tiles++;
+
+ // Convert those coordinates (SW corner of the mipmap tile) into world (meters) coordinates
+ pos_global[VX] = grid_x * REGION_WIDTH_METERS;
+ pos_global[VY] = grid_y * REGION_WIDTH_METERS;
+ // Now to screen coordinates for SW corner of that tile
+ LLVector3 pos_screen = globalPosToView (pos_global);
+ F32 left = pos_screen[VX];
+ F32 bottom = pos_screen[VY];
+ // Compute the NE corner coordinates of the tile now
+ pos_global[VX] += tile_width;
+ pos_global[VY] += tile_width;
+ pos_screen = globalPosToView (pos_global);
+ F32 right = pos_screen[VX];
+ F32 top = pos_screen[VY];
+
+ // Draw the tile
+ LLGLSUIDefault gls_ui;
+ gGL.getTexUnit(0)->bind(simimage.get());
+ simimage->setAddressMode(LLTexUnit::TAM_CLAMP);
+
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ gGL.color4f(1.f, 1.0f, 1.0f, 1.0f);
+
+ gGL.begin(LLRender::QUADS);
+ gGL.texCoord2f(0.f, 1.f);
+ gGL.vertex3f(left, top, 0.f);
+ gGL.texCoord2f(0.f, 0.f);
+ gGL.vertex3f(left, bottom, 0.f);
+ gGL.texCoord2f(1.f, 0.f);
+ gGL.vertex3f(right, bottom, 0.f);
+ gGL.texCoord2f(1.f, 1.f);
+ gGL.vertex3f(right, top, 0.f);
+ gGL.end();
+#if DEBUG_DRAW_TILE
+ drawTileOutline(level, top, left, bottom, right);
+#endif // DEBUG_DRAW_TILE
+ }
+ //else
+ //{
+ // Waiting for a tile -> the level is not complete
+ // LL_INFOS("World Map") << "Unfetched tile. level = " << level << LL_ENDL;
+ //}
}
- if (info->mOverlayImage.notNull())
+ else
{
- info->mOverlayImage->setBoostLevel(0);
+ // Unexistent tiles are counted as "completed"
+ completed_tiles++;
}
+ // Increment the number of tiles in that level / screen
+ total_tiles++;
}
}
+ return (completed_tiles == total_tiles);
+}
+
+// Draw lines (rectangle outline and cross) to visualize the position of the tile
+// Used for debug only
+void LLWorldMapView::drawTileOutline(S32 level, F32 top, F32 left, F32 bottom, F32 right)
+{
+ gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO);
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ if (level == 1)
+ gGL.color3f(1.f, 0.f, 0.f); // red
+ else if (level == 2)
+ gGL.color3f(0.f, 1.f, 0.f); // green
+ else if (level == 3)
+ gGL.color3f(0.f, 0.f, 1.f); // blue
+ else if (level == 4)
+ gGL.color3f(1.f, 1.f, 0.f); // yellow
+ else if (level == 5)
+ gGL.color3f(1.f, 0.f, 1.f); // magenta
+ else if (level == 6)
+ gGL.color3f(0.f, 1.f, 1.f); // cyan
+ else if (level == 7)
+ gGL.color3f(1.f, 1.f, 1.f); // white
+ else
+ gGL.color3f(0.f, 0.f, 0.f); // black
+ gGL.begin(LLRender::LINE_STRIP);
+ gGL.vertex2f(left, top);
+ gGL.vertex2f(right, bottom);
+ gGL.vertex2f(left, bottom);
+ gGL.vertex2f(right, top);
+ gGL.vertex2f(left, top);
+ gGL.vertex2f(left, bottom);
+ gGL.vertex2f(right, bottom);
+ gGL.vertex2f(right, top);
+ gGL.end();
}
-void LLWorldMapView::drawGenericItems(const LLWorldMap::item_info_list_t& items, LLUIImagePtr image)
+void LLWorldMapView::drawGenericItems(const LLSimInfo::item_info_list_t& items, LLUIImagePtr image)
{
- LLWorldMap::item_info_list_t::const_iterator e;
+ LLSimInfo::item_info_list_t::const_iterator e;
for (e = items.begin(); e != items.end(); ++e)
{
drawGenericItem(*e, image);
@@ -884,7 +770,7 @@ void LLWorldMapView::drawGenericItems(const LLWorldMap::item_info_list_t& items,
void LLWorldMapView::drawGenericItem(const LLItemInfo& item, LLUIImagePtr image)
{
- drawImage(item.mPosGlobal, image);
+ drawImage(item.getGlobalPosition(), image);
}
@@ -907,137 +793,91 @@ void LLWorldMapView::drawImageStack(const LLVector3d& global_pos, LLUIImagePtr i
}
}
-
-void LLWorldMapView::drawAgents()
+void LLWorldMapView::drawItems()
{
- static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white);
- static LLUIColor map_avatar_friend_color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::white);
-
- F32 agents_scale = (gMapScale * 0.9f) / 256.f;
+ bool mature_enabled = gAgent.canAccessMature();
+ bool adult_enabled = gAgent.canAccessAdult();
+
+ BOOL show_mature = mature_enabled && gSavedSettings.getBOOL("ShowMatureEvents");
+ BOOL show_adult = adult_enabled && gSavedSettings.getBOOL("ShowAdultEvents");
for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter)
{
U64 handle = *iter;
- LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle);
- if (siminfo && (siminfo->mAccess == SIM_ACCESS_DOWN))
+ LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle);
+ if ((info == NULL) || (info->isDown()))
{
continue;
}
- LLWorldMap::agent_list_map_t::iterator counts_iter = LLWorldMap::getInstance()->mAgentLocationsMap.find(handle);
- if (siminfo && siminfo->mShowAgentLocations && counts_iter != LLWorldMap::getInstance()->mAgentLocationsMap.end())
+ // Infohubs
+ if (gSavedSettings.getBOOL("MapShowInfohubs"))
{
- // Show Individual agents (or little stacks where real agents are)
- LLWorldMap::item_info_list_t& agentcounts = counts_iter->second;
- S32 sim_agent_count = 0;
- for (LLWorldMap::item_info_list_t::iterator iter = agentcounts.begin();
- iter != agentcounts.end(); ++iter)
- {
- const LLItemInfo& info = *iter;
- S32 agent_count = info.mExtra;
- sim_agent_count += info.mExtra;
- // Here's how we'd choose the color if info.mID were available but it's not being sent:
- //LLColor4 color = (agent_count == 1 && is_agent_friend(info.mID)) ? map_avatar_friend_color : map_avatar_color;
- drawImageStack(info.mPosGlobal, sAvatarSmallImage, agent_count, 3.f, map_avatar_color);
- }
- LLWorldMap::getInstance()->mNumAgents[handle] = sim_agent_count; // override mNumAgents for this sim
+ drawGenericItems(info->getInfoHub(), sInfohubImage);
}
- else
+ // Telehubs
+ if (gSavedSettings.getBOOL("MapShowTelehubs"))
+ {
+ drawGenericItems(info->getTeleHub(), sTelehubImage);
+ }
+ // Land for sale
+ if (gSavedSettings.getBOOL("MapShowLandForSale"))
{
- // Show agent 'stack' at center of sim
- S32 num_agents = LLWorldMap::getInstance()->mNumAgents[handle];
- if (num_agents > 0)
+ drawGenericItems(info->getLandForSale(), sForSaleImage);
+ // for 1.23, we're showing normal land and adult land in the same UI; you don't
+ // get a choice about which ones you want. If you're currently asking for adult
+ // content and land you'll get the adult land.
+ if (gAgent.canAccessAdult())
{
- LLVector3d region_center = from_region_handle(handle);
- region_center[VX] += REGION_WIDTH_METERS / 2;
- region_center[VY] += REGION_WIDTH_METERS / 2;
- // Reduce the stack size as you zoom out - always display at lease one agent where there is one or more
- S32 agent_count = (S32)(((num_agents-1) * agents_scale + (num_agents-1) * 0.1f)+.1f) + 1;
- drawImageStack(region_center, sAvatarSmallImage, agent_count, 3.f, map_avatar_color);
+ drawGenericItems(info->getLandForSaleAdult(), sForSaleAdultImage);
}
}
+ // PG Events
+ if (gSavedSettings.getBOOL("MapShowEvents"))
+ {
+ drawGenericItems(info->getPGEvent(), sEventImage);
+ }
+ // Mature Events
+ if (show_mature)
+ {
+ drawGenericItems(info->getMatureEvent(), sEventMatureImage);
+ }
+ // Adult Events
+ if (show_adult)
+ {
+ drawGenericItems(info->getAdultEvent(), sEventAdultImage);
+ }
}
}
-
-void LLWorldMapView::drawEvents()
+void LLWorldMapView::drawAgents()
{
- bool mature_enabled = gAgent.canAccessMature();
- bool adult_enabled = gAgent.canAccessAdult();
-
- BOOL show_pg = gSavedSettings.getBOOL("MapShowEvents");
- BOOL show_mature = mature_enabled && gSavedSettings.getBOOL("ShowMatureEvents");
- BOOL show_adult = adult_enabled && gSavedSettings.getBOOL("ShowAdultEvents");
+ static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white);
- // First the non-selected events
- LLWorldMap::item_info_list_t::const_iterator e;
- if (show_pg)
+ for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter)
{
- for (e = LLWorldMap::getInstance()->mPGEvents.begin(); e != LLWorldMap::getInstance()->mPGEvents.end(); ++e)
+ U64 handle = *iter;
+ LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle);
+ if ((siminfo == NULL) || (siminfo->isDown()))
{
- if (!e->mSelected)
- {
- drawGenericItem(*e, sEventImage);
- }
+ continue;
}
- }
- if (show_mature)
- {
- for (e = LLWorldMap::getInstance()->mMatureEvents.begin(); e != LLWorldMap::getInstance()->mMatureEvents.end(); ++e)
- {
- if (!e->mSelected)
- {
- drawGenericItem(*e, sEventMatureImage);
- }
- }
- }
- if (show_adult)
- {
- for (e = LLWorldMap::getInstance()->mAdultEvents.begin(); e != LLWorldMap::getInstance()->mAdultEvents.end(); ++e)
- {
- if (!e->mSelected)
- {
- drawGenericItem(*e, sEventAdultImage);
- }
- }
- }
- // Then the selected events
- if (show_pg)
- {
- for (e = LLWorldMap::getInstance()->mPGEvents.begin(); e != LLWorldMap::getInstance()->mPGEvents.end(); ++e)
+ LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAgentLocation().begin();
+ while (it != siminfo->getAgentLocation().end())
{
- if (e->mSelected)
- {
- drawGenericItem(*e, sEventImage);
- }
+ // Show Individual agents (or little stacks where real agents are)
+
+ // Here's how we'd choose the color if info.mID were available but it's not being sent:
+ // LLColor4 color = (agent_count == 1 && is_agent_friend(info.mID)) ? friend_color : avatar_color;
+ drawImageStack(it->getGlobalPosition(), sAvatarSmallImage, it->getCount(), 3.f, map_avatar_color);
+ ++it;
}
}
- if (show_mature)
- {
- for (e = LLWorldMap::getInstance()->mMatureEvents.begin(); e != LLWorldMap::getInstance()->mMatureEvents.end(); ++e)
- {
- if (e->mSelected)
- {
- drawGenericItem(*e, sEventMatureImage);
- }
- }
- }
- if (show_adult)
- {
- for (e = LLWorldMap::getInstance()->mAdultEvents.begin(); e != LLWorldMap::getInstance()->mAdultEvents.end(); ++e)
- {
- if (e->mSelected)
- {
- drawGenericItem(*e, sEventAdultImage);
- }
- }
- }
}
-
void LLWorldMapView::drawFrustum()
{
// Draw frustum
- F32 meters_to_pixels = gMapScale/ REGION_WIDTH_METERS;
+ F32 meters_to_pixels = sMapScale/ REGION_WIDTH_METERS;
F32 horiz_fov = LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect();
F32 far_clip_meters = LLViewerCamera::getInstance()->getFar();
@@ -1078,8 +918,8 @@ LLVector3 LLWorldMapView::globalPosToView( const LLVector3d& global_pos )
LLVector3 pos_local;
pos_local.setVec(relative_pos_global); // convert to floats from doubles
- pos_local.mV[VX] *= sPixelsPerMeter;
- pos_local.mV[VY] *= sPixelsPerMeter;
+ pos_local.mV[VX] *= sMapScale / REGION_WIDTH_METERS;
+ pos_local.mV[VY] *= sMapScale / REGION_WIDTH_METERS;
// leave Z component in meters
@@ -1163,7 +1003,7 @@ LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y )
LLVector3 pos_local( (F32)x, (F32)y, 0.f );
- pos_local *= ( REGION_WIDTH_METERS / gMapScale );
+ pos_local *= ( REGION_WIDTH_METERS / sMapScale );
LLVector3d pos_global;
pos_global.setVec( pos_local );
@@ -1184,23 +1024,20 @@ LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y )
BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask )
{
LLVector3d pos_global = viewPosToGlobal(x, y);
-
+ U64 handle = to_region_handle(pos_global);
std::string tooltip_msg;
- LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
+ LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle);
if (info)
{
LLViewerRegion *region = gAgent.getRegion();
- std::string message =
- llformat("%s (%s)",
- info->mName.c_str(),
- LLViewerRegion::accessToString(info->mAccess).c_str());
+ std::string message = llformat("%s (%s)", info->getName().c_str(), info->getAccessString().c_str());
- if (info->mAccess != SIM_ACCESS_DOWN)
+ if (!info->isDown())
{
- S32 agent_count = LLWorldMap::getInstance()->mNumAgents[info->mHandle];
- if (region && region->getHandle() == info->mHandle)
+ S32 agent_count = info->getAgentCount();
+ if (region && (region->getHandle() == handle))
{
++agent_count; // Bump by 1 if we're here
}
@@ -1209,6 +1046,8 @@ BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask )
// zoomed out, so don't display anything about the count. JC
if (agent_count > 0)
{
+ // Merov: i18n horror!!! Even using gettext(), concatenating strings is not localizable.
+ // The singular/plural switch form here under might make no sense in some languages. Don't do that.
message += llformat("\n%d ", agent_count);
if (agent_count == 1)
@@ -1224,7 +1063,7 @@ BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask )
tooltip_msg.assign( message );
// Optionally show region flags
- std::string region_flags = LLViewerRegion::regionFlagsToString(info->mRegionFlags);
+ std::string region_flags = info->getFlagsString();
if (!region_flags.empty())
{
@@ -1264,6 +1103,9 @@ static void drawDot(F32 x_pixels, F32 y_pixels,
}
else
{
+ // Draw V indicator for above or below
+ // *TODO: Replace this vector drawing with icons
+
F32 left = x_pixels - dot_radius;
F32 right = x_pixels + dot_radius;
F32 center = (left + right) * 0.5f;
@@ -1272,13 +1114,14 @@ static void drawDot(F32 x_pixels, F32 y_pixels,
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color4fv( color.mV );
- LLUI::setLineWidth(1.5f);
- F32 h_bar = relative_z > HEIGHT_THRESHOLD ? top : bottom; // horizontal bar Y
+ LLUI::setLineWidth(3.0f);
+ F32 point = relative_z > HEIGHT_THRESHOLD ? top : bottom; // Y pos of the point of the V
+ F32 back = relative_z > HEIGHT_THRESHOLD ? bottom : top; // Y pos of the ends of the V
gGL.begin( LLRender::LINES );
- gGL.vertex2f(center, top);
- gGL.vertex2f(left, h_bar);
- gGL.vertex2f(right, h_bar);
- gGL.vertex2f(right, bottom);
+ gGL.vertex2f(left, back);
+ gGL.vertex2f(center, point);
+ gGL.vertex2f(center, point);
+ gGL.vertex2f(right, back);
gGL.end();
LLUI::setLineWidth(1.0f);
}
@@ -1293,7 +1136,7 @@ void LLWorldMapView::drawAvatar(F32 x_pixels,
F32 dot_radius)
{
const F32 HEIGHT_THRESHOLD = 7.f;
- LLUIImagePtr dot_image = sAvatarSmallImage;
+ LLUIImagePtr dot_image = sAvatarLevelImage;
if(relative_z < -HEIGHT_THRESHOLD)
{
dot_image = sAvatarBelowImage;
@@ -1302,10 +1145,12 @@ void LLWorldMapView::drawAvatar(F32 x_pixels,
{
dot_image = sAvatarAboveImage;
}
- dot_image->draw(
- llround(x_pixels) - dot_image->getWidth()/2,
- llround(y_pixels) - dot_image->getHeight()/2,
- color);
+ S32 dot_width = llround(dot_radius * 2.f);
+ dot_image->draw(llround(x_pixels - dot_radius),
+ llround(y_pixels - dot_radius),
+ dot_width,
+ dot_width,
+ color);
}
// Pass relative Z of 0 to draw at same level.
@@ -1566,7 +1411,7 @@ void LLWorldMapView::reshape( S32 width, S32 height, BOOL called_from_parent )
bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track)
{
- LLVector3 pos_view = globalPosToView(item.mPosGlobal);
+ LLVector3 pos_view = globalPosToView(item.getGlobalPosition());
S32 item_x = llround(pos_view.mV[VX]);
S32 item_y = llround(pos_view.mV[VY]);
@@ -1575,12 +1420,12 @@ bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bo
if (y < item_y - BIG_DOT_RADIUS) return false;
if (y > item_y + BIG_DOT_RADIUS) return false;
- LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromHandle(item.mRegionHandle);
+ LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromHandle(item.getRegionHandle());
if (sim_info)
{
if (track)
{
- gFloaterWorldMap->trackLocation(item.mPosGlobal);
+ gFloaterWorldMap->trackLocation(item.getGlobalPosition());
}
}
@@ -1589,8 +1434,8 @@ bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bo
gFloaterWorldMap->trackGenericItem(item);
}
- item.mSelected = TRUE;
- *id = item.mID;
+// item.setSelected(true);
+ *id = item.getUUID();
return true;
}
@@ -1613,108 +1458,116 @@ void LLWorldMapView::handleClick(S32 x, S32 y, MASK mask,
*hit_type = 0; // hit nothing
- LLWorldMap::getInstance()->mIsTrackingUnknownLocation = FALSE;
- LLWorldMap::getInstance()->mIsTrackingDoubleClick = FALSE;
- LLWorldMap::getInstance()->mIsTrackingCommit = FALSE;
+ LLWorldMap::getInstance()->cancelTracking();
- LLWorldMap::item_info_list_t::iterator it;
-
- // clear old selected stuff
- for (it = LLWorldMap::getInstance()->mPGEvents.begin(); it != LLWorldMap::getInstance()->mPGEvents.end(); ++it)
- {
- (*it).mSelected = FALSE;
- }
- for (it = LLWorldMap::getInstance()->mMatureEvents.begin(); it != LLWorldMap::getInstance()->mMatureEvents.end(); ++it)
+ S32 level = LLWorldMipmap::scaleToLevel(sMapScale);
+ // If the zoom level is not too far out already, test hits
+ if (level <= DRAW_SIMINFO_THRESHOLD)
{
- (*it).mSelected = FALSE;
- }
- for (it = LLWorldMap::getInstance()->mAdultEvents.begin(); it != LLWorldMap::getInstance()->mAdultEvents.end(); ++it)
- {
- (*it).mSelected = FALSE;
- }
- for (it = LLWorldMap::getInstance()->mLandForSale.begin(); it != LLWorldMap::getInstance()->mLandForSale.end(); ++it)
- {
- (*it).mSelected = FALSE;
- }
-
- // Select event you clicked on
- if (gSavedSettings.getBOOL("MapShowEvents"))
- {
- for (it = LLWorldMap::getInstance()->mPGEvents.begin(); it != LLWorldMap::getInstance()->mPGEvents.end(); ++it)
- {
- LLItemInfo& event = *it;
-
- if (checkItemHit(x, y, event, id, false))
- {
- *hit_type = MAP_ITEM_PG_EVENT;
- mItemPicked = TRUE;
- gFloaterWorldMap->trackEvent(event);
- return;
- }
- }
- }
- if (gSavedSettings.getBOOL("ShowMatureEvents"))
- {
- for (it = LLWorldMap::getInstance()->mMatureEvents.begin(); it != LLWorldMap::getInstance()->mMatureEvents.end(); ++it)
- {
- LLItemInfo& event = *it;
-
- if (checkItemHit(x, y, event, id, false))
- {
- *hit_type = MAP_ITEM_MATURE_EVENT;
- mItemPicked = TRUE;
- gFloaterWorldMap->trackEvent(event);
- return;
- }
- }
- }
- if (gSavedSettings.getBOOL("ShowAdultEvents"))
- {
- for (it = LLWorldMap::getInstance()->mAdultEvents.begin(); it != LLWorldMap::getInstance()->mAdultEvents.end(); ++it)
- {
- LLItemInfo& event = *it;
-
- if (checkItemHit(x, y, event, id, false))
- {
- *hit_type = MAP_ITEM_ADULT_EVENT;
- mItemPicked = TRUE;
- gFloaterWorldMap->trackEvent(event);
- return;
- }
- }
- }
-
- if (gSavedSettings.getBOOL("MapShowLandForSale"))
- {
- for (it = LLWorldMap::getInstance()->mLandForSale.begin(); it != LLWorldMap::getInstance()->mLandForSale.end(); ++it)
- {
- LLItemInfo& land = *it;
+ bool show_mature = gAgent.canAccessMature() && gSavedSettings.getBOOL("ShowMatureEvents");
+ bool show_adult = gAgent.canAccessAdult() && gSavedSettings.getBOOL("ShowAdultEvents");
- if (checkItemHit(x, y, land, id, true))
- {
- *hit_type = MAP_ITEM_LAND_FOR_SALE;
- mItemPicked = TRUE;
- return;
- }
- }
-
- for (it = LLWorldMap::getInstance()->mLandForSaleAdult.begin(); it != LLWorldMap::getInstance()->mLandForSaleAdult.end(); ++it)
+ // Test hits if trackable data are displayed, otherwise, we don't even bother
+ if (gSavedSettings.getBOOL("MapShowEvents") || show_mature || show_adult || gSavedSettings.getBOOL("MapShowLandForSale"))
{
- LLItemInfo& land = *it;
-
- if (checkItemHit(x, y, land, id, true))
+ // Iterate through the visible regions
+ for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter)
{
- *hit_type = MAP_ITEM_LAND_FOR_SALE_ADULT;
- mItemPicked = TRUE;
- return;
+ U64 handle = *iter;
+ LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle);
+ if ((siminfo == NULL) || (siminfo->isDown()))
+ {
+ continue;
+ }
+ // If on screen check hits with the visible item lists
+ if (gSavedSettings.getBOOL("MapShowEvents"))
+ {
+ LLSimInfo::item_info_list_t::const_iterator it = siminfo->getPGEvent().begin();
+ while (it != siminfo->getPGEvent().end())
+ {
+ LLItemInfo event = *it;
+ if (checkItemHit(x, y, event, id, false))
+ {
+ *hit_type = MAP_ITEM_PG_EVENT;
+ mItemPicked = TRUE;
+ gFloaterWorldMap->trackEvent(event);
+ return;
+ }
+ ++it;
+ }
+ }
+ if (show_mature)
+ {
+ LLSimInfo::item_info_list_t::const_iterator it = siminfo->getMatureEvent().begin();
+ while (it != siminfo->getMatureEvent().end())
+ {
+ LLItemInfo event = *it;
+ if (checkItemHit(x, y, event, id, false))
+ {
+ *hit_type = MAP_ITEM_MATURE_EVENT;
+ mItemPicked = TRUE;
+ gFloaterWorldMap->trackEvent(event);
+ return;
+ }
+ ++it;
+ }
+ }
+ if (show_adult)
+ {
+ LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAdultEvent().begin();
+ while (it != siminfo->getAdultEvent().end())
+ {
+ LLItemInfo event = *it;
+ if (checkItemHit(x, y, event, id, false))
+ {
+ *hit_type = MAP_ITEM_ADULT_EVENT;
+ mItemPicked = TRUE;
+ gFloaterWorldMap->trackEvent(event);
+ return;
+ }
+ ++it;
+ }
+ }
+ if (gSavedSettings.getBOOL("MapShowLandForSale"))
+ {
+ LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSale().begin();
+ while (it != siminfo->getLandForSale().end())
+ {
+ LLItemInfo event = *it;
+ if (checkItemHit(x, y, event, id, true))
+ {
+ *hit_type = MAP_ITEM_LAND_FOR_SALE;
+ mItemPicked = TRUE;
+ return;
+ }
+ ++it;
+ }
+ // for 1.23, we're showing normal land and adult land in the same UI; you don't
+ // get a choice about which ones you want. If you're currently asking for adult
+ // content and land you'll get the adult land.
+ if (gAgent.canAccessAdult())
+ {
+ LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSaleAdult().begin();
+ while (it != siminfo->getLandForSaleAdult().end())
+ {
+ LLItemInfo event = *it;
+ if (checkItemHit(x, y, event, id, true))
+ {
+ *hit_type = MAP_ITEM_LAND_FOR_SALE_ADULT;
+ mItemPicked = TRUE;
+ return;
+ }
+ ++it;
+ }
+ }
+ }
}
}
}
- // If we get here, we haven't clicked on an icon
+ // If we get here, we haven't clicked on anything
gFloaterWorldMap->trackLocation(pos_global);
mItemPicked = FALSE;
-
*id = LLUUID::null;
return;
}
@@ -1775,59 +1628,36 @@ BOOL LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask )
return FALSE;
}
-U32 LLWorldMapView::updateBlock(S32 block_x, S32 block_y)
+void LLWorldMapView::updateVisibleBlocks()
{
- U32 blocks_requested = 0;
- S32 offset = block_x | (block_y * MAP_BLOCK_RES);
- if (!LLWorldMap::getInstance()->mMapBlockLoaded[LLWorldMap::getInstance()->mCurrentMap][offset])
+ if (LLWorldMipmap::scaleToLevel(sMapScale) > DRAW_SIMINFO_THRESHOLD)
{
-// llinfos << "Loading Block (" << block_x << "," << block_y << ")" << llendl;
- LLWorldMap::getInstance()->sendMapBlockRequest(block_x << 3, block_y << 3, (block_x << 3) + 7, (block_y << 3) + 7);
- LLWorldMap::getInstance()->mMapBlockLoaded[LLWorldMap::getInstance()->mCurrentMap][offset] = TRUE;
- blocks_requested++;
+ // If we're zoomed out too much, we just don't load all those sim info: too much!
+ return;
}
- return blocks_requested;
-}
-U32 LLWorldMapView::updateVisibleBlocks()
-{
- if (gMapScale < SIM_MAP_SCALE)
- {
- // We don't care what is loaded if we're zoomed out
- return 0;
- }
+ // Load the blocks visible in the current World Map view
+ // Get the World Map view coordinates and boundaries
LLVector3d camera_global = gAgent.getCameraPositionGlobal();
-
- F32 pixels_per_region = gMapScale;
const S32 width = getRect().getWidth();
const S32 height = getRect().getHeight();
- // Convert pan to sim coordinates
- S32 world_center_x_lo = S32(((-sPanX - width/2) / pixels_per_region) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
- S32 world_center_x_hi = S32(((-sPanX + width/2) / pixels_per_region) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
- S32 world_center_y_lo = S32(((-sPanY - height/2) / pixels_per_region) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
- S32 world_center_y_hi = S32(((-sPanY + height/2)/ pixels_per_region) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
-
- // Find the corresponding 8x8 block
- S32 world_block_x_lo = world_center_x_lo >> 3;
- S32 world_block_x_hi = world_center_x_hi >> 3;
- S32 world_block_y_lo = world_center_y_lo >> 3;
- S32 world_block_y_hi = world_center_y_hi >> 3;
-
- U32 blocks_requested = 0;
- const U32 max_blocks_requested = 9;
+ const F32 half_width = F32(width) / 2.0f;
+ const F32 half_height = F32(height) / 2.0f;
- for (S32 block_x = llmax(world_block_x_lo, 0); block_x <= llmin(world_block_x_hi, MAP_BLOCK_RES-1); ++block_x)
- {
- for (S32 block_y = llmax(world_block_y_lo, 0); block_y <= llmin(world_block_y_hi, MAP_BLOCK_RES-1); ++block_y)
- {
- blocks_requested += updateBlock(block_x, block_y);
- if (blocks_requested >= max_blocks_requested)
- return blocks_requested;
- }
- }
- return blocks_requested;
-}
+ // Compute center into sim grid coordinates
+ S32 world_center_x = S32((-sPanX / sMapScale) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
+ S32 world_center_y = S32((-sPanY / sMapScale) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
+
+ // Compute the boundaries into sim grid coordinates
+ S32 world_left = world_center_x - S32(half_width / sMapScale) - 1;
+ S32 world_right = world_center_x + S32(half_width / sMapScale) + 1;
+ S32 world_bottom = world_center_y - S32(half_height / sMapScale) - 1;
+ S32 world_top = world_center_y + S32(half_height / sMapScale) + 1;
+
+ //LL_INFOS("World Map") << "LLWorldMapView::updateVisibleBlocks() : sMapScale = " << sMapScale << ", left = " << world_left << ", right = " << world_right << ", bottom = " << world_bottom << ", top = " << world_top << LL_ENDL;
+ LLWorldMap::getInstance()->updateRegions(world_left, world_bottom, world_right, world_top);
+}
BOOL LLWorldMapView::handleHover( S32 x, S32 y, MASK mask )
{
@@ -1916,16 +1746,16 @@ BOOL LLWorldMapView::handleDoubleClick( S32 x, S32 y, MASK mask )
}
default:
{
- if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation)
+ if (LLWorldMap::getInstance()->isTracking())
{
- LLWorldMap::getInstance()->mIsTrackingDoubleClick = TRUE;
+ LLWorldMap::getInstance()->setTrackingDoubleClick();
}
else
{
// Teleport if we got a valid location
LLVector3d pos_global = viewPosToGlobal(x,y);
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
- if (sim_info && sim_info->mAccess != SIM_ACCESS_DOWN)
+ if (sim_info && !sim_info->isDown())
{
gAgent.teleportViaLocation( pos_global );
}
diff --git a/indra/newview/llworldmapview.h b/indra/newview/llworldmapview.h
index 66793f0101..9eecacb2d8 100644
--- a/indra/newview/llworldmapview.h
+++ b/indra/newview/llworldmapview.h
@@ -30,27 +30,23 @@
* $/LicenseInfo$
*/
-// Global map of the world.
+// View of the global map of the world
+
+// The data (model) for the global map (a singleton, unique to the application instance) is
+// in LLWorldMap and is typically accessed using LLWorldMap::getInstance()
#ifndef LL_LLWORLDMAPVIEW_H
#define LL_LLWORLDMAPVIEW_H
#include "llpanel.h"
-#include "v3math.h"
-#include "v3dmath.h"
-#include "v4color.h"
-#include "llviewertexture.h"
-#include "llmapimagetype.h"
#include "llworldmap.h"
-
-class LLItemInfo;
+#include "v4color.h"
const S32 DEFAULT_TRACKING_ARROW_SIZE = 16;
-class LLColor4;
-class LLColor4U;
-class LLCoordGL;
-class LLViewerTexture;
+class LLUUID;
+class LLVector3d;
+class LLVector3;
class LLTextBox;
@@ -77,22 +73,26 @@ public:
bool checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track);
void handleClick(S32 x, S32 y, MASK mask, S32* hit_type, LLUUID* id);
- // Scale and pan are shared across all instances.
+ // Scale and pan are shared across all instances! (i.e. Terrain and Objects maps are always registered)
static void setScale( F32 scale );
static void translatePan( S32 delta_x, S32 delta_y );
static void setPan( S32 x, S32 y, BOOL snap = TRUE );
+ // Return true if the current scale level is above the threshold for accessing region info
+ static bool showRegionInfo();
LLVector3 globalPosToView(const LLVector3d& global_pos);
LLVector3d viewPosToGlobal(S32 x,S32 y);
virtual void draw();
- void drawGenericItems(const LLWorldMap::item_info_list_t& items, LLUIImagePtr image);
+ void drawGenericItems(const LLSimInfo::item_info_list_t& items, LLUIImagePtr image);
void drawGenericItem(const LLItemInfo& item, LLUIImagePtr image);
void drawImage(const LLVector3d& global_pos, LLUIImagePtr image, const LLColor4& color = LLColor4::white);
void drawImageStack(const LLVector3d& global_pos, LLUIImagePtr image, U32 count, F32 offset, const LLColor4& color);
void drawAgents();
- void drawEvents();
+ void drawItems();
void drawFrustum();
+ void drawMipmap(S32 width, S32 height);
+ bool drawMipmapLevel(S32 width, S32 height, S32 level, bool load = true);
static void cleanupTextures();
@@ -108,7 +108,7 @@ public:
F32 y_pixels,
const LLColor4& color,
F32 relative_z = 0.f,
- F32 dot_radius = 3.f);
+ F32 dot_radius = 5.f);
static void drawTrackingCircle( const LLRect& rect, S32 x, S32 y,
const LLColor4& color,
@@ -129,9 +129,7 @@ public:
static void clearLastClick() { sHandledLastClick = FALSE; }
// if the view changes, download additional sim info as needed
- // return value is number of blocks newly requested.
- U32 updateBlock(S32 block_x, S32 block_y);
- U32 updateVisibleBlocks();
+ void updateVisibleBlocks();
protected:
void setDirectionPos( LLTextBox* text_box, F32 rotation );
@@ -140,11 +138,13 @@ protected:
public:
LLColor4 mBackgroundColor;
- static LLUIImagePtr sAvatarYouSmallImage;
static LLUIImagePtr sAvatarSmallImage;
- static LLUIImagePtr sAvatarLargeImage;
+ static LLUIImagePtr sAvatarYouImage;
+ static LLUIImagePtr sAvatarYouLargeImage;
+ static LLUIImagePtr sAvatarLevelImage;
static LLUIImagePtr sAvatarAboveImage;
static LLUIImagePtr sAvatarBelowImage;
+
static LLUIImagePtr sTelehubImage;
static LLUIImagePtr sInfohubImage;
static LLUIImagePtr sHomeImage;
@@ -157,9 +157,7 @@ public:
static LLUIImagePtr sForSaleImage;
static LLUIImagePtr sForSaleAdultImage;
- static F32 sThresholdA;
- static F32 sThresholdB;
- static F32 sPixelsPerMeter; // world meters to map pixels
+ static F32 sMapScale; // scale = size of a region in pixels
BOOL mItemPicked;
@@ -169,6 +167,7 @@ public:
static F32 sTargetPanY; // in pixels
static S32 sTrackingArrowX;
static S32 sTrackingArrowY;
+ static bool sVisibleTilesLoaded;
// Are we mid-pan from a user drag?
BOOL mPanning;
@@ -191,10 +190,14 @@ public:
static BOOL sHandledLastClick;
S32 mSelectIDStart;
+ // Keep the list of regions that are displayed on screen. Avoids iterating through the whole region map after draw().
typedef std::vector<U64> handle_list_t;
handle_list_t mVisibleRegions; // set every frame
static std::map<std::string,std::string> sStringsMap;
+
+private:
+ void drawTileOutline(S32 level, F32 top, F32 left, F32 bottom, F32 right);
};
#endif
diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp
new file mode 100644
index 0000000000..8d3165b98c
--- /dev/null
+++ b/indra/newview/llworldmipmap.cpp
@@ -0,0 +1,275 @@
+/**
+ * @file llworldmipmap.cpp
+ * @brief Data storage for the S3 mipmap of the entire world.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ *
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llworldmipmap.h"
+
+#include "llviewertexturelist.h"
+#include "math.h" // log()
+
+// Turn this on to output tile stats in the standard output
+#define DEBUG_TILES_STAT 0
+
+LLWorldMipmap::LLWorldMipmap() :
+ mCurrentLevel(0)
+{
+}
+
+LLWorldMipmap::~LLWorldMipmap()
+{
+ reset();
+}
+
+// Delete all sublevel maps and clean them
+void LLWorldMipmap::reset()
+{
+ for (int level = 0; level < MAP_LEVELS; level++)
+ {
+ mWorldObjectsMipMap[level].clear();
+ }
+}
+
+// This method should be called before each use of the mipmap (typically, before each draw), so that to let
+// the boost level of unused tiles to drop to 0 (BOOST_NONE).
+// Tiles that are accessed have had their boost level pushed to BOOST_MAP_VISIBLE so we can identify them.
+// The result of this strategy is that if a tile is not used during 2 consecutive loops, its boost level drops to 0.
+void LLWorldMipmap::equalizeBoostLevels()
+{
+#if DEBUG_TILES_STAT
+ S32 nb_missing = 0;
+ S32 nb_tiles = 0;
+ S32 nb_visible = 0;
+#endif // DEBUG_TILES_STAT
+ // For each level
+ for (S32 level = 0; level < MAP_LEVELS; level++)
+ {
+ sublevel_tiles_t& level_mipmap = mWorldObjectsMipMap[level];
+ // For each tile
+ for (sublevel_tiles_t::iterator iter = level_mipmap.begin(); iter != level_mipmap.end(); iter++)
+ {
+ LLPointer<LLViewerFetchedTexture> img = iter->second;
+ S32 current_boost_level = img->getBoostLevel();
+ if (current_boost_level == LLViewerTexture::BOOST_MAP_VISIBLE)
+ {
+ // If level was BOOST_MAP_VISIBLE, the tile has been used in the last draw so keep it high
+ img->setBoostLevel(LLViewerTexture::BOOST_MAP);
+ }
+ else
+ {
+ // If level was BOOST_MAP only (or anything else...), the tile wasn't used in the last draw
+ // so we drop its boost level to BOOST_NONE.
+ img->setBoostLevel(LLViewerTexture::BOOST_NONE);
+ }
+#if DEBUG_TILES_STAT
+ // Increment some stats if compile option on
+ nb_tiles++;
+ if (current_boost_level == LLViewerTexture::BOOST_MAP_VISIBLE)
+ {
+ nb_visible++;
+ }
+ if (img->isMissingAsset())
+ {
+ nb_missing++;
+ }
+#endif // DEBUG_TILES_STAT
+ }
+ }
+#if DEBUG_TILES_STAT
+ LL_INFOS("World Map") << "LLWorldMipmap tile stats : total requested = " << nb_tiles << ", visible = " << nb_visible << ", missing = " << nb_missing << LL_ENDL;
+#endif // DEBUG_TILES_STAT
+}
+
+// This method should be used when the mipmap is not actively used for a while, e.g., the map UI is hidden
+void LLWorldMipmap::dropBoostLevels()
+{
+ // For each level
+ for (S32 level = 0; level < MAP_LEVELS; level++)
+ {
+ sublevel_tiles_t& level_mipmap = mWorldObjectsMipMap[level];
+ // For each tile
+ for (sublevel_tiles_t::iterator iter = level_mipmap.begin(); iter != level_mipmap.end(); iter++)
+ {
+ LLPointer<LLViewerFetchedTexture> img = iter->second;
+ img->setBoostLevel(LLViewerTexture::BOOST_NONE);
+ }
+ }
+}
+
+LLPointer<LLViewerFetchedTexture> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load)
+{
+ // Check the input data
+ llassert(level <= MAP_LEVELS);
+ llassert(level >= 1);
+
+ // If the *loading* level changed, cleared the new level from "missed" tiles
+ // so that we get a chance to reload them
+ if (load && (level != mCurrentLevel))
+ {
+ cleanMissedTilesFromLevel(level);
+ mCurrentLevel = level;
+ }
+
+ // Build the region handle
+ U64 handle = convertGridToHandle(grid_x, grid_y);
+
+ // Check if the image is around already
+ sublevel_tiles_t& level_mipmap = mWorldObjectsMipMap[level-1];
+ sublevel_tiles_t::iterator found = level_mipmap.find(handle);
+
+ // If not there and load on, go load it
+ if (found == level_mipmap.end())
+ {
+ if (load)
+ {
+ // Load it
+ LLPointer<LLViewerFetchedTexture> img = loadObjectsTile(grid_x, grid_y, level);
+ // Insert the image in the map
+ level_mipmap.insert(sublevel_tiles_t::value_type( handle, img ));
+ // Find the element again in the map (it's there now...)
+ found = level_mipmap.find(handle);
+ }
+ else
+ {
+ // Return with NULL if not found and we're not trying to load
+ return NULL;
+ }
+ }
+
+ // Get the image pointer and check if this asset is missing
+ LLPointer<LLViewerFetchedTexture> img = found->second;
+ if (img->isMissingAsset())
+ {
+ // Return NULL if asset missing
+ return NULL;
+ }
+ else
+ {
+ // Boost the tile level so to mark it's in use *if* load on
+ if (load)
+ {
+ img->setBoostLevel(LLViewerTexture::BOOST_MAP_VISIBLE);
+ }
+ return img;
+ }
+}
+
+LLPointer<LLViewerFetchedTexture> LLWorldMipmap::loadObjectsTile(U32 grid_x, U32 grid_y, S32 level)
+{
+ // Get the grid coordinates
+// std::string imageurl = llformat("http://map.secondlife.com.s3.amazonaws.com/%d/%05d/%05d/map-%d-%d-%d-objects.jpg",
+ std::string imageurl = llformat("http://map.secondlife.com.s3.amazonaws.com/map-%d-%d-%d-objects.jpg",
+ level, grid_x, grid_y, level, grid_x, grid_y);
+
+ // DO NOT COMMIT!! DEBUG ONLY!!!
+ // Use a local jpeg for every tile to test map speed without S3 access
+ //imageurl = "file://C:\\Develop\\mapserver-distribute-3\\indra\\build-vc80\\mapserver\\relwithdebinfo\\regions\\00995\\01001\\region-995-1001-prims.jpg";
+ // END DEBUG
+ //LL_INFOS("World Map") << "LLWorldMipmap::loadObjectsTile(), URL = " << imageurl << LL_ENDL;
+
+ LLPointer<LLViewerFetchedTexture> img = LLViewerTextureManager::getFetchedTextureFromUrl(imageurl, TRUE, FALSE, LLViewerTexture::LOD_TEXTURE);
+ img->setBoostLevel(LLViewerTexture::BOOST_MAP);
+
+ // Return the smart pointer
+ return img;
+}
+
+// This method is used to clean up a level from tiles marked as "missing".
+// The idea is to allow tiles that have been improperly marked missing to be reloaded when retraversing the level again.
+// When zooming in and out rapidly, some tiles are never properly loaded and, eventually marked missing.
+// This creates "blue" areas in a subresolution that never got a chance to reload if we don't clean up the level.
+void LLWorldMipmap::cleanMissedTilesFromLevel(S32 level)
+{
+ // Check the input data
+ llassert(level <= MAP_LEVELS);
+ llassert(level >= 0);
+
+ // This happens when the object is first initialized
+ if (level == 0)
+ {
+ return;
+ }
+
+ // Iterate through the subresolution level and suppress the tiles that are marked as missing
+ // Note: erasing in a map while iterating through it is bug prone. Using a postfix increment is mandatory here.
+ sublevel_tiles_t& level_mipmap = mWorldObjectsMipMap[level-1];
+ sublevel_tiles_t::iterator it = level_mipmap.begin();
+ while (it != level_mipmap.end())
+ {
+ LLPointer<LLViewerFetchedTexture> img = it->second;
+ if (img->isMissingAsset())
+ {
+ level_mipmap.erase(it++);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ return;
+}
+
+// static methods
+// Compute the level in the world mipmap (between 1 and MAP_LEVELS, as in the URL) given the scale (size of a sim in screen pixels)
+S32 LLWorldMipmap::scaleToLevel(F32 scale)
+{
+ // If scale really small, picks up the higest level there is (lowest resolution)
+ if (scale <= F32_MIN)
+ return MAP_LEVELS;
+ // Compute the power of two resolution level knowing the base level
+ S32 level = llfloor((log(REGION_WIDTH_METERS/scale)/log(2.0f)) + 1.0f);
+ // Check bounds and return the value
+ if (level > MAP_LEVELS)
+ return MAP_LEVELS;
+ else if (level < 1)
+ return 1;
+ else
+ return level;
+}
+
+// Convert world coordinates to mipmap grid coordinates at a given level (between 1 and MAP_LEVELS)
+void LLWorldMipmap::globalToMipmap(F64 global_x, F64 global_y, S32 level, U32* grid_x, U32* grid_y)
+{
+ // Check the input data
+ llassert(level <= MAP_LEVELS);
+ llassert(level >= 1);
+
+ // Convert world coordinates into grid coordinates
+ *grid_x = lltrunc(global_x/REGION_WIDTH_METERS);
+ *grid_y = lltrunc(global_y/REGION_WIDTH_METERS);
+ // Compute the valid grid coordinates at that level of the mipmap
+ S32 regions_in_tile = 1 << (level - 1);
+ *grid_x = *grid_x - (*grid_x % regions_in_tile);
+ *grid_y = *grid_y - (*grid_y % regions_in_tile);
+}
+
+
diff --git a/indra/newview/llworldmipmap.h b/indra/newview/llworldmipmap.h
new file mode 100644
index 0000000000..ecf1003468
--- /dev/null
+++ b/indra/newview/llworldmipmap.h
@@ -0,0 +1,100 @@
+/**
+ * @file llworldmipmap.h
+ * @brief Data storage for the S3 mipmap of the entire world.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ *
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLWORLDMIPMAP_H
+#define LL_LLWORLDMIPMAP_H
+
+#include <map>
+
+#include "llmemory.h" // LLPointer
+#include "indra_constants.h" // REGION_WIDTH_UNITS
+#include "llregionhandle.h" // to_region_handle()
+
+class LLViewerFetchedTexture;
+
+// LLWorldMipmap : Mipmap handling of all the tiles used to render the world at any resolution.
+// This class provides a clean structured access to the hierarchy of tiles stored in the
+// Amazon S3 repository and abstracts its directory/file structure.
+// The interface of this class though still assumes that the caller knows the general level/tiles
+// structure (at least, that it exists...) but doesn't requite the caller to know the details of it.
+// IOW, you need to know that rendering levels exists as well as grid coordinates for regions,
+// but you can ignore where those tiles are located, how to get them, etc...
+// The class API gives you back LLPointer<LLViewerFetchedTexture> per tile.
+
+// See llworldmipmapview.cpp for the implementation of a class who knows how to render an LLWorldMipmap.
+
+// Implementation notes:
+// - On the S3 servers, the tiles are rendered in 2 flavors: Objects and Terrain.
+// - For the moment, LLWorldMipmap implements access only to the Objects tiles.
+class LLWorldMipmap
+{
+public:
+ // Parameters of the mipmap
+ static const S32 MAP_LEVELS = 8; // Number of subresolution levels computed by the mapserver
+ static const S32 MAP_TILE_SIZE = 256; // Width in pixels of the tiles computed by the mapserver
+
+ LLWorldMipmap();
+ ~LLWorldMipmap();
+
+ // Clear up the maps and release all image handles
+ void reset();
+ // Manage the boost levels between loops (typically draw() loops)
+ void equalizeBoostLevels();
+ // Drop the boost levels to none (used when hiding the map)
+ void dropBoostLevels();
+ // Get the tile smart pointer, does the loading if necessary
+ LLPointer<LLViewerFetchedTexture> getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load = true);
+
+ // Helper functions: those are here as they depend solely on the topology of the mipmap though they don't access it
+ // Convert sim scale (given in sim width in display pixels) into a mipmap level
+ static S32 scaleToLevel(F32 scale);
+ // Convert world coordinates to mipmap grid coordinates at a given level
+ static void globalToMipmap(F64 global_x, F64 global_y, S32 level, U32* grid_x, U32* grid_y);
+
+private:
+ // Get a handle (key) from grid coordinates
+ U64 convertGridToHandle(U32 grid_x, U32 grid_y) { return to_region_handle(grid_x * REGION_WIDTH_UNITS, grid_y * REGION_WIDTH_UNITS); }
+ // Load the relevant tile from S3
+ LLPointer<LLViewerFetchedTexture> loadObjectsTile(U32 grid_x, U32 grid_y, S32 level);
+ // Clear a level from its "missing" tiles
+ void cleanMissedTilesFromLevel(S32 level);
+
+ // The mipmap is organized by resolution level (MAP_LEVELS of them). Each resolution level is an std::map
+ // using a region_handle as a key and storing a smart pointer to the image as a value.
+ typedef std::map<U64, LLPointer<LLViewerFetchedTexture> > sublevel_tiles_t;
+ sublevel_tiles_t mWorldObjectsMipMap[MAP_LEVELS];
+// sublevel_tiles_t mWorldTerrainMipMap[MAP_LEVELS];
+
+ S32 mCurrentLevel; // The level last accessed by a getObjectsTile()
+};
+
+#endif // LL_LLWORLDMIPMAP_H
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 3390463aa6..2daaf77772 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -1464,6 +1464,7 @@ F32 LLPipeline::calcPixelArea(LLVector3 center, LLVector3 size, LLCamera &camera
F32 dist = lookAt.length();
//ramp down distance for nearby objects
+ //shrink dist by dist/16.
if (dist < 16.f)
{
dist /= 16.f;
@@ -3002,15 +3003,6 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
LLAppViewer::instance()->pingMainloopTimeout("Pipeline:ForceVBO");
- //by bao
- //fake vertex buffer updating
- //to guaranttee at least updating one VBO buffer every frame
- //to walk around the bug caused by ATI card --> DEV-3855
- //
- if(forceVBOUpdate)
- gSky.mVOSkyp->updateDummyVertexBuffer() ;
-
-
// Initialize lots of GL state to "safe" values
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
diff --git a/indra/newview/res/viewerRes.rc b/indra/newview/res/viewerRes.rc
index 433070ce34..38291e45c9 100644
--- a/indra/newview/res/viewerRes.rc
+++ b/indra/newview/res/viewerRes.rc
@@ -134,8 +134,8 @@ TOOLMEDIAOPEN CURSOR "toolmediaopen.cur"
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,0,0,3256
- PRODUCTVERSION 2,0,0,3256
+ FILEVERSION 2,0,0,200030
+ PRODUCTVERSION 2,0,0,200030
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -152,12 +152,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Linden Lab"
VALUE "FileDescription", "Second Life"
- VALUE "FileVersion", "2.0.0.3256"
+ VALUE "FileVersion", "2.0.0.200030"
VALUE "InternalName", "Second Life"
VALUE "LegalCopyright", "Copyright © 2001-2008, Linden Research, Inc."
VALUE "OriginalFilename", "SecondLife.exe"
VALUE "ProductName", "Second Life"
- VALUE "ProductVersion", "2.0.0.3256"
+ VALUE "ProductVersion", "2.0.0.200030"
END
END
BLOCK "VarFileInfo"
diff --git a/indra/newview/skins/default/textures/Blank.png b/indra/newview/skins/default/textures/Blank.png
new file mode 100644
index 0000000000..f38e9f9100
--- /dev/null
+++ b/indra/newview/skins/default/textures/Blank.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/default_profile_picture.j2c b/indra/newview/skins/default/textures/default_profile_picture.j2c
index c53a22e816..f21742cf09 100644
--- a/indra/newview/skins/default/textures/default_profile_picture.j2c
+++ b/indra/newview/skins/default/textures/default_profile_picture.j2c
Binary files differ
diff --git a/indra/newview/skins/default/textures/map_avatar_32.tga b/indra/newview/skins/default/textures/map_avatar_32.tga
new file mode 100644
index 0000000000..aebeab4093
--- /dev/null
+++ b/indra/newview/skins/default/textures/map_avatar_32.tga
Binary files differ
diff --git a/indra/newview/skins/default/textures/map_avatar_above_32.tga b/indra/newview/skins/default/textures/map_avatar_above_32.tga
new file mode 100644
index 0000000000..65bd0561a7
--- /dev/null
+++ b/indra/newview/skins/default/textures/map_avatar_above_32.tga
Binary files differ
diff --git a/indra/newview/skins/default/textures/map_avatar_below_32.tga b/indra/newview/skins/default/textures/map_avatar_below_32.tga
new file mode 100644
index 0000000000..496c44b369
--- /dev/null
+++ b/indra/newview/skins/default/textures/map_avatar_below_32.tga
Binary files differ
diff --git a/indra/newview/skins/default/textures/map_avatar_you_32.tga b/indra/newview/skins/default/textures/map_avatar_you_32.tga
new file mode 100644
index 0000000000..782207efd6
--- /dev/null
+++ b/indra/newview/skins/default/textures/map_avatar_you_32.tga
Binary files differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index cf7ab46dd5..7f33330123 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -27,6 +27,8 @@
<texture name="BackArrow_Off" file_name="icons/BackArrow_Off.png" preload="false" />
<texture name="BackArrow_Press" file_name="icons/BackArrow_Press.png" preload="false" />
+ <texture name="Blank" file_name="Blank.png" preload="false" />
+
<texture name="BottomTray_BG" file_name="bottomtray/BottomTray_BG.png" preload="false" />
<texture name="BuyArrow_Off" file_name="navbar/BuyArrow_Off.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0" />
diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml
index c74b1929b4..2ff99dcf5a 100644
--- a/indra/newview/skins/default/xui/en/floater_about.xml
+++ b/indra/newview/skins/default/xui/en/floater_about.xml
@@ -69,7 +69,7 @@ Packets Lost: [PACKETS_LOST,number,0]/[PACKETS_IN,number,0] ([PACKETS_PCT,number
tab_position="top">
<panel
border="true"
- label="Support"
+ label="Info"
help_topic="about_support_tab"
name="support_panel">
<text_editor
diff --git a/indra/newview/skins/default/xui/en/floater_media_settings.xml b/indra/newview/skins/default/xui/en/floater_media_settings.xml
index 6b884d46a7..68dd2001af 100644
--- a/indra/newview/skins/default/xui/en/floater_media_settings.xml
+++ b/indra/newview/skins/default/xui/en/floater_media_settings.xml
@@ -15,7 +15,7 @@
min_height="430"
min_width="620"
mouse_opaque="true"
- name="Medis Settings"
+ name="Media Settings"
help_topic = "media_settings"
title="MEDIA SETTINGS">
<button
diff --git a/indra/newview/skins/default/xui/en/floater_test_textbox.xml b/indra/newview/skins/default/xui/en/floater_test_textbox.xml
index 9e2e9e74e3..8dba05f1ee 100644
--- a/indra/newview/skins/default/xui/en/floater_test_textbox.xml
+++ b/indra/newview/skins/default/xui/en/floater_test_textbox.xml
@@ -2,11 +2,11 @@
<floater
legacy_header_height="18"
can_resize="true"
- height="400"
+ height="450"
layout="topleft"
name="floater_test_textbox"
help_topic="floater_test_textbox"
- width="400">
+ width="600">
<text
type="string"
length="1"
@@ -48,61 +48,80 @@
First line of multiple lines
Second line of multiple lines
</text>
+ <text
+ top_pad="10"
+ left="10"
+ right="-10"
+ follows="top|left"
+ font.name="SansSerifSmall"
+ name="test_text10"
+ tool_tip="text">
+ SansSerifSmall. РуÑÑкий 中文 (简体) The quick brown fox jumped over the lazy dog.
+ </text>
+ <text
+ top_pad="10"
+ left="10"
+ right="-10"
+ follows="top|left"
+ font.name="SansSerif"
+ name="test_text11"
+ tool_tip="text">
+ SansSerif. РуÑÑкий 中文 (简体) The quick brown fox jumped over the lazy dog.
+ </text>
+ <text
+ top_pad="10"
+ left="10"
+ right="-10"
+ follows="top|left"
+ font.name="SansSerifLarge"
+ name="test_text12"
+ tool_tip="text">
+ SansSerifLarge. РуÑÑкий 中文 (简体) The quick brown fox jumped over the lazy dog.
+ </text>
+ <text
+ top_pad="10"
+ left="10"
+ right="-10"
+ follows="top|left"
+ font.name="SansSerifHuge"
+ name="test_text13"
+ tool_tip="text">
+ SansSerifHuge. РуÑÑкий 中文 (简体) The quick brown fox jumped over the lazy dog.
+ </text>
<text
type="string"
length="1"
font="SansSerif"
- height="10"
- layout="topleft"
- left_delta="0"
- top_pad="30"
- width="300">
- font SansSerif
- </text>
- <text
- type="string"
- length="1"
- follows="bottom|right"
- height="10"
- layout="topleft"
- left_delta="0"
- top_pad="10"
- width="300">
- follows bottom right
- </text>
- <text
- type="string"
- length="1"
- font="SansSerifSmall"
font.style="BOLD"
height="10"
layout="topleft"
left_delta="0"
- top_pad="10"
+ top_pad="40"
width="300">
- font style BOLD
+ SansSerif BOLD
</text>
<text
type="string"
length="1"
- font="SansSerifSmall"
+ font="SansSerif"
font.style="BOLD|UNDERLINE"
height="10"
layout="topleft"
left_delta="0"
top_pad="10"
width="300">
- font style BOLD UNDERLINE
+ SansSerif BOLD UNDERLINE
</text>
<text
type="string"
length="1"
+ font="SansSerif"
height="10"
layout="topleft"
left_delta="0"
top_pad="10"
width="300">
- font style UNDERLINE
+ SansSerif UNDERLINE
</text>
<text
type="string"
diff --git a/indra/newview/skins/default/xui/en/floater_test_widgets.xml b/indra/newview/skins/default/xui/en/floater_test_widgets.xml
index 718d724aa6..44dcb07526 100644
--- a/indra/newview/skins/default/xui/en/floater_test_widgets.xml
+++ b/indra/newview/skins/default/xui/en/floater_test_widgets.xml
@@ -338,13 +338,13 @@ line to actually fit
height="400"
halign="center"
layout="topleft"
- left="575"
+ left="525"
name="group_tab_container"
tab_position="top"
tab_height="20"
tool_tip="tab container"
top="80"
- width="250">
+ width="300">
<!-- "panel" is a container for widgets. It is automatically resized to
fit the parent tab_container. -->
<panel
@@ -380,6 +380,46 @@ line to actually fit
tool_tip="Color Swatch: Click to open Color Picker"
top_delta="0"
width="80" />
+ <text
+ top_pad="10"
+ left="10"
+ width="250"
+ follows="top|left"
+ font.name="SansSerifSmall"
+ name="test_text10"
+ tool_tip="text">
+ SansSerifSmall. РуÑÑкий 中文 (简体)
+ </text>
+ <text
+ top_pad="10"
+ left="10"
+ width="250"
+ follows="top|left"
+ font.name="SansSerif"
+ name="test_text11"
+ tool_tip="text">
+ SansSerif. РуÑÑкий 中文 (简体)
+ </text>
+ <text
+ top_pad="10"
+ left="10"
+ width="250"
+ follows="top|left"
+ font.name="SansSerifLarge"
+ name="test_text12"
+ tool_tip="text">
+ SansSerifLarge. РуÑÑкий 中文 (简体)
+ </text>
+ <text
+ top_pad="10"
+ left="10"
+ width="250"
+ follows="top|left"
+ font.name="SansSerifHuge"
+ name="test_text13"
+ tool_tip="text">
+ SansSerifHuge. РуÑÑкий 中文 (简体)
+ </text>
</panel>
<!-- panels can also refer to other floaters or panels -->
<panel
diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml
index f5ae611f93..387ffbd5b3 100644
--- a/indra/newview/skins/default/xui/en/floater_world_map.xml
+++ b/indra/newview/skins/default/xui/en/floater_world_map.xml
@@ -15,38 +15,15 @@
single_instance="true"
title="WORLD MAP"
width="800">
- <tab_container
+ <panel
+ filename="panel_world_map.xml"
follows="left|top|right|bottom"
height="565"
layout="topleft"
left="15"
- name="maptab"
- tab_position="top"
+ name="objects_mapview"
top="25"
- width="542">
- <panel
- filename="panel_world_map.xml"
- follows="left|top|right|bottom"
- height="550"
- label="Objects"
- layout="topleft"
- left="1"
- help_topic="worldmap_objects_tab"
- name="objects_mapview"
- top="19"
- width="540" />
- <panel
- filename="panel_world_map.xml"
- follows="left|top|right|bottom"
- height="550"
- label="Terrain"
- layout="topleft"
- left_delta="0"
- help_topic="worldmap_terrain_tab"
- name="terrain_mapview"
- top_delta="3"
- width="540" />
- </tab_container>
+ width="542" />
<icon
follows="top|right"
height="16"
@@ -210,7 +187,7 @@
label="Telehub"
layout="topleft"
left_pad="4"
- name="telehubchk"
+ name="telehub_chk"
top_delta="0"
width="110" />
<icon
diff --git a/indra/newview/skins/default/xui/en/fonts.xml b/indra/newview/skins/default/xui/en/fonts.xml
index 65dfb13f4a..f3da62a896 100644
--- a/indra/newview/skins/default/xui/en/fonts.xml
+++ b/indra/newview/skins/default/xui/en/fonts.xml
@@ -150,11 +150,9 @@
comment="Size of large font (points, or 1/72 of an inch)"
size="10.0"
/>
- <!-- Changed Medium size to address menu font size not matching spec.
- May want to revert/update after menu font configuration has been enabled. -->
<font_size name="Medium"
comment="Size of medium font (points, or 1/72 of an inch)"
- size="8.0"
+ size="9.0"
/>
<font_size name="Small"
comment="Size of small font (points, or 1/72 of an inch)"
diff --git a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml
index 8ee67b9a02..590621062c 100644
--- a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml
@@ -91,6 +91,8 @@
name="zoom_in">
<menu_item_call.on_click
function="InspectAvatar.ZoomIn"/>
+ <menu_item_call.on_visible
+ function="InspectAvatar.VisibleZoomIn"/>
</menu_item_call>
<menu_item_call
label="Pay"
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index 62940b87dc..8b6ab4e4d8 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -553,6 +553,16 @@
<menu_item_separator
layout="topleft" />
<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_separator
+ layout="topleft" />
+ <menu_item_call
label="Detach From Yourself"
layout="topleft"
name="Detach From Yourself">
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_add.xml b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
new file mode 100644
index 0000000000..b07a8bb512
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<menu
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="menu_inventory_add"
+ visible="false">
+ <menu
+ create_jump_keys="true"
+ label="Upload"
+ layout="topleft"
+ name="upload"
+ tear_off="true">
+ <menu_item_call
+ label="Image (L$[COST])..."
+ layout="topleft"
+ name="Upload Image"
+ shortcut="control|U">
+ <menu_item_call.on_click
+ function="File.UploadImage"
+ parameter="" />
+ <menu_item_call.on_enable
+ function="File.EnableUpload" />
+ </menu_item_call>
+ <menu_item_call
+ label="Sound (L$[COST])..."
+ layout="topleft"
+ name="Upload Sound">
+ <menu_item_call.on_click
+ function="File.UploadSound"
+ parameter="" />
+ <menu_item_call.on_enable
+ function="File.EnableUpload" />
+ </menu_item_call>
+ <menu_item_call
+ label="Animation (L$[COST])..."
+ layout="topleft"
+ name="Upload Animation">
+ <menu_item_call.on_click
+ function="File.UploadAnim"
+ parameter="" />
+ <menu_item_call.on_enable
+ function="File.EnableUpload" />
+ </menu_item_call>
+ <menu_item_call
+ label="Bulk (L$[COST] per file)..."
+ layout="topleft"
+ name="Bulk Upload">
+ <menu_item_call.on_click
+ function="File.UploadBulk"
+ parameter="" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft" />
+ </menu>
+
+ <menu_item_call
+ label="New Folder"
+ layout="topleft"
+ name="New Folder">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="category" />
+ </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 Note"
+ 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
+ height="175"
+ label="New Clothes"
+ layout="topleft"
+ left_delta="0"
+ mouse_opaque="false"
+ name="New Clothes"
+ top_pad="514"
+ width="125">
+ <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"
+ layout="topleft"
+ name="New Alpha">
+ <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>
+ <menu
+ height="85"
+ label="New Body Parts"
+ layout="topleft"
+ left_delta="0"
+ mouse_opaque="false"
+ name="New Body Parts"
+ top_pad="514"
+ width="118">
+ <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> \ No newline at end of file
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
new file mode 100644
index 0000000000..435a3e6d34
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<menu
+ bottom="806"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="menu_gear_default"
+ visible="false">
+ <menu_item_call
+ label="New Inventory Window"
+ layout="topleft"
+ name="new_window">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="new_window" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ label="Sort by Name"
+ layout="topleft"
+ name="sort_by_name">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="sort_by_name" />
+ </menu_item_call>
+ <menu_item_call
+ label="Sort by Most Recent"
+ layout="topleft"
+ name="sort_by_recent">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="sort_by_recent" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ label="Show Filters"
+ layout="topleft"
+ name="show_filters">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="show_filters" />
+ </menu_item_call>
+ <menu_item_call
+ label="Reset Filters"
+ layout="topleft"
+ name="reset_filters">
+ <on_click
+ 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 Trash"
+ layout="topleft"
+ name="empty_trash">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="empty_trash" />
+ </menu_item_call>
+ <menu_item_call
+ label="Empty Lost and Found"
+ layout="topleft"
+ name="empty_lostnfound">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="empty_lostnfound" />
+ </menu_item_call>
+ <menu_item_call
+ label="Save Texture As"
+ layout="topleft"
+ name="Save Texture As">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="save_texture" />
+ <on_enable
+ function="Inventory.GearDefault.Enable"
+ parameter="save_texture" />
+ </menu_item_call>
+</menu>
diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml
index dfa5adb743..5eb0560962 100644
--- a/indra/newview/skins/default/xui/en/menu_login.xml
+++ b/indra/newview/skins/default/xui/en/menu_login.xml
@@ -180,23 +180,6 @@
</menu_item_call>
<menu_item_separator />
<menu_item_call
- label="Widget Test"
- name="Widget Test"
- shortcut="control|shift|T">
- <menu_item_call.on_click
- function="ShowFloater"
- parameter="test_widgets" />
- </menu_item_call>
- <menu_item_call
- label="Inspectors Test"
- name="inspectors_test"
- shortcut="control|shift|I"
- >
- <menu_item_call.on_click
- function="ShowFloater"
- parameter="test_inspectors" />
- </menu_item_call>
- <menu_item_call
label="Show Side Tray"
name="Show Side Tray">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index ebdfb01787..66c9060b06 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -98,6 +98,24 @@
function="World.SetBusy"/>
</menu_item_call>
</menu>
+ <menu_item_call
+ label="Request Admin Status"
+ layout="topleft"
+ name="Request Admin Options"
+ shortcut="control|alt|G"
+ visible="false">
+ <menu_item_call.on_click
+ function="Advanced.RequestAdminStatus" />
+ </menu_item_call>
+ <menu_item_call
+ label="Leave Admin Status"
+ layout="topleft"
+ name="Leave Admin Options"
+ shortcut="control|alt|shift|G"
+ visible="false">
+ <menu_item_call.on_click
+ function="Advanced.LeaveAdminStatus" />
+ </menu_item_call>
<menu_item_separator
layout="topleft" />
<menu_item_call
@@ -1915,6 +1933,42 @@
parameter="debug" />
</menu_item_check>
<menu_item_check
+ label="Notifications Console"
+ layout="topleft"
+ name="Notifications"
+ shortcut="control|shift|5">
+ <menu_item_check.on_check
+ function="Advanced.CheckConsole"
+ parameter="notifications" />
+ <menu_item_check.on_click
+ function="Floater.Show"
+ parameter="notifications_console" />
+ </menu_item_check>
+ <menu_item_check
+ label="Texture Size Console"
+ layout="topleft"
+ name="Texture Size"
+ shortcut="control|shift|6">
+ <menu_item_check.on_check
+ function="Advanced.CheckConsole"
+ parameter="texture size" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleConsole"
+ parameter="texture size" />
+ </menu_item_check>
+ <menu_item_check
+ label="Texture Category Console"
+ layout="topleft"
+ name="Texture Category"
+ shortcut="control|shift|7">
+ <menu_item_check.on_check
+ function="Advanced.CheckConsole"
+ parameter="texture category" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleConsole"
+ parameter="texture category" />
+ </menu_item_check>
+ <menu_item_check
label="Fast Timers"
layout="topleft"
name="Fast Timers"
@@ -2471,19 +2525,43 @@
label="Disable Textures"
name="Disable Textures">
<menu_item_check.on_check
- function="Advanced.CheckDisableTextures"
- parameter="DisableTextures" />
+ function="CheckControl"
+ parameter="TextureDisable" />
<menu_item_check.on_click
- function="Advanced.ToggleDisableTextures" />
+ function="ToggleControl"
+ parameter="TextureDisable" />
+ </menu_item_check>
+ <menu_item_check
+ label="Full Res Textures"
+ layout="topleft"
+ name="Rull Res Textures">
+ <menu_item_check.on_check
+ function="CheckControl"
+ parameter="TextureLoadFullRes" />
+ <menu_item_check.on_click
+ function="ToggleControl"
+ parameter="TextureLoadFullRes" />
+ </menu_item_check>
+ <menu_item_check
+ label="Audit Textures"
+ layout="topleft"
+ name="Audit Textures">
+ <menu_item_check.on_check
+ function="CheckControl"
+ parameter="AuditTexture" />
+ <menu_item_check.on_click
+ function="ToggleControl"
+ parameter="AuditTexture" />
</menu_item_check>
<menu_item_check
label="Texture Atlas"
name="Texture Atlas">
<menu_item_check.on_check
- function="Advanced.CheckTextureAtlas"
+ function="CheckControl"
parameter="TextureAtlas" />
<menu_item_check.on_click
- function="Advanced.ToggleTextureAtlas" />
+ function="ToggleControl"
+ parameter="TextureAtlas" />
</menu_item_check>
<menu_item_check
label="Render Attached Lights"
@@ -2506,8 +2584,8 @@
parameter="RenderAttachedParticles" />
</menu_item_check>
<menu_item_check
- label="Hover Highlight Objects"
- name="Hover Highlight Objects">
+ label="Hover Glow Objects"
+ name="Hover Glow Objects">
<menu_item_check.on_check
function="CheckControl"
parameter="RenderHighlightEnable" />
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index f26a4d09d4..989dc88851 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -5669,7 +5669,7 @@ An error has occurred while trying to connect to voice chat for [VOICE_CHANNEL_N
name="ServerVersionChanged"
priority="high"
type="notifytip">
-You just entered a region using a different server version, which may affect performance. Click to see the release notes.
+You just entered a region using a different server version, which may affect performance. [[URL] View the release notes.]
</notification>
<notification
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 9a3fdcc327..fcee0ef953 100644
--- a/indra/newview/skins/default/xui/en/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/en/panel_main_inventory.xml
@@ -1,415 +1,467 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<panel
- background_visible="true"
- follows="all"
- height="400"
- label="Things"
- layout="topleft"
- min_height="350"
- min_width="240"
- name="inventory panel"
- width="330">
- <panel.string
- name="Title">
- Things
- </panel.string>
- <filter_editor
- text_pad_left="12"
- follows="left|top|right"
- font="SanSerif"
- height="20"
- label="Filter"
- layout="topleft"
- left="15"
- name="inventory search editor"
- top="34"
- width="300" />
- <tab_container
- follows="left|top|right|bottom"
- height="300"
- layout="topleft"
- left_delta="-4"
- name="inventory filter tabs"
- tab_position="top"
- top_pad="4"
- width="305">
- <inventory_panel
- follows="left|top|right|bottom"
- height="295"
- label="All Items"
- layout="topleft"
- left="1"
- name="All Items"
- top="16"
- width="290" />
- <inventory_panel
- follows="left|top|right|bottom"
- height="295"
- label="Recent Items"
- layout="topleft"
- left_delta="0"
- name="Recent Items"
- top_delta="0"
- width="290" />
- </tab_container>
- <menu_bar
- bg_visible="false"
- follows="left|top|right"
- height="18"
- layout="topleft"
- left_delta="0"
- mouse_opaque="false"
- name="Inventory Menu"
- top_delta="-45"
- width="290">
- <menu
- height="101"
- label="File"
- layout="topleft"
- left="0"
- mouse_opaque="false"
- name="File"
- tear_off="true"
- top="-117"
- width="128">
- <menu_item_call
- label="Open"
- layout="topleft"
- name="Open">
- <menu_item_call.on_click
- function="Inventory.DoToSelected"
- parameter="open" />
- </menu_item_call>
- <menu
- create_jump_keys="true"
- label="Upload"
- layout="topleft"
- name="upload"
- tear_off="true">
- <menu_item_call
- label="Image (L$[COST])..."
- layout="topleft"
- name="Upload Image"
- shortcut="control|U">
- <menu_item_call.on_click
- function="File.UploadImage"
- parameter="" />
- <menu_item_call.on_enable
- function="File.EnableUpload" />
- </menu_item_call>
- <menu_item_call
- label="Sound (L$[COST])..."
- layout="topleft"
- name="Upload Sound">
- <menu_item_call.on_click
- function="File.UploadSound"
- parameter="" />
- <menu_item_call.on_enable
- function="File.EnableUpload" />
- </menu_item_call>
- <menu_item_call
- label="Animation (L$[COST])..."
- layout="topleft"
- name="Upload Animation">
- <menu_item_call.on_click
- function="File.UploadAnim"
- parameter="" />
- <menu_item_call.on_enable
- function="File.EnableUpload" />
- </menu_item_call>
- <menu_item_call
- label="Bulk (L$[COST] per file)..."
- layout="topleft"
- name="Bulk Upload">
- <menu_item_call.on_click
- function="File.UploadBulk"
- parameter="" />
- </menu_item_call>
- <menu_item_separator
- layout="topleft" />
- </menu>
- <menu_item_separator
- layout="topleft" />
- <menu_item_call
- label="New Window"
- layout="topleft"
- name="New Window">
- <menu_item_call.on_click
- function="Inventory.NewWindow" />
- </menu_item_call>
- <menu_item_separator
- layout="topleft"
- name="separator2" />
- <menu_item_call
- label="Show Filters"
- layout="topleft"
- name="Show Filters">
- <menu_item_call.on_click
- function="Inventory.ShowFilters" />
- </menu_item_call>
- <menu_item_call
- label="Reset Filters"
- layout="topleft"
- name="Reset Current">
- <menu_item_call.on_click
- function="Inventory.ResetFilter" />
- </menu_item_call>
- <menu_item_call
- label="Close All Folders"
- layout="topleft"
- name="Close All Folders">
- <menu_item_call.on_click
- function="Inventory.CloseAllFolders" />
- </menu_item_call>
- <menu_item_separator
- layout="topleft"
- name="separator3" />
- <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>
- <menu
- height="121"
- label="Create"
- layout="topleft"
- left="0"
- mouse_opaque="false"
- name="Create"
- tear_off="true"
- top="-201"
- width="121">
- <menu_item_call
- label="New Folder"
- layout="topleft"
- name="New Folder">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="category" />
- </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 Note"
- 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
- height="175"
- label="New Clothes"
- layout="topleft"
- left_delta="0"
- mouse_opaque="false"
- name="New Clothes"
- top_pad="514"
- width="125">
- <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"
- layout="topleft"
- name="New Alpha">
- <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>
- <menu
- height="85"
- label="New Body Parts"
- layout="topleft"
- left_delta="0"
- mouse_opaque="false"
- name="New Body Parts"
- top_pad="514"
- width="118">
- <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>
- <menu
- height="49"
- label="Sort"
- layout="topleft"
- left="0"
- mouse_opaque="false"
- name="Sort"
- tear_off="true"
- top="-113"
- width="118">
- <menu_item_check
- control_name="Inventory.SortByName"
- label="By Name"
- layout="topleft"
- name="By Name">
- <menu_item_check.on_click
- function="Inventory.SetSortBy"
- parameter="name" />
- </menu_item_check>
- <menu_item_check
- control_name="Inventory.SortByDate"
- label="By Date"
- layout="topleft"
- name="By Date">
- <menu_item_check.on_click
- function="Inventory.SetSortBy"
- parameter="date" />
- </menu_item_check>
- <menu_item_separator
- layout="topleft" />
- <menu_item_check
- control_name="Inventory.FoldersAlwaysByName"
- label="Folders Always By Name"
- layout="topleft"
- name="Folders Always By Name">
- <menu_item_check.on_click
- function="Inventory.SetSortBy"
- parameter="foldersalwaysbyname" />
- </menu_item_check>
- <menu_item_check
- control_name="Inventory.SystemFoldersToTop"
- label="System Folders To Top"
- layout="topleft"
- name="System Folders To Top">
- <menu_item_check.on_click
- function="Inventory.SetSortBy"
- parameter="systemfolderstotop" />
- </menu_item_check>
- </menu>
- </menu_bar>
-</panel>
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ background_visible="true"
+ follows="all"
+ height="400"
+ label="Things"
+ layout="topleft"
+ min_height="350"
+ min_width="240"
+ name="main inventory panel"
+ width="330">
+ <panel.string
+ name="Title">
+ Things
+ </panel.string>
+ <filter_editor
+ text_pad_left="12"
+ follows="left|top|right"
+ font="SanSerif"
+ height="20"
+ label="Filter"
+ layout="topleft"
+ left="15"
+ name="inventory search editor"
+ top="34"
+ width="300" />
+ <tab_container
+ follows="left|top|right|bottom"
+ height="300"
+ layout="topleft"
+ left_delta="-4"
+ name="inventory filter tabs"
+ tab_position="top"
+ top_pad="4"
+ width="305">
+ <inventory_panel
+ follows="left|top|right|bottom"
+ height="295"
+ label="All Items"
+ layout="topleft"
+ left="1"
+ name="All Items"
+ top="16"
+ width="290" />
+ <inventory_panel
+ follows="left|top|right|bottom"
+ height="295"
+ label="Recent Items"
+ layout="topleft"
+ left_delta="0"
+ name="Recent Items"
+ top_delta="0"
+ width="290" />
+ </tab_container>
+
+ <panel
+ background_visible="true"
+ bevel_style="none"
+ bottom="0"
+ follows="left|right|bottom"
+ height="30"
+ layout="bottomleft"
+ left="0"
+ visible="true"
+ name="bottom_panel"
+ width="330">
+ <button
+ follows="bottom|left"
+ tool_tip="Show additional options"
+ height="18"
+ image_disabled="OptionsMenu_Disabled"
+ image_selected="OptionsMenu_Press"
+ image_unselected="OptionsMenu_Off"
+ layout="topleft"
+ left="10"
+ name="options_gear_btn"
+ picture_style="true"
+ top="6"
+ width="18" />
+ <button
+ follows="bottom|left"
+ height="18"
+ image_selected="AddItem_Press"
+ image_unselected="AddItem_Off"
+ image_disabled="AddItem_Disabled"
+ layout="topleft"
+ left_pad="5"
+ name="add_btn"
+ picture_style="true"
+ tool_tip="Add new item"
+ width="18" />
+ <dnd_button
+ follows="bottom|right"
+ height="18"
+ image_selected="TrashItem_Press"
+ image_unselected="TrashItem_Off"
+ layout="topleft"
+ right="-5"
+ name="trash_btn"
+ picture_style="true"
+ tool_tip="Remove selected item"
+ top="6"
+ width="18" />
+ </panel>
+
+ <menu_bar
+ bg_visible="false"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left_delta="0"
+ mouse_opaque="false"
+ name="Inventory Menu"
+ top="15"
+ visible="true"
+ width="290">
+ <menu
+ height="101"
+ label="File"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="File"
+ tear_off="true"
+ top="-117"
+ width="128">
+ <menu_item_call
+ label="Open"
+ layout="topleft"
+ name="Open">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="open" />
+ </menu_item_call>
+ <menu
+ create_jump_keys="true"
+ label="Upload"
+ layout="topleft"
+ name="upload"
+ tear_off="true">
+ <menu_item_call
+ label="Image (L$[COST])..."
+ layout="topleft"
+ name="Upload Image"
+ shortcut="control|U">
+ <menu_item_call.on_click
+ function="File.UploadImage"
+ parameter="" />
+ <menu_item_call.on_enable
+ function="File.EnableUpload" />
+ </menu_item_call>
+ <menu_item_call
+ label="Sound (L$[COST])..."
+ layout="topleft"
+ name="Upload Sound">
+ <menu_item_call.on_click
+ function="File.UploadSound"
+ parameter="" />
+ <menu_item_call.on_enable
+ function="File.EnableUpload" />
+ </menu_item_call>
+ <menu_item_call
+ label="Animation (L$[COST])..."
+ layout="topleft"
+ name="Upload Animation">
+ <menu_item_call.on_click
+ function="File.UploadAnim"
+ parameter="" />
+ <menu_item_call.on_enable
+ function="File.EnableUpload" />
+ </menu_item_call>
+ <menu_item_call
+ label="Bulk (L$[COST] per file)..."
+ layout="topleft"
+ name="Bulk Upload">
+ <menu_item_call.on_click
+ function="File.UploadBulk"
+ parameter="" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft" />
+ </menu>
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ label="New Window"
+ layout="topleft"
+ name="New Window">
+ <menu_item_call.on_click
+ function="Inventory.NewWindow" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="separator2" />
+ <menu_item_call
+ label="Show Filters"
+ layout="topleft"
+ name="Show Filters">
+ <menu_item_call.on_click
+ function="Inventory.ShowFilters" />
+ </menu_item_call>
+ <menu_item_call
+ label="Reset Filters"
+ layout="topleft"
+ name="Reset Current">
+ <menu_item_call.on_click
+ function="Inventory.ResetFilter" />
+ </menu_item_call>
+ <menu_item_call
+ label="Close All Folders"
+ layout="topleft"
+ name="Close All Folders">
+ <menu_item_call.on_click
+ function="Inventory.CloseAllFolders" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="separator3" />
+ <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>
+ <menu
+ height="121"
+ label="Create"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="Create"
+ tear_off="true"
+ top="-201"
+ width="121">
+ <menu_item_call
+ label="New Folder"
+ layout="topleft"
+ name="New Folder">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="category" />
+ </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 Note"
+ 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
+ height="175"
+ label="New Clothes"
+ layout="topleft"
+ left_delta="0"
+ mouse_opaque="false"
+ name="New Clothes"
+ top_pad="514"
+ width="125">
+ <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"
+ layout="topleft"
+ name="New Alpha">
+ <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>
+ <menu
+ height="85"
+ label="New Body Parts"
+ layout="topleft"
+ left_delta="0"
+ mouse_opaque="false"
+ name="New Body Parts"
+ top_pad="514"
+ width="118">
+ <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>
+ <menu
+ height="49"
+ label="Sort"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="Sort"
+ tear_off="true"
+ top="-113"
+ width="118">
+ <menu_item_check
+ control_name="Inventory.SortByName"
+ label="By Name"
+ layout="topleft"
+ name="By Name">
+ <menu_item_check.on_click
+ function="Inventory.SetSortBy"
+ parameter="name" />
+ </menu_item_check>
+ <menu_item_check
+ control_name="Inventory.SortByDate"
+ label="By Date"
+ layout="topleft"
+ name="By Date">
+ <menu_item_check.on_click
+ function="Inventory.SetSortBy"
+ parameter="date" />
+ </menu_item_check>
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_check
+ control_name="Inventory.FoldersAlwaysByName"
+ label="Folders Always By Name"
+ layout="topleft"
+ name="Folders Always By Name">
+ <menu_item_check.on_click
+ function="Inventory.SetSortBy"
+ parameter="foldersalwaysbyname" />
+ </menu_item_check>
+ <menu_item_check
+ control_name="Inventory.SystemFoldersToTop"
+ label="System Folders To Top"
+ layout="topleft"
+ name="System Folders To Top">
+ <menu_item_check.on_click
+ function="Inventory.SetSortBy"
+ parameter="systemfolderstotop" />
+ </menu_item_check>
+ </menu>
+ </menu_bar>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
index c3e0cb352b..ba2ddd8b32 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
@@ -504,17 +504,4 @@
top_pad="0"
width="200" />
</panel>
- <!-- Until new panel is hooked up to code, we need to be able to get to
- the old window to change input devices. James -->
- <button
- follows="left|bottom"
- label="Old"
- name="legacy_device_window_btn"
- height="16"
- left="20"
- top="-270"
- width="40"
- commit_callback.function="Floater.Show"
- commit_callback.parameter="pref_voicedevicesettings"
- />
</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml
index fc5ccdb63e..3bdd7114ee 100644
--- a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml
+++ b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml
@@ -494,13 +494,32 @@ disabled
image_selected="media_btn_optimalzoom.png"
image_unselected="media_btn_optimalzoom.png"
layout="topleft"
- tool_tip="Zoom"
+ tool_tip="Zoom into media"
min_width="22"
width="22">
<button.commit_callback
function="MediaCtrl.Zoom" />
</button>
</layout_panel>
+ <layout_panel
+ name="close"
+ auto_resize="false"
+ user_resize="false"
+ layout="topleft"
+ min_width="21"
+ width="21" >
+ <button
+ height="22"
+ image_selected="media_btn_done.png"
+ image_unselected="media_btn_done.png"
+ layout="topleft"
+ tool_tip ="Zoom Back"
+ top_delta="-4"
+ width="21" >
+ <button.commit_callback
+ function="MediaCtrl.Close" />
+ </button>
+ </layout_panel>
<!--
<panel
height="22"
@@ -554,25 +573,6 @@ disabled
</panel>
-->
<layout_panel
- name="close"
- auto_resize="false"
- user_resize="false"
- layout="topleft"
- min_width="21"
- width="21" >
- <button
- height="22"
- image_selected="media_btn_done.png"
- image_unselected="media_btn_done.png"
- layout="topleft"
- tool_tip ="Close media control"
- top_delta="-4"
- width="21" >
- <button.commit_callback
- function="MediaCtrl.Close" />
- </button>
- </layout_panel>
- <layout_panel
width="0"
layout="topleft"
user_resize="false" />
diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml
index 23d55b33e2..a0055383b1 100644
--- a/indra/newview/skins/default/xui/en/panel_profile.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile.xml
@@ -58,7 +58,7 @@
default_image_name="None"
enabled="false"
follows="top|left"
- height="117"
+ height="102"
layout="topleft"
left="0"
name="2nd_life_pic"
@@ -102,7 +102,7 @@
default_image_name="None"
enabled="false"
follows="top|left"
- height="117"
+ height="102"
layout="topleft"
left="0"
name="real_world_pic"
diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml
index d0c3cdfafc..cbcc3f10ad 100644
--- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml
@@ -1,122 +1,127 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<panel
- background_visible="true"
- follows="all"
- height="400"
- label="Things"
- layout="topleft"
- min_height="350"
- min_width="240"
- name="objects panel"
- width="333">
- <tab_container
- follows="all"
- height="390"
- layout="topleft"
- left="9"
- name="Inventory Tabs"
- tab_position="top"
- top="0"
- width="313"
- tab_height="0"
- visible="true">
- <panel
- class="panel_main_inventory"
- filename="panel_main_inventory.xml"
- follows="all"
- layout="topleft"
- left="0"
- name="panel_main_inventory"
- top="15"
- label=""
- height="330"
- width="467">
- <panel
- height="25"
- layout="bottomright"
- left="0"
- help_topic="objects_button_tab"
- name="button_panel"
- bottom="0"
- width="313">
- <button
- enabled="true"
- follows="bottom|left"
- font="SansSerifSmallBold"
- height="25"
- label="Info"
- layout="topleft"
- left="0"
- name="info_btn"
- top="0"
- width="60" />
- <button
- enabled="true"
- follows="bottom|left"
- font="SansSerifSmallBold"
- height="25"
- label="Share"
- layout="topleft"
- left_pad="5"
- name="share_btn"
- top="0"
- width="60" />
- <button
- enabled="false"
- follows="bottom|left"
- font="SansSerifSmallBold"
- height="25"
- label="Wear"
- layout="topleft"
- left="130"
- name="wear_btn"
- top="0"
- width="60" />
- <button
- enabled="false"
- follows="bottom|left"
- font="SansSerifSmallBold"
- height="25"
- label="Play"
- layout="topleft"
- name="play_btn"
- left="130"
- top="0"
- width="50" />
- <button
- enabled="false"
- follows="bottom|left"
- font="SansSerifSmallBold"
- height="25"
- label="Teleport"
- layout="topleft"
- left="130"
- name="teleport_btn"
- top="0"
- width="77" />
- <button
- follows="bottom|right"
- font="SansSerifSmallBold"
- height="25"
- label="v"
- layout="topleft"
- name="overflow_btn"
- right="-10"
- top="0"
- width="30" />
- </panel>
- </panel>
- </tab_container>
-
- <panel
- class="sidepanel_object_info"
- filename="sidepanel_object_info.xml"
- follows="all"
- height="360"
- layout="topleft"
- left="0"
- help_topic="objects_info_tab"
- name="sidepanel_object_info"
- top="30"
- visible="false" />
-</panel>
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ background_visible="true"
+ follows="all"
+ height="570"
+ label="Things"
+ layout="topleft"
+ min_height="350"
+ min_width="240"
+ name="objects panel"
+ width="333">
+ <panel
+ follows="all"
+ layout="topleft"
+ left="0"
+ name="sidepanel__inventory_panel"
+ top="0"
+ label=""
+ height="570"
+ visible="true"
+ width="330">
+ <panel
+ class="panel_main_inventory"
+ filename="panel_main_inventory.xml"
+ follows="all"
+ layout="topleft"
+ left="0"
+ name="panel_main_inventory"
+ top="0"
+ label=""
+ height="500"
+ width="330" />
+ <panel
+ height="25"
+ layout="bottomright"
+ help_topic="item_button_tab"
+ name="button_panel"
+ left="5"
+ bottom="5"
+ width="313">
+ <button
+ enabled="true"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Info"
+ layout="topleft"
+ left="0"
+ name="info_btn"
+ top="0"
+ width="60" />
+ <button
+ enabled="true"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Share"
+ layout="topleft"
+ left_pad="5"
+ name="share_btn"
+ top="0"
+ width="60" />
+ <button
+ enabled="false"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Wear"
+ layout="topleft"
+ left="130"
+ name="wear_btn"
+ top="0"
+ width="60" />
+ <button
+ enabled="false"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Play"
+ layout="topleft"
+ name="play_btn"
+ left="130"
+ top="0"
+ width="50" />
+ <button
+ enabled="false"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Teleport"
+ layout="topleft"
+ left="130"
+ name="teleport_btn"
+ top="0"
+ width="77" />
+ </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
new file mode 100644
index 0000000000..39cd75074e
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
@@ -0,0 +1,511 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ auto_tile="true"
+ height="570"
+ layout="topleft"
+ name="item properties"
+ help_topic="item_properties"
+ save_rect="true"
+ title="Inventory Item Properties"
+ width="333">
+ <panel.string
+ name="unknown">
+ (unknown)
+ </panel.string>
+ <panel.string
+ name="public">
+ (public)
+ </panel.string>
+ <panel.string
+ name="you_can">
+ You can:
+ </panel.string>
+ <panel.string
+ name="owner_can">
+ Owner can:
+ </panel.string>
+ <panel.string
+ name="acquiredDate">
+ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
+ </panel.string>
+ <icon
+ follows="top|right"
+ height="18"
+ image_name="Lock"
+ layout="topleft"
+ right="-50"
+ mouse_opaque="true"
+ name="IconLocked"
+ top="4"
+ width="18" />
+ <button
+ follows="top|right"
+ height="25"
+ image_overlay="BackArrow_Off"
+ layout="topleft"
+ name="back_btn"
+ picture_style="true"
+ right="-5"
+ tab_stop="false"
+ top="0"
+ width="25" />
+ <panel
+ follows="all"
+ height="500"
+ label=""
+ layout="topleft"
+ left="5"
+ help_topic=""
+ top="30"
+ border="1"
+ width="313">
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="5"
+ name="LabelItemNameTitle"
+ top="5"
+ width="78">
+ Name:
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ max_length="63"
+ name="LabelItemName"
+ top_delta="0"
+ width="225" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="5"
+ name="LabelItemDescTitle"
+ top_delta="20"
+ width="78">
+ Description:
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ max_length="127"
+ name="LabelItemDesc"
+ top_delta="0"
+ width="225" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="LabelCreatorTitle"
+ top="65"
+ width="78">
+ Creator:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ name="LabelCreatorName"
+ top_delta="0"
+ width="200">
+ Nicole Linden
+ </text>
+ <button
+ follows="top|right"
+ font="SansSerifSmall"
+ height="16"
+ label="Profile..."
+ layout="topleft"
+ left_delta="144"
+ name="BtnCreator"
+ top_delta="0"
+ width="78" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="LabelOwnerTitle"
+ top="85"
+ width="78">
+ Owner:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ name="LabelOwnerName"
+ top_delta="0"
+ width="200">
+ Thrax Linden
+ </text>
+ <button
+ follows="top|right"
+ font="SansSerifSmall"
+ height="16"
+ label="Profile..."
+ layout="topleft"
+ left_delta="144"
+ name="BtnOwner"
+ top_delta="0"
+ width="78" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="LabelAcquiredTitle"
+ top="105"
+ width="78">
+ Acquired:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ name="LabelAcquiredDate"
+ top_delta="0"
+ width="252">
+ Wed May 24 12:50:46 2006
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="OwnerLabel"
+ top="125"
+ width="78">
+ You:
+ </text>
+ <check_box
+ height="16"
+ label="Edit"
+ layout="topleft"
+ left_pad="5"
+ name="CheckOwnerModify"
+ top_delta="0"
+ width="78" />
+ <check_box
+ height="16"
+ label="Copy"
+ layout="topleft"
+ left_delta="0"
+ name="CheckOwnerCopy"
+ top_pad="5"
+ width="88" />
+ <check_box
+ height="16"
+ label="Resell"
+ layout="topleft"
+ left_delta="0"
+ name="CheckOwnerTransfer"
+ top_pad="5"
+ width="106" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="AnyoneLabel"
+ top_pad="5"
+ width="78">
+ Anyone:
+ </text>
+ <check_box
+ height="16"
+ label="Copy"
+ layout="topleft"
+ left_pad="5"
+ name="CheckEveryoneCopy"
+ top_delta="0"
+ width="130" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="GroupLabel"
+ top_pad="5"
+ width="78">
+ Group:
+ </text>
+ <check_box
+ height="16"
+ label="Share"
+ layout="topleft"
+ left_pad="5"
+ name="CheckShareWithGroup"
+ top_delta="5"
+ width="106" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="NextOwnerLabel"
+ top_pad="5"
+ width="78">
+ Next owner:
+ </text>
+ <check_box
+ height="16"
+ label="Edit"
+ layout="topleft"
+ left_pad="5"
+ name="CheckNextOwnerModify"
+ top_delta="0"
+ width="78" />
+ <check_box
+ height="16"
+ label="Copy"
+ layout="topleft"
+ left_delta="0"
+ name="CheckNextOwnerCopy"
+ top_pad="5"
+ width="88" />
+ <check_box
+ height="16"
+ label="Resell"
+ layout="topleft"
+ left_delta="0"
+ name="CheckNextOwnerTransfer"
+ top_pad="5"
+ width="106" />
+ <check_box
+ height="16"
+ label="For Sale"
+ layout="topleft"
+ left="10"
+ name="CheckPurchase"
+ top_pad="5"
+ width="78" />
+ <combo_box
+ height="19"
+ left_pad="5"
+ layout="topleft"
+ follows="left|top"
+ name="combobox sale copy"
+ width="90">
+ <combo_box.item
+ label="Copy"
+ name="Copy"
+ value="Copy" />
+ <combo_box.item
+ label="Original"
+ name="Original"
+ value="Original" />
+ </combo_box>
+ <spinner
+ follows="left|top"
+ decimal_digits="0"
+ increment="1"
+ control_name="Edit Cost"
+ name="Edit Cost"
+ label="Price: L$"
+ label_width="60"
+ left="10"
+ width="180"
+ min_val="1"
+ height="19"
+ max_val="999999999"
+ top_pad="5"/>
+ <!--line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left_pad="5"
+ max_length="25"
+ name="EditPrice"
+ top_delta="0"
+ width="242" /-->
+ <!--text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="BaseMaskDebug"
+ top="155"
+ width="330">
+ B:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="OwnerMaskDebug"
+ top_delta="0"
+ width="270">
+ O:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="GroupMaskDebug"
+ top_delta="0"
+ width="210">
+ G:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="EveryoneMaskDebug"
+ top_delta="0"
+ width="150">
+ E:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="NextMaskDebug"
+ top_delta="0"
+ width="90">
+ N:
+ </text-->
+ <!--text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="SaleLabel"
+ top_pad="5"
+ width="330">
+ Mark Item:
+ </text-->
+ <!--radio_group
+ draw_border="false"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ name="RadioSaleType"
+ top_delta="0"
+ width="252">
+ <radio_item
+ height="16"
+ label="Original"
+ layout="topleft"
+ left="0"
+ name="radio"
+ top="0"
+ width="70" />
+ <radio_item
+ height="16"
+ label="Copy"
+ layout="topleft"
+ left_delta="60"
+ name="radio2"
+ top_delta="0"
+ width="70" />
+ </radio_group-->
+ <!--text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="TextPrice"
+ top_pad="5"
+ width="78">
+ Price: L$
+ </text-->
+ </panel>
+ <panel
+ height="25"
+ layout="bottomright"
+ help_topic="button_tab"
+ name="button_panel"
+ left="5"
+ bottom="5"
+ width="313">
+ <button
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Edit"
+ layout="topleft"
+ left="0"
+ name="edit_btn"
+ top="0"
+ width="50" />
+ <button
+ follows="bottom|right"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Cancel"
+ layout="topleft"
+ name="cancel_btn"
+ right="-1"
+ top="0"
+ width="70" />
+ <button
+ follows="bottom|right"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Save"
+ layout="topleft"
+ name="save_btn"
+ left_pad="-135"
+ top="0"
+ width="60" />
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/widgets/menu_item.xml b/indra/newview/skins/default/xui/en/widgets/menu_item.xml
new file mode 100644
index 0000000000..c98e9cb6b8
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/menu_item.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<!-- Use this for the top-level menu styling -->
+<menu_item
+ font="SansSerifSmall"
+ >
+</menu_item>
diff --git a/indra/newview/tests/lltextureinfo_test.cpp b/indra/newview/tests/lltextureinfo_test.cpp
new file mode 100644
index 0000000000..8dfba46262
--- /dev/null
+++ b/indra/newview/tests/lltextureinfo_test.cpp
@@ -0,0 +1,284 @@
+/**
+ * @file llwtextureinfo_test.cpp
+ * @author Si & Gabriel
+ * @date 2009-03-30
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header: almost always required for newview cpp files
+#include "../llviewerprecompiledheaders.h"
+// Class to test
+#include "../lltextureinfo.h"
+// Dependencies
+#include "../lltextureinfodetails.cpp"
+
+// Tut header
+#include "../test/lltut.h"
+
+// -------------------------------------------------------------------------------------------
+// Stubbing: Declarations required to link and run the class being tested
+// Notes:
+// * Add here stubbed implementation of the few classes and methods used in the class to be tested
+// * Add as little as possible (let the link errors guide you)
+// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
+// * A simulator for a class can be implemented here. Please comment and document thoroughly.
+
+
+// End Stubbing
+// -------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------
+// TUT
+// -------------------------------------------------------------------------------------------
+
+namespace tut
+{
+ // Test wrapper declarations
+ struct textureinfo_test
+ {
+ // Constructor and destructor of the test wrapper
+ textureinfo_test()
+ {
+ }
+ ~textureinfo_test()
+ {
+ }
+ };
+
+ // Tut templating thingamagic: test group, object and test instance
+ typedef test_group<textureinfo_test> textureinfo_t;
+ typedef textureinfo_t::object textureinfo_object_t;
+ tut::textureinfo_t tut_textureinfo("textureinfo");
+
+
+ // ---------------------------------------------------------------------------------------
+ // Test functions
+ // Notes:
+ // * Test as many as you possibly can without requiring a full blown simulation of everything
+ // * The tests are executed in sequence so the test instance state may change between calls
+ // * Remember that you cannot test private methods with tut
+ // ---------------------------------------------------------------------------------------
+
+ // ---------------------------------------------------------------------------------------
+ // Test the LLTextureInfo
+ // ---------------------------------------------------------------------------------------
+
+
+ // Test instantiation
+ template<> template<>
+ void textureinfo_object_t::test<1>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+ ensure("have we crashed?", true);
+ }
+
+ // Check lltextureinfo does not contain UUIDs we haven't added
+ template<> template<>
+ void textureinfo_object_t::test<2>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ LLUUID nonExistant("3a0efa3b-84dc-4e17-9b8c-79ea028850c1");
+ ensure(!tex_info.has(nonExistant));
+ }
+
+ // Check we can add a request time for a texture
+ template<> template<>
+ void textureinfo_object_t::test<3>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3");
+ tex_info.setRequestStartTime(id, 200);
+
+ ensure_equals(tex_info.getRequestStartTime(id), 200);
+ }
+
+ // Check time for non-existant texture
+ template<> template<>
+ void textureinfo_object_t::test<4>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ LLUUID nonExistant("3a0efa3b-84dc-4e17-9b8c-79ea028850c1");
+ ensure_equals(tex_info.getRequestStartTime(nonExistant), 0);
+ }
+
+ // Check download complete time for non existant texture
+ template<> template<>
+ void textureinfo_object_t::test<5>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ LLUUID nonExistant("3a0efa3b-84dc-4e17-9b8c-79ea028850c1");
+ ensure_equals(tex_info.getRequestCompleteTime(nonExistant), 0);
+ }
+
+ // requested size is passed in correctly
+ template<> template<>
+ void textureinfo_object_t::test<6>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3");
+ tex_info.setRequestSize(id, 600);
+
+ ensure_equals(tex_info.getRequestSize(id), 600);
+ }
+
+ // transport type is recorded correctly (http)
+ template<> template<>
+ void textureinfo_object_t::test<7>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3");
+ tex_info.setRequestType(id, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+
+ ensure_equals(tex_info.getRequestType(id), LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+ }
+
+ // transport type is recorded correctly (udp)
+ template<> template<>
+ void textureinfo_object_t::test<8>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3");
+ tex_info.setRequestType(id, LLTextureInfoDetails::REQUEST_TYPE_UDP);
+
+ ensure_equals(tex_info.getRequestType(id), LLTextureInfoDetails::REQUEST_TYPE_UDP);
+ }
+
+ // request offset is recorded correctly
+ template<> template<>
+ void textureinfo_object_t::test<9>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3");
+ tex_info.setRequestOffset(id, 1234);
+
+ ensure_equals(tex_info.getRequestOffset(id), 1234);
+ }
+
+ // ask for averages gives us correct figure
+ template<> template<>
+ void textureinfo_object_t::test<10>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ S32 requestStartTimeOne = 200;
+ S32 requestEndTimeOne = 400;
+ S32 requestSizeOne = 1024;
+ S32 requestSizeOneBits = requestSizeOne * 8;
+ LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3");
+ tex_info.setRequestStartTime(id1, requestStartTimeOne);
+ tex_info.setRequestSize(id1, requestSizeOne);
+ tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+ tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne);
+
+ U32 requestStartTimeTwo = 100;
+ U32 requestEndTimeTwo = 500;
+ U32 requestSizeTwo = 2048;
+ S32 requestSizeTwoBits = requestSizeTwo * 8;
+ LLUUID id2("10e65d70-46fd-429f-841a-bf698e9424d4");
+ tex_info.setRequestStartTime(id2, requestStartTimeTwo);
+ tex_info.setRequestSize(id2, requestSizeTwo);
+ tex_info.setRequestType(id2, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+ tex_info.setRequestCompleteTimeAndLog(id2, requestEndTimeTwo);
+
+ S32 averageBitRate = ((requestSizeOneBits/(requestEndTimeOne - requestStartTimeOne)) +
+ (requestSizeTwoBits/(requestEndTimeTwo - requestStartTimeTwo))) / 2;
+
+ S32 totalBytes = requestSizeOne + requestSizeTwo;
+
+ LLSD results = tex_info.getAverages();
+ ensure_equals("is average bits per second correct", results["bits_per_second"].asInteger(), averageBitRate);
+ ensure_equals("is total bytes is correct", results["bytes_downloaded"].asInteger(), totalBytes);
+ ensure_equals("is transport correct", results["transport"].asString(), std::string("HTTP"));
+ }
+
+ // make sure averages cleared when reset is called
+ template<> template<>
+ void textureinfo_object_t::test<11>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ S32 requestStartTimeOne = 200;
+ S32 requestEndTimeOne = 400;
+ S32 requestSizeOne = 1024;
+ LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3");
+ tex_info.setRequestStartTime(id1, requestStartTimeOne);
+ tex_info.setRequestSize(id1, requestSizeOne);
+ tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+ tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne);
+
+ tex_info.getAverages();
+ tex_info.reset();
+ LLSD results = tex_info.getAverages();
+ ensure_equals("is average bits per second correct", results["bits_per_second"].asInteger(), 0);
+ ensure_equals("is total bytes is correct", results["bytes_downloaded"].asInteger(), 0);
+ ensure_equals("is transport correct", results["transport"].asString(), std::string("NONE"));
+ }
+
+ // make sure map item removed when expired
+ template<> template<>
+ void textureinfo_object_t::test<12>()
+ {
+ LLTextureInfo tex_info;
+ tex_info.setUpLogging(true, true);
+
+ S32 requestStartTimeOne = 200;
+ S32 requestEndTimeOne = 400;
+ S32 requestSizeOne = 1024;
+ LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3");
+ tex_info.setRequestStartTime(id1, requestStartTimeOne);
+ tex_info.setRequestSize(id1, requestSizeOne);
+ tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+
+ ensure_equals("map item created", tex_info.getTextureInfoMapSize(), 1);
+
+ tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne);
+
+ ensure_equals("map item removed when consumed", tex_info.getTextureInfoMapSize(), 0);
+ }
+}
+
diff --git a/indra/newview/tests/lltextureinfodetails_test.cpp b/indra/newview/tests/lltextureinfodetails_test.cpp
new file mode 100644
index 0000000000..aa2697fb8e
--- /dev/null
+++ b/indra/newview/tests/lltextureinfodetails_test.cpp
@@ -0,0 +1,98 @@
+/**
+ * @file llwtextureinfodetails_test.cpp
+ * @author Si & Gabriel
+ * @date 2009-03-30
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header: almost always required for newview cpp files
+#include "../llviewerprecompiledheaders.h"
+// Class to test
+#include "../lltextureinfodetails.h"
+// Dependencies
+
+// Tut header
+#include "../test/lltut.h"
+
+// -------------------------------------------------------------------------------------------
+// Stubbing: Declarations required to link and run the class being tested
+// Notes:
+// * Add here stubbed implementation of the few classes and methods used in the class to be tested
+// * Add as little as possible (let the link errors guide you)
+// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
+// * A simulator for a class can be implemented here. Please comment and document thoroughly.
+
+
+// End Stubbing
+// -------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------
+// TUT
+// -------------------------------------------------------------------------------------------
+
+namespace tut
+{
+ // Test wrapper declarations
+ struct textureinfodetails_test
+ {
+ // Constructor and destructor of the test wrapper
+ textureinfodetails_test()
+ {
+ }
+ ~textureinfodetails_test()
+ {
+ }
+ };
+
+ // Tut templating thingamagic: test group, object and test instance
+ typedef test_group<textureinfodetails_test> textureinfodetails_t;
+ typedef textureinfodetails_t::object textureinfodetails_object_t;
+ tut::textureinfodetails_t tut_textureinfodetails("textureinfodetails");
+
+
+ // ---------------------------------------------------------------------------------------
+ // Test functions
+ // Notes:
+ // * Test as many as you possibly can without requiring a full blown simulation of everything
+ // * The tests are executed in sequence so the test instance state may change between calls
+ // * Remember that you cannot test private methods with tut
+ // ---------------------------------------------------------------------------------------
+
+ // ---------------------------------------------------------------------------------------
+ // Test the LLTextureInfo
+ // ---------------------------------------------------------------------------------------
+
+
+ // Test instantiation
+ template<> template<>
+ void textureinfodetails_object_t::test<1>()
+ {
+ ensure("have we crashed?", true);
+ }
+}
diff --git a/indra/newview/tests/lltexturestatsuploader_test.cpp b/indra/newview/tests/lltexturestatsuploader_test.cpp
new file mode 100644
index 0000000000..77a3e2c3d8
--- /dev/null
+++ b/indra/newview/tests/lltexturestatsuploader_test.cpp
@@ -0,0 +1,156 @@
+/**
+ * @file lltexturestatsuploader_test.cpp
+ * @author Si
+ * @date 2009-05-27
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header: almost always required for newview cpp files
+#include "../llviewerprecompiledheaders.h"
+// Class to test
+#include "../lltexturestatsuploader.h"
+// Dependencies
+
+// Tut header
+#include "../test/lltut.h"
+
+// -------------------------------------------------------------------------------------------
+// Stubbing: Declarations required to link and run the class being tested
+// Notes:
+// * Add here stubbed implementation of the few classes and methods used in the class to be tested
+// * Add as little as possible (let the link errors guide you)
+// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
+// * A simulator for a class can be implemented here. Please comment and document thoroughly.
+
+#include "boost/intrusive_ptr.hpp"
+void boost::intrusive_ptr_add_ref(LLCurl::Responder*){}
+void boost::intrusive_ptr_release(LLCurl::Responder* p){}
+const F32 HTTP_REQUEST_EXPIRY_SECS = 0.0f;
+
+static std::string most_recent_url;
+static LLSD most_recent_body;
+
+void LLHTTPClient::post(
+ const std::string& url,
+ const LLSD& body,
+ ResponderPtr,
+ const LLSD& headers,
+ const F32 timeout)
+{
+ // set some sensor code
+ most_recent_url = url;
+ most_recent_body = body;
+ return;
+}
+
+// End Stubbing
+// -------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------
+// TUT
+// -------------------------------------------------------------------------------------------
+
+namespace tut
+{
+ // Test wrapper declarations
+ struct texturestatsuploader_test
+ {
+ // Constructor and destructor of the test wrapper
+ texturestatsuploader_test()
+ {
+ most_recent_url = "some sort of default text that should never match anything the tests are expecting!";
+ LLSD blank_llsd;
+ most_recent_body = blank_llsd;
+ }
+ ~texturestatsuploader_test()
+ {
+ }
+ };
+
+ // Tut templating thingamagic: test group, object and test instance
+ typedef test_group<texturestatsuploader_test> texturestatsuploader_t;
+ typedef texturestatsuploader_t::object texturestatsuploader_object_t;
+ tut::texturestatsuploader_t tut_texturestatsuploader("texturestatsuploader");
+
+
+ // ---------------------------------------------------------------------------------------
+ // Test functions
+ // Notes:
+ // * Test as many as you possibly can without requiring a full blown simulation of everything
+ // * The tests are executed in sequence so the test instance state may change between calls
+ // * Remember that you cannot test private methods with tut
+ // ---------------------------------------------------------------------------------------
+
+ // ---------------------------------------------------------------------------------------
+ // Test the LLTextureInfo
+ // ---------------------------------------------------------------------------------------
+
+
+ // Test instantiation
+ template<> template<>
+ void texturestatsuploader_object_t::test<1>()
+ {
+ LLTextureStatsUploader tsu;
+ llinfos << &tsu << llendl;
+ ensure("have we crashed?", true);
+ }
+
+ // does it call out to the provided url if we ask it to?
+ template<> template<>
+ void texturestatsuploader_object_t::test<2>()
+ {
+ LLTextureStatsUploader tsu;
+ std::string url = "http://blahblahblah";
+ LLSD texture_stats;
+ tsu.uploadStatsToSimulator(url, texture_stats);
+ ensure_equals("did the right url get called?", most_recent_url, url);
+ ensure_equals("did the right body get sent?", most_recent_body, texture_stats);
+ }
+
+ // does it not call out to the provided url if we send it an ungranted cap?
+ template<> template<>
+ void texturestatsuploader_object_t::test<3>()
+ {
+ LLTextureStatsUploader tsu;
+
+ // this url left intentionally blank to mirror
+ // not getting a cap in the caller.
+ std::string url_for_ungranted_cap = "";
+
+ LLSD texture_stats;
+ std::string most_recent_url_before_test = most_recent_url;
+ tsu.uploadStatsToSimulator(url_for_ungranted_cap, texture_stats);
+
+ ensure_equals("hopefully no url got called!", most_recent_url, most_recent_url_before_test);
+ }
+
+ // does it call out if the data is empty?
+ // should it even do that?
+}
+
diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp
new file mode 100644
index 0000000000..56cf86f6df
--- /dev/null
+++ b/indra/newview/tests/llworldmap_test.cpp
@@ -0,0 +1,523 @@
+/**
+ * @file llworldmap_test.cpp
+ * @author Merov Linden
+ * @date 2009-03-09
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header: almost always required for newview cpp files
+#include "../llviewerprecompiledheaders.h"
+// Class to test
+#include "../llworldmap.h"
+// Dependencies
+#include "../llviewerimagelist.h"
+#include "../llworldmapmessage.h"
+// Tut header
+#include "../test/lltut.h"
+
+// -------------------------------------------------------------------------------------------
+// Stubbing: Declarations required to link and run the class being tested
+// Notes:
+// * Add here stubbed implementation of the few classes and methods used in the class to be tested
+// * Add as little as possible (let the link errors guide you)
+// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
+// * A simulator for a class can be implemented here. Please comment and document thoroughly.
+
+// Stub image calls
+LLViewerImageList::LLViewerImageList() { }
+LLViewerImageList::~LLViewerImageList() { }
+LLViewerImageList gImageList;
+LLViewerImage* LLViewerImageList::getImage(const LLUUID &image_id,
+ BOOL usemipmaps,
+ BOOL level_immediate,
+ LLGLint internal_format,
+ LLGLenum primary_format,
+ LLHost request_from_host)
+{ return NULL; }
+void LLViewerImage::setBoostLevel(S32 level) { }
+void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode) { }
+
+// Stub related map calls
+LLWorldMapMessage::LLWorldMapMessage() { }
+LLWorldMapMessage::~LLWorldMapMessage() { }
+void LLWorldMapMessage::sendItemRequest(U32 type, U64 handle) { }
+void LLWorldMapMessage::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent) { }
+LLWorldMipmap::LLWorldMipmap() { }
+LLWorldMipmap::~LLWorldMipmap() { }
+void LLWorldMipmap::reset() { }
+void LLWorldMipmap::dropBoostLevels() { }
+void LLWorldMipmap::equalizeBoostLevels() { }
+LLPointer<LLViewerImage> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load)
+{ return NULL; }
+
+// Stub other stuff
+BOOL gPacificDaylightTime;
+
+// End Stubbing
+// -------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------
+// TUT
+// -------------------------------------------------------------------------------------------
+
+const F32 X_WORLD_TEST = 1000.0f * REGION_WIDTH_METERS;
+const F32 Y_WORLD_TEST = 2000.0f * REGION_WIDTH_METERS;
+const F32 Z_WORLD_TEST = 240.0f;
+const std::string ITEM_NAME_TEST = "Item Foo";
+const std::string TOOLTIP_TEST = "Tooltip Foo";
+
+const std::string SIM_NAME_TEST = "Sim Foo";
+
+namespace tut
+{
+ // Test wrapper declarations
+ struct iteminfo_test
+ {
+ // Instance to be tested
+ LLItemInfo* mItem;
+
+ // Constructor and destructor of the test wrapper
+ iteminfo_test()
+ {
+ LLUUID id;
+ mItem = new LLItemInfo(X_WORLD_TEST, Y_WORLD_TEST, ITEM_NAME_TEST, id);
+ }
+ ~iteminfo_test()
+ {
+ delete mItem;
+ }
+ };
+
+ struct siminfo_test
+ {
+ // Instance to be tested
+ LLSimInfo* mSim;
+
+ // Constructor and destructor of the test wrapper
+ siminfo_test()
+ {
+ U64 handle = to_region_handle_global(X_WORLD_TEST, Y_WORLD_TEST);
+ mSim = new LLSimInfo(handle);
+ }
+ ~siminfo_test()
+ {
+ delete mSim;
+ }
+ };
+
+ struct worldmap_test
+ {
+ // Instance to be tested
+ LLWorldMap* mWorld;
+
+ // Constructor and destructor of the test wrapper
+ worldmap_test()
+ {
+ mWorld = LLWorldMap::getInstance();
+ }
+ ~worldmap_test()
+ {
+ mWorld = NULL;
+ }
+ };
+
+ // Tut templating thingamagic: test group, object and test instance
+ typedef test_group<iteminfo_test> iteminfo_t;
+ typedef iteminfo_t::object iteminfo_object_t;
+ tut::iteminfo_t tut_iteminfo("iteminfo");
+
+ typedef test_group<siminfo_test> siminfo_t;
+ typedef siminfo_t::object siminfo_object_t;
+ tut::siminfo_t tut_siminfo("siminfo");
+
+ typedef test_group<worldmap_test> worldmap_t;
+ typedef worldmap_t::object worldmap_object_t;
+ tut::worldmap_t tut_worldmap("worldmap");
+
+ // ---------------------------------------------------------------------------------------
+ // Test functions
+ // Notes:
+ // * Test as many as you possibly can without requiring a full blown simulation of everything
+ // * The tests are executed in sequence so the test instance state may change between calls
+ // * Remember that you cannot test private methods with tut
+ // ---------------------------------------------------------------------------------------
+
+ // ---------------------------------------------------------------------------------------
+ // Test the LLItemInfo interface
+ // ---------------------------------------------------------------------------------------
+ template<> template<>
+ void iteminfo_object_t::test<1>()
+ {
+ // Test 1 : setCount() / getCount()
+ mItem->setCount(10);
+ ensure("LLItemInfo::setCount() test failed", mItem->getCount() == 10);
+ // Test 2 : setTooltip() / getToolTip()
+ std::string tooltip = TOOLTIP_TEST;
+ mItem->setTooltip(tooltip);
+ ensure("LLItemInfo::setTooltip() test failed", mItem->getToolTip() == TOOLTIP_TEST);
+ // Test 3 : setElevation() / getGlobalPosition()
+ mItem->setElevation(Z_WORLD_TEST);
+ LLVector3d pos = mItem->getGlobalPosition();
+ LLVector3d ref(X_WORLD_TEST, Y_WORLD_TEST, Z_WORLD_TEST);
+ ensure("LLItemInfo::getGlobalPosition() test failed", pos == ref);
+ // Test 4 : getName()
+ std::string name = mItem->getName();
+ ensure("LLItemInfo::getName() test failed", name == ITEM_NAME_TEST);
+ // Test 5 : isName()
+ ensure("LLItemInfo::isName() test failed", mItem->isName(name));
+ // Test 6 : getUUID()
+ LLUUID id;
+ ensure("LLItemInfo::getUUID() test failed", mItem->getUUID() == id);
+ // Test 7 : getRegionHandle()
+ U64 handle = to_region_handle_global(X_WORLD_TEST, Y_WORLD_TEST);
+ ensure("LLItemInfo::getRegionHandle() test failed", mItem->getRegionHandle() == handle);
+ }
+ // ---------------------------------------------------------------------------------------
+ // Test the LLSimInfo interface
+ // ---------------------------------------------------------------------------------------
+ // Test Setters and Accessors methods
+ template<> template<>
+ void siminfo_object_t::test<1>()
+ {
+ // Test 1 : setName() / getName()
+ std::string name = SIM_NAME_TEST;
+ mSim->setName(name);
+ ensure("LLSimInfo::setName() test failed", mSim->getName() == SIM_NAME_TEST);
+ // Test 2 : isName()
+ ensure("LLSimInfo::isName() test failed", mSim->isName(name));
+ // Test 3 : getGlobalPos()
+ LLVector3 local;
+ LLVector3d ref(X_WORLD_TEST, Y_WORLD_TEST, 0.0f);
+ LLVector3d pos = mSim->getGlobalPos(local);
+ ensure("LLSimInfo::getGlobalPos() test failed", pos == ref);
+ // Test 4 : getGlobalOrigin()
+ pos = mSim->getGlobalOrigin();
+ ensure("LLSimInfo::getGlobalOrigin() test failed", pos == ref);
+ // Test 5 : clearImage()
+ try {
+ mSim->clearImage();
+ } catch (...) {
+ fail("LLSimInfo::clearImage() test failed");
+ }
+ // Test 6 : dropImagePriority()
+ try {
+ mSim->dropImagePriority();
+ } catch (...) {
+ fail("LLSimInfo::dropImagePriority() test failed");
+ }
+ // Test 7 : updateAgentCount()
+ try {
+ mSim->updateAgentCount(0.0f);
+ } catch (...) {
+ fail("LLSimInfo::updateAgentCount() test failed");
+ }
+ // Test 8 : getAgentCount()
+ S32 agents = mSim->getAgentCount();
+ ensure("LLSimInfo::getAgentCount() test failed", agents == 0);
+ // Test 9 : setLandForSaleImage() / getLandForSaleImage()
+ LLUUID id;
+ mSim->setLandForSaleImage(id);
+ LLPointer<LLViewerImage> image = mSim->getLandForSaleImage();
+ ensure("LLSimInfo::getLandForSaleImage() test failed", image.isNull());
+ // Test 10 : isPG()
+ mSim->setAccess(SIM_ACCESS_PG);
+ ensure("LLSimInfo::isPG() test failed", mSim->isPG());
+ // Test 11 : isDown()
+ mSim->setAccess(SIM_ACCESS_DOWN);
+ ensure("LLSimInfo::isDown() test failed", mSim->isDown());
+ // Test 12 : Access strings can't be accessed from unit test...
+ //ensure("LLSimInfo::getAccessString() test failed", mSim->getAccessString() == "Offline");
+ // Test 13 : Region strings can't be accessed from unit test...
+ //mSim->setRegionFlags(REGION_FLAGS_SANDBOX);
+ //ensure("LLSimInfo::setRegionFlags() test failed", mSim->getFlagsString() == "Sandbox");
+ }
+ // Test management of LLInfoItem lists
+ template<> template<>
+ void siminfo_object_t::test<2>()
+ {
+ // Test 14 : clearItems()
+ try {
+ mSim->clearItems();
+ } catch (...) {
+ fail("LLSimInfo::clearItems() at init test failed");
+ }
+
+ // Test 15 : Verify that all the lists are empty
+ LLSimInfo::item_info_list_t list;
+ list = mSim->getTeleHub();
+ ensure("LLSimInfo::getTeleHub() empty at init test failed", list.empty());
+ list = mSim->getInfoHub();
+ ensure("LLSimInfo::getInfoHub() empty at init test failed", list.empty());
+ list = mSim->getPGEvent();
+ ensure("LLSimInfo::getPGEvent() empty at init test failed", list.empty());
+ list = mSim->getMatureEvent();
+ ensure("LLSimInfo::getMatureEvent() empty at init test failed", list.empty());
+ list = mSim->getLandForSale();
+ ensure("LLSimInfo::getLandForSale() empty at init test failed", list.empty());
+ list = mSim->getAgentLocation();
+ ensure("LLSimInfo::getAgentLocation() empty at init test failed", list.empty());
+
+ // Create an item to be inserted
+ LLUUID id;
+ LLItemInfo item(X_WORLD_TEST, Y_WORLD_TEST, ITEM_NAME_TEST, id);
+
+ // Insert the item in each list
+ mSim->insertTeleHub(item);
+ mSim->insertInfoHub(item);
+ mSim->insertPGEvent(item);
+ mSim->insertMatureEvent(item);
+ mSim->insertLandForSale(item);
+ mSim->insertAgentLocation(item);
+
+ // Test 16 : Verify that the lists contain 1 item each
+ list = mSim->getTeleHub();
+ ensure("LLSimInfo::insertTeleHub() test failed", list.size() == 1);
+ list = mSim->getInfoHub();
+ ensure("LLSimInfo::insertInfoHub() test failed", list.size() == 1);
+ list = mSim->getPGEvent();
+ ensure("LLSimInfo::insertPGEvent() test failed", list.size() == 1);
+ list = mSim->getMatureEvent();
+ ensure("LLSimInfo::insertMatureEvent() test failed", list.size() == 1);
+ list = mSim->getLandForSale();
+ ensure("LLSimInfo::insertLandForSale() test failed", list.size() == 1);
+ list = mSim->getAgentLocation();
+ ensure("LLSimInfo::insertAgentLocation() test failed", list.size() == 1);
+
+ // Test 17 : clearItems()
+ try {
+ mSim->clearItems();
+ } catch (...) {
+ fail("LLSimInfo::clearItems() at end test failed");
+ }
+
+ // Test 18 : Verify that all the lists are empty again... *except* agent which is persisted!! (on purpose)
+ list = mSim->getTeleHub();
+ ensure("LLSimInfo::getTeleHub() empty after clear test failed", list.empty());
+ list = mSim->getInfoHub();
+ ensure("LLSimInfo::getInfoHub() empty after clear test failed", list.empty());
+ list = mSim->getPGEvent();
+ ensure("LLSimInfo::getPGEvent() empty after clear test failed", list.empty());
+ list = mSim->getMatureEvent();
+ ensure("LLSimInfo::getMatureEvent() empty after clear test failed", list.empty());
+ list = mSim->getLandForSale();
+ ensure("LLSimInfo::getLandForSale() empty after clear test failed", list.empty());
+ list = mSim->getAgentLocation();
+ ensure("LLSimInfo::getAgentLocation() empty after clear test failed", list.size() == 1);
+ }
+
+ // ---------------------------------------------------------------------------------------
+ // Test the LLWorldMap interface
+ // ---------------------------------------------------------------------------------------
+ // Test Setters and Accessors methods
+ template<> template<>
+ void worldmap_object_t::test<1>()
+ {
+ // Test 1 : reset()
+ try {
+ mWorld->reset();
+ } catch (...) {
+ fail("LLWorldMap::reset() at init test failed");
+ }
+ // Test 2 : clearImageRefs()
+ try {
+ mWorld->clearImageRefs();
+ } catch (...) {
+ fail("LLWorldMap::clearImageRefs() test failed");
+ }
+ // Test 3 : dropImagePriorities()
+ try {
+ mWorld->dropImagePriorities();
+ } catch (...) {
+ fail("LLWorldMap::dropImagePriorities() test failed");
+ }
+ // Test 4 : reloadItems()
+ try {
+ mWorld->reloadItems(true);
+ } catch (...) {
+ fail("LLWorldMap::reloadItems() test failed");
+ }
+ // Test 5 : updateRegions()
+ try {
+ mWorld->updateRegions(1000, 1000, 1004, 1004);
+ } catch (...) {
+ fail("LLWorldMap::updateRegions() test failed");
+ }
+ // Test 6 : equalizeBoostLevels()
+ try {
+ mWorld->equalizeBoostLevels();
+ } catch (...) {
+ fail("LLWorldMap::equalizeBoostLevels() test failed");
+ }
+ // Test 7 : getObjectsTile()
+ try {
+ LLPointer<LLViewerImage> image = mWorld->getObjectsTile((U32)(X_WORLD_TEST/REGION_WIDTH_METERS), (U32)(Y_WORLD_TEST/REGION_WIDTH_METERS), 1);
+ ensure("LLWorldMap::getObjectsTile() failed", image.isNull());
+ } catch (...) {
+ fail("LLWorldMap::getObjectsTile() test failed with exception");
+ }
+ }
+ // Test management of LLSimInfo lists
+ template<> template<>
+ void worldmap_object_t::test<2>()
+ {
+ // Test 8 : reset()
+ try {
+ mWorld->reset();
+ } catch (...) {
+ fail("LLWorldMap::reset() at init test failed");
+ }
+
+ // Test 9 : Verify that all the region list is empty
+ LLWorldMap::sim_info_map_t list;
+ list = mWorld->getRegionMap();
+ ensure("LLWorldMap::getRegionMap() empty at init test failed", list.empty());
+
+ // Test 10 : Insert a region
+ bool success;
+ LLUUID id;
+ std::string name_sim = SIM_NAME_TEST;
+ success = mWorld->insertRegion( U32(X_WORLD_TEST),
+ U32(Y_WORLD_TEST),
+ name_sim,
+ id,
+ SIM_ACCESS_PG,
+ REGION_FLAGS_SANDBOX);
+ list = mWorld->getRegionMap();
+ ensure("LLWorldMap::insertRegion() failed", success && (list.size() == 1));
+
+ // Test 11 : Insert an item in the same region -> number of regions doesn't increase
+ std::string name_item = ITEM_NAME_TEST;
+ success = mWorld->insertItem( U32(X_WORLD_TEST + REGION_WIDTH_METERS/2),
+ U32(Y_WORLD_TEST + REGION_WIDTH_METERS/2),
+ name_item,
+ id,
+ MAP_ITEM_LAND_FOR_SALE,
+ 0, 0);
+ list = mWorld->getRegionMap();
+ ensure("LLWorldMap::insertItem() in existing region failed", success && (list.size() == 1));
+
+ // Test 12 : Insert an item in another region -> number of regions increases
+ success = mWorld->insertItem( U32(X_WORLD_TEST + REGION_WIDTH_METERS*2),
+ U32(Y_WORLD_TEST + REGION_WIDTH_METERS*2),
+ name_item,
+ id,
+ MAP_ITEM_LAND_FOR_SALE,
+ 0, 0);
+ list = mWorld->getRegionMap();
+ ensure("LLWorldMap::insertItem() in unexisting region failed", success && (list.size() == 2));
+
+ // Test 13 : simInfoFromPosGlobal() in region
+ LLVector3d pos1( X_WORLD_TEST + REGION_WIDTH_METERS*2 + REGION_WIDTH_METERS/2,
+ Y_WORLD_TEST + REGION_WIDTH_METERS*2 + REGION_WIDTH_METERS/2,
+ 0.0f);
+ LLSimInfo* sim;
+ sim = mWorld->simInfoFromPosGlobal(pos1);
+ ensure("LLWorldMap::simInfoFromPosGlobal() test on existing region failed", sim != NULL);
+
+ // Test 14 : simInfoFromPosGlobal() outside region
+ LLVector3d pos2( X_WORLD_TEST + REGION_WIDTH_METERS*4 + REGION_WIDTH_METERS/2,
+ Y_WORLD_TEST + REGION_WIDTH_METERS*4 + REGION_WIDTH_METERS/2,
+ 0.0f);
+ sim = mWorld->simInfoFromPosGlobal(pos2);
+ ensure("LLWorldMap::simInfoFromPosGlobal() test outside region failed", sim == NULL);
+
+ // Test 15 : simInfoFromName()
+ sim = mWorld->simInfoFromName(name_sim);
+ ensure("LLWorldMap::simInfoFromName() test on existing region failed", sim != NULL);
+
+ // Test 16 : simInfoFromHandle()
+ U64 handle = to_region_handle_global(X_WORLD_TEST, Y_WORLD_TEST);
+ sim = mWorld->simInfoFromHandle(handle);
+ ensure("LLWorldMap::simInfoFromHandle() test on existing region failed", sim != NULL);
+
+ // Test 17 : simNameFromPosGlobal()
+ LLVector3d pos3( X_WORLD_TEST + REGION_WIDTH_METERS/2,
+ Y_WORLD_TEST + REGION_WIDTH_METERS/2,
+ 0.0f);
+ success = mWorld->simNameFromPosGlobal(pos3, name_sim);
+ ensure("LLWorldMap::simNameFromPosGlobal() test on existing region failed", success && (name_sim == SIM_NAME_TEST));
+
+ // Test 18 : reset()
+ try {
+ mWorld->reset();
+ } catch (...) {
+ fail("LLWorldMap::reset() at end test failed");
+ }
+
+ // Test 19 : Verify that all the region list is empty
+ list = mWorld->getRegionMap();
+ ensure("LLWorldMap::getRegionMap() empty at end test failed", list.empty());
+ }
+ // Test tracking
+ template<> template<>
+ void worldmap_object_t::test<3>()
+ {
+ // Point to track
+ LLVector3d pos( X_WORLD_TEST + REGION_WIDTH_METERS/2, Y_WORLD_TEST + REGION_WIDTH_METERS/2, Z_WORLD_TEST);
+
+ // Test 20 : no tracking
+ mWorld->cancelTracking();
+ ensure("LLWorldMap::cancelTracking() at begin test failed", mWorld->isTracking() == false);
+
+ // Test 21 : set tracking
+ mWorld->setTracking(pos);
+ ensure("LLWorldMap::setTracking() failed", mWorld->isTracking() && !mWorld->isTrackingValidLocation());
+
+ // Test 22 : set click and commit flags
+ mWorld->setTrackingDoubleClick();
+ ensure("LLWorldMap::setTrackingDoubleClick() failed", mWorld->isTrackingDoubleClick());
+ mWorld->setTrackingCommit();
+ ensure("LLWorldMap::setTrackingCommit() failed", mWorld->isTrackingCommit());
+
+ // Test 23 : in rectangle test
+ bool inRect = mWorld->isTrackingInRectangle( X_WORLD_TEST, Y_WORLD_TEST,
+ X_WORLD_TEST + REGION_WIDTH_METERS,
+ Y_WORLD_TEST + REGION_WIDTH_METERS);
+ ensure("LLWorldMap::isTrackingInRectangle() in rectangle failed", inRect);
+ inRect = mWorld->isTrackingInRectangle( X_WORLD_TEST + REGION_WIDTH_METERS,
+ Y_WORLD_TEST + REGION_WIDTH_METERS,
+ X_WORLD_TEST + 2 * REGION_WIDTH_METERS,
+ Y_WORLD_TEST + 2 * REGION_WIDTH_METERS);
+ ensure("LLWorldMap::isTrackingInRectangle() outside rectangle failed", !inRect);
+
+ // Test 24 : set tracking to valid and invalid
+ mWorld->setTrackingValid();
+ ensure("LLWorldMap::setTrackingValid() failed", mWorld->isTrackingValidLocation() && !mWorld->isTrackingInvalidLocation());
+ mWorld->setTrackingInvalid();
+ ensure("LLWorldMap::setTrackingInvalid() failed", !mWorld->isTrackingValidLocation() && mWorld->isTrackingInvalidLocation());
+
+ // Test 25 : getTrackedPositionGlobal()
+ LLVector3d res = mWorld->getTrackedPositionGlobal();
+ ensure("LLWorldMap::getTrackedPositionGlobal() failed", res == pos);
+
+ // Test 26 : reset tracking
+ mWorld->cancelTracking();
+ ensure("LLWorldMap::cancelTracking() at end test failed", mWorld->isTracking() == false);
+ }
+}
diff --git a/indra/newview/tests/llworldmipmap_test.cpp b/indra/newview/tests/llworldmipmap_test.cpp
new file mode 100644
index 0000000000..9938175c55
--- /dev/null
+++ b/indra/newview/tests/llworldmipmap_test.cpp
@@ -0,0 +1,176 @@
+/**
+ * @file llworldmipmap_test.cpp
+ * @author Merov Linden
+ * @date 2009-02-03
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header: almost always required for newview cpp files
+#include "../llviewerprecompiledheaders.h"
+// Class to test
+#include "../llworldmipmap.h"
+// Dependencies
+#include "../llviewerimagelist.h"
+// Tut header
+#include "../test/lltut.h"
+
+// -------------------------------------------------------------------------------------------
+// Stubbing: Declarations required to link and run the class being tested
+// Notes:
+// * Add here stubbed implementation of the few classes and methods used in the class to be tested
+// * Add as little as possible (let the link errors guide you)
+// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
+// * A simulator for a class can be implemented here. Please comment and document thoroughly.
+
+LLViewerImageList::LLViewerImageList() { }
+LLViewerImageList::~LLViewerImageList() { }
+
+LLViewerImageList gImageList;
+
+LLViewerImage* LLViewerImageList::getImageFromUrl(const std::string& url,
+ BOOL usemipmaps,
+ BOOL level_immediate,
+ LLGLint internal_format,
+ LLGLenum primary_format,
+ const LLUUID& force_id)
+{ return NULL; }
+void LLViewerImage::setBoostLevel(S32 level) { }
+
+// End Stubbing
+// -------------------------------------------------------------------------------------------
+
+// -------------------------------------------------------------------------------------------
+// TUT
+// -------------------------------------------------------------------------------------------
+namespace tut
+{
+ // Test wrapper declaration
+ struct worldmipmap_test
+ {
+ // Derived test class
+ class LLTestWorldMipmap : public LLWorldMipmap
+ {
+ // Put here stubbs of virtual methods we shouldn't call all the way down
+ };
+ // Instance to be tested
+ LLTestWorldMipmap* mMap;
+
+ // Constructor and destructor of the test wrapper
+ worldmipmap_test()
+ {
+ mMap = new LLTestWorldMipmap;
+ }
+ ~worldmipmap_test()
+ {
+ delete mMap;
+ }
+ };
+
+ // Tut templating thingamagic: test group, object and test instance
+ typedef test_group<worldmipmap_test> worldmipmap_t;
+ typedef worldmipmap_t::object worldmipmap_object_t;
+ tut::worldmipmap_t tut_worldmipmap("worldmipmap");
+
+ // ---------------------------------------------------------------------------------------
+ // Test functions
+ // Notes:
+ // * Test as many as you possibly can without requiring a full blown simulation of everything
+ // * The tests are executed in sequence so the test instance state may change between calls
+ // * Remember that you cannot test private methods with tut
+ // ---------------------------------------------------------------------------------------
+ // Test static methods
+ // Test 1 : scaleToLevel()
+ template<> template<>
+ void worldmipmap_object_t::test<1>()
+ {
+ S32 level = mMap->scaleToLevel(0.0);
+ ensure("scaleToLevel() test 1 failed", level == LLWorldMipmap::MAP_LEVELS);
+ level = mMap->scaleToLevel(LLWorldMipmap::MAP_TILE_SIZE);
+ ensure("scaleToLevel() test 2 failed", level == 1);
+ level = mMap->scaleToLevel(10 * LLWorldMipmap::MAP_TILE_SIZE);
+ ensure("scaleToLevel() test 3 failed", level == 1);
+ }
+ // Test 2 : globalToMipmap()
+ template<> template<>
+ void worldmipmap_object_t::test<2>()
+ {
+ U32 grid_x, grid_y;
+ mMap->globalToMipmap(1000.f*REGION_WIDTH_METERS, 1000.f*REGION_WIDTH_METERS, 1, &grid_x, &grid_y);
+ ensure("globalToMipmap() test 1 failed", (grid_x == 1000) && (grid_y == 1000));
+ mMap->globalToMipmap(0.0, 0.0, LLWorldMipmap::MAP_LEVELS, &grid_x, &grid_y);
+ ensure("globalToMipmap() test 2 failed", (grid_x == 0) && (grid_y == 0));
+ }
+ // Test 3 : getObjectsTile()
+ template<> template<>
+ void worldmipmap_object_t::test<3>()
+ {
+ // Depends on some inline methods in LLViewerImage... Thinking about how to make this work
+ // LLPointer<LLViewerImage> img = mMap->getObjectsTile(0, 0, 1);
+ // ensure("getObjectsTile() test failed", img.isNull());
+ }
+ // Test 4 : equalizeBoostLevels()
+ template<> template<>
+ void worldmipmap_object_t::test<4>()
+ {
+ try
+ {
+ mMap->equalizeBoostLevels();
+ }
+ catch (...)
+ {
+ fail("equalizeBoostLevels() test failed");
+ }
+ }
+ // Test 5 : dropBoostLevels()
+ template<> template<>
+ void worldmipmap_object_t::test<5>()
+ {
+ try
+ {
+ mMap->dropBoostLevels();
+ }
+ catch (...)
+ {
+ fail("dropBoostLevels() test failed");
+ }
+ }
+ // Test 6 : reset()
+ template<> template<>
+ void worldmipmap_object_t::test<6>()
+ {
+ try
+ {
+ mMap->reset();
+ }
+ catch (...)
+ {
+ fail("reset() test failed");
+ }
+ }
+}