From 4d086c7faa2a4347a89cce5e86801d8f4d521551 Mon Sep 17 00:00:00 2001 From: Techwolf Lupindo Date: Mon, 29 Oct 2012 21:39:06 -0400 Subject: [PATCH] Firestorm Area Search re-write. See full comment comments for details. Engine redone and is now 10x to 100x faster and many bugs fixed. UI changed and added tabs. Added search for creator, last owner. Added regular expressions to the find tab. Added filter result list for: For sale, distance, mouse click action, touch, locked, physical, phantom, tempary, and attachment. Can include or exclude attachments, physical, or temporary objects in the search. Added distance, creator, last owner columes. Options to display any columes. Right click menu on result list to perform action on selected objects: Script info, touch, teleport to, inspect, edit, return, delete, and buy object. Display beacons on found objects. --- indra/llui/llscrolllistctrl.cpp | 48 + indra/llui/llscrolllistctrl.h | 3 + indra/newview/app_settings/settings.xml | 11 + indra/newview/fsareasearch.cpp | 1794 +++++++++++++++-- indra/newview/fsareasearch.h | 398 +++- indra/newview/llstartup.cpp | 8 +- indra/newview/llviewermenu.h | 5 + indra/newview/llviewermessage.cpp | 14 +- indra/newview/llviewermessage.h | 6 +- .../default/xui/en/floater_fs_area_search.xml | 837 ++++++-- .../default/xui/en/menu_fs_area_search.xml | 77 + .../skins/default/xui/en/notifications.xml | 9 + 12 files changed, 2766 insertions(+), 444 deletions(-) create mode 100644 indra/newview/skins/default/xui/en/menu_fs_area_search.xml diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 5c4928e035..75a8f17ae0 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -2758,6 +2758,54 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params dirtyColumns(); } +// area search +// area search support for deleting a column +LLScrollListColumn::Params LLScrollListCtrl::delColumn(std::string name) +{ + std::vector column_params; + LLScrollListColumn::Params params; + + // save params for each column + ordered_columns_t::iterator column_itor; + for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) + { + LLScrollListColumn* column = (*column_itor); + params.header.label = column->mLabel; + params.name = column->mName; + params.width.dynamic_width = column->mDynamicWidth; + params.width.relative_width = column->mRelWidth; + params.width.pixel_width = column->getWidth(); + params.halign = column->mFontAlignment; + + LLScrollColumnHeader *header = column->mHeader; + if (header) + { + params.tool_tip = header->getToolTip(); + } + + column_params.push_back(params); + } + + clearColumns(); + + // restore colums except named column. + for (std::vector::iterator iter = column_params.begin(); iter != column_params.end(); ++iter) + { + std::string i_name = iter->name; + if (i_name != name) + { + addColumn((*iter)); + } + else + { + params = (*iter); + } + } + + return params; +} +// area search + // static void LLScrollListCtrl::onClickColumn(void *userdata) { diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 9c067b5d5a..3fa0cdb810 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -156,6 +156,9 @@ public: // Adds a single column descriptor: ["name" : string, "label" : string, "width" : integer, "relwidth" : integer ] virtual void addColumn(const LLScrollListColumn::Params& column, EAddPosition pos = ADD_BOTTOM); virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM); + // area search, allow deleting a column + virtual LLScrollListColumn::Params delColumn(std::string name); + // area search virtual void clearColumns(); virtual void setColumnLabel(const std::string& column, const std::string& label); virtual bool preProcessChildNode(LLXMLNodePtr child); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 25d1dd2dbe..0071367f4c 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -18319,6 +18319,17 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 + FSAreaSearchAdvanced + + Comment + Displays the advanced settings tab + Persist + 1 + Type + Boolean + Value + 0 + FSCollisionMessagesInChat Comment diff --git a/indra/newview/fsareasearch.cpp b/indra/newview/fsareasearch.cpp index 87f0ce9a9f..3cf24b812e 100644 --- a/indra/newview/fsareasearch.cpp +++ b/indra/newview/fsareasearch.cpp @@ -1,35 +1,28 @@ -/* Copyright (c) 2009 +/** + * @file fsareasearch.cpp + * @brief Floater to search and list objects in view or is known to the viewer. * - * Modular Systems Ltd. All rights reserved. + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (c) 2012 Techwolf Lupindo * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * 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. - * Refactored for Viewer2 by Kadah Coba, April 2011 + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The Phoenix Viewer Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.phoenixviewer.com + * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" @@ -45,11 +38,34 @@ #include "llviewerobjectlist.h" #include "llviewercontrol.h" #include "llviewerparcelmgr.h" - #include //for boost::ifind_first +#include "llviewerregion.h" +#include "llselectmgr.h" +#include "llcallbacklist.h" +#include "lltoolpie.h" +#include "llsaleinfo.h" +#include "llcheckboxctrl.h" +#include "llviewermenu.h" // handle_object_touch(), handle_buy() +#include "llclickaction.h" +#include "lltabcontainer.h" +#include "llspinctrl.h" +#include "lltoolgrab.h" +#include "fslslbridge.h" +#include "llcombobox.h" +#include "llnotificationsutil.h" -const std::string request_string = "FSAreaSearch::Requested_ø§µ"; -const F32 min_refresh_interval = 0.25f; // Minimum interval between list refreshes in seconds. + +// max number of objects that can be (de-)selected in a single packet. +const S32 MAX_OBJECTS_PER_PACKET = 254; + +// time in seconds between refreshes when active +const F32 REFRESH_INTERVAL = 1.0f; + +// this is used to prevent refreshing too often and affecting performance. +const F32 MIN_REFRESH_INTERVAL = 0.25f; + +// how far the avatar needs to move to trigger a distance update +const F32 MIN_DISTANCE_MOVED = 1.0f; class FSAreaSearch::FSParcelChangeObserver : public LLParcelObserver @@ -70,230 +86,1584 @@ private: }; FSAreaSearch::FSAreaSearch(const LLSD& key) : -LLFloater(key), -mCounterText(0), -mResultList(0) + LLFloater(key), + mActive(false), + mFilterForSale(FALSE), + mFilterForSaleMin(0), + mFilterForSaleMax(999999), + mFilterPhysicial(FALSE), + mFilterTemporary(FALSE), + mRegexSearch(FALSE), + mFilterClickAction(false), + mFilterLocked(false), + mFilterPhantom(false), + mFilterAttachment(false), + mBeaconColor(), + mBeaconTextColor(), + mBeacons(false), + mExcludeAttachment(true), + mExcludeTempary(true), + mExcludePhysics(true) { - mLastUpdateTimer.reset(); + //TODO: Multi-floater support and get rid of the singletin. + mInstance = this; + + mFactoryMap["area_search_list_panel"] = LLCallbackMap(createPanelList, this); + mFactoryMap["area_search_find_panel"] = LLCallbackMap(createPanelFind, this); + mFactoryMap["area_search_filter_panel"] = LLCallbackMap(createPanelFilter, this); + mFactoryMap["area_search_advanced_panel"] = LLCallbackMap(createPanelAdvanced, this); + mFactoryMap["area_search_options_panel"] = LLCallbackMap(createPanelOptions, this); + + // Register an idle update callback + gIdleCallbacks.addFunction(idle, this); + + mParcelChangedObserver = new FSParcelChangeObserver(this); + LLViewerParcelMgr::getInstance()->addObserver(mParcelChangedObserver); } FSAreaSearch::~FSAreaSearch() { + if (!gIdleCallbacks.deleteFunction(idle, this)) + { + LL_WARNS("FSAreaSearch") << "FSAreaSearch::~FSAreaSearch() failed to delete callback" << LL_ENDL; + } + if (mParcelChangedObserver) { LLViewerParcelMgr::getInstance()->removeObserver(mParcelChangedObserver); delete mParcelChangedObserver; + mParcelChangedObserver = NULL; } } BOOL FSAreaSearch::postBuild() { - mResultList = getChild("result_list"); - mResultList->setDoubleClickCallback( boost::bind(&FSAreaSearch::onDoubleClick, this)); - mResultList->sortByColumn("Name", TRUE); - - mCounterText = getChild("counter"); - - childSetAction("Refresh", boost::bind(&FSAreaSearch::search, this)); - childSetAction("Stop", boost::bind(&FSAreaSearch::cancel, this)); + mTab = getChild("area_searchtab"); - getChild("Name query chunk")->setKeystrokeCallback( boost::bind(&FSAreaSearch::onCommitLine, this, _1, _2),NULL); - getChild("Description query chunk")->setKeystrokeCallback( boost::bind(&FSAreaSearch::onCommitLine, this, _1, _2),NULL); - getChild("Owner query chunk")->setKeystrokeCallback( boost::bind(&FSAreaSearch::onCommitLine, this, _1, _2),NULL); - getChild("Group query chunk")->setKeystrokeCallback( boost::bind(&FSAreaSearch::onCommitLine, this, _1, _2),NULL); + if (!gSavedSettings.getBOOL("FSAreaSearchAdvanced")) + { + LLPanel* advanced_tab = mTab->getPanelByName("area_search_advanced_panel"); + if (advanced_tab) + { + mTab->removeTabPanel(advanced_tab); + } + } - mParcelChangedObserver = new FSParcelChangeObserver(this); - LLViewerParcelMgr::getInstance()->addObserver(mParcelChangedObserver); + // TODO: add area search settings to the color.xml file + mBeaconColor = LLUIColorTable::getInstance()->getColor("PathfindingLinksetBeaconColor"); + mBeaconTextColor = LLUIColorTable::getInstance()->getColor("PathfindingDefaultBeaconTextColor"); + mBeaconLineWidth = gSavedSettings.getS32("DebugBeaconLineWidth"); - return TRUE; + return LLFloater::postBuild(); +} + +void FSAreaSearch::draw() +{ + LLFloater::draw(); + + if (mBeacons) + { + std::vector items = mPanelList->getResultList()->getAllData(); + + for (std::vector::const_iterator item_it = items.begin(); + item_it != items.end(); + ++item_it) + { + const LLScrollListItem* item = (*item_it); + LLViewerObject* objectp = gObjectList.findObject(item->getUUID()); + + if (objectp) + { + const std::string &objectName = mObjectDetails[item->getUUID()].description; + gObjectList.addDebugBeacon(objectp->getPositionAgent(), objectName, mBeaconColor, mBeaconTextColor, mBeaconLineWidth); + } + } + } +} + +//static +void FSAreaSearch::idle(void* user_data) +{ + FSAreaSearch* self = (FSAreaSearch*)user_data; + self->findObjects(); +} + +// static +void* FSAreaSearch::createPanelList(void* data) +{ + FSAreaSearch* self = (FSAreaSearch*)data; + self->mPanelList = new FSPanelAreaSearchList(self); + return self->mPanelList; +} + +// static +void* FSAreaSearch::createPanelFind(void* data) +{ + FSAreaSearch* self = (FSAreaSearch*)data; + self->mPanelFind = new FSPanelAreaSearchFind(self); + return self->mPanelFind; +} + +// static +void* FSAreaSearch::createPanelFilter(void* data) +{ + FSAreaSearch* self = (FSAreaSearch*)data; + self->mPanelFilter = new FSPanelAreaSearchFilter(self); + return self->mPanelFilter; +} + +// static +void* FSAreaSearch::createPanelAdvanced(void* data) +{ + FSAreaSearch* self = (FSAreaSearch*)data; + self->mPanelAdvanced = new FSPanelAreaSearchAdvanced(self); + return self->mPanelAdvanced; +} + +// static +void* FSAreaSearch::createPanelOptions(void* data) +{ + FSAreaSearch* self = (FSAreaSearch*)data; + self->mPanelOptions = new FSPanelAreaSearchOptions(self); + return self->mPanelOptions; } void FSAreaSearch::checkRegion() { - // Check if we changed region, and if we did, clear the object details cache. - LLViewerRegion* region = gAgent.getRegion(); - if (region != mLastRegion) + if (mInstance && mActive) + { + // Check if we changed region, and if we did, clear the object details cache. + LLViewerRegion* region = gAgent.getRegion(); + if (region != mLastRegion) + { + mLastRegion = region; + mRequested = 0; + mObjectDetails.clear(); + mPanelList->getResultList()->deleteAllItems(); + mPanelList->setCounterText(); + mPanelList->setAgentLastPosition(gAgent.getPositionGlobal()); + mRefresh = true; + } + } +} + +void FSAreaSearch::refreshList(bool cache_clear) +{ + mActive = true; + checkRegion(); + if (cache_clear) { - mLastRegion = region; mRequested = 0; mObjectDetails.clear(); - - mResultList->deleteAllItems(); - mCounterText->setText(getString("ListedPendingTotalBlank")); } -} - -void FSAreaSearch::onDoubleClick() -{ - LLScrollListItem *item = mResultList->getFirstSelected(); - if (!item) return; - LLUUID object_id = item->getUUID(); - LLViewerObject* objectp = gObjectList.findObject(object_id); - if (objectp) + else { - LLTracker::trackLocation(objectp->getPositionGlobal(), mObjectDetails[object_id].name, "", LLTracker::LOCATION_ITEM); + for (std::map::iterator object_it = mObjectDetails.begin(); + object_it != mObjectDetails.end(); + ++object_it) + { + object_it->second.listed = false; + } } + mPanelList->getResultList()->deleteAllItems(); + mPanelList->setCounterText(); + mPanelList->setAgentLastPosition(gAgent.getPositionGlobal()); + mNamesRequested.clear(); + mRefresh = true; + findObjects(); } -void FSAreaSearch::cancel() +void FSAreaSearch::findObjects() { - checkRegion(); - closeFloater(); + // Only loop through the gObjectList every so often. There is a performance hit if done too often. + if (!(mActive && ((mRefresh && mLastUpdateTimer.getElapsedTimeF32() > MIN_REFRESH_INTERVAL) || mLastUpdateTimer.getElapsedTimeF32() > REFRESH_INTERVAL))) + { + return; + } - mSearchedName = ""; - mSearchedDesc = ""; - mSearchedOwner = ""; - mSearchedGroup = ""; -} - -void FSAreaSearch::search() -{ + LL_DEBUGS("FSAreaSearch") << "Doing a FSAreaSearch::findObjects" << LL_ENDL; + + mLastUpdateTimer.stop(); // stop sets getElapsedTimeF32() time to zero. checkRegion(); - results(); -} - -void FSAreaSearch::onCommitLine(LLLineEditor* line, void* user_data) -{ - std::string name = line->getName(); - std::string text = line->getText(); - - if (name == "Name query chunk") mSearchedName = text; - else if (name == "Description query chunk") mSearchedDesc = text; - else if (name == "Owner query chunk") mSearchedOwner = text; - else if (name == "Group query chunk") mSearchedGroup = text; - - if (text.length() > 3) - { - checkRegion(); - results(); - } -} - -void FSAreaSearch::requestIfNeeded(LLViewerObject *objectp) -{ - LLUUID object_id = objectp->getID(); - if (mObjectDetails.count(object_id) == 0) - { - AObjectDetails* details = &mObjectDetails[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(); - mRequested++; - } -} - -void FSAreaSearch::results() -{ - if (!getVisible()) return; - if (mRequested > 0 && mLastUpdateTimer.getElapsedTimeF32() < min_refresh_interval) return; - - const LLUUID selected = mResultList->getCurrentID(); - const S32 scrollpos = mResultList->getScrollPos(); - mResultList->deleteAllItems(); - - S32 i; - S32 total = gObjectList.getNumObjects(); + mRefresh = false; + mSearchableObjects = 0; + S32 object_count = gObjectList.getNumObjects(); + std::vector request_list; LLViewerRegion* our_region = gAgent.getRegion(); - for (i = 0; i < total; i++) + + for (S32 i = 0; i < object_count; i++) { LLViewerObject *objectp = gObjectList.getObject(i); - if (objectp) + if (!(objectp && isSearchableObject(objectp, our_region))) { - if (objectp->getRegion() == our_region && !objectp->isAvatar() && objectp->isRoot() && - !objectp->flagTemporary() && !objectp->flagTemporaryOnRez()) + continue; + } + + LLUUID object_id = objectp->getID(); + + if (object_id.isNull()) + { + LL_WARNS("FSAreaSearch") << "WTF?! Selectable object with id of NULL!!" << LL_ENDL; + continue; + } + + mSearchableObjects++; + + if (mObjectDetails.count(object_id) == 0) + { + FSObjectProperties& details = mObjectDetails[object_id]; + details.id = object_id; + request_list.push_back(objectp->getLocalID()); + + mRequested++; + } + else + { + FSObjectProperties& details = mObjectDetails[object_id]; + if (details.valid == true) { - LLUUID object_id = objectp->getID(); - if (mObjectDetails.count(object_id) == 0) - { - requestIfNeeded(objectp); - } - else - { - AObjectDetails* details = &mObjectDetails[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) - { - if ((mSearchedName == "" || boost::ifind_first(object_name, mSearchedName)) && - (mSearchedDesc == "" || boost::ifind_first(object_desc, mSearchedDesc)) && - (mSearchedOwner == "" || boost::ifind_first(object_owner, mSearchedOwner)) && - (mSearchedGroup == "" || boost::ifind_first(object_group, mSearchedGroup))) - { - 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; - element["columns"][LIST_OBJECT_DESC]["column"] = "Description"; - element["columns"][LIST_OBJECT_DESC]["type"] = "text"; - element["columns"][LIST_OBJECT_DESC]["value"] = details->desc; - element["columns"][LIST_OBJECT_OWNER]["column"] = "Owner"; - element["columns"][LIST_OBJECT_OWNER]["type"] = "text"; - element["columns"][LIST_OBJECT_OWNER]["value"] = object_owner; - element["columns"][LIST_OBJECT_GROUP]["column"] = "Group"; - element["columns"][LIST_OBJECT_GROUP]["type"] = "text"; - element["columns"][LIST_OBJECT_GROUP]["value"] = object_group; - mResultList->addElement(element, ADD_BOTTOM); - } - } - } + matchObject(details, objectp); + } + } + + } + + // Select then de-deslect the objects to get properties. + requestObjectProperties(request_list, true); + requestObjectProperties(request_list, false); + + mPanelList->updateScrollList(); + + //TODO: make mRequested more accruate. + // requests for non-existent objects will never arrive, + // update mRequested counter to reflect that. + for (std::map::iterator object_it = mObjectDetails.begin(); + object_it != mObjectDetails.end(); + ++object_it) + { + if (!object_it->second.valid) + { + LLUUID id = object_it->second.id; + LLViewerObject* objectp = gObjectList.findObject(id); + if (((!objectp) || (!isSearchableObject(objectp, our_region))) && (mRequested > 0)) + { + mRequested--; } } } - mResultList->updateSort(); - mResultList->selectByID(selected); - mResultList->setScrollPos(scrollpos); - LLStringUtil::format_map_t args; - args["[LISTED]"] = llformat("%d", mResultList->getItemCount()); - args["[PENDING]"] = llformat("%d", mRequested); - args["[TOTAL]"] = llformat("%d", mObjectDetails.size()); - mCounterText->setText(getString("ListedPendingTotalFilled", args)); - mLastUpdateTimer.reset(); + updateCounterText(); + mLastUpdateTimer.start(); // start also reset elapsed time to zero } - -void FSAreaSearch::callbackLoadOwnerName(const LLUUID& id, const std::string& full_name) -{ - results(); -} - -void FSAreaSearch::processObjectPropertiesFamily(LLMessageSystem* msg) -{ - checkRegion(); - - LLUUID object_id; - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, object_id); - - bool exists = (mObjectDetails.count(object_id) != 0); - AObjectDetails* details = &mObjectDetails[object_id]; - if (!exists || details->name == request_string) +bool FSAreaSearch::isSearchableObject(LLViewerObject* objectp, LLViewerRegion* our_region) +{ + //TODO: add child prim support + if (!(objectp->isRoot() || (objectp->isAttachment() && objectp->isRootEdit()))) { - // We cache unknown objects (to avoid having to request them later) - // and requested objects. - if (exists && mRequested > 0) mRequested--; - 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, boost::bind( - &FSAreaSearch::callbackLoadOwnerName, this, _1, _2)); - gCacheName->get(details->group_id, true, boost::bind( - &FSAreaSearch::callbackLoadOwnerName, this, _1, _2)); + return false; + } + + //TODO: add mult-region support + if (!(objectp->getRegion() == our_region)) + { + return false; + } + + // Land doesn't have object properties + if (objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) + { + return false; + } + + // Object needs to be selectable to get properties from the server + if (!objectp->mbCanSelect) + { + return false; + } + + // Avatars are spechiel objects that don't have normal properties + if (objectp->isAvatar()) + { + return false; + } + + //----------------------------------------------------------------------- + // Excludes + //----------------------------------------------------------------------- + + if (mExcludeAttachment && objectp->isAttachment()) + { + return false; + } + + if (mExcludePhysics && objectp->flagUsePhysics()) + { + return false; + } + + if (mExcludeTempary && objectp->flagTemporaryOnRez()) + { + return false; + } + + return true; +} + +void FSAreaSearch::requestObjectProperties(const std::vector& request_list, bool select) +{ + bool start_new_message = true; + S32 select_count = 0; + LLMessageSystem* msg = gMessageSystem; + LLViewerRegion* regionp = gAgent.getRegion(); + + for (std::vector::const_iterator iter = request_list.begin(); + iter != request_list.end(); ++iter) + { + if (start_new_message) + { + if (select) + { + msg->newMessageFast(_PREHASH_ObjectSelect); + } + else + { + msg->newMessageFast(_PREHASH_ObjectDeselect); + } + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + select_count++; + start_new_message = false; + } + + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU32Fast(_PREHASH_ObjectLocalID, (*iter) ); + select_count++; + + if(msg->isSendFull(NULL) || select_count >= MAX_OBJECTS_PER_PACKET) + { + LL_DEBUGS("FSAreaSearch") << "Sent one" << (select ? "ObjectSelect" : "ObjectDeselect") << "message" << LL_ENDL; + msg->sendReliable(regionp->getHost() ); + select_count = 0; + start_new_message = true; + } + } + + if (!start_new_message) + { + LL_DEBUGS("FSAreaSearch") << "Sent one" << (select ? "ObjectSelect" : "ObjectDeselect") << "message" << LL_ENDL; + msg->sendReliable(regionp->getHost() ); } } + +void FSAreaSearch::processObjectProperties(LLMessageSystem* msg) +{ + // This fuction is called by llviewermessage even if no floater has been created. + if (!(mInstance && mActive)) + { + return; + } + + LLViewerRegion* our_region = gAgent.getRegion(); + bool counter_text_update = false; + + S32 count = msg->getNumberOfBlocksFast(_PREHASH_ObjectData); + LL_DEBUGS("FSAreaSearch") << "Got processObjectProperties message with " << count << "object(s)" << LL_ENDL; + for (S32 i = 0; i < count; i++) + { + LLUUID object_id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, object_id, i); + if (object_id.isNull()) + { + LL_WARNS("FSAreaSearch") << "Got Object Properties with NULL id" << LL_ENDL; + continue; + } + + LLViewerObject* objectp = gObjectList.findObject(object_id); + if (!(objectp && objectp->isRootEdit())) + { + continue; + } + + FSObjectProperties& details = mObjectDetails[object_id]; + if (!details.valid) + { + // We cache un-requested objects (to avoid having to request them later) + // and requested objects. + + details.valid = true; + + if (details.id.isNull()) + { + // Recieved object properties without requesting it. + details.id = object_id; + } + else + { + if (mRequested > 0) + { + mRequested--; + } + counter_text_update = true; + } + + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_CreatorID, details.creator_id, i); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, details.owner_id, i); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, details.group_id, i); + msg->getU64Fast(_PREHASH_ObjectData, _PREHASH_CreationDate, details.creation_date, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, details.base_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, details.owner_mask, i); + msg->getU32Fast(_PREHASH_ObjectData,_PREHASH_GroupMask, details.group_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, details.everyone_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, details.next_owner_mask, i); + details.sale_info.unpackMultiMessage(msg, _PREHASH_ObjectData, i); + details.ag_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePerms, i); + details.ag_texture_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTextures, i); + details.ag_texture_perms_owner.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTexturesOwner, i); + details.category.unpackMultiMessage(msg, _PREHASH_ObjectData, i); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, details.last_owner_id, i); + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, details.name, i); + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, details.description, i); + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_TouchName, details.touch_name, i); + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_SitName, details.sit_name, i); + + S32 size = msg->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_TextureID); + if (size > 0) + { + S8 packed_buffer[SELECT_MAX_TES * UUID_BYTES]; + msg->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureID, packed_buffer, 0, i, SELECT_MAX_TES * UUID_BYTES); + + for (S32 buf_offset = 0; buf_offset < size; buf_offset += UUID_BYTES) + { + LLUUID tid; + memcpy(tid.mData, packed_buffer + buf_offset, UUID_BYTES); /* Flawfinder: ignore */ + details.texture_ids.push_back(tid); + } + } + + details.permissions.init(details.creator_id, details.owner_id, details.last_owner_id, details.group_id); + details.permissions.initMasks(details.base_mask, details.owner_mask, details.everyone_mask, details.group_mask, details.next_owner_mask); + + // Sets the group owned BOOL and real owner id, group or owner depending if object is group owned. + details.permissions.getOwnership(details.ownership_id, details.group_owned); + + LL_DEBUGS("FSAreaSearch") << "Got properties for object: " << object_id << LL_ENDL; + + if (isSearchableObject(objectp, our_region)) + { + matchObject(details, objectp); + } + } + } + + if (counter_text_update) + { + updateCounterText(); + } +} + +void FSAreaSearch::matchObject(FSObjectProperties& details, LLViewerObject* objectp) +{ + if (details.listed) + { + // object allready listed on the scroll list. + return; + } + + //----------------------------------------------------------------------- + // Filters + //----------------------------------------------------------------------- + + if (mFilterForSale && !(details.sale_info.isForSale() && (details.sale_info.getSalePrice() >= mFilterForSaleMin && details.sale_info.getSalePrice() <= mFilterForSaleMax))) + { + return; + } + + if (mFilterDistance) + { + S32 distance = dist_vec(mPanelList->getAgentLastPosition(), objectp->getPositionGlobal());// used mAgentLastPosition instead of gAgent->getPositionGlobal for performace + if (!(distance >= mFilterDistanceMin && distance <= mFilterDistanceMax)) + { + return; + } + } + + if (mFilterClickAction) + { + switch(mFilterClickActionType) + { + case 0: // "(blank)", should not end up here, but just in case + break; + case 1: // "any" mouse click action + if (!(objectp->flagHandleTouch() || objectp->getClickAction() != 0)) + { + return; + } + break; + case 2: // "touch" is a seperate mouse click action flag + if (!objectp->flagHandleTouch()) + { + return; + } + break; + default: // all other mouse click action types + if ((mFilterClickActionType - 2) != objectp->getClickAction()) + { + return; + } + break; + } + } + + //TODO: texture id search +// for (uuid_vec_t::const_iterator texture_it = details.texture_ids.begin(); +// texture_it != details.texture_ids.end(); ++texture_it) +// { +// if ( "" == (*texture_it).asString()) +// { +// } +// } + + if (mFilterPhysicial && !objectp->flagUsePhysics()) + { + return; + } + + if (mFilterTemporary && !objectp->flagTemporaryOnRez()) + { + return; + } + + if (mFilterLocked && !objectp->flagObjectPermanent()) + { + return; + } + + if (mFilterPhantom && !objectp->flagPhantom()) + { + return; + } + + if (mFilterAttachment && !objectp->isAttachment()) + { + return; + } + + //----------------------------------------------------------------------- + // Find text + //----------------------------------------------------------------------- + + LLUUID object_id = details.id; + std::string creator_name; + std::string owner_name; + std::string last_owner_name; + std::string group_name; + std::string object_name = details.name; + std::string object_description = details.description; + + details.name_requested = false; + getName(details.ownership_id, owner_name, details.group_owned, details.name_requested); + getName(details.creator_id, creator_name, false, details.name_requested); + getName(details.last_owner_id, last_owner_name, false, details.name_requested); + getName(details.group_id, group_name, true, details.name_requested); + + if (mRegexSearch) + { + try + { + if (!mSearchName.empty() && !boost::regex_match(object_name, mRegexSearchName)) + { + return; + } + if (!mSearchDescription.empty() && !boost::regex_match(object_description, mRegexSearchDescription)) + { + return; + } + if (!mSearchOwner.empty() && !boost::regex_match(owner_name, mRegexSearchOwner)) + { + return; + } + if (!mSearchGroup.empty() && !boost::regex_match(group_name, mRegexSearchGroup)) + { + return; + } + if (!mSearchCreator.empty() && !boost::regex_match(creator_name, mRegexSearchCreator)) + { + return; + } + if (!mSearchLastOwner.empty() && !boost::regex_match(last_owner_name, mRegexSearchLastOwner)) + { + return; + } + } + + // Should not end up here due to error checking in Find class. However, some complex regexes may + // cause excessive resources and boost will throw an execption. + // Due to the possiablitey of hitting this block a 1000 times per second, only logonce it. + catch(boost::regex_error& e) + { + LL_WARNS_ONCE("FSAreaSearch") << "boost::regex_error error in regex: "<< e.what() << LL_ENDL; + } + catch(const std::exception& e) + { + LL_WARNS_ONCE("FSAreaSearch") << "std::exception error in regex: "<< e.what() << LL_ENDL; + } + catch (...) + { + LL_WARNS_ONCE("FSAreaSearch") << "Unknown error in regex" << LL_ENDL; + } + } + else + { + if (!mSearchName.empty() && boost::ifind_first(object_name, mSearchName).empty()) + { + return; + } + if (!mSearchDescription.empty() && boost::ifind_first(object_description, mSearchDescription).empty()) + { + return; + } + if (!mSearchOwner.empty() && boost::ifind_first(owner_name, mSearchOwner).empty()) + { + return; + } + if (!mSearchGroup.empty() && boost::ifind_first(group_name, mSearchGroup).empty()) + { + return; + } + if (!mSearchCreator.empty() && boost::ifind_first(creator_name, mSearchCreator).empty()) + { + return; + } + if (!mSearchLastOwner.empty() && boost::ifind_first(last_owner_name, mSearchLastOwner).empty()) + { + return; + } + } + + //----------------------------------------------------------------------- + // Object passed all above tests, add it to the List tab. + //----------------------------------------------------------------------- + + details.listed = true; + + LLScrollListColumn* column; + LLSD element; + element["id"] = object_id; + + column = mPanelList->getResultList()->getColumn("distance"); + if (column) + { + element["columns"][column->mIndex]["column"] = "distance"; + element["columns"][column->mIndex]["value"] = llformat("%1.0f m", dist_vec(mPanelList->getAgentLastPosition(), objectp->getPositionGlobal())); // used mAgentLastPosition instead of gAgent->getPositionGlobal for performace + } + + column = mPanelList->getResultList()->getColumn("name"); + if (column) + { + element["columns"][column->mIndex]["column"] = "name"; + element["columns"][column->mIndex]["value"] = details.name; + } + + column = mPanelList->getResultList()->getColumn("description"); + if (column) + { + element["columns"][column->mIndex]["column"] = "description"; + element["columns"][column->mIndex]["value"] = details.description; + } + + column = mPanelList->getResultList()->getColumn("owner"); + if (column) + { + element["columns"][column->mIndex]["column"] = "owner"; + element["columns"][column->mIndex]["value"] = owner_name; + } + + column = mPanelList->getResultList()->getColumn("group"); + if (column) + { + element["columns"][column->mIndex]["column"] = "group"; + element["columns"][column->mIndex]["value"] = group_name; + } + + column = mPanelList->getResultList()->getColumn("creator"); + if (column) + { + element["columns"][column->mIndex]["column"] = "creator"; + element["columns"][column->mIndex]["value"] = creator_name; + } + + column = mPanelList->getResultList()->getColumn("last_owner"); + if (column) + { + element["columns"][column->mIndex]["column"] = "last_owner"; + element["columns"][column->mIndex]["value"] = last_owner_name; + } + + LLScrollListItem* list_row = mPanelList->getResultList()->addElement(element); + + if (objectp->flagTemporaryOnRez() || objectp->flagUsePhysics()) + { + U8 font_style = LLFontGL::NORMAL; + if (objectp->flagTemporaryOnRez()) + { + font_style |= LLFontGL::ITALIC; + } + if (objectp->flagUsePhysics()) + { + font_style |= LLFontGL::BOLD; + } + + S32 num_colums = list_row->getNumColumns(); + for (S32 i = 0; i < num_colums; i++) + { + LLScrollListText* list_cell = (LLScrollListText*)list_row->getColumn(i); + list_cell->setFontStyle(font_style); + } + } +} + +void FSAreaSearch::getName(LLUUID& id, std::string& name, BOOL group, bool& name_requested) +{ + BOOL is_group; + + if(!gCacheName->getIfThere(id, name, is_group)) + { + if(std::find(mNamesRequested.begin(), mNamesRequested.end(), id) == mNamesRequested.end()) + { + mNamesRequested.push_back(id); + gCacheName->get(id, group, boost::bind(&FSAreaSearch::callbackLoadFullName, this, _1, _2)); + } + name_requested = true; + } +} + +void FSAreaSearch::callbackLoadFullName(const LLUUID& id, const std::string& full_name) +{ + LLViewerRegion* our_region = gAgent.getRegion(); + + for (std::map::iterator object_it = mObjectDetails.begin(); + object_it != mObjectDetails.end(); + ++object_it) + { + if (object_it->second.name_requested && !object_it->second.listed) + { + LLUUID id = object_it->second.id; + LLViewerObject* objectp = gObjectList.findObject(id); + if (objectp && isSearchableObject(objectp, our_region)) + { + matchObject(object_it->second, objectp); + } + } + } + + mPanelList->updateName(id, full_name); +} + +void FSAreaSearch::updateCounterText() +{ + LLStringUtil::format_map_t args; + args["[LISTED]"] = llformat("%d", mPanelList->getResultList()->getItemCount()); + args["[PENDING]"] = llformat("%d", mRequested); + args["[TOTAL]"] = llformat("%d", mSearchableObjects); + mPanelList->setCounterText(args); +} + +void FSAreaSearch::onCommitLine() +{ + mSearchName = mPanelFind->mNameLineEditor->getText(); + mSearchDescription = mPanelFind->mDescriptionLineEditor->getText(); + mSearchOwner = mPanelFind->mOwnerLineEditor->getText(); + mSearchGroup = mPanelFind->mGroupLineEditor->getText(); + mSearchCreator = mPanelFind->mCreatorLineEditor->getText(); + mSearchLastOwner = mPanelFind->mLastOwnerLineEditor->getText(); + + if (mRegexSearch) + { + if (!mSearchName.empty()) + { + if (regexTest(mSearchName)) + { + mRegexSearchName = mSearchName.c_str(); + } + else + { + // empty the search text to prevent error in matchObject + mSearchName.erase(); + } + } + + if (!mSearchDescription.empty()) + { + if (regexTest(mSearchDescription)) + { + mRegexSearchDescription = mSearchDescription.c_str(); + } + else + { + mSearchDescription.erase(); + } + } + + if (!mSearchOwner.empty()) + { + if (regexTest(mSearchOwner)) + { + mRegexSearchOwner = mSearchOwner.c_str(); + } + else + { + mSearchOwner.erase(); + } + } + + if (!mSearchGroup.empty()) + { + if (regexTest(mSearchGroup)) + { + mRegexSearchGroup = mSearchGroup.c_str(); + } + else + { + mSearchGroup.erase(); + } + } + + if (!mSearchCreator.empty()) + { + if (regexTest(mSearchCreator)) + { + mRegexSearchCreator = mSearchCreator.c_str(); + } + else + { + mSearchCreator.erase(); + } + } + + if (!mSearchLastOwner.empty()) + { + if (regexTest(mSearchLastOwner)) + { + mRegexSearchLastOwner = mSearchLastOwner.c_str(); + } + else + { + mSearchLastOwner.erase(); + } + } + } +} + +bool FSAreaSearch::regexTest(std::string text) +{ + // couple regex patters one can use for testing. The regex will match a UUID. + // boost::regex pattern("[\\w]{8}-[\\w]{4}-[\\w]{4}-[\\w]{4}-[\\w]{12}"); + // [\p{XDigit}]{8}(-[\p{XDigit}]{4}){3}-[\p{XDigit}]{12} + // to find all objects that don't belong to a group, use (?!^Name of the group$).* in the group field. + + try + { + std::string test_text = "asdfghjklqwerty1234567890"; + boost::regex pattern(text.c_str()); + boost::regex_match(test_text, pattern); + } + catch(boost::regex_error& e) + { + LLSD args; + args["EWHAT"] = e.what(); + LLNotificationsUtil::add("RegExFail", args); + LL_DEBUGS("FSAreaSearch") << "boost::regex_error error in regex: "<< e.what() << LL_ENDL; + return false; + } + catch(const std::exception& e) + { + LLSD args; + args["EWHAT"] = e.what(); + LLNotificationsUtil::add("RegExFail", args); + LL_DEBUGS("FSAreaSearch") << "std::exception error in regex: "<< e.what() << LL_ENDL; + return false; + } + catch (...) + { + LLSD args; + args["EWHAT"] = "Unknown Error."; + LLNotificationsUtil::add("RegExFail", args); + LL_DEBUGS("FSAreaSearch") << "Unknown error in regex" << LL_ENDL; + return false; + } + + return true; +} + +void FSAreaSearch::clearSearchText() +{ + mSearchName.erase(); + mSearchDescription.erase(); + mSearchOwner.erase(); + mSearchGroup.erase(); + mSearchCreator.erase(); + mSearchLastOwner.erase(); +} + +void FSAreaSearch::onButtonClickedSearch() +{ + // if the user blanks out a line, onCommitLine is not fired/called. + // calling this will make sure to update any blanked out lines. + onCommitLine(); + + mTab->selectFirstTab(); + refreshList(false); +} + +void FSAreaSearch::onCommitCheckboxRegex() +{ + mRegexSearch = mPanelFind->mCheckboxRegex->get(); + + if (mRegexSearch) + { + onCommitLine(); + } +} + + +//--------------------------------------------------------------------------- +// List panel +//--------------------------------------------------------------------------- + +FSPanelAreaSearchList::FSPanelAreaSearchList(FSAreaSearch* pointer) +: LLPanel(), + mCounterText(0), + mResultList(0), + mFSAreaSearch(pointer) +{ + // Set up context menu. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("AreaSearch.Action", boost::bind(&FSPanelAreaSearchList::onContextMenuItemClick, this, _2)); + enable_registrar.add("AreaSearch.Enable", boost::bind(&FSPanelAreaSearchList::onContextMenuItemEnable, this, _2)); + + mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile( + "menu_fs_area_search.xml", LLContextMenu::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); +} + +BOOL FSPanelAreaSearchList::postBuild() +{ + mResultList = getChild("result_list"); + mResultList->setDoubleClickCallback(boost::bind(&FSPanelAreaSearchList::onDoubleClick, this)); + mResultList->sortByColumn("name", TRUE); + + mCounterText = getChild("counter"); + + mRefreshButton = getChild("Refresh"); + mRefreshButton->setClickedCallback(boost::bind(&FSPanelAreaSearchList::onClickRefresh, this)); + + mCheckboxBeacons = getChild("beacons"); + mCheckboxBeacons->setCommitCallback(boost::bind(&FSPanelAreaSearchList::onCommitCheckboxBeacons, this)); + + mAgentLastPosition = gAgent.getPositionGlobal(); + + return LLPanel::postBuild(); +} + +// virtual +FSPanelAreaSearchList::~FSPanelAreaSearchList() +{ } + +void FSPanelAreaSearchList::onClickRefresh() +{ + mFSAreaSearch->refreshList(true); +} + +void FSPanelAreaSearchList::onCommitCheckboxBeacons() +{ + mFSAreaSearch->setBeacons(mCheckboxBeacons->get()); +} + +void FSPanelAreaSearchList::setCounterText() +{ + mCounterText->setText(getString("ListedPendingTotalBlank")); +} + +void FSPanelAreaSearchList::setCounterText(LLStringUtil::format_map_t args) +{ + mCounterText->setText(getString("ListedPendingTotalFilled", args)); +} + +void FSPanelAreaSearchList::onDoubleClick() +{ + LLScrollListItem *item = mResultList->getFirstSelected(); + if (!item) return; + LLUUID object_id = item->getUUID(); + LLViewerObject* objectp = gObjectList.findObject(object_id); + if (objectp) + { + FSObjectProperties& details = mFSAreaSearch->mObjectDetails[object_id]; + LLTracker::trackLocation(objectp->getPositionGlobal(), details.name, "", LLTracker::LOCATION_ITEM); + + if (mFSAreaSearch->getPanelAdvanced()->mCheckboxClickBuy->get()) + { + buyObject(details, objectp); + } + + if (mFSAreaSearch->getPanelAdvanced()->mCheckboxClickTouch->get()) + { + touchObject(objectp); + } + } +} + +void FSPanelAreaSearchList::updateScrollList() +{ + bool agent_moved = false; + const LLVector3d current_agent_position = gAgent.getPositionGlobal(); + + if (dist_vec(mAgentLastPosition, current_agent_position) > MIN_DISTANCE_MOVED) + { + agent_moved = true; + mAgentLastPosition = current_agent_position; + } + + bool deleted = false; + LLScrollListColumn* distance_column = mResultList->getColumn("distance"); + LLViewerRegion* our_region = gAgent.getRegion(); + + // Iterate over the rows in the list, deleting ones whose object has gone away. + std::vector items = mResultList->getAllData(); + for (std::vector::iterator item_it = items.begin(); + item_it != items.end(); + ++item_it) + { + LLScrollListItem* item = (*item_it); + LLUUID row_id = item->getUUID(); + LLViewerObject* objectp = gObjectList.findObject(row_id); + + if ((!objectp) || (!mFSAreaSearch->isSearchableObject(objectp, our_region))) + { + // This item's object has been deleted -- remove the row. + // Removing the row won't throw off our iteration, since we have a local copy of the array. + // We just need to make sure we don't access this item after the delete. + mResultList->deleteSingleItem(mResultList->getItemIndex(row_id)); + + mFSAreaSearch->mObjectDetails[row_id].listed = false ; + deleted = true; + } + else + { + if (agent_moved && distance_column) + { + item->getColumn(distance_column->mIndex)->setValue(LLSD(llformat("%1.0f m", dist_vec(current_agent_position, objectp->getPositionGlobal())))); + } + } + } + + if (deleted || agent_moved) + { + mResultList->updateLayout(); + } +} + +void FSPanelAreaSearchList::updateName(LLUUID id, std::string name) +{ + LLScrollListColumn* creator_column = mResultList->getColumn("creator"); + LLScrollListColumn* owner_column = mResultList->getColumn("owner"); + LLScrollListColumn* group_column = mResultList->getColumn("group"); + LLScrollListColumn* last_owner_column = mResultList->getColumn("last_owner"); + + // Iterate over the rows in the list, updating the ones with matching id. + std::vector items = mResultList->getAllData(); + + for (std::vector::iterator item_it = items.begin(); + item_it != items.end(); + ++item_it) + { + LLScrollListItem* item = (*item_it); + LLUUID row_id = item->getUUID(); + FSObjectProperties& details = mFSAreaSearch->mObjectDetails[row_id]; + + if (creator_column && (id == details.creator_id)) + { + item->getColumn(creator_column->mIndex)->setValue(LLSD(name)); + } + + if (owner_column && (id == details.owner_id)) + { + item->getColumn(owner_column->mIndex)->setValue(LLSD(name)); + } + + if (group_column && (id == details.group_id)) + { + item->getColumn(group_column->mIndex)->setValue(LLSD(name)); + } + + if (last_owner_column && (id == details.last_owner_id)) + { + item->getColumn(last_owner_column->mIndex)->setValue(LLSD(name)); + } + } +} + +// virtual +BOOL FSPanelAreaSearchList::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); + + // BUG: hitItem is off by three rows up. + // when fixed, selectByID(item->getValue()) if nothing was selected. +// LLScrollListItem *item = mResultList->hitItem(x, y); +// if (item) +// { +// mResultList->selectByID(item->getValue()); +// +// } + + if (mPopupMenu) + { + if (mResultList->getNumSelected() !=0) + { + mPopupMenu->show(x, y); + LLMenuGL::showPopup(this, mPopupMenu, x, y); + return TRUE; + } + } + + return handled; +} + +bool FSPanelAreaSearchList::onContextMenuItemEnable(const LLSD& userdata) +{ + std::string parameter = userdata.asString(); + if (parameter == "one") + { + // return true if just one item is selected. + return (mResultList->getNumSelected() == 1); + } + else + { + // return true if more then one is selected, but not just one. + return (mResultList->getNumSelected() > 1); + } +} + +bool FSPanelAreaSearchList::onContextMenuItemClick(const LLSD& userdata) +{ + std::string action = userdata.asString(); + LL_DEBUGS("FSAreaSearch") << "Right click menu " << action << " was selected." << LL_ENDL; + + // NOTE that each action command MUST begin with a different letter. + char c = action.at(0); + switch(c) + { + case 't': // touch + case 's': // script + { + std::vector selected = mResultList->getAllSelected(); + + for(std::vector::iterator item_it = selected.begin(); + item_it != selected.end(); ++item_it) + { + switch (c) + { + case 't': // touch + { + LLViewerObject* objectp = gObjectList.findObject((*item_it)->getUUID()); + if (objectp) + { + touchObject(objectp); + } + } + break; + case 's': // script + FSLSLBridge::instance().viewerToLSL("getScriptInfo|" + (*item_it)->getUUID().asString()); + break; + default: + break; + } + } + } + break; + + case 'b': // buy + case 'p': // p_teleport + { + LLUUID object_id = mResultList->getFirstSelected()->getUUID(); + LLViewerObject* objectp = gObjectList.findObject(object_id); + if (objectp) + { + switch (c) + { + case 'b': // buy + buyObject(mFSAreaSearch->mObjectDetails[object_id], objectp); + break; + case 'p': // p_teleport + gAgent.teleportViaLocation(objectp->getPositionGlobal()); + break; + default: + break; + } + } + } + break; + + case 'i': // inspect + case 'e': // edit + case 'd': // delete + case 'r': // return + { + // select the objects first + LLSelectMgr::getInstance()->deselectAll(); + std::vector selected = mResultList->getAllSelected(); + + for(std::vector::iterator item_it = selected.begin(); + item_it != selected.end(); ++item_it) + { + LLUUID object_id = (*item_it)->getUUID(); + LLViewerObject* objectp = gObjectList.findObject(object_id); + if (objectp) + { + LLSelectMgr::getInstance()->selectObjectAndFamily(objectp); + if ( c == 'r' ) + { + // need to set permissions for object return + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->findNode(objectp); + FSObjectProperties& details = mFSAreaSearch->mObjectDetails[object_id]; + node->mValid = TRUE; + node->mPermissions->init(details.creator_id, details.owner_id, details.last_owner_id, details.group_id); + node->mPermissions->initMasks(details.base_mask, details.owner_mask, details.everyone_mask, details.group_mask, details.next_owner_mask); + node->mAggregatePerm = details.ag_perms; + } + } + } + + // now act on those selected objects + switch (c) + { + case 'i': // inspect + LLFloaterReg::showInstance("inspect"); + break; + case 'e': // edit + handle_object_edit(); + break; + case 'd': // delete + handle_object_delete(); + break; + case 'r': // return + handle_object_return(); + break; + default: + break; + } + } + break; + default: + break; + } + + return true; +} + +void FSPanelAreaSearchList::touchObject(LLViewerObject* objectp) +{ + // *NOTE: Hope the packets arrive safely and in order or else + // there will be some problems. + LLPickInfo pick; // default constructor will set sane values. + send_ObjectGrab_message(objectp, pick, LLVector3::zero); + send_ObjectDeGrab_message(objectp, pick); +} + +void FSPanelAreaSearchList::buyObject(FSObjectProperties& details, LLViewerObject* objectp) +{ + LLSelectMgr::getInstance()->deselectAll(); + LLSelectMgr::getInstance()->selectObjectAndFamily(objectp); + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->findNode(objectp); + + if (node) + { + node->mValid = TRUE; + node->mPermissions->init(details.creator_id, details.owner_id, details.last_owner_id, details.group_id); + node->mPermissions->initMasks(details.base_mask, details.owner_mask, details.everyone_mask, details.group_mask, details.next_owner_mask); + node->mSaleInfo = details.sale_info; + node->mAggregatePerm = details.ag_perms; + node->mCategory = details.category; + node->mName.assign(details.name); + node->mDescription.assign(details.description); + + handle_buy(); + } + else + { + LL_WARNS("FSAreaSearch") << "No LLSelectNode node" << LL_ENDL; + } +} + +//--------------------------------------------------------------------------- +// Find panel +//--------------------------------------------------------------------------- + +FSPanelAreaSearchFind::FSPanelAreaSearchFind(FSAreaSearch* pointer) +: LLPanel(), + mFSAreaSearch(pointer) +{ +} + +BOOL FSPanelAreaSearchFind::postBuild() +{ + mNameLineEditor = getChild("name_search"); + mNameLineEditor->setCommitCallback(boost::bind(&FSAreaSearch::onCommitLine, mFSAreaSearch)); + + mDescriptionLineEditor = getChild("description_search"); + mDescriptionLineEditor->setCommitCallback(boost::bind(&FSAreaSearch::onCommitLine, mFSAreaSearch)); + + mOwnerLineEditor = getChild("owner_search"); + mOwnerLineEditor->setCommitCallback(boost::bind(&FSAreaSearch::onCommitLine, mFSAreaSearch)); + + mGroupLineEditor = getChild("group_search"); + mGroupLineEditor->setCommitCallback(boost::bind(&FSAreaSearch::onCommitLine, mFSAreaSearch)); + + mCreatorLineEditor = getChild("creator_search"); + mCreatorLineEditor->setCommitCallback(boost::bind(&FSAreaSearch::onCommitLine, mFSAreaSearch)); + + mLastOwnerLineEditor = getChild("last_owner_search"); + mLastOwnerLineEditor->setCommitCallback(boost::bind(&FSAreaSearch::onCommitLine, mFSAreaSearch)); + + mCheckboxRegex = getChild("regular_expression"); + mCheckboxRegex->setCommitCallback(boost::bind(&FSAreaSearch::onCommitCheckboxRegex, mFSAreaSearch)); + + mSearchButton = getChild("search"); + mSearchButton->setClickedCallback(boost::bind(&FSAreaSearch::onButtonClickedSearch, mFSAreaSearch)); + + mClearButton = getChild("clear"); + mClearButton->setClickedCallback(boost::bind(&FSPanelAreaSearchFind::onButtonClickedClear, this)); + + return LLPanel::postBuild(); +} + +// virtual +FSPanelAreaSearchFind::~FSPanelAreaSearchFind() +{ } + +void FSPanelAreaSearchFind::onButtonClickedClear() +{ + mNameLineEditor->clear(); + mDescriptionLineEditor->clear(); + mOwnerLineEditor->clear(); + mGroupLineEditor->clear(); + mCreatorLineEditor->clear(); + mLastOwnerLineEditor->clear(); + mFSAreaSearch->clearSearchText(); +} + +//--------------------------------------------------------------------------- +// Filter panel +//--------------------------------------------------------------------------- + +FSPanelAreaSearchFilter::FSPanelAreaSearchFilter(FSAreaSearch* pointer) +: LLPanel(), + mFSAreaSearch(pointer) +{ +} + +BOOL FSPanelAreaSearchFilter::postBuild() +{ + mCheckboxLocked = getChild("filter_locked"); + mCheckboxLocked->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mCheckboxPhysical = getChild("filter_physical"); + mCheckboxPhysical->setEnabled(FALSE); + mCheckboxPhysical->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mCheckboxTemporary = getChild("filter_temporary"); + mCheckboxTemporary->setEnabled(FALSE); + mCheckboxTemporary->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mCheckboxPhantom = getChild("filter_phantom"); + mCheckboxPhantom->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mCheckboxForSale = getChild("filter_for_sale"); + mCheckboxForSale->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mCheckboxAttachment = getChild("filter_attachment"); + mCheckboxAttachment->setEnabled(FALSE); + mCheckboxAttachment->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mSpinForSaleMinValue= getChild("min_price"); + mSpinForSaleMinValue->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitSpin, this)); + + mSpinForSaleMaxValue= getChild("max_price"); + mSpinForSaleMaxValue->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitSpin, this)); + + mComboClickAction = getChild("click_action"); + mComboClickAction->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCombo, this)); + + mCheckboxExcludeAttachment = getChild("exclude_attachment"); + mCheckboxExcludeAttachment->set(TRUE); + mCheckboxExcludeAttachment->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mCheckboxExcludePhysics = getChild("exclude_physical"); + mCheckboxExcludePhysics->set(TRUE); + mCheckboxExcludePhysics->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mCheckboxExcludeTempary = getChild("exclude_temporary"); + mCheckboxExcludeTempary->set(TRUE); + mCheckboxExcludeTempary->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mButtonApply = getChild("apply"); + mButtonApply->setClickedCallback(boost::bind(&FSAreaSearch::onButtonClickedSearch, mFSAreaSearch)); + + mCheckboxDistance = getChild("filter_distance"); + mCheckboxDistance->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + + mSpinDistanceMinValue = getChild("min_distance"); + mSpinDistanceMinValue->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitSpin, this)); + + mSpinDistanceMaxValue= getChild("max_distance"); + mSpinDistanceMaxValue->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitSpin, this)); + + return LLPanel::postBuild(); +} + +// virtual +FSPanelAreaSearchFilter::~FSPanelAreaSearchFilter() +{ } + +void FSPanelAreaSearchFilter::onCommitCheckbox() +{ + mFSAreaSearch->setFilterLocked(mCheckboxLocked->get()); + mFSAreaSearch->setFilterPhantom(mCheckboxPhantom->get()); + mFSAreaSearch->setFilterForSale(mCheckboxForSale->get()); + mFSAreaSearch->setFilterDistance(mCheckboxDistance->get()); + + if (mCheckboxExcludePhysics->get()) + { + mFSAreaSearch->setFilterPhysicial(false); + mCheckboxPhysical->set(FALSE); + mCheckboxPhysical->setEnabled(FALSE); + mFSAreaSearch->setExcludePhysics(true); + } + else + { + mCheckboxPhysical->setEnabled(TRUE); + mFSAreaSearch->setExcludePhysics(false); + } + mFSAreaSearch->setFilterPhysicial(mCheckboxPhysical->get()); + + if (mCheckboxExcludeTempary->get()) + { + mFSAreaSearch->setFilterTemporary(false); + mCheckboxTemporary->set(FALSE); + mCheckboxTemporary->setEnabled(FALSE); + mFSAreaSearch->setExcludeTempary(true); + } + else + { + mCheckboxTemporary->setEnabled(TRUE); + mFSAreaSearch->setExcludeTempary(false); + } + mFSAreaSearch->setFilterTemporary(mCheckboxTemporary->get()); + + if (mCheckboxExcludeAttachment->get()) + { + mFSAreaSearch->setFilterAttachment(false); + mCheckboxAttachment->set(FALSE); + mCheckboxAttachment->setEnabled(FALSE); + mFSAreaSearch->setExcludeAttachment(true); + } + else + { + mCheckboxAttachment->setEnabled(TRUE); + mFSAreaSearch->setExcludeAttachment(false); + } + mFSAreaSearch->setFilterAttachment(mCheckboxAttachment->get()); +} + +void FSPanelAreaSearchFilter::onCommitSpin() +{ + mFSAreaSearch->setFilterForSaleMin(mSpinForSaleMinValue->getValue().asInteger()); + mFSAreaSearch->setFilterForSaleMax(mSpinForSaleMaxValue->getValue().asInteger()); + + mFSAreaSearch->setFilterDistanceMin(mSpinDistanceMinValue->getValue().asInteger()); + mFSAreaSearch->setFilterDistanceMax(mSpinDistanceMaxValue->getValue().asInteger()); +} + +void FSPanelAreaSearchFilter::onCommitCombo() +{ + if (mComboClickAction->getCurrentIndex() > 0) + { + mFSAreaSearch->setFilterClickAction(true); + mFSAreaSearch->setFilterClickActionType((U8)mComboClickAction->getCurrentIndex()); + } + else + { + mFSAreaSearch->setFilterClickAction(false); + mFSAreaSearch->setFilterClickActionType(0); + } +} + +//--------------------------------------------------------------------------- +// Options tab +//--------------------------------------------------------------------------- + +FSPanelAreaSearchOptions::FSPanelAreaSearchOptions(FSAreaSearch* pointer) +: LLPanel(), + mFSAreaSearch(pointer) +{ + mCommitCallbackRegistrar.add("AreaSearch.DisplayColumn", boost::bind(&FSPanelAreaSearchOptions::onCommitCheckboxDisplayColumn, this, _2)); +} + +// virtual +FSPanelAreaSearchOptions::~FSPanelAreaSearchOptions() +{ } + +void FSPanelAreaSearchOptions::onCommitCheckboxDisplayColumn(const LLSD& userdata) +{ + std::string column_name = userdata.asString(); + if (column_name.empty()) + { + LL_WARNS("FSAreaSearch") << "Missing action text." << LL_ENDL; + return; + } + + LLScrollListCtrl* result_list = mFSAreaSearch->getPanelList()->getResultList(); + result_list->deleteAllItems(); + LLCheckBoxCtrl* checkboxctrl = getChild("show_" + column_name); + + if (checkboxctrl) + { + if (checkboxctrl->get()) + { + result_list->addColumn(mColumnParms[column_name]); + } + else + { + mColumnParms[column_name] = result_list->delColumn(column_name); + } + } + + result_list->updateLayout(); +} + + +//--------------------------------------------------------------------------- +// Advanced tab +//--------------------------------------------------------------------------- + +FSPanelAreaSearchAdvanced::FSPanelAreaSearchAdvanced(FSAreaSearch* pointer) +: LLPanel(), + mFSAreaSearch(pointer) +{ +} + +BOOL FSPanelAreaSearchAdvanced::postBuild() +{ + mCheckboxClickTouch = getChild("double_click_touch"); + mCheckboxClickBuy = getChild("double_click_buy"); + + return LLPanel::postBuild(); +} + +// virtual +FSPanelAreaSearchAdvanced::~FSPanelAreaSearchAdvanced() +{ } + diff --git a/indra/newview/fsareasearch.h b/indra/newview/fsareasearch.h index 27bc95ff7a..932225c197 100644 --- a/indra/newview/fsareasearch.h +++ b/indra/newview/fsareasearch.h @@ -1,35 +1,28 @@ -/* Copyright (c) 2009 +/** + * @file fsareasearch.h + * @brief Floater to search and list objects in view or is known to the viewer. * - * Modular Systems. All rights reserved. + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (c) 2012 Techwolf Lupindo * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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 nor the names of its contributors - * may be used to endorse or promote products derived from this - * software without specific prior written permission. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * THIS SOFTWARE IS PROVIDED BY MODULAR SYSTEMS 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. - * Refactored for Viewer2 by Kadah Coba, April 2011 + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The Phoenix Viewer Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.phoenixviewer.com + * $/LicenseInfo$ */ #include "llfloater.h" @@ -37,64 +30,359 @@ #include "lluuid.h" #include "llstring.h" #include "llframetimer.h" +#include "llsaleinfo.h" +#include "llcategory.h" +#include "llpermissions.h" +#include "llviewerobject.h" +#include +#include +#include "llscrolllistcolumn.h" class LLTextBox; -class LLScrollListCtrl; class LLViewerRegion; +class LLCheckBoxCtrl; +class LLLineEditor; +class LLTabContainer; +class LLContextMenu; +class LLSpinCtrl; +class LLComboBox; -struct AObjectDetails +class FSPanelAreaSearchList; +class FSPanelAreaSearchFind; +class FSPanelAreaSearchFilter; +class FSPanelAreaSearchAdvanced; +class FSPanelAreaSearchOptions; + +struct FSObjectProperties { LLUUID id; + bool valid; + bool listed; std::string name; - std::string desc; + std::string description; + std::string touch_name; + std::string sit_name; + LLUUID creator_id; LLUUID owner_id; LLUUID group_id; + LLUUID ownership_id; + BOOL group_owned; // used in LL function that uses BOOL instead of bool + U64 creation_date; + U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask; + LLSaleInfo sale_info; + LLCategory category; + LLUUID last_owner_id; + LLAggregatePermissions ag_perms; + LLAggregatePermissions ag_texture_perms; + LLAggregatePermissions ag_texture_perms_owner; + LLPermissions permissions; + uuid_vec_t texture_ids; + bool name_requested; + + FSObjectProperties() : + valid(false), + listed(false), + name_requested(false) + { + } }; +//--------------------------------------------------------------------- +// Main class for area search +// holds the search engine and main floater +// -------------------------------------------------------------------- class FSAreaSearch : public LLSingleton, public LLFloater { + LOG_CLASS(FSAreaSearch); public: FSAreaSearch(const LLSD &); virtual ~FSAreaSearch(); /*virtual*/ BOOL postBuild(); + virtual void draw(); - void callbackLoadOwnerName(const LLUUID& id, const std::string& full_name); - void processObjectPropertiesFamily(LLMessageSystem* msg); + void callbackLoadFullName(const LLUUID& id, const std::string& full_name); + void processObjectProperties(LLMessageSystem* msg); + static void idle(void *user_data); + + void checkRegion(); + void refreshList(bool cache_clear); + void onCommitLine(); + void clearSearchText(); + void onButtonClickedSearch(); + void onCommitCheckboxRegex(); + bool isSearchableObject (LLViewerObject* objectp, LLViewerRegion* our_region); + + std::map mObjectDetails; + + FSPanelAreaSearchAdvanced* getPanelAdvanced() { return mPanelAdvanced; } + FSPanelAreaSearchList* getPanelList() { return mPanelList; } + + void setFilterForSale(bool b) { mFilterForSale = b; } + void setFilterLocked(bool b) { mFilterLocked = b; } + void setFilterPhysicial(bool b) { mFilterPhysicial = b; } + void setFilterTemporary(bool b) { mFilterTemporary = b; } + void setFilterPhantom(bool b) { mFilterPhantom = b; } + void setFilterAttachment(bool b) { mFilterAttachment = b; } + + void setRegexSearch(bool b) { mRegexSearch = b; } + void setBeacons(bool b) { mBeacons = b; } + + void setExcludeAttachment(bool b) { mExcludeAttachment = b; } + void setExcludeTempary(bool b) { mExcludeTempary = b; } + void setExcludePhysics(bool b) { mExcludePhysics = b; } + + void setFilterForSaleMin(S32 s) { mFilterForSaleMin = s; } + void setFilterForSaleMax(S32 s) { mFilterForSaleMax = s; } + + void setFilterClickAction(bool b) { mFilterClickAction = b; } + void setFilterClickActionType(U8 u) { mFilterClickActionType = u; } + + void setFilterDistance(bool b) { mFilterDistance = b; } + void setFilterDistanceMin(S32 s) { mFilterDistanceMin = s; } + void setFilterDistanceMax(S32 s) { mFilterDistanceMax = s; } private: - void results(); - void checkRegion(); - void cancel(); - void search(); - void onCommitLine(class LLLineEditor* line, void* user_data); - void requestIfNeeded(class LLViewerObject *objectp); - void onDoubleClick(); + void requestObjectProperties(const std::vector& request_list, bool select); + void matchObject(FSObjectProperties& details, LLViewerObject* objectp); + void getName(LLUUID& id, std::string& name, BOOL group, bool& name_requested); - enum OBJECT_COLUMN_ORDER - { - LIST_OBJECT_NAME, - LIST_OBJECT_DESC, - LIST_OBJECT_OWNER, - LIST_OBJECT_GROUP - }; + void updateCounterText(); + bool regexTest(std::string text); + void findObjects(); S32 mRequested; + bool mRefresh; + S32 mSearchableObjects; + bool mActive; + + std::string mSearchName; + std::string mSearchDescription; + std::string mSearchOwner; + std::string mSearchGroup; + std::string mSearchCreator; + std::string mSearchLastOwner; + + bool mRegexSearch; + boost::regex mRegexSearchName; + boost::regex mRegexSearchDescription; + boost::regex mRegexSearchOwner; + boost::regex mRegexSearchGroup; + boost::regex mRegexSearchCreator; + boost::regex mRegexSearchLastOwner; - LLTextBox* mCounterText; - LLScrollListCtrl* mResultList; LLFrameTimer mLastUpdateTimer; - std::map mObjectDetails; - - std::string mSearchedName; - std::string mSearchedDesc; - std::string mSearchedOwner; - std::string mSearchedGroup; + std::vector mNamesRequested; LLViewerRegion* mLastRegion; class FSParcelChangeObserver; friend class FSParcelChangeObserver; FSParcelChangeObserver* mParcelChangedObserver; + + // Used for checking to see if a floater has been created. + // Can not be trusted as a singleton pointer, don't use as a pointer. + FSAreaSearch* mInstance; + + LLTabContainer* mTab; + FSPanelAreaSearchList* mPanelList; + FSPanelAreaSearchFind* mPanelFind; + FSPanelAreaSearchFilter* mPanelFilter; + FSPanelAreaSearchOptions* mPanelOptions; + FSPanelAreaSearchAdvanced* mPanelAdvanced; + + bool mBeacons; + LLColor4 mBeaconColor; + LLColor4 mBeaconTextColor; + S32 mBeaconLineWidth; + + bool mExcludeAttachment; + bool mExcludeTempary; + bool mExcludePhysics; + + bool mFilterLocked; + bool mFilterPhysicial; + bool mFilterTemporary; + bool mFilterPhantom; + bool mFilterAttachment; + + bool mFilterForSale; + S32 mFilterForSaleMin; + S32 mFilterForSaleMax; + + bool mFilterDistance; + S32 mFilterDistanceMin; + S32 mFilterDistanceMax; + + bool mFilterClickAction; + U8 mFilterClickActionType; + +protected: + static void* createPanelList(void* data); + static void* createPanelFind(void* data); + static void* createPanelFilter(void* data); + static void* createPanelAdvanced(void* data); + static void* createPanelOptions(void* data); }; + + +//------------------------------------------------------------ +// List panel +// displays the found objects +//------------------------------------------------------------ +class FSPanelAreaSearchList +: public LLPanel +{ + LOG_CLASS(FSPanelAreaSearchList); +public: + FSPanelAreaSearchList(FSAreaSearch* pointer); + virtual ~FSPanelAreaSearchList(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + + void setCounterText(); + void setCounterText(LLStringUtil::format_map_t args); + void updateScrollList(); + void updateName(LLUUID id, std::string name); + void touchObject(LLViewerObject* objectp); + + LLScrollListCtrl* getResultList() { return mResultList; } + + void setAgentLastPosition(LLVector3d d) { mAgentLastPosition = d; } + LLVector3d getAgentLastPosition() { return mAgentLastPosition; } + +private: + void onDoubleClick(); + void onClickRefresh(); + void buyObject(FSObjectProperties& details, LLViewerObject* objectp); + void onCommitCheckboxBeacons(); + + LLContextMenu* mPopupMenu; + bool onContextMenuItemClick(const LLSD& userdata); + bool onContextMenuItemEnable(const LLSD& userdata); + + LLVector3d mAgentLastPosition; + + FSAreaSearch* mFSAreaSearch; + LLButton* mRefreshButton; + LLScrollListCtrl* mResultList; + LLCheckBoxCtrl* mCheckboxBeacons; + LLTextBox* mCounterText; +}; + + +//------------------------------------------------------------ +// Find panel +// UI for what objects to search for +//------------------------------------------------------------ +class FSPanelAreaSearchFind +: public LLPanel +{ + LOG_CLASS(FSPanelAreaSearchFind); +public: + FSPanelAreaSearchFind(FSAreaSearch* pointer); + virtual ~FSPanelAreaSearchFind(); + + /*virtual*/ BOOL postBuild(); + + LLLineEditor* mNameLineEditor; + LLLineEditor* mDescriptionLineEditor; + LLLineEditor* mOwnerLineEditor; + LLLineEditor* mGroupLineEditor; + LLLineEditor* mCreatorLineEditor; + LLLineEditor* mLastOwnerLineEditor; + LLCheckBoxCtrl* mCheckboxRegex; + +private: + void onButtonClickedClear(); + + FSAreaSearch* mFSAreaSearch; + + LLButton* mSearchButton; + LLButton* mClearButton; +}; + + +//------------------------------------------------------------ +// Filter panel +// "filter" the list to certion object types. +//------------------------------------------------------------ +class FSPanelAreaSearchFilter +: public LLPanel +{ + LOG_CLASS(FSPanelAreaSearchFilter); +public: + FSPanelAreaSearchFilter(FSAreaSearch* pointer); + virtual ~FSPanelAreaSearchFilter(); + + /*virtual*/ BOOL postBuild(); + +private: + void onCommitCheckbox(); + void onCommitSpin(); + void onCommitCombo(); + + FSAreaSearch* mFSAreaSearch; + LLCheckBoxCtrl* mCheckboxForSale; + LLCheckBoxCtrl* mCheckboxPhysical; + LLCheckBoxCtrl* mCheckboxTemporary; + LLCheckBoxCtrl* mCheckboxLocked; + LLCheckBoxCtrl* mCheckboxPhantom; + LLCheckBoxCtrl* mCheckboxDistance; + LLSpinCtrl* mSpinDistanceMinValue; + LLSpinCtrl* mSpinDistanceMaxValue; + LLSpinCtrl* mSpinForSaleMinValue; + LLSpinCtrl* mSpinForSaleMaxValue; + LLButton* mButtonApply; + LLComboBox* mComboClickAction; + LLCheckBoxCtrl* mCheckboxAttachment; + LLCheckBoxCtrl* mCheckboxExcludeAttachment; + LLCheckBoxCtrl* mCheckboxExcludePhysics; + LLCheckBoxCtrl* mCheckboxExcludeTempary; +}; + + +//------------------------------------------------------------ +// Options panel +//------------------------------------------------------------ +class FSPanelAreaSearchOptions +: public LLPanel +{ + LOG_CLASS(FSPanelAreaSearchOptions); +public: + FSPanelAreaSearchOptions(FSAreaSearch* pointer); + virtual ~FSPanelAreaSearchOptions(); + + // not used +// /*virtual*/ BOOL postBuild(); + +private: + void onCommitCheckboxDisplayColumn(const LLSD& userdata); + + FSAreaSearch* mFSAreaSearch; + + std::map mColumnParms; +}; + + +//------------------------------------------------------------ +// Advanced panel +//------------------------------------------------------------ +class FSPanelAreaSearchAdvanced +: public LLPanel +{ + LOG_CLASS(FSPanelAreaSearchAdvanced); +public: + FSPanelAreaSearchAdvanced(FSAreaSearch* pointer); + virtual ~FSPanelAreaSearchAdvanced(); + + /*virtual*/ BOOL postBuild(); + + LLCheckBoxCtrl* mCheckboxClickTouch; + LLCheckBoxCtrl* mCheckboxClickBuy; + +private: + FSAreaSearch* mFSAreaSearch; +}; \ No newline at end of file diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 47a622ff8e..9ba5c4e768 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2685,9 +2685,11 @@ void register_viewer_callbacks(LLMessageSystem* msg) msg->setHandlerFuncFast(_PREHASH_ImprovedInstantMessage, process_improved_im); msg->setHandlerFuncFast(_PREHASH_ScriptQuestion, process_script_question); - msg->setHandlerFuncFast(_PREHASH_ObjectProperties, LLSelectMgr::processObjectProperties, NULL); - //msg->setHandlerFuncFast(_PREHASH_ObjectPropertiesFamily, LLSelectMgr::processObjectPropertiesFamily, NULL); - msg->setHandlerFuncFast(_PREHASH_ObjectPropertiesFamily, process_object_properties_family, NULL); //KC + // area search + //msg->setHandlerFuncFast(_PREHASH_ObjectProperties, LLSelectMgr::processObjectProperties, NULL); + msg->setHandlerFuncFast(_PREHASH_ObjectProperties, process_object_properties, NULL); + // area search + msg->setHandlerFuncFast(_PREHASH_ObjectPropertiesFamily, LLSelectMgr::processObjectPropertiesFamily, NULL); msg->setHandlerFunc("ForceObjectSelect", LLSelectMgr::processForceObjectSelect); msg->setHandlerFuncFast(_PREHASH_MoneyBalanceReply, process_money_balance_reply, NULL); diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index c44f59ccf8..5e8a31241b 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -118,6 +118,11 @@ void handle_zoom_to_object(LLUUID object_id); void handle_object_return(); void handle_object_delete(); +// area search +// expose this function so other classes can call it +void handle_object_edit(); +// + void handle_buy_land(); // Takes avatar UUID, or if no UUID passed, uses last selected object diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 7ce587312e..d9700bc520 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -5380,17 +5380,19 @@ void process_kill_object(LLMessageSystem *mesgsys, void **user_data) } } -//ObjectPropertiesFamily -KC -void process_object_properties_family(LLMessageSystem *msg, void**user_data) +// area search +void process_object_properties(LLMessageSystem *msg, void**user_data) { // Send the result to the corresponding requesters. - LLSelectMgr::processObjectPropertiesFamily(msg, user_data); + LLSelectMgr::processObjectProperties(msg, user_data); - FSAreaSearch* area_search_floater = dynamic_cast(LLFloaterReg::getInstance("area_search")); + FSAreaSearch* area_search_floater = LLFloaterReg::getTypedInstance("area_search"); if( area_search_floater ) - area_search_floater->processObjectPropertiesFamily(msg); + { + area_search_floater->processObjectProperties(msg); + } } - +// area search void process_time_synch(LLMessageSystem *mesgsys, void **user_data) { diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index 12d8cc9796..ae0d5a074b 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -84,7 +84,11 @@ void process_object_update(LLMessageSystem *mesgsys, void **user_data); void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data); void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data); void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data); -void process_object_properties_family(LLMessageSystem *msg, void**user_data); + +// area search +// send object properties to all classes that need it +void process_object_properties(LLMessageSystem *msg, void**user_data); +// area search void send_simulator_throttle_settings(const LLHost &host); void process_kill_object( LLMessageSystem *mesgsys, void **user_data); diff --git a/indra/newview/skins/default/xui/en/floater_fs_area_search.xml b/indra/newview/skins/default/xui/en/floater_fs_area_search.xml index 4e5a9364eb..55ff1afa10 100644 --- a/indra/newview/skins/default/xui/en/floater_fs_area_search.xml +++ b/indra/newview/skins/default/xui/en/floater_fs_area_search.xml @@ -1,170 +1,673 @@ - - - - Name search string: - - - Description search string: - - - - - Owner search string: - - - Group search string: - - - - - - - - - -