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: - - - - - - - - - -