Add support for pinning/unpinning groups in people panel and contacts floater

Signed-off-by: PanteraPolnocy <panterapolnocy@gmail.com>
master
PanteraPolnocy 2025-12-07 20:21:46 +01:00
parent a1ba797c46
commit 77023234d3
16 changed files with 527 additions and 17 deletions

View File

@ -114,6 +114,7 @@ set(viewer_SOURCE_FILES
fsdroptarget.cpp
fsexportperms.cpp
fsfloateraddtocontactset.cpp
fsfavoritegroups.cpp
fsfloaterassetblacklist.cpp
fsfloateravatarrendersettings.cpp
fsfloaterblocklist.cpp
@ -956,6 +957,7 @@ set(viewer_HEADER_FILES
fsdroptarget.h
fsexportperms.h
fsfloateraddtocontactset.h
fsfavoritegroups.h
fsfloaterassetblacklist.h
fsfloateravatarrendersettings.h
fsfloaterblocklist.h

View File

@ -1374,6 +1374,17 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>FSFavoriteGroups</key>
<map>
<key>Comment</key>
<string>List of favorite/pinned group UUIDs</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>LLSD</string>
<key>Value</key>
<array></array>
</map>
<key>FSMaxSharedMaturity</key>
<map>
<key>Comment</key>

View File

@ -0,0 +1,137 @@
/**
* @file fsfavoritegroups.cpp
* @brief Favorite groups storage management
*
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2025, The Phoenix Firestorm Project, 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
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "fsfavoritegroups.h"
#include "llagent.h"
#include "llviewercontrol.h"
FSFavoriteGroups::FSFavoriteGroups()
{
}
FSFavoriteGroups::~FSFavoriteGroups()
{
saveFavorites();
}
bool FSFavoriteGroups::isFavorite(const LLUUID& group_id) const
{
return mFavoriteGroups.find(group_id) != mFavoriteGroups.end();
}
void FSFavoriteGroups::addFavorite(const LLUUID& group_id)
{
if (group_id.isNull())
{
return;
}
if (mFavoriteGroups.insert(group_id).second)
{
saveFavorites();
mFavoritesChangedSignal();
}
}
void FSFavoriteGroups::removeFavorite(const LLUUID& group_id)
{
if (mFavoriteGroups.erase(group_id) > 0)
{
saveFavorites();
mFavoritesChangedSignal();
}
}
void FSFavoriteGroups::toggleFavorite(const LLUUID& group_id)
{
if (isFavorite(group_id))
{
removeFavorite(group_id);
}
else
{
addFavorite(group_id);
}
}
void FSFavoriteGroups::loadFavorites()
{
mFavoriteGroups.clear();
LLSD favorites = gSavedPerAccountSettings.getLLSD("FSFavoriteGroups");
if (favorites.isArray())
{
for (LLSD::array_const_iterator it = favorites.beginArray(); it != favorites.endArray(); ++it)
{
if (it->isUUID())
{
mFavoriteGroups.insert(it->asUUID());
}
}
}
LL_INFOS("FavoriteGroups") << "Loaded " << mFavoriteGroups.size() << " favorite groups from per-account settings" << LL_ENDL;
}
void FSFavoriteGroups::saveFavorites()
{
std::set<LLUUID> stale_groups;
for (const auto& group_id : mFavoriteGroups)
{
if (!gAgent.isInGroup(group_id))
{
stale_groups.insert(group_id);
}
}
if (!stale_groups.empty())
{
for (const auto& group_id : stale_groups)
{
mFavoriteGroups.erase(group_id);
}
LL_INFOS("FavoriteGroups") << "Removed " << stale_groups.size() << " stale group(s) from favorites (no longer a member)" << LL_ENDL;
}
LLSD favorites_array = LLSD::emptyArray();
for (const auto& group_id : mFavoriteGroups)
{
favorites_array.append(group_id);
}
gSavedPerAccountSettings.setLLSD("FSFavoriteGroups", favorites_array);
LL_DEBUGS("FavoriteGroups") << "Saved " << mFavoriteGroups.size() << " favorite groups to per-account settings" << LL_ENDL;
}

View File

@ -0,0 +1,62 @@
/**
* @file fsfavoritegroups.h
* @brief Favorite groups storage management
*
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2025, The Phoenix Firestorm Project, 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
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#ifndef FS_FAVORITEGROUPS_H
#define FS_FAVORITEGROUPS_H
#include "llsingleton.h"
#include "lluuid.h"
#include <boost/signals2.hpp>
#include <set>
class FSFavoriteGroups : public LLSingleton<FSFavoriteGroups>
{
LLSINGLETON(FSFavoriteGroups);
~FSFavoriteGroups();
public:
bool isFavorite(const LLUUID& group_id) const;
void addFavorite(const LLUUID& group_id);
void removeFavorite(const LLUUID& group_id);
void toggleFavorite(const LLUUID& group_id);
const std::set<LLUUID>& getFavorites() const { return mFavoriteGroups; }
bool hasFavorites() const { return !mFavoriteGroups.empty(); }
void loadFavorites();
void saveFavorites();
typedef boost::signals2::signal<void()> favorites_changed_signal_t;
boost::signals2::connection setFavoritesChangedCallback(const favorites_changed_signal_t::slot_type& cb)
{
return mFavoritesChangedSignal.connect(cb);
}
private:
std::set<LLUUID> mFavoriteGroups;
favorites_changed_signal_t mFavoritesChangedSignal;
};
#endif // FS_FAVORITEGROUPS_H

View File

@ -31,6 +31,7 @@
#include "fscommon.h"
#include "fscontactsfriendsmenu.h"
#include "fsfavoritegroups.h"
#include "fsfloaterimcontainer.h"
#include "fsscrolllistctrl.h"
#include "llagent.h"
@ -147,6 +148,7 @@ bool FSFloaterContacts::postBuild()
mGroupsChatBtn = mGroupsTab->getChild<LLButton>("chat_btn");
mGroupsInfoBtn = mGroupsTab->getChild<LLButton>("info_btn");
mGroupsActivateBtn = mGroupsTab->getChild<LLButton>("activate_btn");
mGroupsFavoriteBtn = mGroupsTab->getChild<LLButton>("favorite_btn");
mGroupsLeaveBtn = mGroupsTab->getChild<LLButton>("leave_btn");
mGroupsCreateBtn = mGroupsTab->getChild<LLButton>("create_btn");
mGroupsSearchBtn = mGroupsTab->getChild<LLButton>("search_btn");
@ -156,6 +158,7 @@ bool FSFloaterContacts::postBuild()
mGroupsChatBtn->setCommitCallback(boost::bind(&FSFloaterContacts::onGroupChatButtonClicked, this));
mGroupsInfoBtn->setCommitCallback(boost::bind(&FSFloaterContacts::onGroupInfoButtonClicked, this));
mGroupsActivateBtn->setCommitCallback(boost::bind(&FSFloaterContacts::onGroupActivateButtonClicked, this));
mGroupsFavoriteBtn->setCommitCallback(boost::bind(&FSFloaterContacts::onGroupFavoriteButtonClicked, this));
mGroupsLeaveBtn->setCommitCallback(boost::bind(&FSFloaterContacts::onGroupLeaveButtonClicked, this));
mGroupsCreateBtn->setCommitCallback(boost::bind(&FSFloaterContacts::onGroupCreateButtonClicked, this));
mGroupsSearchBtn->setCommitCallback(boost::bind(&FSFloaterContacts::onGroupSearchButtonClicked, this));
@ -247,6 +250,12 @@ void FSFloaterContacts::updateGroupButtons()
mGroupsLeaveBtn->setEnabled(isGroup);
mGroupsCreateBtn->setEnabled((!gMaxAgentGroups) || (gAgent.mGroups.size() < gMaxAgentGroups));
mGroupsInviteBtn->setEnabled(isGroup && gAgent.hasPowerInGroup(groupId, GP_MEMBER_INVITE));
mGroupsFavoriteBtn->setEnabled(isGroup);
if (isGroup)
{
bool is_favorite = FSFavoriteGroups::getInstance()->isFavorite(groupId);
mGroupsFavoriteBtn->setLabel(is_favorite ? getString("unfavorite_label") : getString("favorite_label"));
}
}
void FSFloaterContacts::onOpen(const LLSD& key)
@ -432,6 +441,15 @@ void FSFloaterContacts::onGroupActivateButtonClicked()
LLGroupActions::activate(mGroupList->getSelectedUUID());
}
void FSFloaterContacts::onGroupFavoriteButtonClicked()
{
if (LLUUID group_id = getCurrentItemID(); group_id.notNull())
{
FSFavoriteGroups::getInstance()->toggleFavorite(group_id);
updateGroupButtons();
}
}
void FSFloaterContacts::onGroupLeaveButtonClicked()
{
if (LLUUID group_id = getCurrentItemID(); group_id.notNull())

View File

@ -141,6 +141,7 @@ private:
void onGroupChatButtonClicked();
void onGroupInfoButtonClicked();
void onGroupActivateButtonClicked();
void onGroupFavoriteButtonClicked();
void onGroupLeaveButtonClicked();
void onGroupCreateButtonClicked();
void onGroupSearchButtonClicked();
@ -167,6 +168,7 @@ private:
LLButton* mGroupsChatBtn{ nullptr };
LLButton* mGroupsInfoBtn{ nullptr };
LLButton* mGroupsActivateBtn{ nullptr };
LLButton* mGroupsFavoriteBtn{ nullptr };
LLButton* mGroupsLeaveBtn{ nullptr };
LLButton* mGroupsCreateBtn{ nullptr };
LLButton* mGroupsSearchBtn{ nullptr };

View File

@ -46,6 +46,7 @@
// [RLVa:KB] - Checked: RLVa-2.0.3
#include "rlvactions.h"
// [/RLVa:KB]
#include "fsfavoritegroups.h" // <FS:PP> Group favorites / pinning
#include "llslurl.h"
#include "llurlaction.h"
@ -59,8 +60,37 @@ public:
/** Returns true if item1 < item2, false otherwise */
/*virtual*/ bool compare(const LLPanel* item1, const LLPanel* item2) const
{
std::string name1 = static_cast<const LLGroupListItem*>(item1)->getGroupName();
std::string name2 = static_cast<const LLGroupListItem*>(item2)->getGroupName();
// <FS:PP> Group favorites / pinning
// std::string name1 = static_cast<const LLGroupListItem*>(item1)->getGroupName();
// std::string name2 = static_cast<const LLGroupListItem*>(item2)->getGroupName();
const LLGroupListItem* group_item1 = dynamic_cast<const LLGroupListItem*>(item1);
const LLGroupListItem* group_item2 = dynamic_cast<const LLGroupListItem*>(item2);
if (!group_item1 || !group_item2)
{
if (!group_item1 && group_item2)
{
return group_item2->isFavorite() ? false : true;
}
if (group_item1 && !group_item2)
{
return group_item1->isFavorite() ? true : false;
}
return false;
}
bool fav1 = group_item1->isFavorite();
bool fav2 = group_item2->isFavorite();
if (fav1 != fav2)
{
return fav1 > fav2;
}
std::string name1 = group_item1->getGroupName();
std::string name2 = group_item2->getGroupName();
// </FS:PP>
LLStringUtil::toUpper(name1);
LLStringUtil::toUpper(name2);
@ -76,11 +106,22 @@ public:
/*virtual*/ bool compare(const LLPanel* item1, const LLPanel* item2) const
{
const LLGroupListItem* group_item1 = static_cast<const LLGroupListItem*>(item1);
// <FS:PP> Group favorites / pinning
const LLGroupListItem* group_item1 = dynamic_cast<const LLGroupListItem*>(item1);
const LLGroupListItem* group_item2 = dynamic_cast<const LLGroupListItem*>(item2);
if (!group_item1 || !group_item2)
{
// Sorting doesn't matter, as we hit a separator that shouldn't be visible in this view
return false;
}
// const LLGroupListItem* group_item1 = static_cast<const LLGroupListItem*>(item1);
// </FS:PP>
std::string name1 = group_item1->getGroupName();
bool item1_shared = gAgent.isInGroup(group_item1->getGroupID(), true);
const LLGroupListItem* group_item2 = static_cast<const LLGroupListItem*>(item2);
// const LLGroupListItem* group_item2 = static_cast<const LLGroupListItem*>(item2); - <FS:PP> Group favorites / pinning
std::string name2 = group_item2->getGroupName();
bool item2_shared = gAgent.isInGroup(group_item2->getGroupID(), true);
@ -134,6 +175,12 @@ LLGroupList::~LLGroupList()
{
if (mForAgent) gAgent.removeListener(this);
if (mContextMenuHandle.get()) mContextMenuHandle.get()->die();
// <FS:PP> Group favorites / pinning
if (mFavoritesChangedConnection.connected())
{
mFavoritesChangedConnection.disconnect();
}
// </FS:PP>
}
void LLGroupList::enableForAgent(bool show_icons)
@ -152,6 +199,11 @@ void LLGroupList::enableForAgent(bool show_icons)
registrar.add("People.Groups.Action", boost::bind(&LLGroupList::onContextMenuItemClick, this, _2));
enable_registrar.add("People.Groups.Enable", boost::bind(&LLGroupList::onContextMenuItemEnable, this, _2));
// <FS:PP> Group favorites / pinning
mFavoritesChangedConnection = FSFavoriteGroups::getInstance()->setFavoritesChangedCallback(boost::bind(&LLGroupList::onFavoritesChanged, this));
enable_registrar.add("People.Groups.Visible", boost::bind(&LLGroupList::onContextMenuItemVisible, this, _2));
// </FS:PP>
LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups.xml",
gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
if(context_menu)
@ -174,6 +226,13 @@ bool LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask)
if (mForAgent)
{
// <FS:PP> Group favorites / pinning
LLPanel* selected_item = getSelectedItem();
if (dynamic_cast<LLGroupListSeparator*>(selected_item))
{
return handled;
}
// </FS:PP>
LLToggleableMenu* context_menu = mContextMenuHandle.get();
if (context_menu && size() > 0)
{
@ -234,24 +293,53 @@ void LLGroupList::refresh()
clear();
// <FS:PP> Group favorites / pinning
bool has_favorites = false;
bool has_non_favorites = false;
// </FS:PP>
for(S32 i = 0; i < count; ++i)
{
id = gAgent.mGroups.at(i).mID;
const LLGroupData& group_data = gAgent.mGroups.at(i);
if (have_filter && !findInsensitive(group_data.mName, mNameFilter))
continue;
addNewItem(id, group_data.mName, group_data.mInsigniaID, ADD_BOTTOM, group_data.mListInProfile);
// <FS:PP> Group favorites / pinning
// addNewItem(id, group_data.mName, group_data.mInsigniaID, ADD_BOTTOM, group_data.mListInProfile);
bool is_favorite = FSFavoriteGroups::getInstance()->isFavorite(id);
if (is_favorite)
{
has_favorites = true;
}
else
{
has_non_favorites = true;
}
addNewItem(id, group_data.mName, group_data.mInsigniaID, ADD_BOTTOM, group_data.mListInProfile, is_favorite);
// </FS:PP>
}
// Sort the list.
sort();
// <FS:PP> Group favorites / pinning
if (has_favorites && has_non_favorites && !have_filter)
{
addFavoritesSeparator();
}
// </FS:PP>
// Add "none" to list at top if filter not set (what's the point of filtering "none"?).
// but only if some real groups exists. EXT-4838
if (!have_filter && count > 0 && mShowNone)
{
std::string loc_none = LLTrans::getString("GroupsNone");
addNewItem(LLUUID::null, loc_none, LLUUID::null, ADD_TOP);
// <FS:PP> Group favorites / pinning
// addNewItem(LLUUID::null, loc_none, LLUUID::null, ADD_TOP);
addNewItem(LLUUID::null, loc_none, LLUUID::null, ADD_TOP, true, false);
// </FS:PP>
}
selectItemByUUID(highlight_id);
@ -298,13 +386,17 @@ void LLGroupList::setGroups(const std::map< std::string,LLUUID> group_list)
// PRIVATE Section
//////////////////////////////////////////////////////////////////////////
void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos, bool visible_in_profile)
// <FS:PP> Group favorites / pinning
// void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos, bool visible_in_profile)
void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos, bool visible_in_profile, bool is_favorite)
// </FS:PP>
{
LLGroupListItem* item = new LLGroupListItem(mForAgent, mShowIcons);
item->setGroupID(id);
item->setName(name, mNameFilter);
item->setGroupIconID(icon_id);
item->setFavorite(is_favorite); // <FS:PP> Group favorites / pinning
item->getChildView("info_btn")->setVisible( false);
item->getChildView("profile_btn")->setVisible( false);
@ -390,6 +482,16 @@ bool LLGroupList::onContextMenuItemClick(const LLSD& userdata)
LLUrlAction::copyURLToClipboard(LLSLURL("group", selected_group, "about").getSLURLString());
}
// </FS:Ansariel>
// <FS:PP> Group favorites / pinning
else if (action == "favorite")
{
FSFavoriteGroups::getInstance()->addFavorite(selected_group);
}
else if (action == "unfavorite")
{
FSFavoriteGroups::getInstance()->removeFavorite(selected_group);
}
// </FS:PP>
return true;
}
@ -412,9 +514,84 @@ bool LLGroupList::onContextMenuItemEnable(const LLSD& userdata)
if (userdata.asString() == "call")
return real_group_selected && LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
// <FS:PP> Group favorites / pinning
if (userdata.asString() == "favorite" || userdata.asString() == "unfavorite")
return real_group_selected; // Just in case
// </FS:PP>
return real_group_selected;
}
// <FS:PP> Group favorites / pinning
bool LLGroupList::onContextMenuItemVisible(const LLSD& userdata)
{
std::string action = userdata.asString();
LLUUID selected_group_id = getSelectedUUID();
if (action == "favorite")
{
return selected_group_id.notNull() && !FSFavoriteGroups::getInstance()->isFavorite(selected_group_id);
}
else if (action == "unfavorite")
{
return selected_group_id.notNull() && FSFavoriteGroups::getInstance()->isFavorite(selected_group_id);
}
return true;
}
void LLGroupList::onFavoritesChanged()
{
setDirty();
}
void LLGroupList::refreshFavorites()
{
setDirty();
}
void LLGroupList::addFavoritesSeparator()
{
// Find the position to insert the separator (after the last favorite)
std::vector<LLPanel*> items;
getItems(items);
// Count favorite items to find where separator should go
S32 favorite_count = 0;
for (auto* panel : items)
{
LLGroupListItem* item = dynamic_cast<LLGroupListItem*>(panel);
if (item && item->isFavorite())
{
++favorite_count;
}
}
if (favorite_count > 0)
{
LLGroupListSeparator* separator = new LLGroupListSeparator();
static const LLUUID SEPARATOR_UUID("00000000-0000-0000-0000-000000000001");
addItem(separator, SEPARATOR_UUID, ADD_BOTTOM, false);
sort();
}
}
/************************************************************************/
/* LLGroupListSeparator implementation */
/************************************************************************/
LLGroupListSeparator::LLGroupListSeparator()
: LLPanel()
{
buildFromFile("panel_group_list_separator.xml");
}
bool LLGroupListSeparator::postBuild()
{
return true;
}
// </FS:PP>
/************************************************************************/
/* LLGroupListItem implementation */
/************************************************************************/

View File

@ -37,6 +37,8 @@
#include "llgroupmgr.h"
#include <boost/signals2.hpp> // <FS:PP> Group favorites / pinning
/**
* Auto-updating list of agent groups.
*
@ -72,14 +74,22 @@ public:
LLToggleableMenu* getContextMenu() const { return mContextMenuHandle.get(); }
void refreshFavorites(); // <FS:PP> Group favorites / pinning
private:
void setDirty(bool val = true) { mDirty = val; }
void refresh();
void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM, bool visible_in_profile = true);
// <FS:PP> Group favorites / pinning
// void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM, bool visible_in_profile = true);
void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM, bool visible_in_profile = true, bool is_favorite = false);
void addFavoritesSeparator();
void onFavoritesChanged();
// </FS:PP>
bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); // called on agent group list changes
bool onContextMenuItemClick(const LLSD& userdata);
bool onContextMenuItemEnable(const LLSD& userdata);
bool onContextMenuItemVisible(const LLSD& userdata); // <FS:PP> Group favorites / pinning
LLHandle<LLToggleableMenu> mContextMenuHandle;
@ -91,12 +101,29 @@ private:
bool mShowNone;
typedef std::map< std::string,LLUUID> group_map_t;
group_map_t mGroups;
boost::signals2::connection mFavoritesChangedConnection; // <FS:PP> Group favorites / pinning
};
class LLButton;
class LLGroupIconCtrl;
class LLTextBox;
// <FS:PP> Group favorites / pinning
class LLGroupListSeparator : public LLPanel
{
public:
LLGroupListSeparator();
/*virtual*/ bool postBuild();
// Ignore all mouse interactions on the separator
/*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) { return true; }
/*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) { return true; }
/*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) { return true; }
/*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) { return true; }
/*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) { return true; }
};
// </FS:PP>
class LLGroupListItem : public LLPanel
, public LLGroupMgrObserver
{
@ -110,11 +137,13 @@ public:
const LLUUID& getGroupID() const { return mGroupID; }
const std::string& getGroupName() const { return mGroupName; }
bool isFavorite() const { return mIsFavorite; } // <FS:PP> Group favorites / pinning
void setName(const std::string& name, const std::string& highlight = LLStringUtil::null);
void setGroupID(const LLUUID& group_id);
void setGroupIconID(const LLUUID& group_icon_id);
void setGroupIconVisible(bool visible);
void setFavorite(bool favorite) { mIsFavorite = favorite; } // <FS:PP> Group favorites / pinning
virtual void changed(LLGroupChange gc);
@ -137,6 +166,7 @@ private:
std::string mGroupName;
bool mForAgent;
bool mIsFavorite{ false }; // <FS:PP> Group favorites / pinning
LLStyle::Params mGroupNameStyle;
S32 mIconWidth;

View File

@ -233,6 +233,7 @@
#include "fscommon.h"
#include "fscorehttputil.h"
#include "fsdata.h"
#include "fsfavoritegroups.h" // <FS:PP> Group favorites / pinning
#include "fsfloatercontacts.h"
#include "fsfloaterimcontainer.h"
#include "fsfloaternearbychat.h"
@ -3315,6 +3316,10 @@ bool idle_startup()
// Clean up the userauth stuff.
// LLUserAuth::getInstance()->reset();
// <FS:PP> Group favorites / pinning
FSFavoriteGroups::getInstance()->loadFavorites();
// </FS:PP>
LLStartUp::setStartupState( STATE_STARTED );
do_startup_frame();

View File

@ -24,6 +24,12 @@
<string
name="no_groups_msg"
value="Looking for Groups to join? Try [secondlife:///app/search/groups Search]." />
<string
name="favorite_label"
value="Pin" />
<string
name="unfavorite_label"
value="Unpin" />
<tab_container
follows="all"
height="390"

View File

@ -52,6 +52,32 @@
function="People.Groups.Enable"
parameter="call" />
</menu_item_call>
<menu_item_call
label="Pin group"
name="Favorite">
<menu_item_call.on_click
function="People.Groups.Action"
parameter="favorite" />
<menu_item_call.on_enable
function="People.Groups.Enable"
parameter="favorite" />
<menu_item_call.on_visible
function="People.Groups.Visible"
parameter="favorite" />
</menu_item_call>
<menu_item_call
label="Unpin group"
name="Unfavorite">
<menu_item_call.on_click
function="People.Groups.Action"
parameter="unfavorite" />
<menu_item_call.on_enable
function="People.Groups.Enable"
parameter="unfavorite" />
<menu_item_call.on_visible
function="People.Groups.Visible"
parameter="unfavorite" />
</menu_item_call>
<menu_item_separator />
<menu_item_call
label="Leave"

View File

@ -49,7 +49,7 @@
<button
top="2"
follows="top|right"
height="22"
height="20"
label="IM/Call"
layout="topleft"
right="-1"
@ -59,7 +59,7 @@
<button
top_delta="25"
follows="top|right"
height="22"
height="20"
label="Profile"
layout="topleft"
name="info_btn"
@ -67,7 +67,7 @@
<button
top_delta="25"
follows="top|right"
height="22"
height="20"
label="Group Titles"
layout="topleft"
name="titles_btn"
@ -75,7 +75,7 @@
<button
top_delta="25"
follows="top|right"
height="22"
height="20"
label="Activate"
layout="topleft"
name="activate_btn"
@ -83,15 +83,24 @@
<button
top_delta="25"
follows="top|right"
height="22"
height="20"
label="Pin"
layout="topleft"
name="favorite_btn"
tool_tip="Add or remove group from favorites"
width="80" />
<button
top_delta="25"
follows="top|right"
height="20"
label="Leave"
layout="topleft"
name="leave_btn"
width="80" />
<button
top_delta="35"
top_delta="31"
follows="top|right"
height="22"
height="20"
label="Create..."
layout="topleft"
name="create_btn"
@ -99,7 +108,7 @@
<button
top_delta="25"
follows="top|right"
height="22"
height="20"
label="Search..."
layout="topleft"
name="search_btn"
@ -107,7 +116,7 @@
<button
top_delta="25"
follows="top|right"
height="22"
height="20"
label="Invite..."
layout="topleft"
name="invite_btn"

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
follows="left|top|right"
height="10"
layout="topleft"
left="0"
name="group_list_separator"
top="0"
width="300">
<view_border
bevel_style="none"
follows="left|right|top"
height="1"
layout="topleft"
left="5"
name="separator_line"
right="-5"
top="5" />
</panel>

View File

@ -3,6 +3,8 @@
<string name="no_friends" value="Brak znajomych" />
<string name="no_filtered_groups_msg" value="Nie znaleziono grup zawierających frazę &quot;[SEARCH_TERM]&quot;." />
<string name="no_groups_msg" value="Szukasz grup, do jakich można by dołączyć? Spróbuj [secondlife:///app/search/groups wyszukać]." />
<string name="favorite_label" value="Przypnij" />
<string name="unfavorite_label" value="Odepnij" />
<tab_container name="friends_and_groups">
<panel label="Znajomi" name="friends_panel" />
<panel label="Grupy" name="groups_panel" />

View File

@ -5,5 +5,7 @@
<menu_item_call label="Kopiuj SLurl do schowka" name="Copy SLurl to clipboard"/>
<menu_item_call label="Czat" name="Chat"/>
<menu_item_call label="Rozmowa głosowa" name="Call"/>
<menu_item_call label="Przypnij grupę" name="Favorite" />
<menu_item_call label="Odepnij grupę" name="Unfavorite" />
<menu_item_call label="Opuść" name="Leave"/>
</toggleable_menu>

View File

@ -5,6 +5,7 @@
<button label="Profil" name="info_btn"/>
<button label="Tytuły grup" name="titles_btn"/>
<button label="Aktywuj" name="activate_btn"/>
<button label="Przypnij" name="favorite_btn" tool_tip="Dodaj lub usuń grupę z ulubionych" />
<button label="Opuść" name="leave_btn"/>
<button label="Utwórz..." name="create_btn"/>
<button label="Szukaj..." name="search_btn"/>