310 lines
11 KiB
C++
310 lines
11 KiB
C++
/* Copyright (c) 2009
|
|
*
|
|
* Modular Systems Ltd. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or
|
|
* without modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
* 3. Neither the name Modular Systems Ltd nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY MODULAR SYSTEMS LTD AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MODULAR SYSTEMS OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Modified, debugged, optimized and improved by Henri Beauchamp Feb 2010.
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "jcfloaterareasearch.h"
|
|
|
|
#include "llfiltereditor.h"
|
|
#include "llscrolllistctrl.h"
|
|
#include "llscrolllistitem.h"
|
|
#include "lluictrlfactory.h"
|
|
|
|
#include "llagent.h"
|
|
#include "llagentcamera.h"
|
|
#include "lltracker.h"
|
|
#include "llviewerobjectlist.h"
|
|
|
|
const std::string request_string = "JCFloaterAreaSearch::Requested_\xF8\xA7\xB5";
|
|
const F32 min_refresh_interval = 0.25f; // Minimum interval between list refreshes in seconds.
|
|
|
|
JCFloaterAreaSearch::JCFloaterAreaSearch(const LLSD& data) :
|
|
LLFloater(),
|
|
mCounterText(0),
|
|
mResultList(0),
|
|
mLastRegion(0),
|
|
mStopped(false)
|
|
{
|
|
mLastUpdateTimer.reset();
|
|
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_area_search.xml");
|
|
}
|
|
|
|
JCFloaterAreaSearch::~JCFloaterAreaSearch()
|
|
{
|
|
}
|
|
|
|
void JCFloaterAreaSearch::close(bool app)
|
|
{
|
|
if (app || mStopped)
|
|
{
|
|
LLFloater::close(app);
|
|
}
|
|
else
|
|
{
|
|
setVisible(FALSE);
|
|
}
|
|
}
|
|
|
|
BOOL JCFloaterAreaSearch::postBuild()
|
|
{
|
|
mResultList = getChild<LLScrollListCtrl>("result_list");
|
|
mResultList->setDoubleClickCallback(boost::bind(&JCFloaterAreaSearch::onDoubleClick,this));
|
|
mResultList->sortByColumn("Name", TRUE);
|
|
auto tp = getChild<LLButton>("TP");
|
|
auto look = getChild<LLButton>("Look");
|
|
mResultList->setCommitOnSelectionChange(true);
|
|
mResultList->setCommitCallback([=](LLUICtrl* ctrl, const LLSD& param){
|
|
bool enabled = mResultList->getNumSelected() == 1;
|
|
tp->setEnabled(enabled);
|
|
look->setEnabled(enabled);
|
|
});
|
|
|
|
mCounterText = getChild<LLTextBox>("counter");
|
|
|
|
getChild<LLButton>("Refresh")->setClickedCallback(boost::bind(&JCFloaterAreaSearch::onRefresh,this));
|
|
getChild<LLButton>("Stop")->setClickedCallback(boost::bind(&JCFloaterAreaSearch::onStop,this));
|
|
tp->setClickedCallback(boost::bind(&JCFloaterAreaSearch::teleportToSelected, this));
|
|
look->setClickedCallback(boost::bind(&JCFloaterAreaSearch::lookAtSelected, this));
|
|
|
|
getChild<LLFilterEditor>("Name query chunk")->setCommitCallback(boost::bind(&JCFloaterAreaSearch::onCommitLine,this,_1,_2,LIST_OBJECT_NAME));
|
|
getChild<LLFilterEditor>("Description query chunk")->setCommitCallback(boost::bind(&JCFloaterAreaSearch::onCommitLine,this,_1,_2,LIST_OBJECT_DESC));
|
|
getChild<LLFilterEditor>("Owner query chunk")->setCommitCallback(boost::bind(&JCFloaterAreaSearch::onCommitLine,this,_1,_2,LIST_OBJECT_OWNER));
|
|
getChild<LLFilterEditor>("Group query chunk")->setCommitCallback(boost::bind(&JCFloaterAreaSearch::onCommitLine,this,_1,_2,LIST_OBJECT_GROUP));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void JCFloaterAreaSearch::onOpen()
|
|
{
|
|
checkRegion();
|
|
results();
|
|
}
|
|
|
|
void JCFloaterAreaSearch::checkRegion(bool force_clear)
|
|
{
|
|
// Check if we changed region, and if we did, clear the object details cache.
|
|
LLViewerRegion* region = gAgent.getRegion();
|
|
if (force_clear || region != mLastRegion)
|
|
{
|
|
mLastRegion = region;
|
|
mPendingObjects.clear();
|
|
mCachedObjects.clear();
|
|
mResultList->deleteAllItems();
|
|
mCounterText->setText(std::string("Listed/Pending/Total"));
|
|
}
|
|
}
|
|
|
|
LLViewerObject* JCFloaterAreaSearch::getSelectedObject()
|
|
{
|
|
if (LLScrollListItem* item = mResultList->getFirstSelected())
|
|
return gObjectList.findObject(item->getUUID());
|
|
return NULL;
|
|
}
|
|
|
|
void JCFloaterAreaSearch::onDoubleClick()
|
|
{
|
|
if (LLViewerObject* objectp = getSelectedObject())
|
|
LLTracker::trackLocation(objectp->getPositionGlobal(), mCachedObjects[objectp->getID()].name, "", LLTracker::LOCATION_ITEM);
|
|
}
|
|
|
|
void JCFloaterAreaSearch::teleportToSelected()
|
|
{
|
|
if (LLViewerObject* objectp = getSelectedObject())
|
|
gAgent.teleportViaLocation(objectp->getPositionGlobal());
|
|
}
|
|
|
|
void JCFloaterAreaSearch::lookAtSelected()
|
|
{
|
|
if (LLScrollListItem* item = mResultList->getFirstSelected())
|
|
gAgentCamera.lookAtObject(item->getUUID(), false);
|
|
}
|
|
|
|
|
|
void JCFloaterAreaSearch::onStop()
|
|
{
|
|
mStopped = true;
|
|
mPendingObjects.clear();
|
|
mCounterText->setText(std::string("Stopped"));
|
|
}
|
|
|
|
void JCFloaterAreaSearch::onRefresh()
|
|
{
|
|
//LL_INFOS() << "Clicked search" << LL_ENDL;
|
|
mStopped = false;
|
|
checkRegion(true);
|
|
results();
|
|
}
|
|
|
|
void JCFloaterAreaSearch::onCommitLine(LLUICtrl* caller, const LLSD& value, OBJECT_COLUMN_ORDER type)
|
|
{
|
|
std::string text = value.asString();
|
|
LLStringUtil::toLower(text);
|
|
caller->setValue(text);
|
|
mFilterStrings[type] = text;
|
|
//LL_INFOS() << "loaded " << name << " with "<< text << LL_ENDL;
|
|
checkRegion();
|
|
results();
|
|
}
|
|
|
|
bool JCFloaterAreaSearch::requestIfNeeded(LLUUID object_id)
|
|
{
|
|
if (!mCachedObjects.count(object_id) && !mPendingObjects.count(object_id))
|
|
{
|
|
if(mStopped)
|
|
return true;
|
|
|
|
//LL_INFOS() << "not in list" << LL_ENDL;
|
|
mPendingObjects.insert(object_id);
|
|
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->nextBlockFast(_PREHASH_ObjectData);
|
|
msg->addU32Fast(_PREHASH_RequestFlags, 0 );
|
|
msg->addUUIDFast(_PREHASH_ObjectID, object_id);
|
|
gAgent.sendReliableMessage();
|
|
//LL_INFOS() << "Sent data request for object " << object_id << LL_ENDL;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void JCFloaterAreaSearch::results()
|
|
{
|
|
if (!getVisible()) return;
|
|
|
|
if (mPendingObjects.size() > 0 && mLastUpdateTimer.getElapsedTimeF32() < min_refresh_interval) return;
|
|
//LL_INFOS() << "results()" << LL_ENDL;
|
|
uuid_vec_t selected = mResultList->getSelectedIDs();
|
|
S32 scrollpos = mResultList->getScrollPos();
|
|
mResultList->deleteAllItems();
|
|
S32 i;
|
|
S32 total = gObjectList.getNumObjects();
|
|
|
|
LLViewerRegion* our_region = gAgent.getRegion();
|
|
for (i = 0; i < total; i++)
|
|
{
|
|
LLViewerObject *objectp = gObjectList.getObject(i);
|
|
if (objectp)
|
|
{
|
|
if (objectp->getRegion() == our_region && !objectp->isAvatar() && objectp->isRoot() &&
|
|
!objectp->flagTemporary() && !objectp->flagTemporaryOnRez())
|
|
{
|
|
LLUUID object_id = objectp->getID();
|
|
if(!requestIfNeeded(object_id))
|
|
{
|
|
auto it = mCachedObjects.find(object_id);
|
|
if(it != mCachedObjects.end())
|
|
{
|
|
//LL_INFOS() << "all entries are \"\" or we have data" << LL_ENDL;
|
|
std::string object_name = it->second.name;
|
|
std::string object_desc = it->second.desc;
|
|
std::string object_owner;
|
|
std::string object_group;
|
|
gCacheName->getFullName(it->second.owner_id, object_owner);
|
|
gCacheName->getGroupName(it->second.group_id, object_group);
|
|
//LL_INFOS() << "both names are loaded or aren't needed" << LL_ENDL;
|
|
std::string onU = object_owner;
|
|
std::string cnU = object_group;
|
|
LLStringUtil::toLower(object_name);
|
|
LLStringUtil::toLower(object_desc);
|
|
LLStringUtil::toLower(object_owner);
|
|
LLStringUtil::toLower(object_group);
|
|
if ((mFilterStrings[LIST_OBJECT_NAME].empty() || object_name.find(mFilterStrings[LIST_OBJECT_NAME]) != std::string::npos) &&
|
|
(mFilterStrings[LIST_OBJECT_DESC].empty() || object_desc.find(mFilterStrings[LIST_OBJECT_DESC]) != std::string::npos) &&
|
|
(mFilterStrings[LIST_OBJECT_OWNER].empty() || object_owner.find(mFilterStrings[LIST_OBJECT_OWNER]) != std::string::npos) &&
|
|
(mFilterStrings[LIST_OBJECT_GROUP].empty() || object_group.find(mFilterStrings[LIST_OBJECT_GROUP]) != std::string::npos))
|
|
{
|
|
//LL_INFOS() << "pass" << LL_ENDL;
|
|
LLSD element;
|
|
element["id"] = object_id;
|
|
element["columns"][LIST_OBJECT_NAME]["column"] = "Name";
|
|
element["columns"][LIST_OBJECT_NAME]["type"] = "text";
|
|
element["columns"][LIST_OBJECT_NAME]["value"] = it->second.name;
|
|
element["columns"][LIST_OBJECT_DESC]["column"] = "Description";
|
|
element["columns"][LIST_OBJECT_DESC]["type"] = "text";
|
|
element["columns"][LIST_OBJECT_DESC]["value"] = it->second.desc;
|
|
element["columns"][LIST_OBJECT_OWNER]["column"] = "Owner";
|
|
element["columns"][LIST_OBJECT_OWNER]["type"] = "text";
|
|
element["columns"][LIST_OBJECT_OWNER]["value"] = onU;
|
|
element["columns"][LIST_OBJECT_GROUP]["column"] = "Group";
|
|
element["columns"][LIST_OBJECT_GROUP]["type"] = "text";
|
|
element["columns"][LIST_OBJECT_GROUP]["value"] = cnU; //ai->second;
|
|
mResultList->addElement(element, ADD_BOTTOM);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mResultList->updateSort();
|
|
mResultList->selectMultiple(selected);
|
|
mResultList->setScrollPos(scrollpos);
|
|
mCounterText->setText(llformat("%d listed/%d pending/%d total", mResultList->getItemCount(), mPendingObjects.size(), mPendingObjects.size()+mCachedObjects.size()));
|
|
mLastUpdateTimer.reset();
|
|
}
|
|
|
|
// static
|
|
void JCFloaterAreaSearch::processObjectPropertiesFamily(LLMessageSystem* msg, void** user_data)
|
|
{
|
|
JCFloaterAreaSearch* floater = findInstance();
|
|
if(!floater)
|
|
return;
|
|
floater->checkRegion();
|
|
|
|
LLUUID object_id;
|
|
msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, object_id);
|
|
|
|
auto it = floater->mPendingObjects.find(object_id);
|
|
if(it != floater->mPendingObjects.end())
|
|
floater->mPendingObjects.erase(it);
|
|
//else if(floater->mCachedObjects.count(object_id)) //Let entries update.
|
|
// return;
|
|
|
|
ObjectData* data = &floater->mCachedObjects[object_id];
|
|
// We cache unknown objects (to avoid having to request them later)
|
|
// and requested objects.
|
|
msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, data->owner_id);
|
|
if (auto obj = gObjectList.findObject(object_id)) obj->mOwnerID = data->owner_id; // Singu Note: Try to get Owner whenever possible
|
|
msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, data->group_id);
|
|
msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, data->name);
|
|
msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, data->desc);
|
|
gCacheName->get(data->owner_id, false, boost::bind(&JCFloaterAreaSearch::results,floater));
|
|
gCacheName->get(data->group_id, true, boost::bind(&JCFloaterAreaSearch::results,floater));
|
|
//LL_INFOS() << "Got info for " << (exists ? "requested" : "unknown") << " object " << object_id << LL_ENDL;
|
|
}
|