From 24f6c378b5c1e67f11eeca38c33ebbbe83046945 Mon Sep 17 00:00:00 2001 From: Todd Stinson Date: Thu, 23 Feb 2012 19:56:02 -0800 Subject: PATH-292: Re-enabling filtering for the linksets floater. --- indra/newview/CMakeLists.txt | 2 - indra/newview/llfilteredpathfindinglinksets.cpp | 282 ------------------------ indra/newview/llfilteredpathfindinglinksets.h | 103 --------- indra/newview/llfloaterpathfindinglinksets.cpp | 193 +++++++++------- indra/newview/llfloaterpathfindinglinksets.h | 1 + 5 files changed, 112 insertions(+), 469 deletions(-) delete mode 100644 indra/newview/llfilteredpathfindinglinksets.cpp delete mode 100644 indra/newview/llfilteredpathfindinglinksets.h (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 726f0d1e28..5223eead35 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -162,7 +162,6 @@ set(viewer_SOURCE_FILES llfavoritesbar.cpp llfeaturemanager.cpp llfilepicker.cpp - llfilteredpathfindinglinksets.cpp llfilteredwearablelist.cpp llfirstuse.cpp llflexibleobject.cpp @@ -728,7 +727,6 @@ set(viewer_HEADER_FILES llfavoritesbar.h llfeaturemanager.h llfilepicker.h - llfilteredpathfindinglinksets.h llfilteredwearablelist.h llfirstuse.h llflexibleobject.h diff --git a/indra/newview/llfilteredpathfindinglinksets.cpp b/indra/newview/llfilteredpathfindinglinksets.cpp deleted file mode 100644 index b76d95737c..0000000000 --- a/indra/newview/llfilteredpathfindinglinksets.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/** - * @file llfilteredpathfindinglinksets.h - * @author William Todd Stinson - * @brief Class to implement the filtering of a set of pathfinding linksets - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * 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. - * - * 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. - * - * 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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include -#include - -#include "llsd.h" -#include "lluuid.h" -#include "llpathfindinglinkset.h" -#include "llfilteredpathfindinglinksets.h" - -//--------------------------------------------------------------------------- -// FilterString -//--------------------------------------------------------------------------- - -FilterString::FilterString() - : mFilter(), - mUpperFilter() -{ -} - -FilterString::FilterString(const std::string& pFilter) - : mFilter(pFilter), - mUpperFilter() -{ - LLStringUtil::trim(mFilter); - mUpperFilter = mFilter; - if (!mUpperFilter.empty()) - { - LLStringUtil::toUpper(mUpperFilter); - } -} - -FilterString::~FilterString() -{ -} - -const std::string& FilterString::get() const -{ - return mFilter; -} - -bool FilterString::set(const std::string& pFilter) -{ - std::string newFilter(pFilter); - LLStringUtil::trim(newFilter); - bool didFilterChange = (mFilter.compare(newFilter) != 0); - if (didFilterChange) - { - mFilter = newFilter; - mUpperFilter = newFilter; - LLStringUtil::toUpper(mUpperFilter); - } - - return didFilterChange; -} - -void FilterString::clear() -{ - mFilter.clear(); - mUpperFilter.clear(); -} - -bool FilterString::isActive() const -{ - return !mFilter.empty(); -} - -bool FilterString::doesMatch(const std::string& pTestString) const -{ - bool doesMatch = true; - - if (isActive()) - { - std::string upperTestString(pTestString); - LLStringUtil::toUpper(upperTestString); - doesMatch = (upperTestString.find(mUpperFilter) != std::string::npos); - } - - return doesMatch; -} - -//--------------------------------------------------------------------------- -// LLFilteredPathfindingLinksets -//--------------------------------------------------------------------------- - -LLFilteredPathfindingLinksets::LLFilteredPathfindingLinksets() - : mAllLinksets(), - mFilteredLinksets(), - mIsFiltersDirty(false), - mNameFilter(), - mDescriptionFilter(), - mLinksetUseFilter(LLPathfindingLinkset::kUnknown) -{ -} - -LLFilteredPathfindingLinksets::LLFilteredPathfindingLinksets(const LLSD& pLinksetItems) - : mAllLinksets(), - mFilteredLinksets(), - mIsFiltersDirty(false), - mNameFilter(), - mDescriptionFilter(), - mLinksetUseFilter(LLPathfindingLinkset::kUnknown) -{ - setPathfindingLinksets(pLinksetItems); -} - -LLFilteredPathfindingLinksets::LLFilteredPathfindingLinksets(const LLFilteredPathfindingLinksets& pOther) - : mAllLinksets(pOther.mAllLinksets), - mFilteredLinksets(pOther.mFilteredLinksets), - mIsFiltersDirty(pOther.mIsFiltersDirty), - mNameFilter(pOther.mNameFilter), - mDescriptionFilter(pOther.mDescriptionFilter), - mLinksetUseFilter(pOther.mLinksetUseFilter) -{ -} - -LLFilteredPathfindingLinksets::~LLFilteredPathfindingLinksets() -{ - clearPathfindingLinksets(); -} - -void LLFilteredPathfindingLinksets::setPathfindingLinksets(const LLSD& pLinksetItems) -{ - clearPathfindingLinksets(); - - for (LLSD::map_const_iterator linksetItemIter = pLinksetItems.beginMap(); - linksetItemIter != pLinksetItems.endMap(); ++linksetItemIter) - { - const std::string& uuid(linksetItemIter->first); - const LLSD& linksetData = linksetItemIter->second; - LLPathfindingLinkset linkset(uuid, linksetData); - - mAllLinksets.insert(std::pair(uuid, linkset)); - } - - mIsFiltersDirty = true; -} - -void LLFilteredPathfindingLinksets::updatePathfindingLinksets(const LLSD& pLinksetItems) -{ - for (LLSD::map_const_iterator linksetItemIter = pLinksetItems.beginMap(); - linksetItemIter != pLinksetItems.endMap(); ++linksetItemIter) - { - const std::string& uuid(linksetItemIter->first); - const LLSD& linksetData = linksetItemIter->second; - LLPathfindingLinkset linkset(uuid, linksetData); - - PathfindingLinksetMap::iterator linksetIter = mAllLinksets.find(uuid); - if (linksetIter == mAllLinksets.end()) - { - mAllLinksets.insert(std::pair(uuid, linkset)); - } - else - { - linksetIter->second = linkset; - } - } - - mIsFiltersDirty = true; -} - -void LLFilteredPathfindingLinksets::clearPathfindingLinksets() -{ - mAllLinksets.clear(); - mFilteredLinksets.clear(); - mIsFiltersDirty = false; -} - -const LLFilteredPathfindingLinksets::PathfindingLinksetMap& LLFilteredPathfindingLinksets::getAllLinksets() const -{ - return mAllLinksets; -} - -const LLFilteredPathfindingLinksets::PathfindingLinksetMap& LLFilteredPathfindingLinksets::getFilteredLinksets() -{ - if (!isFiltersActive()) - { - return mAllLinksets; - } - else - { - applyFilters(); - return mFilteredLinksets; - } -} - -BOOL LLFilteredPathfindingLinksets::isFiltersActive() const -{ - return (mNameFilter.isActive() || mDescriptionFilter.isActive() || - (mLinksetUseFilter != LLPathfindingLinkset::kUnknown)); -} - -void LLFilteredPathfindingLinksets::setNameFilter(const std::string& pNameFilter) -{ - mIsFiltersDirty = (mNameFilter.set(pNameFilter) || mIsFiltersDirty); -} - -const std::string& LLFilteredPathfindingLinksets::getNameFilter() const -{ - return mNameFilter.get(); -} - -void LLFilteredPathfindingLinksets::setDescriptionFilter(const std::string& pDescriptionFilter) -{ - mIsFiltersDirty = (mDescriptionFilter.set(pDescriptionFilter) || mIsFiltersDirty); -} - -const std::string& LLFilteredPathfindingLinksets::getDescriptionFilter() const -{ - return mDescriptionFilter.get(); -} - -void LLFilteredPathfindingLinksets::setLinksetUseFilter(LLPathfindingLinkset::ELinksetUse pLinksetUse) -{ - mIsFiltersDirty = (mIsFiltersDirty || (mLinksetUseFilter == pLinksetUse)); - mLinksetUseFilter = pLinksetUse; -} - -LLPathfindingLinkset::ELinksetUse LLFilteredPathfindingLinksets::getLinksetUseFilter() const -{ - return mLinksetUseFilter; -} - -void LLFilteredPathfindingLinksets::clearFilters() -{ - mNameFilter.clear(); - mDescriptionFilter.clear(); - mLinksetUseFilter = LLPathfindingLinkset::kUnknown; - mIsFiltersDirty = false; -} - -void LLFilteredPathfindingLinksets::applyFilters() -{ - mFilteredLinksets.clear(); - - for (PathfindingLinksetMap::const_iterator linksetIter = mAllLinksets.begin(); - linksetIter != mAllLinksets.end(); ++linksetIter) - { - const std::string& uuid(linksetIter->first); - const LLPathfindingLinkset& linkset(linksetIter->second); - if (doesMatchFilters(linkset)) - { - mFilteredLinksets.insert(std::pair(uuid, linkset)); - } - } - - mIsFiltersDirty = false; -} - -BOOL LLFilteredPathfindingLinksets::doesMatchFilters(const LLPathfindingLinkset& pLinkset) const -{ - return (((mLinksetUseFilter == LLPathfindingLinkset::kUnknown) || (mLinksetUseFilter == pLinkset.getLinksetUse())) && - (!mNameFilter.isActive() || mNameFilter.doesMatch(pLinkset.getName())) && - (!mDescriptionFilter.isActive() || mDescriptionFilter.doesMatch(pLinkset.getDescription()))); -} diff --git a/indra/newview/llfilteredpathfindinglinksets.h b/indra/newview/llfilteredpathfindinglinksets.h deleted file mode 100644 index 388818c32b..0000000000 --- a/indra/newview/llfilteredpathfindinglinksets.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * @file llfilteredpathfindinglinksets.h - * @author William Todd Stinson - * @brief Class to implement the filtering of a set of pathfinding linksets - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * 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. - * - * 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. - * - * 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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_FILTEREDPATHFINDINGLINKSETS_H -#define LL_FILTEREDPATHFINDINGLINKSETS_H - -#include -#include -#include "llpathfindinglinkset.h" - -class LLSD; - -class FilterString -{ -public: - FilterString(); - FilterString(const std::string& pFilter); - virtual ~FilterString(); - - const std::string& get() const; - bool set(const std::string& pFilter); - void clear(); - - bool isActive() const; - bool doesMatch(const std::string& pTestString) const; - -protected: - -private: - std::string mFilter; - std::string mUpperFilter; -}; - -class LLFilteredPathfindingLinksets -{ -public: - typedef std::map PathfindingLinksetMap; - - LLFilteredPathfindingLinksets(); - LLFilteredPathfindingLinksets(const LLSD& pLinksetItems); - LLFilteredPathfindingLinksets(const LLFilteredPathfindingLinksets& pOther); - virtual ~LLFilteredPathfindingLinksets(); - - void setPathfindingLinksets(const LLSD& pLinksetItems); - void updatePathfindingLinksets(const LLSD& pLinksetItems); - void clearPathfindingLinksets(); - - const PathfindingLinksetMap& getAllLinksets() const; - const PathfindingLinksetMap& getFilteredLinksets(); - - BOOL isFiltersActive() const; - - void setNameFilter(const std::string& pNameFilter); - const std::string& getNameFilter() const; - - void setDescriptionFilter(const std::string& pDescriptionFilter); - const std::string& getDescriptionFilter() const; - - void setLinksetUseFilter(LLPathfindingLinkset::ELinksetUse pLinksetUse); - LLPathfindingLinkset::ELinksetUse getLinksetUseFilter() const; - - void clearFilters(); - -protected: - -private: - PathfindingLinksetMap mAllLinksets; - PathfindingLinksetMap mFilteredLinksets; - - bool mIsFiltersDirty; - FilterString mNameFilter; - FilterString mDescriptionFilter; - LLPathfindingLinkset::ELinksetUse mLinksetUseFilter; - - void applyFilters(); - BOOL doesMatchFilters(const LLPathfindingLinkset& pLinkset) const; -}; - -#endif // LL_FILTEREDPATHFINDINGLINKSETS_H diff --git a/indra/newview/llfloaterpathfindinglinksets.cpp b/indra/newview/llfloaterpathfindinglinksets.cpp index 88328ee8a8..6dd9a7ced3 100644 --- a/indra/newview/llfloaterpathfindinglinksets.cpp +++ b/indra/newview/llfloaterpathfindinglinksets.cpp @@ -508,22 +508,14 @@ void LLFloaterPathfindingLinksets::onAgentStateCB(LLPathfindingManager::EAgentSt void LLFloaterPathfindingLinksets::applyFilters() { -#if 0 - mLinksetsListPtr.setNameFilter(mFilterByName->getText()); - mLinksetsListPtr.setDescriptionFilter(mFilterByDescription->getText()); - mLinksetsListPtr.setLinksetUseFilter(getFilterLinksetUse()); -#endif updateScrollList(); } void LLFloaterPathfindingLinksets::clearFilters() { -#if 0 - mLinksetsListPtr.clearFilters(); - mFilterByName->setText(LLStringExplicit(mLinksetsListPtr.getNameFilter())); - mFilterByDescription->setText(LLStringExplicit(mLinksetsListPtr.getDescriptionFilter())); - setFilterLinksetUse(mLinksetsListPtr.getLinksetUseFilter()); -#endif + mFilterByName->clear(); + mFilterByDescription->clear(); + setFilterLinksetUse(LLPathfindingLinkset::kUnknown); updateScrollList(); } @@ -601,92 +593,129 @@ void LLFloaterPathfindingLinksets::updateScrollList() if (mLinksetsListPtr != NULL) { + std::string nameFilter = mFilterByName->getText(); + std::string descriptionFilter = mFilterByDescription->getText(); + LLPathfindingLinkset::ELinksetUse linksetUseFilter = getFilterLinksetUse(); + bool isFilteringName = !nameFilter.empty(); + bool isFilteringDescription = !descriptionFilter.empty(); + bool isFilteringLinksetUse = (linksetUseFilter != LLPathfindingLinkset::kUnknown); + const LLVector3& avatarPosition = gAgent.getPositionAgent(); - for (LLPathfindingLinksetList::const_iterator linksetIter = mLinksetsListPtr->begin(); - linksetIter != mLinksetsListPtr->end(); ++linksetIter) + + if (isFilteringName || isFilteringDescription || isFilteringLinksetUse) + { + LLStringUtil::toUpper(nameFilter); + LLStringUtil::toUpper(descriptionFilter); + for (LLPathfindingLinksetList::const_iterator linksetIter = mLinksetsListPtr->begin(); + linksetIter != mLinksetsListPtr->end(); ++linksetIter) + { + const LLPathfindingLinksetPtr linksetPtr(linksetIter->second); + std::string linksetName = linksetPtr->getName(); + std::string linksetDescription = linksetPtr->getDescription(); + LLStringUtil::toUpper(linksetName); + LLStringUtil::toUpper(linksetDescription); + if ((!isFilteringName || (linksetName.find(nameFilter) != std::string::npos)) && + (!isFilteringDescription || (linksetDescription.find(descriptionFilter) != std::string::npos)) && + (!isFilteringLinksetUse || (linksetPtr->getLinksetUse() == linksetUseFilter))) + { + LLSD element = buildScrollListElement(linksetPtr, avatarPosition); + mLinksetsScrollList->addElement(element); + } + } + } + else { - const LLPathfindingLinksetPtr linksetPtr(linksetIter->second); + for (LLPathfindingLinksetList::const_iterator linksetIter = mLinksetsListPtr->begin(); + linksetIter != mLinksetsListPtr->end(); ++linksetIter) + { + const LLPathfindingLinksetPtr linksetPtr(linksetIter->second); + LLSD element = buildScrollListElement(linksetPtr, avatarPosition); + mLinksetsScrollList->addElement(element); + } + } + } - LLSD columns; + mLinksetsScrollList->selectMultiple(selectedUUIDs); + updateEditFieldValues(); + updateControls(); +} - columns[0]["column"] = "name"; - columns[0]["value"] = linksetPtr->getName(); - columns[0]["font"] = "SANSSERIF"; +LLSD LLFloaterPathfindingLinksets::buildScrollListElement(const LLPathfindingLinksetPtr pLinksetPtr, const LLVector3 &pAvatarPosition) +{ + LLSD columns; - columns[1]["column"] = "description"; - columns[1]["value"] = linksetPtr->getDescription(); - columns[1]["font"] = "SANSSERIF"; + columns[0]["column"] = "name"; + columns[0]["value"] = pLinksetPtr->getName(); + columns[0]["font"] = "SANSSERIF"; - columns[2]["column"] = "land_impact"; - columns[2]["value"] = llformat("%1d", linksetPtr->getLandImpact()); - columns[2]["font"] = "SANSSERIF"; + columns[1]["column"] = "description"; + columns[1]["value"] = pLinksetPtr->getDescription(); + columns[1]["font"] = "SANSSERIF"; - columns[3]["column"] = "dist_from_you"; - columns[3]["value"] = llformat("%1.0f m", dist_vec(avatarPosition, linksetPtr->getLocation())); - columns[3]["font"] = "SANSSERIF"; + columns[2]["column"] = "land_impact"; + columns[2]["value"] = llformat("%1d", pLinksetPtr->getLandImpact()); + columns[2]["font"] = "SANSSERIF"; - columns[4]["column"] = "linkset_use"; - std::string linksetUse; - switch (linksetPtr->getLinksetUse()) - { - case LLPathfindingLinkset::kWalkable : - linksetUse = getString("linkset_use_walkable"); - break; - case LLPathfindingLinkset::kStaticObstacle : - linksetUse = getString("linkset_use_static_obstacle"); - break; - case LLPathfindingLinkset::kDynamicObstacle : - linksetUse = getString("linkset_use_dynamic_obstacle"); - break; - case LLPathfindingLinkset::kMaterialVolume : - linksetUse = getString("linkset_use_material_volume"); - break; - case LLPathfindingLinkset::kExclusionVolume : - linksetUse = getString("linkset_use_exclusion_volume"); - break; - case LLPathfindingLinkset::kDynamicPhantom : - linksetUse = getString("linkset_use_dynamic_phantom"); - break; - case LLPathfindingLinkset::kUnknown : - default : - linksetUse = getString("linkset_use_dynamic_obstacle"); - llassert(0); - break; - } - if (linksetPtr->isLocked()) - { - linksetUse += (" " + getString("linkset_is_locked_state")); - } - columns[4]["value"] = linksetUse; - columns[4]["font"] = "SANSSERIF"; + columns[3]["column"] = "dist_from_you"; + columns[3]["value"] = llformat("%1.0f m", dist_vec(pAvatarPosition, pLinksetPtr->getLocation())); + columns[3]["font"] = "SANSSERIF"; - columns[5]["column"] = "a_percent"; - columns[5]["value"] = llformat("%3d", linksetPtr->getWalkabilityCoefficientA()); - columns[5]["font"] = "SANSSERIF"; + columns[4]["column"] = "linkset_use"; + std::string linksetUse; + switch (pLinksetPtr->getLinksetUse()) + { + case LLPathfindingLinkset::kWalkable : + linksetUse = getString("linkset_use_walkable"); + break; + case LLPathfindingLinkset::kStaticObstacle : + linksetUse = getString("linkset_use_static_obstacle"); + break; + case LLPathfindingLinkset::kDynamicObstacle : + linksetUse = getString("linkset_use_dynamic_obstacle"); + break; + case LLPathfindingLinkset::kMaterialVolume : + linksetUse = getString("linkset_use_material_volume"); + break; + case LLPathfindingLinkset::kExclusionVolume : + linksetUse = getString("linkset_use_exclusion_volume"); + break; + case LLPathfindingLinkset::kDynamicPhantom : + linksetUse = getString("linkset_use_dynamic_phantom"); + break; + case LLPathfindingLinkset::kUnknown : + default : + linksetUse = getString("linkset_use_dynamic_obstacle"); + llassert(0); + break; + } + if (pLinksetPtr->isLocked()) + { + linksetUse += (" " + getString("linkset_is_locked_state")); + } + columns[4]["value"] = linksetUse; + columns[4]["font"] = "SANSSERIF"; - columns[6]["column"] = "b_percent"; - columns[6]["value"] = llformat("%3d", linksetPtr->getWalkabilityCoefficientB()); - columns[6]["font"] = "SANSSERIF"; + columns[5]["column"] = "a_percent"; + columns[5]["value"] = llformat("%3d", pLinksetPtr->getWalkabilityCoefficientA()); + columns[5]["font"] = "SANSSERIF"; - columns[7]["column"] = "c_percent"; - columns[7]["value"] = llformat("%3d", linksetPtr->getWalkabilityCoefficientC()); - columns[7]["font"] = "SANSSERIF"; + columns[6]["column"] = "b_percent"; + columns[6]["value"] = llformat("%3d", pLinksetPtr->getWalkabilityCoefficientB()); + columns[6]["font"] = "SANSSERIF"; - columns[8]["column"] = "d_percent"; - columns[8]["value"] = llformat("%3d", linksetPtr->getWalkabilityCoefficientD()); - columns[8]["font"] = "SANSSERIF"; + columns[7]["column"] = "c_percent"; + columns[7]["value"] = llformat("%3d", pLinksetPtr->getWalkabilityCoefficientC()); + columns[7]["font"] = "SANSSERIF"; - LLSD element; - element["id"] = linksetPtr->getUUID().asString(); - element["column"] = columns; + columns[8]["column"] = "d_percent"; + columns[8]["value"] = llformat("%3d", pLinksetPtr->getWalkabilityCoefficientD()); + columns[8]["font"] = "SANSSERIF"; - mLinksetsScrollList->addElement(element); - } - } + LLSD element; + element["id"] = pLinksetPtr->getUUID().asString(); + element["column"] = columns; - mLinksetsScrollList->selectMultiple(selectedUUIDs); - updateEditFieldValues(); - updateControls(); + return element; } void LLFloaterPathfindingLinksets::updateStatusMessage() diff --git a/indra/newview/llfloaterpathfindinglinksets.h b/indra/newview/llfloaterpathfindinglinksets.h index 2a280cb4af..83c74c8530 100644 --- a/indra/newview/llfloaterpathfindinglinksets.h +++ b/indra/newview/llfloaterpathfindinglinksets.h @@ -139,6 +139,7 @@ private: void updateControls(); void updateEditFieldValues(); void updateScrollList(); + LLSD buildScrollListElement(const LLPathfindingLinksetPtr pLinksetPtr, const LLVector3 &pAvatarPosition); void updateStatusMessage(); void updateEnableStateOnListActions(); -- cgit v1.2.3