/* 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 "lluuid.h" #include "lluictrlfactory.h" #include "llscrolllistctrl.h" #include "llagent.h" #include "llfloatertools.h" #include "llmenugl.h" #include "llselectmgr.h" #include "lltoolcomp.h" #include "lltoolmgr.h" #include "lltracker.h" #include "llviewerjoystick.h" #include "llviewerobjectlist.h" #include "llviewercontrol.h" #include "jcfloaterareasearch.h" JCFloaterAreaSearch* JCFloaterAreaSearch::sInstance = NULL; LLViewerRegion* JCFloaterAreaSearch::sLastRegion = NULL; S32 JCFloaterAreaSearch::sRequested = 0; std::map JCFloaterAreaSearch::sObjectDetails; std::string JCFloaterAreaSearch::sSearchedName; std::string JCFloaterAreaSearch::sSearchedDesc; std::string JCFloaterAreaSearch::sSearchedOwner; std::string JCFloaterAreaSearch::sSearchedGroup; const std::string request_string = "JCFloaterAreaSearch::Requested_๘งต"; const F32 min_refresh_interval = 0.25f; // Minimum interval between list refreshes in seconds. JCFloaterAreaSearch::JCFloaterAreaSearch() : LLFloater(), mCounterText(0), mResultList(0) { llassert_always(sInstance == NULL); sInstance = this; mLastUpdateTimer.reset(); // Register event listeners for popup menu mPopupMenuHandler = new PopupMenuHandler(this); mPopupMenuHandler->registerListener(this, "Popup.HandleMenu"); LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_areasearch.xml", this); if (!menu) { menu = new LLMenuGL(LLStringUtil::null); } menu->setVisible(FALSE); mPopupMenuHandle = menu->getHandle(); } JCFloaterAreaSearch::~JCFloaterAreaSearch() { sInstance = NULL; } void JCFloaterAreaSearch::close(bool app) { if (app) { LLFloater::close(app); } else { if (sInstance) { sInstance->setVisible(FALSE); } } } BOOL JCFloaterAreaSearch::postBuild() { mResultList = getChild("result_list"); mResultList->setCallbackUserData(this); mResultList->setDoubleClickCallback(onDoubleClick); mResultList->setRightMouseDownCallback(onRightMouseDown); mResultList->sortByColumn("Name", TRUE); mCounterText = getChild("counter"); childSetAction("Refresh", search, this); childSetAction("Stop", cancel, this); childSetKeystrokeCallback("Name query chunk", onCommitLine, 0); childSetKeystrokeCallback("Description query chunk", onCommitLine, 0); childSetKeystrokeCallback("Owner query chunk", onCommitLine, 0); childSetKeystrokeCallback("Group query chunk", onCommitLine, 0); return TRUE; } // static void JCFloaterAreaSearch::checkRegion() { // Check if we changed region, and if we did, clear the object details cache. LLViewerRegion* region = gAgent.getRegion(); if (region != sLastRegion) { sLastRegion = region; sRequested = 0; sObjectDetails.clear(); if (sInstance) { sInstance->mResultList->deleteAllItems(); sInstance->mCounterText->setText(std::string("Listed/Pending/Total")); } } } // static void JCFloaterAreaSearch::toggle() { if (sInstance) { if (sInstance->getVisible()) { sInstance->setVisible(FALSE); } else { checkRegion(); sInstance->setVisible(TRUE); } } else { sInstance = new JCFloaterAreaSearch(); LLUICtrlFactory::getInstance()->buildFloater(sInstance, "floater_area_search.xml"); } } // static void JCFloaterAreaSearch::onDoubleClick(void *userdata) { JCFloaterAreaSearch *self = (JCFloaterAreaSearch*)userdata; LLScrollListItem *item = self->mResultList->getFirstSelected(); if (!item) return; LLUUID object_id = item->getUUID(); LLViewerObject* objectp = gObjectList.findObject(object_id); if (objectp) { LLTracker::trackLocation(objectp->getPositionGlobal(), sObjectDetails[object_id].name, "", LLTracker::LOCATION_ITEM); } } //static void JCFloaterAreaSearch::onRightMouseDown(S32 x, S32 y, void *userdata) { JCFloaterAreaSearch* self = (JCFloaterAreaSearch*)userdata; self->setFocus( TRUE ); LLMenuGL* menu = (LLMenuGL*)self->mPopupMenuHandle.get(); if(menu) { if(menu->getVisible()) { menu->setVisible(FALSE); } else { LLScrollListItem *item = self->mResultList->getFirstSelected(); if (item) { self->mSelectedItem = item; menu->setVisible(TRUE); menu->setFocus(TRUE); menu->arrange(); menu->updateParent(LLMenuGL::sMenuContainer); LLMenuGL::showPopup(self, menu, x, y+50); } else { self->mSelectedItem = NULL; } } } } JCFloaterAreaSearch::PopupMenuHandler::PopupMenuHandler(const JCFloaterAreaSearch* instance) : mInstance(instance) { } // static bool JCFloaterAreaSearch::PopupMenuHandler::handleEvent(LLPointer event, const LLSD& userdata) { std::string command = userdata.asString(); JCFloaterAreaSearch* self = (JCFloaterAreaSearch*)mInstance; if (self && self->mSelectedItem && !command.empty()) { LLUUID object_id = self->mSelectedItem->getUUID(); LLViewerObject* object = gObjectList.findObject(object_id); if (object && !object->isAvatar()) { if ("teleport" == command) { LLVector3d pos = object->getPositionGlobal(); gAgent.teleportViaLocation(pos); } else { if ("cam" || "edit" == command) { gAgent.setFocusOnAvatar(FALSE, FALSE); gAgent.changeCameraToThirdPerson(); gAgent.setFocusGlobal(object->getPositionGlobal(), object_id); gAgent.setCameraPosAndFocusGlobal(object->getPositionGlobal() + LLVector3d(3.5,1.35,0.75) * object->getRotation(), object->getPositionGlobal(), object_id ); if ("edit" == command) { if(!object->isSelected()) { LLSelectMgr::getInstance()->deselectAll(); LLSelectMgr::getInstance()->selectObjectAndFamily(object); } gFloaterTools->open(); LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); gFloaterTools->setEditTool( LLToolCompTranslate::getInstance() ); LLViewerJoystick::getInstance()->moveObjects(true); LLViewerJoystick::getInstance()->setNeedsReset(true); } } } } } return true; } // static void JCFloaterAreaSearch::cancel(void* data) { checkRegion(); if (sInstance) { sInstance->close(TRUE); } sSearchedName = ""; sSearchedDesc = ""; sSearchedOwner = ""; sSearchedGroup = ""; } // static void JCFloaterAreaSearch::search(void* data) { //llinfos << "Clicked search" << llendl; checkRegion(); results(); } // static void JCFloaterAreaSearch::onCommitLine(LLLineEditor* line, void* user_data) { std::string name = line->getName(); std::string text = line->getText(); LLStringUtil::toLower(text); line->setText(text); if (name == "Name query chunk") sSearchedName = text; else if (name == "Description query chunk") sSearchedDesc = text; else if (name == "Owner query chunk") sSearchedOwner = text; else if (name == "Group query chunk") sSearchedGroup = text; //llinfos << "loaded " << name << " with "<< text << llendl; if (text.length() > 3) { checkRegion(); results(); } } // static void JCFloaterAreaSearch::requestIfNeeded(LLViewerObject *objectp) { LLUUID object_id = objectp->getID(); if (sObjectDetails.count(object_id) == 0) { //llinfos << "not in list" << llendl; AObjectDetails* details = &sObjectDetails[object_id]; details->name = request_string; details->desc = request_string; details->owner_id = LLUUID::null; details->group_id = LLUUID::null; 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; sRequested++; } } // static void JCFloaterAreaSearch::results() { if (!sInstance) return; if (!(sInstance->getVisible())) return; if (sRequested > 0 && sInstance->mLastUpdateTimer.getElapsedTimeF32() < min_refresh_interval) return; //llinfos << "results()" << llendl; LLDynamicArray selected = sInstance->mResultList->getSelectedIDs(); S32 scrollpos = sInstance->mResultList->getScrollPos(); sInstance->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 (sObjectDetails.count(object_id) == 0) { //llinfos << "not all entries are \"\"" << llendl; requestIfNeeded(objectp); } else { //llinfos << "all entries are \"\" or we have data" << llendl; AObjectDetails* details = &sObjectDetails[object_id]; std::string object_name = details->name; std::string object_desc = details->desc; std::string object_owner; std::string object_group; gCacheName->getFullName(details->owner_id, object_owner); gCacheName->getGroupName(details->group_id, object_group); if (object_name != request_string) { //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 ((sSearchedName == "" || object_name.find(sSearchedName) != -1) && (sSearchedDesc == "" || object_desc.find(sSearchedDesc) != -1) && (sSearchedOwner == "" || object_owner.find(sSearchedOwner) != -1) && (sSearchedGroup == "" || object_group.find(sSearchedGroup) != -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"] = details->name; //item->getName();//ai->second//"avatar_icon"; element["columns"][LIST_OBJECT_DESC]["column"] = "Description"; element["columns"][LIST_OBJECT_DESC]["type"] = "text"; element["columns"][LIST_OBJECT_DESC]["value"] = details->desc; //ai->second; element["columns"][LIST_OBJECT_OWNER]["column"] = "Owner"; element["columns"][LIST_OBJECT_OWNER]["type"] = "text"; element["columns"][LIST_OBJECT_OWNER]["value"] = onU; //ai->first; element["columns"][LIST_OBJECT_GROUP]["column"] = "Group"; element["columns"][LIST_OBJECT_GROUP]["type"] = "text"; element["columns"][LIST_OBJECT_GROUP]["value"] = cnU; //ai->second; sInstance->mResultList->addElement(element, ADD_BOTTOM); } } } } } } sInstance->mResultList->sortItems(); sInstance->mResultList->selectMultiple(selected); sInstance->mResultList->setScrollPos(scrollpos); sInstance->mCounterText->setText(llformat("%d listed/%d pending/%d total", sInstance->mResultList->getItemCount(), sRequested, sObjectDetails.size())); sInstance->mLastUpdateTimer.reset(); } // static void JCFloaterAreaSearch::callbackLoadOwnerName(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data) { results(); } // static void JCFloaterAreaSearch::processObjectPropertiesFamily(LLMessageSystem* msg, void** user_data) { checkRegion(); LLUUID object_id; msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, object_id); bool exists = (sObjectDetails.count(object_id) != 0); AObjectDetails* details = &sObjectDetails[object_id]; if (!exists || details->name == request_string) { // We cache unknown objects (to avoid having to request them later) // and requested objects. if (exists && sRequested > 0) sRequested--; msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, details->owner_id); msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, details->group_id); msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, details->name); msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, details->desc); gCacheName->get(details->owner_id, FALSE, callbackLoadOwnerName); gCacheName->get(details->group_id, TRUE, callbackLoadOwnerName); //llinfos << "Got info for " << (exists ? "requested" : "unknown") << " object " << object_id << llendl; } }