summaryrefslogtreecommitdiff
path: root/indra/newview/lloutfitslist.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/lloutfitslist.cpp')
-rw-r--r--indra/newview/lloutfitslist.cpp471
1 files changed, 415 insertions, 56 deletions
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 0f5f7aebf8..32831fcd9b 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -34,10 +34,13 @@
#include "llaccordionctrl.h"
#include "llaccordionctrltab.h"
#include "llagentwearables.h"
+#include "llaisapi.h"
#include "llappearancemgr.h"
+#include "llappviewer.h"
#include "llfloaterreg.h"
#include "llfloatersidepanelcontainer.h"
#include "llinspecttexture.h"
+#include "llinventorymodelbackgroundfetch.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
#include "llmenubutton.h"
@@ -45,22 +48,33 @@
#include "lloutfitobserver.h"
#include "lltoggleablemenu.h"
#include "lltransutil.h"
+#include "llviewercontrol.h"
#include "llviewermenu.h"
#include "llvoavatar.h"
#include "llvoavatarself.h"
#include "llwearableitemslist.h"
-static bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y);
+static bool is_tab_header_clicked(LLOutfitAccordionCtrlTab* tab, S32 y);
static const LLOutfitTabNameComparator OUTFIT_TAB_NAME_COMPARATOR;
+static const LLOutfitTabFavComparator OUTFIT_TAB_FAV_COMPARATOR;
/*virtual*/
bool LLOutfitTabNameComparator::compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const
{
- std::string name1 = tab1->getTitle();
- std::string name2 = tab2->getTitle();
+ return (LLStringUtil::compareDict(tab1->getTitle(), tab2->getTitle()) < 0);
+}
- return (LLStringUtil::compareDict(name1, name2) < 0);
+bool LLOutfitTabFavComparator::compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const
+{
+ LLOutfitAccordionCtrlTab* taba = (LLOutfitAccordionCtrlTab*)tab1;
+ LLOutfitAccordionCtrlTab* tabb = (LLOutfitAccordionCtrlTab*)tab2;
+ if (taba->getFavorite() != tabb->getFavorite())
+ {
+ return taba->getFavorite();
+ }
+
+ return (LLStringUtil::compareDict(tab1->getTitle(), tab2->getTitle()) < 0);
}
struct outfit_accordion_tab_params : public LLInitParam::Block<outfit_accordion_tab_params, LLOutfitAccordionCtrlTab::Params>
@@ -80,6 +94,9 @@ const outfit_accordion_tab_params& get_accordion_tab_params()
{
initialized = true;
+ LLOutfitAccordionCtrlTab::sFavoriteIcon = LLUI::getUIImage("Inv_Favorite_Star_Full");
+ LLOutfitAccordionCtrlTab::sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", LLColor4U(255, 255, 255));
+
LLXMLNodePtr xmlNode;
if (LLUICtrlFactory::getLayeredXMLNode("outfit_accordion_tab.xml", xmlNode))
{
@@ -103,11 +120,20 @@ LLOutfitsList::LLOutfitsList()
, mAccordion(NULL)
, mListCommands(NULL)
, mItemSelected(false)
+ , mSortMenu(nullptr)
{
+ LLControlVariable* ctrl = gSavedSettings.getControl("InventoryFavoritesColorText");
+ if (ctrl)
+ {
+ mSavedSettingInvFavColor = ctrl->getSignal()->connect(boost::bind(&LLOutfitsList::handleInvFavColorChange, this));
+ }
}
LLOutfitsList::~LLOutfitsList()
{
+ delete mSortMenu;
+ mSavedSettingInvFavColor.disconnect();
+ mGearMenuConnection.disconnect();
}
bool LLOutfitsList::postBuild()
@@ -115,9 +141,25 @@ bool LLOutfitsList::postBuild()
mAccordion = getChild<LLAccordionCtrl>("outfits_accordion");
mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR);
+ initComparator();
+
return LLOutfitListBase::postBuild();
}
+void LLOutfitsList::initComparator()
+{
+ S32 mode = gSavedSettings.getS32("OutfitListSortOrder");
+ if (mode == 0)
+ {
+ mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR);
+ }
+ else
+ {
+ mAccordion->setComparator(&OUTFIT_TAB_FAV_COMPARATOR);
+ }
+ sortOutfits();
+}
+
//virtual
void LLOutfitsList::onOpen(const LLSD& info)
{
@@ -139,9 +181,21 @@ void LLOutfitsList::onOpen(const LLSD& info)
void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
{
+ LL_PROFILE_ZONE_SCOPED;
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
if (!cat) return;
+ if (!isOutfitFolder(cat))
+ {
+ // Assume a subfolder that contains or will contain outfits, track it
+ const LLUUID outfits = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+ mCategoriesObserver->addCategory(cat_id, [this, outfits]()
+ {
+ observerCallback(outfits);
+ });
+ return;
+ }
+
std::string name = cat->getName();
outfit_accordion_tab_params tab_params(get_accordion_tab_params());
@@ -154,6 +208,7 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
tab->setName(name);
tab->setTitle(name);
+ tab->setFavorite(cat->getIsFavorite());
// *TODO: LLUICtrlFactory::defaultBuilder does not use "display_children" from xml. Should be investigated.
tab->setDisplayChildren(false);
@@ -183,23 +238,44 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
// Setting callback to reset items selection inside outfit on accordion collapsing and expanding (EXT-7875)
tab->setDropDownStateChangedCallback(boost::bind(&LLOutfitsList::resetItemSelection, this, list, cat_id));
- // force showing list items that don't match current filter(EXT-7158)
- list->setForceShowingUnmatchedItems(true);
+ // Depending on settings, force showing list items that don't match current filter(EXT-7158)
+ static LLCachedControl<bool> list_filter(gSavedSettings, "OutfitListFilterFullList");
+ list->setForceShowingUnmatchedItems(list_filter(), false);
// Setting list commit callback to monitor currently selected wearable item.
list->setCommitCallback(boost::bind(&LLOutfitsList::onListSelectionChange, this, _1));
// Setting list refresh callback to apply filter on list change.
- list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onRefreshComplete, this, _1));
+ list->setRefreshCompleteCallback([this, tab](LLUICtrl* ctrl, const LLSD& sd)
+ {
+ onRefreshComplete(ctrl, tab);
+ });
list->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onWearableItemsListRightClick, this, _1, _2, _3));
- // Fetch the new outfit contents.
- cat->fetch();
-
- // Refresh the list of outfit items after fetch().
- // Further list updates will be triggered by the category observer.
- list->updateList(cat_id);
+ if (AISAPI::isAvailable() && LLInventoryModelBackgroundFetch::instance().folderFetchActive())
+ {
+ // For reliability just fetch it whole, linked items included
+ // Todo: list is not warrantied to exist once callback arrives
+ // Fix it!
+ LLInventoryModelBackgroundFetch::instance().fetchFolderAndLinks(cat_id, [cat_id, list]
+ {
+ if (list)
+ {
+ list->updateList(cat_id);
+ list->setForceRefresh(true);
+ }
+ });
+ }
+ else
+ {
+ // Fetch the new outfit contents.
+ cat->fetch();
+ // Refresh the list of outfit items after fetch().
+ // Further list updates will be triggered by the category observer.
+ list->updateList(cat_id);
+ list->setForceRefresh(true);
+ }
// If filter is currently applied we store the initial tab state.
if (!getFilterSubString().empty())
@@ -221,7 +297,7 @@ void LLOutfitsList::updateRemovedCategory(LLUUID cat_id)
if (outfits_iter != mOutfitsMap.end())
{
const LLUUID& outfit_id = outfits_iter->first;
- LLAccordionCtrlTab* tab = outfits_iter->second;
+ LLOutfitAccordionCtrlTab* tab = outfits_iter->second;
// An outfit is removed from the list. Do the following:
// 1. Remove outfit category from observer to stop monitoring its changes.
@@ -249,13 +325,11 @@ void LLOutfitsList::onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id)
{
if (mOutfitsMap[prev_id])
{
- mOutfitsMap[prev_id]->setTitleFontStyle("NORMAL");
- mOutfitsMap[prev_id]->setTitleColor(LLUIColorTable::instance().getColor("AccordionHeaderTextColor"));
+ mOutfitsMap[prev_id]->setOutfitSelected(false);
}
if (mOutfitsMap[base_id])
{
- mOutfitsMap[base_id]->setTitleFontStyle("BOLD");
- mOutfitsMap[base_id]->setTitleColor(LLUIColorTable::instance().getColor("SelectedOutfitTextColor"));
+ mOutfitsMap[base_id]->setOutfitSelected(true);
}
}
@@ -299,7 +373,7 @@ void LLOutfitsList::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid)
{
if (outfit_uuid == iter->first)
{
- LLAccordionCtrlTab* tab = iter->second;
+ LLOutfitAccordionCtrlTab* tab = iter->second;
if (!tab) continue;
LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
@@ -313,6 +387,11 @@ void LLOutfitsList::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid)
}
}
+void LLOutfitListBase::onAction(const LLSD& userdata)
+{
+ performAction(userdata.asString());
+}
+
// virtual
bool LLOutfitListBase::isActionEnabled(const LLSD& userdata)
{
@@ -388,7 +467,7 @@ void LLOutfitsList::onCollapseAllFolders()
iter != mOutfitsMap.end();
++iter)
{
- LLAccordionCtrlTab* tab = iter->second;
+ LLOutfitAccordionCtrlTab* tab = iter->second;
if(tab && tab->isExpanded())
{
tab->changeOpenClose(true);
@@ -402,7 +481,7 @@ void LLOutfitsList::onExpandAllFolders()
iter != mOutfitsMap.end();
++iter)
{
- LLAccordionCtrlTab* tab = iter->second;
+ LLOutfitAccordionCtrlTab* tab = iter->second;
if(tab && !tab->isExpanded())
{
tab->changeOpenClose(false);
@@ -425,11 +504,12 @@ void LLOutfitsList::updateChangedCategoryName(LLViewerInventoryCategory *cat, st
if (outfits_iter != mOutfitsMap.end())
{
// Update tab name with the new category name.
- LLAccordionCtrlTab* tab = outfits_iter->second;
+ LLOutfitAccordionCtrlTab* tab = outfits_iter->second;
if (tab)
{
tab->setName(name);
tab->setTitle(name);
+ tab->setFavorite(cat->getIsFavorite());
}
}
}
@@ -477,7 +557,7 @@ void LLOutfitsList::deselectOutfit(const LLUUID& category_id)
LLOutfitListBase::deselectOutfit(category_id);
}
-void LLOutfitsList::restoreOutfitSelection(LLAccordionCtrlTab* tab, const LLUUID& category_id)
+void LLOutfitsList::restoreOutfitSelection(LLOutfitAccordionCtrlTab* tab, const LLUUID& category_id)
{
// Try restoring outfit selection after filtering.
if (mAccordion->getSelectedTab() == tab)
@@ -486,23 +566,21 @@ void LLOutfitsList::restoreOutfitSelection(LLAccordionCtrlTab* tab, const LLUUID
}
}
-void LLOutfitsList::onRefreshComplete(LLUICtrl* ctrl)
+void LLOutfitsList::onRefreshComplete(LLUICtrl* ctrl, LLOutfitAccordionCtrlTab* tab)
{
if (!ctrl || getFilterSubString().empty())
return;
- for (outfits_map_t::iterator
- iter = mOutfitsMap.begin(),
- iter_end = mOutfitsMap.end();
- iter != iter_end; ++iter)
+ LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
+ if (list != ctrl)
{
- LLAccordionCtrlTab* tab = iter->second;
- if (!tab) continue;
-
- LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
- if (list != ctrl) continue;
-
- applyFilterToTab(iter->first, tab, getFilterSubString());
+ llassert(false);
+ LL_WARNS() << "LLOutfitsList::onRefreshComplete: ctrl does not match tab's list!" << LL_ENDL;
+ return;
+ }
+ if (tab->getFilterGeneration() != getFilterGeneration())
+ {
+ applyFilterToTab(tab->getFolderID(), tab, getFilterSubString());
}
}
@@ -515,7 +593,7 @@ void LLOutfitsList::onFilterSubStringChanged(const std::string& new_string, cons
while (iter != iter_end)
{
const LLUUID& category_id = iter->first;
- LLAccordionCtrlTab* tab = iter++->second;
+ LLOutfitAccordionCtrlTab* tab = iter++->second;
if (!tab) continue;
LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
@@ -554,9 +632,10 @@ void LLOutfitsList::onFilterSubStringChanged(const std::string& new_string, cons
void LLOutfitsList::applyFilterToTab(
const LLUUID& category_id,
- LLAccordionCtrlTab* tab,
+ LLOutfitAccordionCtrlTab* tab,
const std::string& filter_substring)
{
+ LL_PROFILE_ZONE_SCOPED;
if (!tab) return;
LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
if (!list) return;
@@ -568,6 +647,7 @@ void LLOutfitsList::applyFilterToTab(
LLStringUtil::toUpper(cur_filter);
tab->setTitle(tab->getTitle(), cur_filter);
+ tab->setFilterGeneration(getFilterGeneration());
if (std::string::npos == title.find(cur_filter))
{
@@ -691,7 +771,7 @@ void LLOutfitsList::onCOFChanged()
outfits_map_t::iterator map_iter = mOutfitsMap.begin(), map_end = mOutfitsMap.end();
while (map_iter != map_end)
{
- LLAccordionCtrlTab* tab = (map_iter++)->second;
+ LLOutfitAccordionCtrlTab* tab = (map_iter++)->second;
if (!tab) continue;
LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
@@ -722,7 +802,7 @@ void LLOutfitsList::sortOutfits()
void LLOutfitsList::onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id)
{
- LLAccordionCtrlTab* tab = dynamic_cast<LLAccordionCtrlTab*>(ctrl);
+ LLOutfitAccordionCtrlTab* tab = dynamic_cast<LLOutfitAccordionCtrlTab*>(ctrl);
if (mOutfitMenu && is_tab_header_clicked(tab, y) && cat_id.notNull())
{
// Focus tab header to trigger tab selection change.
@@ -738,13 +818,82 @@ void LLOutfitsList::onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUI
}
}
+void LLOutfitsList::handleInvFavColorChange()
+{
+ for (outfits_map_t::iterator iter = mOutfitsMap.begin();
+ iter != mOutfitsMap.end();
+ ++iter)
+ {
+ if (!iter->second) continue;
+ LLOutfitAccordionCtrlTab* tab = iter->second;
+
+ // refresh font color
+ tab->setFavorite(tab->getFavorite());
+ }
+}
+
+void LLOutfitsList::onChangeSortOrder(const LLSD& userdata)
+{
+ std::string sort_data = userdata.asString();
+ if (sort_data == "favorites_to_top")
+ {
+ // at the moment this is a toggle
+ S32 val = gSavedSettings.getS32("OutfitListSortOrder");
+ gSavedSettings.setS32("OutfitListSortOrder", (val ? 0 : 1));
+
+ initComparator();
+ }
+ else if (sort_data == "show_entire_outfit")
+ {
+ bool new_val = !gSavedSettings.getBOOL("OutfitListFilterFullList");
+ gSavedSettings.setBOOL("OutfitListFilterFullList", new_val);
+
+ if (!getFilterSubString().empty())
+ {
+ for (outfits_map_t::value_type& outfit : mOutfitsMap)
+ {
+ LLOutfitAccordionCtrlTab* tab = outfit.second;
+ const LLUUID& category_id = outfit.first;
+ if (!tab) continue;
+
+ LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
+ if (list)
+ {
+ list->setForceRefresh(true);
+ list->setForceShowingUnmatchedItems(new_val, tab->getDisplayChildren());
+ }
+ applyFilterToTab(category_id, tab, getFilterSubString());
+ }
+ mAccordion->arrange();
+ }
+ }
+}
+
+LLToggleableMenu* LLOutfitsList::getSortMenu()
+{
+ if (!mSortMenu)
+ {
+ mSortMenu = new LLOutfitListSortMenu(this);
+ }
+ return mSortMenu->getMenu();
+}
+
+void LLOutfitsList::updateMenuItemsVisibility()
+{
+ if (mSortMenu)
+ {
+ mSortMenu->updateItemsVisibility();
+ }
+ LLOutfitListBase::updateMenuItemsVisibility();
+}
+
LLOutfitListGearMenuBase* LLOutfitsList::createGearMenu()
{
return new LLOutfitListGearMenu(this);
}
-bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y)
+bool is_tab_header_clicked(LLOutfitAccordionCtrlTab* tab, S32 y)
{
if(!tab || !tab->getHeaderVisible()) return false;
@@ -755,10 +904,10 @@ bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y)
LLOutfitListBase::LLOutfitListBase()
: LLPanelAppearanceTab()
, mIsInitialized(false)
+ , mGearMenu(nullptr)
{
mCategoriesObserver = new LLInventoryCategoriesObserver();
mOutfitMenu = new LLOutfitContextMenu(this);
- //mGearMenu = createGearMenu();
}
LLOutfitListBase::~LLOutfitListBase()
@@ -819,8 +968,45 @@ void LLOutfitListBase::observerCallback(const LLUUID& category_id)
refreshList(category_id);
}
+bool LLOutfitListBase::isOutfitFolder(LLViewerInventoryCategory* cat) const
+{
+ if (!cat)
+ {
+ return false;
+ }
+ if (cat->getPreferredType() == LLFolderType::FT_OUTFIT)
+ {
+ return true;
+ }
+ // assumes that folder is somewhere inside MyOutfits
+ if (cat->getPreferredType() == LLFolderType::FT_NONE)
+ {
+ LLViewerInventoryCategory* inv_cat = dynamic_cast<LLViewerInventoryCategory*>(cat);
+ if (inv_cat && inv_cat->getDescendentCount() > 3)
+ {
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items);
+ if (cats->empty() // protection against outfits inside
+ && items->size() > 3) // arbitrary, if doesn't have at least base parts, not an outfit
+ {
+ // For now assume this to be an old style outfit, not a subfolder
+ // but ideally no such 'outfits' should be left in My Outfits
+ // Todo: stop counting FT_NONE as outfits,
+ // convert obvious outfits into FT_OUTFIT
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void LLOutfitListBase::refreshList(const LLUUID& category_id)
{
+ if (LLAppViewer::instance()->quitRequested())
+ {
+ return;
+ }
bool wasNull = mRefreshListState.CategoryUUID.isNull();
mRefreshListState.CategoryUUID.setNull();
@@ -879,10 +1065,21 @@ void LLOutfitListBase::onIdle(void* userdata)
void LLOutfitListBase::onIdleRefreshList()
{
+ LL_PROFILE_ZONE_SCOPED;
+ if (LLAppViewer::instance()->quitRequested())
+ {
+ mRefreshListState.CategoryUUID.setNull();
+ gIdleCallbacks.deleteFunction(onIdle, this);
+ return;
+ }
if (mRefreshListState.CategoryUUID.isNull())
+ {
+ LL_WARNS() << "Called onIdleRefreshList without id" << LL_ENDL;
+ gIdleCallbacks.deleteFunction(onIdle, this);
return;
+ }
- const F64 MAX_TIME = 0.05f;
+ const F64 MAX_TIME = 0.005f;
F64 curent_time = LLTimer::getTotalSeconds();
const F64 end_time = curent_time + MAX_TIME;
@@ -924,8 +1121,8 @@ void LLOutfitListBase::onIdleRefreshList()
if (cat)
{
std::string name = cat->getName();
- updateChangedCategoryName(cat, name);
- }
+ updateChangedCategoryName(cat, name);
+ }
curent_time = LLTimer::getTotalSeconds();
if (curent_time >= end_time)
@@ -1022,12 +1219,6 @@ void LLOutfitListBase::ChangeOutfitSelection(LLWearableItemsList* list, const LL
bool LLOutfitListBase::postBuild()
{
- mGearMenu = createGearMenu();
-
- LLMenuButton* menu_gear_btn = getChild<LLMenuButton>("options_gear_btn");
-
- menu_gear_btn->setMouseDownCallback(boost::bind(&LLOutfitListGearMenuBase::updateItemsVisibility, mGearMenu));
- menu_gear_btn->setMenu(mGearMenu->getMenu());
return true;
}
@@ -1041,6 +1232,20 @@ void LLOutfitListBase::expandAllFolders()
onExpandAllFolders();
}
+void LLOutfitListBase::updateMenuItemsVisibility()
+{
+ mGearMenu->updateItemsVisibility();
+}
+
+LLToggleableMenu* LLOutfitListBase::getGearMenu()
+{
+ if (!mGearMenu)
+ {
+ mGearMenu = createGearMenu();
+ }
+ return mGearMenu->getMenu();
+};
+
void LLOutfitListBase::deselectOutfit(const LLUUID& category_id)
{
// Reset selection if the outfit is selected.
@@ -1067,6 +1272,7 @@ LLContextMenu* LLOutfitContextMenu::createMenu()
registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id));
registrar.add("Outfit.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList));
registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitContextMenu::onThumbnail, this, selected_id));
+ registrar.add("Outfit.Favorite", boost::bind(&LLOutfitContextMenu::onFavorite, this, selected_id));
registrar.add("Outfit.Save", boost::bind(&LLOutfitContextMenu::onSave, this, selected_id));
enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitContextMenu::onEnable, this, _2));
@@ -1117,6 +1323,16 @@ bool LLOutfitContextMenu::onVisible(LLSD::String param)
{
return LLAppearanceMgr::instance().getCanRemoveOutfit(outfit_cat_id);
}
+ else if ("favorites_add" == param)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_cat_id);
+ return cat && !cat->getIsFavorite();
+ }
+ else if ("favorites_remove" == param)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_cat_id);
+ return cat && cat->getIsFavorite();
+ }
return true;
}
@@ -1141,6 +1357,14 @@ void LLOutfitContextMenu::onThumbnail(const LLUUID &outfit_cat_id)
}
}
+void LLOutfitContextMenu::onFavorite(const LLUUID& outfit_cat_id)
+{
+ if (outfit_cat_id.notNull())
+ {
+ toggle_favorite(outfit_cat_id);
+ }
+}
+
void LLOutfitContextMenu::onSave(const LLUUID &outfit_cat_id)
{
if (outfit_cat_id.notNull())
@@ -1171,14 +1395,13 @@ LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist)
registrar.add("Gear.Rename", boost::bind(&LLOutfitListGearMenuBase::onRename, this));
registrar.add("Gear.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList));
registrar.add("Gear.Create", boost::bind(&LLOutfitListGearMenuBase::onCreate, this, _2));
- registrar.add("Gear.Collapse", boost::bind(&LLOutfitListBase::onCollapseAllFolders, mOutfitList));
- registrar.add("Gear.Expand", boost::bind(&LLOutfitListBase::onExpandAllFolders, mOutfitList));
registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenuBase::onAdd, this));
registrar.add("Gear.Save", boost::bind(&LLOutfitListGearMenuBase::onSave, this));
registrar.add("Gear.Thumbnail", boost::bind(&LLOutfitListGearMenuBase::onThumbnail, this));
- registrar.add("Gear.SortByName", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this));
+ registrar.add("Gear.Favorite", boost::bind(&LLOutfitListGearMenuBase::onFavorite, this));
+ registrar.add("Gear.SortByImage", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this));
enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenuBase::onEnable, this, _2));
enable_registrar.add("Gear.OnVisible", boost::bind(&LLOutfitListGearMenuBase::onVisible, this, _2));
@@ -1312,6 +1535,16 @@ bool LLOutfitListGearMenuBase::onVisible(LLSD::String param)
{
return false;
}
+ else if ("favorites_add" == param)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id);
+ return cat && !cat->getIsFavorite();
+ }
+ else if ("favorites_remove" == param)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id);
+ return cat && cat->getIsFavorite();
+ }
return true;
}
@@ -1323,6 +1556,12 @@ void LLOutfitListGearMenuBase::onThumbnail()
LLFloaterReg::showInstance("change_item_thumbnail", data);
}
+void LLOutfitListGearMenuBase::onFavorite()
+{
+ const LLUUID& selected_outfit_id = getSelectedOutfitID();
+ toggle_favorite(selected_outfit_id);
+}
+
void LLOutfitListGearMenuBase::onChangeSortOrder()
{
@@ -1338,21 +1577,91 @@ LLOutfitListGearMenu::~LLOutfitListGearMenu()
void LLOutfitListGearMenu::onUpdateItemsVisibility()
{
if (!mMenu) return;
- mMenu->setItemVisible("expand", true);
- mMenu->setItemVisible("collapse", true);
mMenu->setItemVisible("thumbnail", getSelectedOutfitID().notNull());
+ mMenu->setItemVisible("favorite", getSelectedOutfitID().notNull());
mMenu->setItemVisible("sepatator3", false);
mMenu->setItemVisible("sort_folders_by_name", false);
LLOutfitListGearMenuBase::onUpdateItemsVisibility();
}
+//////////////////// LLOutfitListSortMenu ////////////////////
+
+LLOutfitListSortMenu::LLOutfitListSortMenu(LLOutfitListBase* parent_panel)
+ : mPanelHandle(parent_panel->getHandle())
+{
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+
+ registrar.add("Sort.Collapse", boost::bind(&LLOutfitListBase::onCollapseAllFolders, parent_panel));
+ registrar.add("Sort.Expand", boost::bind(&LLOutfitListBase::onExpandAllFolders, parent_panel));
+ registrar.add("Sort.OnSort", boost::bind(&LLOutfitListBase::onChangeSortOrder, parent_panel, _2));
+ enable_registrar.add("Sort.OnEnable", boost::bind(&LLOutfitListSortMenu::onEnable, this, _2));
+
+ mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(
+ "menu_outfit_list_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ llassert(mMenu);
+}
+
+
+LLToggleableMenu* LLOutfitListSortMenu::getMenu()
+{
+ return mMenu;
+}
+
+void LLOutfitListSortMenu::updateItemsVisibility()
+{
+ onUpdateItemsVisibility();
+}
+
+void LLOutfitListSortMenu::onUpdateItemsVisibility()
+{
+ if (!mMenu) return;
+ mMenu->setItemVisible("expand", true);
+ mMenu->setItemVisible("collapse", true);
+ mMenu->setItemVisible("sort_favorites_to_top", true);
+ mMenu->setItemVisible("show_entire_outfit_in_search", true);
+}
+
+bool LLOutfitListSortMenu::onEnable(LLSD::String param)
+{
+ if ("favorites_to_top" == param)
+ {
+ static LLCachedControl<S32> sort_order(gSavedSettings, "OutfitListSortOrder", 0);
+ return sort_order == 1;
+ }
+ else if ("show_entire_outfit" == param)
+ {
+ static LLCachedControl<bool> filter_mode(gSavedSettings, "OutfitListFilterFullList", 0);
+ return filter_mode;
+ }
+
+ return false;
+}
+
+
+//////////////////// LLOutfitAccordionCtrlTab ////////////////////
+
+LLUIImage* LLOutfitAccordionCtrlTab::sFavoriteIcon;
+LLUIColor LLOutfitAccordionCtrlTab::sFgColor;
+
+void LLOutfitAccordionCtrlTab::draw()
+{
+ LLAccordionCtrlTab::draw();
+ drawFavoriteIcon();
+}
+
bool LLOutfitAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask)
{
if (y >= getLocalRect().getHeight() - getHeaderHeight())
{
LLSD params;
params["inv_type"] = LLInventoryType::IT_CATEGORY;
- params["thumbnail_id"] = gInventory.getCategory(mFolderID)->getThumbnailUUID();
+ LLViewerInventoryCategory* cat = gInventory.getCategory(mFolderID);
+ if (cat)
+ {
+ params["thumbnail_id"] = cat->getThumbnailUUID();
+ }
+ // else consider returning
params["item_id"] = mFolderID;
LLToolTipMgr::instance().show(LLToolTip::Params()
@@ -1366,4 +1675,54 @@ bool LLOutfitAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask)
return LLAccordionCtrlTab::handleToolTip(x, y, mask);
}
+
+void LLOutfitAccordionCtrlTab::setFavorite(bool is_favorite)
+{
+ mIsFavorite = is_favorite;
+ updateTitleColor();
+}
+
+void LLOutfitAccordionCtrlTab::setOutfitSelected(bool val)
+{
+ mIsSelected = val;
+ setTitleFontStyle(mIsSelected ? "BOLD" : "NORMAL");
+ updateTitleColor();
+ }
+
+void LLOutfitAccordionCtrlTab::updateTitleColor()
+ {
+ static LLUICachedControl<bool> highlight_color("InventoryFavoritesColorText", true);
+ if (mIsFavorite && highlight_color())
+ {
+ setTitleColor(LLUIColorTable::instance().getColor("InventoryFavoriteColor"));
+ }
+ else if (mIsSelected)
+ {
+ setTitleColor(LLUIColorTable::instance().getColor("SelectedOutfitTextColor"));
+ }
+ else
+ {
+ setTitleColor(LLUIColorTable::instance().getColor("AccordionHeaderTextColor"));
+ }
+ }
+
+void LLOutfitAccordionCtrlTab::drawFavoriteIcon()
+{
+ if (!mIsFavorite)
+ {
+ return;
+ }
+ static LLUICachedControl<bool> draw_star("InventoryFavoritesUseStar", true);
+ if (!draw_star)
+ {
+ return;
+ }
+
+ const S32 PAD = 2;
+ const S32 image_size = 18;
+
+ gl_draw_scaled_image(
+ getRect().getWidth() - image_size - PAD, getRect().getHeight() - image_size - PAD,
+ image_size, image_size, sFavoriteIcon->getImage(), sFgColor);
+}
// EOF