/* 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 "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("result_list"); mResultList->setDoubleClickCallback(boost::bind(&JCFloaterAreaSearch::onDoubleClick,this)); mResultList->sortByColumn("Name", TRUE); mCounterText = getChild("counter"); getChild("Refresh")->setClickedCallback(boost::bind(&JCFloaterAreaSearch::onRefresh,this)); getChild("Stop")->setClickedCallback(boost::bind(&JCFloaterAreaSearch::onStop,this)); getChild("Name query chunk")->setCommitCallback(boost::bind(&JCFloaterAreaSearch::onCommitLine,this,_1,_2,LIST_OBJECT_NAME)); getChild("Description query chunk")->setCommitCallback(boost::bind(&JCFloaterAreaSearch::onCommitLine,this,_1,_2,LIST_OBJECT_DESC)); getChild("Owner query chunk")->setCommitCallback(boost::bind(&JCFloaterAreaSearch::onCommitLine,this,_1,_2,LIST_OBJECT_OWNER)); getChild("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")); } } void JCFloaterAreaSearch::onDoubleClick() { LLScrollListItem *item = mResultList->getFirstSelected(); if (!item) return; LLUUID object_id = item->getUUID(); std::map::iterator it = mCachedObjects.find(object_id); if(it != mCachedObjects.end()) { LLViewerObject* objectp = gObjectList.findObject(object_id); if (objectp) { LLTracker::trackLocation(objectp->getPositionGlobal(), it->second.name, "", LLTracker::LOCATION_ITEM); } } } void JCFloaterAreaSearch::onStop() { mStopped = true; mPendingObjects.clear(); mCounterText->setText(std::string("Stopped")); } void JCFloaterAreaSearch::onRefresh() { //llinfos << "Clicked search" << llendl; 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; //llinfos << "loaded " << name << " with "<< text << llendl; checkRegion(); results(); } bool JCFloaterAreaSearch::requestIfNeeded(LLUUID object_id) { if (!mCachedObjects.count(object_id) && !mPendingObjects.count(object_id)) { if(mStopped) return true; //llinfos << "not in list" << llendl; 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(); //llinfos << "Sent data request for object " << object_id << llendl; return true; } return false; } void JCFloaterAreaSearch::results() { if (!getVisible()) return; if (mPendingObjects.size() > 0 && mLastUpdateTimer.getElapsedTimeF32() < min_refresh_interval) return; //llinfos << "results()" << llendl; 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)) { std::map::iterator it = mCachedObjects.find(object_id); if(it != mCachedObjects.end()) { //llinfos << "all entries are \"\" or we have data" << llendl; 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); //llinfos << "both names are loaded or aren't needed" << llendl; 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]) != -1) && (mFilterStrings[LIST_OBJECT_DESC].empty() || object_desc.find(mFilterStrings[LIST_OBJECT_DESC]) != -1) && (mFilterStrings[LIST_OBJECT_OWNER].empty() || object_owner.find(mFilterStrings[LIST_OBJECT_OWNER]) != -1) && (mFilterStrings[LIST_OBJECT_GROUP].empty() || object_group.find(mFilterStrings[LIST_OBJECT_GROUP]) != -1)) { //llinfos << "pass" << llendl; 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); std::set::iterator 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); 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)); //llinfos << "Got info for " << (exists ? "requested" : "unknown") << " object " << object_id << llendl; }