FIRE-36734 Add option to set a "default fallback group tag" when entering unassigned regions

Signed-off-by: PanteraPolnocy <panterapolnocy@gmail.com>
This commit is contained in:
PanteraPolnocy
2026-06-08 18:34:26 +02:00
parent 07ef63ae57
commit 47a2425953
15 changed files with 183 additions and 63 deletions
+65 -5
View File
@@ -106,7 +106,8 @@ bool FSFloaterGroupTitles::postBuild()
mSetRegionButton = getChild<LLButton>("btnSetRegion");
mSetRegionManualButton = getChild<LLButton>("btnSetRegionManual");
mClearRegionButton = getChild<LLButton>("btnClearRegion");
mNoneOnUnassigned = getChild<LLCheckBoxCtrl>("none_on_unassigned");
mSetDefaultButton = getChild<LLButton>("btnSetDefault");
mUseDefaultCheck = getChild<LLCheckBoxCtrl>("use_default_title");
mTitleList = getChild<LLScrollListCtrl>("title_list");
mFilterEditor = getChild<LLFilterEditor>("filter_input");
@@ -116,15 +117,17 @@ bool FSFloaterGroupTitles::postBuild()
mSetRegionButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onSetRegion, this));
mSetRegionManualButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onSetRegionManual, this));
mClearRegionButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onClearRegion, this));
mNoneOnUnassigned->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onNoneOnUnassignedToggle, this));
mSetDefaultButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onSetAsDefault, this));
mUseDefaultCheck->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onUseDefaultToggle, this));
mTitleList->setDoubleClickCallback(boost::bind(&FSFloaterGroupTitles::activateGroupTitle, this));
mTitleList->setCommitCallback(boost::bind(&FSFloaterGroupTitles::selectedTitleChanged, this));
mFilterEditor->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onFilterEdit, this, _2));
mNoneOnUnassigned->set(FSGroupTitleRegionMgr::getInstance()->getNoneOnUnassigned());
mSetRegionButton->setEnabled(false);
mSetRegionManualButton->setEnabled(false);
mClearRegionButton->setEnabled(false);
mSetDefaultButton->setEnabled(false);
mUseDefaultCheck->set(FSGroupTitleRegionMgr::getInstance()->getEnabled());
mAssignmentsChangedConnection = FSGroupTitleRegionMgr::getInstance()->setAssignmentsChangedCallback([this]() { updateRegionColumn(); });
mTitleList->sortByColumn("title_sort_column", true);
@@ -222,6 +225,10 @@ void FSFloaterGroupTitles::addListItem(const LLUUID& group_id, const LLUUID& rol
item["columns"][7]["column"] = "filter_text";
item["columns"][7]["type"] = "text";
item["columns"][7]["value"] = title + " " + group_name + " " + region_name;
item["columns"][8]["column"] = "default_marker";
item["columns"][8]["type"] = "icon";
item["columns"][8]["halign"] = "center";
item["columns"][8]["value"] = LLStringUtil::null;
mTitleList->addElement(item);
@@ -253,6 +260,7 @@ void FSFloaterGroupTitles::processGroupTitleResults(const LLGroupData& group_dat
}
mTitleList->scrollToShowSelected();
updateDefaultColumn();
// Remove observer
observer_map_t::iterator found_it = mGroupTitleObserverMap.find(group_data.mID);
@@ -300,6 +308,8 @@ void FSFloaterGroupTitles::refreshGroupTitles()
mGroupTitleObserverMap[group_data.mID] = roleObserver;
LLGroupMgr::getInstance()->sendGroupTitlesRequest(group_data.mID);
}
updateDefaultColumn();
}
void FSFloaterGroupTitles::selectedTitleChanged()
@@ -314,12 +324,14 @@ void FSFloaterGroupTitles::selectedTitleChanged()
mSetRegionButton->setEnabled(true);
mSetRegionManualButton->setEnabled(true);
mClearRegionButton->setEnabled(has_region);
mSetDefaultButton->setEnabled(true);
}
else
{
mSetRegionButton->setEnabled(false);
mSetRegionManualButton->setEnabled(false);
mClearRegionButton->setEnabled(false);
mSetDefaultButton->setEnabled(false);
}
}
@@ -445,9 +457,57 @@ void FSFloaterGroupTitles::onClearRegion()
menu->show(screen_x, screen_y, mClearRegionButton);
}
void FSFloaterGroupTitles::onNoneOnUnassignedToggle()
void FSFloaterGroupTitles::onUseDefaultToggle()
{
FSGroupTitleRegionMgr::getInstance()->setNoneOnUnassigned(mNoneOnUnassigned->get());
FSGroupTitleRegionMgr::getInstance()->setEnabled(mUseDefaultCheck->get());
updateDefaultColumn();
}
void FSFloaterGroupTitles::onSetAsDefault()
{
LLScrollListItem* selected_item = mTitleList->getFirstSelected();
if (!selected_item)
{
return;
}
const LLUUID group_id = selected_item->getColumn(mTitleList->getColumn("group_id")->mIndex)->getValue().asUUID();
const LLUUID role_id = selected_item->getColumn(mTitleList->getColumn("role_id")->mIndex)->getValue().asUUID();
auto* mgr = FSGroupTitleRegionMgr::getInstance();
mgr->setDefaultTitle(group_id, role_id);
mgr->setEnabled(true);
mUseDefaultCheck->set(true);
updateDefaultColumn();
}
void FSFloaterGroupTitles::updateDefaultColumn()
{
LLScrollListColumn* default_col = mTitleList->getColumn("default_marker");
if (!default_col)
{
return;
}
const auto default_col_idx = default_col->mIndex;
const auto group_id_col_idx = mTitleList->getColumn("group_id")->mIndex;
const auto role_id_col_idx = mTitleList->getColumn("role_id")->mIndex;
auto* mgr = FSGroupTitleRegionMgr::getInstance();
const bool enabled = mgr->getEnabled();
const LLUUID default_group = mgr->getDefaultGroupID();
const LLUUID default_role = mgr->getDefaultRoleID();
for (auto* item : mTitleList->getAllData())
{
auto* cell = item->getColumn(default_col_idx);
if (!cell)
{
continue;
}
const LLUUID group_id = item->getColumn(group_id_col_idx)->getValue().asUUID();
const LLUUID role_id = item->getColumn(role_id_col_idx)->getValue().asUUID();
const bool is_default = enabled && group_id == default_group && role_id == default_role;
cell->setValue(is_default ? LLSD("Check_Mark") : LLSD(LLStringUtil::null));
}
}
void FSFloaterGroupTitles::updateRegionColumn()
+5 -2
View File
@@ -85,7 +85,9 @@ private:
void onSetRegion();
void onSetRegionManual();
void onClearRegion();
void onNoneOnUnassignedToggle();
void onUseDefaultToggle();
void onSetAsDefault();
void updateDefaultColumn();
void updateRegionColumn();
LLButton* mActivateButton;
@@ -94,7 +96,8 @@ private:
LLButton* mSetRegionButton;
LLButton* mSetRegionManualButton;
LLButton* mClearRegionButton;
LLCheckBoxCtrl* mNoneOnUnassigned;
LLButton* mSetDefaultButton;
LLCheckBoxCtrl* mUseDefaultCheck;
LLScrollListCtrl* mTitleList;
LLFilterEditor* mFilterEditor;
+81 -41
View File
@@ -92,7 +92,9 @@ void FSGroupTitleRegionMgr::loadFromDisk()
}
mAssignments.clear();
mNoneOnUnassigned = false;
mEnabled = false;
mDefaultGroupID.setNull();
mDefaultRoleID.setNull();
mLastAppliedRegion.clear();
const auto filename = getFilename();
@@ -120,7 +122,20 @@ void FSGroupTitleRegionMgr::loadFromDisk()
}
file.close();
mNoneOnUnassigned = data["none_on_unassigned"].asBoolean();
if (data.has("none_on_unassigned"))
{
mEnabled = data["none_on_unassigned"].asBoolean();
}
if (data.has("default_group_id"))
{
mDefaultGroupID = data["default_group_id"].asUUID();
}
if (data.has("default_role_id"))
{
mDefaultRoleID = data["default_role_id"].asUUID();
}
if (data.has("assignments") && data["assignments"].isMap())
{
@@ -175,7 +190,9 @@ void FSGroupTitleRegionMgr::saveToDisk()
}
LLSD data;
data["none_on_unassigned"] = mNoneOnUnassigned;
data["none_on_unassigned"] = mEnabled;
data["default_group_id"] = mDefaultGroupID;
data["default_role_id"] = mDefaultRoleID;
data["assignments"] = assignments;
llofstream file(filename.c_str());
@@ -305,9 +322,24 @@ bool FSGroupTitleRegionMgr::getAssignmentForRegion(const std::string& region_nam
// Preferences
// ---------------------------------------------------------------------------
void FSGroupTitleRegionMgr::setNoneOnUnassigned(bool enabled)
void FSGroupTitleRegionMgr::setEnabled(bool enabled)
{
mNoneOnUnassigned = enabled;
if (enabled == mEnabled)
{
return;
}
mEnabled = enabled;
saveToDisk();
}
void FSGroupTitleRegionMgr::setDefaultTitle(const LLUUID& group_id, const LLUUID& role_id)
{
if (group_id == mDefaultGroupID && role_id == mDefaultRoleID)
{
return;
}
mDefaultGroupID = group_id;
mDefaultRoleID = role_id;
saveToDisk();
}
@@ -483,6 +515,41 @@ void FSGroupTitleRegionMgr::onValidationTimeout()
// Region change handler
// ---------------------------------------------------------------------------
void FSGroupTitleRegionMgr::applyGroupTitle(const LLUUID& group_id, const LLUUID& role_id)
{
if (group_id.notNull() && !gAgent.isInGroup(group_id))
{
LL_WARNS() << "Requested group title belongs to a group we are no longer in, skipping" << LL_ENDL;
return;
}
bool title_already_active = false;
if (group_id.notNull())
{
if (const auto* group_data = LLGroupMgr::getInstance()->getGroupData(group_id))
{
for (const auto& title : group_data->mTitles)
{
if (title.mRoleID == role_id && title.mSelected)
{
title_already_active = true;
break;
}
}
}
}
if (group_id.notNull() && !title_already_active)
{
LLGroupMgr::getInstance()->sendGroupTitleUpdate(group_id, role_id);
}
if (gAgent.getGroupID() != group_id)
{
LLGroupActions::activate(group_id);
}
}
void FSGroupTitleRegionMgr::onRegionChanged()
{
if (!mDataLoaded)
@@ -518,43 +585,16 @@ void FSGroupTitleRegionMgr::onRegionChanged()
LLUUID role_id;
if (getAssignmentForRegion(region_name, group_id, role_id))
{
if (group_id.notNull() && !gAgent.isInGroup(group_id))
{
LL_WARNS() << "Region '" << region->getName() << "' has a title assignment for a group we are no longer in, skipping" << LL_ENDL;
return;
}
bool title_already_active = false;
if (group_id.notNull())
{
if (const auto* group_data = LLGroupMgr::getInstance()->getGroupData(group_id))
{
for (const auto& title : group_data->mTitles)
{
if (title.mRoleID == role_id && title.mSelected)
{
title_already_active = true;
break;
}
}
}
}
LL_INFOS() << "Region '" << region->getName() << "', switching to assigned group title" << LL_ENDL;
if (group_id.notNull() && !title_already_active)
{
LLGroupMgr::getInstance()->sendGroupTitleUpdate(group_id, role_id);
}
if (gAgent.getGroupID() != group_id)
{
LLGroupActions::activate(group_id);
}
LL_DEBUGS() << "Region '" << region->getName() << "' has an assigned group title, applying it" << LL_ENDL;
applyGroupTitle(group_id, role_id);
return;
}
else if (mNoneOnUnassigned && gAgent.getGroupID().notNull())
if (!mEnabled)
{
LL_INFOS() << "Region '" << region->getName() << "' has no title preset, deactivating group" << LL_ENDL;
LLGroupActions::activate(LLUUID::null);
return;
}
LL_DEBUGS() << "Region '" << region->getName() << "' is unassigned, applying default group title" << LL_ENDL;
applyGroupTitle(mDefaultGroupID, mDefaultRoleID);
}
+9 -3
View File
@@ -59,8 +59,11 @@ public:
std::string getRegionForTitle(const LLUUID& group_id, const LLUUID& role_id) const;
std::vector<std::string> getRegionDisplayNamesForTitle(const LLUUID& group_id, const LLUUID& role_id) const;
bool getAssignmentForRegion(const std::string& region_name, LLUUID& group_id, LLUUID& role_id) const;
void setNoneOnUnassigned(bool enabled);
bool getNoneOnUnassigned() const { return mNoneOnUnassigned; }
void setEnabled(bool enabled);
bool getEnabled() const { return mEnabled; }
void setDefaultTitle(const LLUUID& group_id, const LLUUID& role_id);
const LLUUID& getDefaultGroupID() const { return mDefaultGroupID; }
const LLUUID& getDefaultRoleID() const { return mDefaultRoleID; }
void showRegionInputDialog(const LLUUID& group_id, const LLUUID& role_id);
static std::string sanitizeRegionName(const std::string& input);
@@ -69,6 +72,7 @@ public:
private:
void validateAndSetAssignment(const LLUUID& group_id, const LLUUID& role_id, const std::string& region_name);
void applyGroupTitle(const LLUUID& group_id, const LLUUID& role_id);
void onRegionChanged();
void cancelPendingValidation();
void onValidationResult(U64 region_handle);
@@ -76,7 +80,9 @@ private:
std::string getFilename() const;
static std::string normalizeRegionName(const std::string& name);
static bool onRegionInputCallback(const LLSD& notification, const LLSD& response);
bool mNoneOnUnassigned = false;
bool mEnabled = false;
LLUUID mDefaultGroupID;
LLUUID mDefaultRoleID;
bool mDataLoaded = false;
std::string mLastAppliedRegion;
@@ -14,5 +14,4 @@
<button name="btnClearRegion" label="Bölgəni sil" tool_tip="Seçilmiş başlıqdan bölgə təyinatını silin" />
<button name="btnInfo" label="Məlumat" tool_tip="Seçilmiş qrup başlıqı ilə bağlı qrup haqqında məlumat göstərin" />
<button name="btnActivate" label="Aktivləşdir" tool_tip="Seçilmiş qrup başlıqını aktivləşdirin" />
<check_box label="Təyinatsız bölgələrdə 'yoxdur' et" name="none_on_unassigned" />
</floater>
@@ -14,5 +14,4 @@
<button name="btnClearRegion" label="Region löschen" tool_tip="Regionszuweisung des ausgewählten Titels entfernen"/>
<button name="btnInfo" label="Info" tool_tip="Informationen zur Gruppe des aktuell ausgewählten Titels anzeigen"/>
<button name="btnActivate" label="Aktivieren" tool_tip="Aktuell ausgewählten Gruppentitel aktivieren"/>
<check_box label="'Keiner' in Regionen ohne Zuweisung" name="none_on_unassigned" />
</floater>
@@ -36,6 +36,11 @@
right="-5"
left="5"
height="255">
<column
name="default_marker"
label="D"
tool_tip="Default title worn in regions without an assignment"
width="24" />
<column
name="grouptitle"
sort_column="title_sort_column"
@@ -128,8 +133,21 @@
<check_box
follows="left|bottom"
height="16"
label="Set to 'none' in unassigned regions"
layout="topleft"
left="5"
name="none_on_unassigned" />
top_pad="6"
name="use_default_title"
label="Use default title in regions without assignment"
tool_tip="When you enter a region with no title assignment, switch to the title marked as default. A default of &lt;No group title&gt; wears no tag."
width="350" />
<button
name="btnSetDefault"
label="Set as default"
font="SansSerifSmall"
tool_tip="Mark the selected title as the default for regions without an assignment"
height="22"
width="130"
right="-4"
top_delta="-3"
follows="right|bottom"/>
</floater>
@@ -13,5 +13,4 @@
<button name="btnClearRegion" label="Borrar región" tool_tip="Eliminar la asignación de región del título seleccionado"/>
<button name="btnInfo" label="Información" tool_tip="Muestra información sobre el grupo del título seleccionado"/>
<button name="btnActivate" label="Activar" tool_tip="Activa el título de grupo seleccionado"/>
<check_box label="'Ninguno' en regiones sin preajuste" name="none_on_unassigned" />
</floater>
@@ -13,5 +13,4 @@
<button name="btnClearRegion" label="Effacer région" tool_tip="Supprimer l'assignation de région du titre sélectionné"/>
<button name="btnInfo" label="Infos" tool_tip="Afficher les informations sur le groupe du titre sélectionné"/>
<button name="btnActivate" label="Activer" tool_tip="Activer le titre du groupe sélectionné"/>
<check_box label="'Aucun' pour régions sans préréglage" name="none_on_unassigned" />
</floater>
@@ -14,5 +14,4 @@
<button name="btnClearRegion" label="Cancella reg." tool_tip="Rimuovi l'assegnazione della regione dal titolo selezionato" />
<button name="btnInfo" tool_tip="Mostra il profilo del gruppo del titolo selezionato" />
<button name="btnActivate" label="Attiva" tool_tip="Attiva il titolo selezionato" />
<check_box label="'Nessuno' nelle regioni senza preset" name="none_on_unassigned" />
</floater>
@@ -14,5 +14,4 @@
<button name="btnClearRegion" label="地域を消去" tool_tip="選択したタイトルから地域の割り当てを削除します"/>
<button name="btnInfo" label="情報" tool_tip="選択したタイトルのグループに関する情報を表示します。" />
<button name="btnActivate" label="アクティブ" tool_tip="選択したグループのタイトルをアクティブにします。"/>
<check_box label="未設定の地域では「なし」にする" name="none_on_unassigned" />
</floater>
@@ -4,6 +4,7 @@
<floater.string name="ClearAllRegions" value="Wyczyść wszystkie regiony"/>
<filter_editor label="Filtruj tytuły grup" name="filter_input" />
<scroll_list name="title_list">
<column name="default_marker" tool_tip="Domyślny tytuł włączony w regionach bez przypisania" />
<column name="grouptitle" label="Tytuł" />
<column name="groupname" label="Grupa" />
<column name="regionname" label="Auto-region" />
@@ -14,5 +15,6 @@
<button name="btnClearRegion" label="Wyczyść region" tool_tip="Usuń przypisanie regionu z wybranego tytułu" />
<button name="btnInfo" label="Info" tool_tip="Pokaż informacje o grupie związanej z wybranym tytułem" />
<button name="btnActivate" label="Aktywuj" tool_tip="Aktywuj wybrany tytuł grupy" />
<check_box label="Ustaw 'brak' w regionach bez przypisania" name="none_on_unassigned" />
<check_box name="use_default_title" label="Użyj domyślnego tytułu w regionach bez przypisania" tool_tip="Gdy pojawisz się w regionie do którego nie przypisano tytułu, to przełącz się na tytuł oznaczony jako domyślny. Domyślne &lt;Brak tytułu grupy&gt; nie dodaje żadnego taga grupy." />
<button name="btnSetDefault" label="Ustaw jako domyślny" tool_tip="Oznacz wybrany tytuł jako domyślny dla regionów bez przypisania" />
</floater>
@@ -14,5 +14,4 @@
<button name="btnClearRegion" label="Limpar região" tool_tip="Remover atribuição de região do título selecionado"/>
<button name="btnInfo" label="Info" tool_tip="Mostrar informações do grupo do título selecionado"/>
<button name="btnActivate" label="Ativar" tool_tip="Ativar o título de grupo selecionado"/>
<check_box label="'Nenhum' em regiões sem atribuição" name="none_on_unassigned" />
</floater>
@@ -14,5 +14,4 @@
<button name="btnClearRegion" label="Очистить рег." tool_tip="Удалить привязку региона для выбранного титула" />
<button name="btnInfo" label="инфо" tool_tip="Показать информацию о группе, связанную с выбранным титулом" />
<button name="btnActivate" label="Активировать" tool_tip="Активировать выбранный титул группы" />
<check_box label="«Нет» в регионах без назначения" name="none_on_unassigned" />
</floater>
@@ -14,5 +14,4 @@
<button name="btnClearRegion" label="清除區域" tool_tip="移除所選頭銜的區域指派"/>
<button name="btnInfo" label="資訊" tool_tip="顯示目前所選頭銜所屬群組的資訊"/>
<button name="btnActivate" label="啟用" tool_tip="啟用目前選取的群組頭銜"/>
<check_box label="在未指派區域中顯示「無」" name="none_on_unassigned" />
</floater>