diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h index bdc292def..0d87a0fbf 100644 --- a/indra/llui/llscrolllistitem.h +++ b/indra/llui/llscrolllistitem.h @@ -28,8 +28,6 @@ #ifndef LLSCROLLLISTITEM_H #define LLSCROLLLISTITEM_H -#include "llrefcount.h" -#include "llhandle.h" #include "llsd.h" #include "llscrolllistcell.h" @@ -37,7 +35,6 @@ // LLScrollListItem //--------------------------------------------------------------------------- class LLScrollListItem - : public LLHandleProvider // Singu TODO: Break out into LLNameListItem { friend class LLScrollListCtrl; public: diff --git a/indra/newview/llfloatermute.cpp b/indra/newview/llfloatermute.cpp index 6812a75e4..b4ea23ef4 100644 --- a/indra/newview/llfloatermute.cpp +++ b/indra/newview/llfloatermute.cpp @@ -238,46 +238,48 @@ void LLFloaterMute::refreshMuteList() for (it = mutes.begin(); it != mutes.end(); ++it) { std::string display_name = it->mName; - LLSD element; + LLNameListCtrl::NameItem element; LLUUID entry_id; if(it->mType == LLMute::GROUP || it->mType == LLMute::AGENT) entry_id = it->mID; else entry_id.generate(boost::lexical_cast( count++ )); mMuteDict.insert(std::make_pair(entry_id,*it)); - element["id"] = entry_id; - element["name"] = display_name; + element.value = entry_id; + element.name = display_name; - LLSD& name_column = element["columns"][1]; - name_column["column"] = "name"; - name_column["type"] = "text"; - name_column["value"] = ""; + LLScrollListCell::Params name_column; + name_column.column = "name"; + name_column.type = "text"; + name_column.value = ""; - LLSD& icon_column = element["columns"][0]; - icon_column["column"] = "icon"; - icon_column["type"] = "icon"; + LLScrollListCell::Params icon_column; + icon_column.column = "icon"; + icon_column.type = "icon"; switch(it->mType) { case LLMute::GROUP: - icon_column["value"] = mGroupIcon->getName(); - element["target"] = LLNameListCtrl::GROUP; + icon_column.value = mGroupIcon->getName(); + element.target = LLNameListCtrl::GROUP; break; case LLMute::AGENT: - icon_column["value"] = mAvatarIcon->getName(); - element["target"] = LLNameListCtrl::INDIVIDUAL; + icon_column.value = mAvatarIcon->getName(); + element.target = LLNameListCtrl::INDIVIDUAL; break; case LLMute::OBJECT: - icon_column["value"] = mObjectIcon->getName(); - element["target"] = LLNameListCtrl::SPECIAL; + icon_column.value = mObjectIcon->getName(); + element.target = LLNameListCtrl::SPECIAL; break; case LLMute::BY_NAME: default: - icon_column["value"] = mNameIcon->getName(); - element["target"] = LLNameListCtrl::SPECIAL; + icon_column.value = mNameIcon->getName(); + element.target = LLNameListCtrl::SPECIAL; break; } + element.columns.add(icon_column); + element.columns.add(name_column); mMuteList->addNameItemRow(element); } mMuteList->updateSort(); diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 1675a2f71..653b951d3 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -2,31 +2,25 @@ * @file llnamelistctrl.cpp * @brief A list of names, automatically refreshed from name cache. * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -42,55 +36,41 @@ #include "llinventory.h" #include "llscrolllistitem.h" #include "llscrolllistcolumn.h" +#include "llsdparam.h" #include "lltrans.h" static LLRegisterWidget r("name_list"); -// statics -std::set LLNameListCtrl::sInstances; -LLNameListCtrl::LLNameListCtrl(const std::string& name, - const LLRect& rect, - BOOL allow_multiple_selection, - BOOL draw_border, - bool draw_heading, - S32 name_column_index, - const std::string& tooltip) -: LLScrollListCtrl(name, rect, NULL, allow_multiple_selection, - draw_border,draw_heading), +void LLNameListCtrl::NameTypeNames::declareValues() +{ + declare("INDIVIDUAL", LLNameListCtrl::INDIVIDUAL); + declare("GROUP", LLNameListCtrl::GROUP); + declare("SPECIAL", LLNameListCtrl::SPECIAL); +} + +LLNameListCtrl::LLNameListCtrl(const std::string& name, const LLRect& rect, BOOL allow_multiple_selection, BOOL draw_border, bool draw_heading, S32 name_column_index, const std::string& tooltip) +: LLScrollListCtrl(name, rect, NULL, allow_multiple_selection, draw_border,draw_heading), mNameColumnIndex(name_column_index), - mAllowCallingCardDrop(FALSE), - mShortNames(FALSE) + mAllowCallingCardDrop(false), + mShortNames(false), + mAvatarNameCacheConnection() { setToolTip(tooltip); - LLNameListCtrl::sInstances.insert(this); } - -// virtual -LLNameListCtrl::~LLNameListCtrl() -{ - LLNameListCtrl::sInstances.erase(this); -} - - // public LLScrollListItem* LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos, BOOL enabled, const std::string& suffix) { //llinfos << "LLNameListCtrl::addNameItem " << agent_id << llendl; - LLSD item; - item["id"] = agent_id; - item["enabled"] = enabled; - item["target"] = INDIVIDUAL; - item["suffix"] = suffix; - LLSD& column = item["columns"][0]; - column["value"] = ""; - column["font"] = "SANSSERIF"; - column["column"] = "name"; + NameItem item; + item.value = agent_id; + item.enabled = enabled; + item.target = INDIVIDUAL; - return addNameItemRow(item, pos); + return addNameItemRow(item, pos, suffix); } // virtual, public @@ -146,50 +126,52 @@ BOOL LLNameListCtrl::handleDragAndDrop( void LLNameListCtrl::addGroupNameItem(const LLUUID& group_id, EAddPosition pos, BOOL enabled) { - LLSD item; - item["id"] = group_id; - item["enabled"] = enabled; - item["target"] = GROUP; - LLSD& column = item["columns"][0]; - column["value"] = ""; - column["font"] = "SANSSERIF"; - column["column"] = "name"; + NameItem item; + item.value = group_id; + item.enabled = enabled; + item.target = GROUP; addNameItemRow(item, pos); } // public -void LLNameListCtrl::addGroupNameItem(LLSD& item, EAddPosition pos) +void LLNameListCtrl::addGroupNameItem(LLNameListCtrl::NameItem& item, EAddPosition pos) { - item["target"] = GROUP; + item.target = GROUP; addNameItemRow(item, pos); } -LLScrollListItem* LLNameListCtrl::addNameItem(LLSD& item, EAddPosition pos) +LLScrollListItem* LLNameListCtrl::addNameItem(LLNameListCtrl::NameItem& item, EAddPosition pos) { - item["target"] = INDIVIDUAL; + item.target = INDIVIDUAL; return addNameItemRow(item, pos); } -LLScrollListItem* LLNameListCtrl::addElement(const LLSD& value, EAddPosition pos, void* userdata) +LLScrollListItem* LLNameListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata) { - return addNameItemRow(value, pos, userdata); + LLNameListCtrl::NameItem item_params; + LLParamSDParser parser; + parser.readSD(element, item_params); + item_params.userdata = userdata; + return addNameItemRow(item_params, pos); } -LLScrollListItem* LLNameListCtrl::addNameItemRow(const LLSD& value, EAddPosition pos, void* userdata) -{ - // Singu Note: ScrollLists don't use "target" or "suffix", for now, just remove them - LLSD scroll_value = value; - scroll_value.erase("target"); - scroll_value.erase("suffix"); - LLScrollListItem* item = LLScrollListCtrl::addElement(scroll_value, pos, userdata); + +LLScrollListItem* LLNameListCtrl::addNameItemRow( + const LLNameListCtrl::NameItem& name_item, + EAddPosition pos, + const std::string& suffix) +{ + LLUUID id = name_item.value().asUUID(); + LLNameListItem* item = new LLNameListItem(name_item,name_item.target() == GROUP); + if (!item) return NULL; - LLUUID id = item->getUUID(); + LLScrollListCtrl::addRow(item, name_item, pos); // use supplied name by default - std::string fullname = value["name"].asString(); - switch(value["target"].asInteger()) + std::string fullname = name_item.name; + switch(name_item.target) { case GROUP: gCacheName->getGroupName(id, fullname); @@ -214,11 +196,14 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow(const LLSD& value, EAddPosition } else { - fullname = " ( " + LLTrans::getString("LoadingData") + " ) "; // ...schedule a callback - LLAvatarNameCache::get(id, - boost::bind(&LLNameListCtrl::onAvatarNameCache, - this, _1, _2, item->getHandle())); + // This is not correct and will likely lead to partially populated lists in cases where avatar names are not cached. + // *TODO : Change this to have 2 callbacks : one callback per list item and one for the whole list. + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + mAvatarNameCacheConnection = LLAvatarNameCache::get(id,boost::bind(&LLNameListCtrl::onAvatarNameCache,this, _1, _2, item->getHandle())); } break; } @@ -227,14 +212,13 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow(const LLSD& value, EAddPosition } // Append optional suffix. - std::string suffix = value["suffix"]; if(!suffix.empty()) { fullname.append(suffix); } LLScrollListCell* cell = item->getColumn(mNameColumnIndex); - if (cell && !fullname.empty() && cell->getValue().asString().empty()) + if (cell) { cell->setValue(fullname); } @@ -276,15 +260,17 @@ void LLNameListCtrl::removeNameItem(const LLUUID& agent_id) void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, - LLHandle item) + LLHandle item) { + mAvatarNameCacheConnection.disconnect(); + std::string name; if (mShortNames) name = av_name.mDisplayName; else name = av_name.getCompleteName(); - LLScrollListItem* list_item = item.get(); + LLNameListItem* list_item = item.get(); if (list_item && list_item->getUUID() == agent_id) { LLScrollListCell* cell = list_item->getColumn(mNameColumnIndex); @@ -339,13 +325,7 @@ LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto S32 name_column_index = 0; node->getAttributeS32("name_column_index", name_column_index); - LLNameListCtrl* name_list = new LLNameListCtrl("name_list", - rect, - multi_select, - draw_border, - draw_heading, - name_column_index - ); + LLNameListCtrl* name_list = new LLNameListCtrl("name_list", rect, multi_select, draw_border, draw_heading, name_column_index); if (node->hasAttribute("heading_height")) { S32 heading_height; @@ -376,7 +356,6 @@ LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto std::string columnname(labelname); child->getAttributeString("name", columnname); - std::string sortname(columnname); child->getAttributeString("sort", sortname); @@ -457,6 +436,3 @@ LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto return name_list; } - - - diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index 9f672d1f1..451f1ae7c 100644 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -2,31 +2,25 @@ * @file llnamelistctrl.h * @brief A list of names, automatically refreshing from the name cache. * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -39,6 +33,36 @@ class LLAvatarName; +/** + * LLNameListCtrl item + * + * We don't use LLScrollListItem to be able to override getUUID(), which is needed + * because the name list item value is not simply an UUID but a map (uuid, is_group). + */ +class LLNameListItem : public LLScrollListItem, public LLHandleProvider +{ +public: + bool isGroup() const { return mIsGroup; } + void setIsGroup(bool is_group) { mIsGroup = is_group; } + +protected: + friend class LLNameListCtrl; + + LLNameListItem( const LLScrollListItem::Params& p ) + : LLScrollListItem(p), mIsGroup(false) + { + } + + LLNameListItem( const LLScrollListItem::Params& p, bool is_group ) + : LLScrollListItem(p), mIsGroup(is_group) + { + } + +private: + bool mIsGroup; +}; + + class LLNameListCtrl : public LLScrollListCtrl, public LLInstanceTracker { @@ -50,33 +74,61 @@ public: SPECIAL } ENameType; - LLNameListCtrl(const std::string& name, - const LLRect& rect, - BOOL allow_multiple_selection, - BOOL draw_border = TRUE, - bool draw_heading = false, - S32 name_column_index = 0, - const std::string& tooltip = LLStringUtil::null); - virtual ~LLNameListCtrl(); + // provide names for enums + struct NameTypeNames : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + struct NameItem : public LLInitParam::Block + { + Optional name; + Optional target; + + NameItem() + : name("name"), + target("target", INDIVIDUAL) + {} + }; + + struct NameColumn : public LLInitParam::ChoiceBlock + { + Alternative column_index; + Alternative column_name; + NameColumn() + : column_name("name_column"), + column_index("name_column_index", 0) + {} + }; + +protected: + LLNameListCtrl(const std::string& name, const LLRect& rect, BOOL allow_multiple_selection, BOOL draw_border = TRUE, bool draw_heading = false, S32 name_column_index = 0, const std::string& tooltip = LLStringUtil::null); + virtual ~LLNameListCtrl() + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + } + friend class LLUICtrlFactory; +public: virtual LLXMLNodePtr getXML(bool save_children = true) const; static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); // Add a user to the list by name. It will be added, the name // requested from the cache, and updated as necessary. LLScrollListItem* addNameItem(const LLUUID& agent_id, EAddPosition pos = ADD_BOTTOM, - BOOL enabled = TRUE, std::string const& suffix = LLStringUtil::null); - LLScrollListItem* addNameItem(LLSD& item, EAddPosition pos = ADD_BOTTOM); + BOOL enabled = TRUE, const std::string& suffix = LLStringUtil::null); + LLScrollListItem* addNameItem(NameItem& item, EAddPosition pos = ADD_BOTTOM); /*virtual*/ LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL); - LLScrollListItem* addNameItemRow(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL); + LLScrollListItem* addNameItemRow(const NameItem& value, EAddPosition pos = ADD_BOTTOM, const std::string& suffix = LLStringUtil::null); // Add a user to the list by name. It will be added, the name // requested from the cache, and updated as necessary. void addGroupNameItem(const LLUUID& group_id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); - - void addGroupNameItem(LLSD& item, EAddPosition pos = ADD_BOTTOM); + void addGroupNameItem(NameItem& item, EAddPosition pos = ADD_BOTTOM); void removeNameItem(const LLUUID& agent_id); @@ -91,11 +143,14 @@ public: void sortByName(BOOL ascending); private: - static std::set sInstances; - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, LLHandle item); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, LLHandle item); + +private: S32 mNameColumnIndex; BOOL mAllowCallingCardDrop; bool mShortNames; // display name only, no SLID + boost::signals2::connection mAvatarNameCacheConnection; }; + #endif diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 180504751..ac3ecd30e 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -34,11 +34,8 @@ #include "llpanelgroupgeneral.h" -#include "lluictrlfactory.h" #include "llagent.h" -#include "llavataractions.h" -#include "llfloatergroups.h" -#include "llgroupactions.h" +#include "lluictrlfactory.h" #include "roles_constants.h" // UI elements @@ -46,6 +43,8 @@ #include "llcheckboxctrl.h" #include "llcombobox.h" #include "lldbstrings.h" +#include "llavataractions.h" +#include "llgroupactions.h" #include "llimview.h" #include "lllineeditor.h" #include "llnamebox.h" @@ -837,23 +836,17 @@ void LLPanelGroupGeneral::addMember(LLGroupMemberData* member) { style = "BOLD"; } - LLSD row; - row["id"] = member->getID(); + LLNameListCtrl::NameItem item_params; + item_params.value = member->getID(); - row["columns"][0]["column"] = "name"; - row["columns"][0]["font-style"] = style; - row["columns"][0]["font"] = "SANSSERIF_SMALL"; - row["columns"][1]["column"] = "title"; - row["columns"][1]["value"] = member->getTitle(); - row["columns"][1]["font-style"] = style; - row["columns"][1]["font"] = "SANSSERIF_SMALL"; + LLScrollListCell::Params column; + item_params.columns.add().column("name").font/*.name*/("SANSSERIF_SMALL").font_style(style); - row["columns"][2]["column"] = "online"; - row["columns"][2]["value"] = member->getOnlineStatus(); - row["columns"][2]["font-style"] = style; - row["columns"][1]["font"] = "SANSSERIF_SMALL"; + item_params.columns.add().column("title").value(member->getTitle()).font/*.name*/("SANSSERIF_SMALL").font_style(style); - mListVisibleMembers->addNameItemRow(row); + item_params.columns.add().column("online").value(member->getOnlineStatus()).font/*.name*/("SANSSERIF_SMALL").font_style(style); + + /*LLScrollListItem* member_row =*/ mListVisibleMembers->addNameItemRow(item_params); } void LLPanelGroupGeneral::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name) diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 91edb183a..2a540e1c1 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -1528,19 +1528,17 @@ void LLPanelGroupMembersSubTab::addMemberToList(LLGroupMemberData* data) LLUIString donated = getString("donation_area"); donated.setArg("[AREA]", llformat("%d", data->getContribution())); - LLSD row; - row["id"] = data->getID(); + LLNameListCtrl::NameItem item_params; + item_params.value = data->getID(); - row["columns"][0]["column"] = "name"; - row["columns"][0]["font"] = "SANSSERIF_SMALL"; + item_params.columns.add().column("name").font/*.name*/("SANSSERIF_SMALL")/*.style("NORMAL")*/; - row["columns"][1]["column"] = "donated"; - row["columns"][1]["value"] = donated.getString(); + item_params.columns.add().column("donated").value(donated.getString()) + .font/*.name*/("SANSSERIF_SMALL")/*.style("NORMAL")*/; - row["columns"][2]["column"] = "online"; - row["columns"][2]["value"] = data->getOnlineStatus(); - row["columns"][2]["font"] = "SANSSERIF_SMALL"; - mMembersList->addNameItemRow(row); + item_params.columns.add().column("online").value(data->getOnlineStatus()) + .font/*.name*/("SANSSERIF_SMALL")/*.style("NORMAL")*/; + mMembersList->addNameItemRow(item_params); mHasMatch = TRUE; } diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index f4efd7096..18f47f468 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -1976,7 +1976,9 @@ Where tag = tag string to match. Removes bot's matching the tag. keine (unbekannt) - + + Für Gruppe wurden keine Gruppendaten gefunden +