PageRenderTime 194ms CodeModel.GetById 16ms app.highlight 164ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llwearableitemslist.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1060 lines | 765 code | 182 blank | 113 comment | 145 complexity | 949dcde6c929dde8f4470431c6cb2e68 MD5 | raw file
   1/**
   2 * @file llwearableitemslist.cpp
   3 * @brief A flat list of wearable items.
   4 *
   5 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29#include "llwearableitemslist.h"
  30
  31#include "lliconctrl.h"
  32#include "llmenugl.h" // for LLContextMenu
  33
  34#include "llagentwearables.h"
  35#include "llappearancemgr.h"
  36#include "llinventoryfunctions.h"
  37#include "lltransutil.h"
  38#include "llviewerattachmenu.h"
  39#include "llvoavatarself.h"
  40
  41class LLFindOutfitItems : public LLInventoryCollectFunctor
  42{
  43public:
  44	LLFindOutfitItems() {}
  45	virtual ~LLFindOutfitItems() {}
  46	virtual bool operator()(LLInventoryCategory* cat,
  47							LLInventoryItem* item);
  48};
  49
  50bool LLFindOutfitItems::operator()(LLInventoryCategory* cat,
  51								   LLInventoryItem* item)
  52{
  53	if(item)
  54	{
  55		if((item->getType() == LLAssetType::AT_CLOTHING)
  56		   || (item->getType() == LLAssetType::AT_BODYPART)
  57		   || (item->getType() == LLAssetType::AT_OBJECT))
  58		{
  59			return TRUE;
  60		}
  61	}
  62	return FALSE;
  63}
  64
  65//////////////////////////////////////////////////////////////////////////
  66//////////////////////////////////////////////////////////////////////////
  67//////////////////////////////////////////////////////////////////////////
  68
  69void LLPanelWearableListItem::onMouseEnter(S32 x, S32 y, MASK mask)
  70{
  71	LLPanelInventoryListItemBase::onMouseEnter(x, y, mask);
  72	setWidgetsVisible(true);
  73	reshapeWidgets();
  74}
  75
  76void LLPanelWearableListItem::onMouseLeave(S32 x, S32 y, MASK mask)
  77{
  78	LLPanelInventoryListItemBase::onMouseLeave(x, y, mask);
  79	setWidgetsVisible(false);
  80	reshapeWidgets();
  81}
  82
  83LLPanelWearableListItem::LLPanelWearableListItem(LLViewerInventoryItem* item, const LLPanelWearableListItem::Params& params)
  84: LLPanelInventoryListItemBase(item, params)
  85{
  86}
  87
  88//////////////////////////////////////////////////////////////////////////
  89//////////////////////////////////////////////////////////////////////////
  90//////////////////////////////////////////////////////////////////////////
  91
  92// static
  93LLPanelWearableOutfitItem* LLPanelWearableOutfitItem::create(LLViewerInventoryItem* item,
  94															 bool worn_indication_enabled)
  95{
  96	LLPanelWearableOutfitItem* list_item = NULL;
  97	if (item)
  98	{
  99		const LLPanelInventoryListItemBase::Params& params = LLUICtrlFactory::getDefaultParams<LLPanelInventoryListItemBase>();
 100
 101		list_item = new LLPanelWearableOutfitItem(item, worn_indication_enabled, params);
 102		list_item->initFromParams(params);
 103		list_item->postBuild();
 104	}
 105	return list_item;
 106}
 107
 108LLPanelWearableOutfitItem::LLPanelWearableOutfitItem(LLViewerInventoryItem* item,
 109													 bool worn_indication_enabled,
 110													 const LLPanelWearableOutfitItem::Params& params)
 111: LLPanelInventoryListItemBase(item, params)
 112, mWornIndicationEnabled(worn_indication_enabled)
 113{
 114}
 115
 116// virtual
 117void LLPanelWearableOutfitItem::updateItem(const std::string& name,
 118										   EItemState item_state)
 119{
 120	std::string search_label = name;
 121
 122	// Updating item's worn status depending on whether it is linked in COF or not.
 123	// We don't use get_is_item_worn() here because this update is triggered by
 124	// an inventory observer upon link in COF beind added or removed so actual
 125	// worn status of a linked item may still remain unchanged.
 126	if (mWornIndicationEnabled && LLAppearanceMgr::instance().isLinkInCOF(mInventoryItemUUID))
 127	{
 128		search_label += LLTrans::getString("worn");
 129		item_state = IS_WORN;
 130	}
 131
 132	LLPanelInventoryListItemBase::updateItem(search_label, item_state);
 133}
 134
 135//////////////////////////////////////////////////////////////////////////
 136//////////////////////////////////////////////////////////////////////////
 137//////////////////////////////////////////////////////////////////////////
 138static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelClothingListItem(&typeid(LLPanelClothingListItem::Params), "clothing_list_item");
 139
 140
 141LLPanelClothingListItem::Params::Params()
 142:	up_btn("up_btn"),
 143	down_btn("down_btn"),
 144	edit_btn("edit_btn"),
 145	lock_panel("lock_panel"),
 146	edit_panel("edit_panel"),
 147	lock_icon("lock_icon")
 148{}
 149
 150// static
 151LLPanelClothingListItem* LLPanelClothingListItem::create(LLViewerInventoryItem* item)
 152{
 153	LLPanelClothingListItem* list_item = NULL;
 154	if(item)
 155	{
 156		const LLPanelClothingListItem::Params& params = LLUICtrlFactory::getDefaultParams<LLPanelClothingListItem>();
 157		list_item = new LLPanelClothingListItem(item, params);
 158		list_item->initFromParams(params);
 159		list_item->postBuild();
 160	}
 161	return list_item;
 162}
 163
 164LLPanelClothingListItem::LLPanelClothingListItem(LLViewerInventoryItem* item, const LLPanelClothingListItem::Params& params)
 165 : LLPanelDeletableWearableListItem(item, params)
 166{
 167	LLButton::Params button_params = params.up_btn;
 168	applyXUILayout(button_params, this);
 169	addChild(LLUICtrlFactory::create<LLButton>(button_params));
 170
 171	button_params = params.down_btn;
 172	applyXUILayout(button_params, this);
 173	addChild(LLUICtrlFactory::create<LLButton>(button_params));
 174
 175	LLPanel::Params panel_params = params.lock_panel;
 176	applyXUILayout(panel_params, this);
 177	LLPanel* lock_panelp = LLUICtrlFactory::create<LLPanel>(panel_params);
 178	addChild(lock_panelp);
 179
 180	panel_params = params.edit_panel;
 181	applyXUILayout(panel_params, this);
 182	LLPanel* edit_panelp = LLUICtrlFactory::create<LLPanel>(panel_params);
 183	addChild(edit_panelp);
 184
 185	if (lock_panelp)
 186{
 187		LLIconCtrl::Params icon_params = params.lock_icon;
 188		applyXUILayout(icon_params, this);
 189		lock_panelp->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_params));
 190}
 191
 192	if (edit_panelp)
 193{
 194		button_params = params.edit_btn;
 195		applyXUILayout(button_params, this);
 196		edit_panelp->addChild(LLUICtrlFactory::create<LLButton>(button_params));
 197	}
 198
 199	setSeparatorVisible(false);
 200}
 201
 202LLPanelClothingListItem::~LLPanelClothingListItem()
 203{
 204}
 205
 206BOOL LLPanelClothingListItem::postBuild()
 207{
 208	LLPanelDeletableWearableListItem::postBuild();
 209
 210	addWidgetToRightSide("btn_move_up");
 211	addWidgetToRightSide("btn_move_down");
 212	addWidgetToRightSide("btn_lock");
 213	addWidgetToRightSide("btn_edit_panel");
 214
 215	setWidgetsVisible(false);
 216	reshapeWidgets();
 217
 218	return TRUE;
 219}
 220
 221//////////////////////////////////////////////////////////////////////////
 222//////////////////////////////////////////////////////////////////////////
 223//////////////////////////////////////////////////////////////////////////
 224
 225static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelBodyPartsListItem(&typeid(LLPanelBodyPartsListItem::Params), "bodyparts_list_item");
 226
 227
 228LLPanelBodyPartsListItem::Params::Params()
 229:	edit_btn("edit_btn"),
 230	edit_panel("edit_panel"),
 231	lock_panel("lock_panel"),
 232	lock_icon("lock_icon")
 233{}
 234
 235// static
 236LLPanelBodyPartsListItem* LLPanelBodyPartsListItem::create(LLViewerInventoryItem* item)
 237{
 238	LLPanelBodyPartsListItem* list_item = NULL;
 239	if(item)
 240	{
 241		const Params& params = LLUICtrlFactory::getDefaultParams<LLPanelBodyPartsListItem>();
 242		list_item = new LLPanelBodyPartsListItem(item, params);
 243		list_item->initFromParams(params);
 244		list_item->postBuild();
 245	}
 246	return list_item;
 247}
 248
 249LLPanelBodyPartsListItem::LLPanelBodyPartsListItem(LLViewerInventoryItem* item, const LLPanelBodyPartsListItem::Params& params)
 250: LLPanelWearableListItem(item, params)
 251{
 252	LLPanel::Params panel_params = params.edit_panel;
 253	applyXUILayout(panel_params, this);
 254	LLPanel* edit_panelp = LLUICtrlFactory::create<LLPanel>(panel_params);
 255	addChild(edit_panelp);
 256
 257	panel_params = params.lock_panel;
 258	applyXUILayout(panel_params, this);
 259	LLPanel* lock_panelp = LLUICtrlFactory::create<LLPanel>(panel_params);
 260	addChild(lock_panelp);
 261	
 262	if (edit_panelp)
 263	{
 264		LLButton::Params btn_params = params.edit_btn;
 265		applyXUILayout(btn_params, this);
 266		edit_panelp->addChild(LLUICtrlFactory::create<LLButton>(btn_params));
 267}
 268
 269	if (lock_panelp)
 270{
 271		LLIconCtrl::Params icon_params = params.lock_icon;
 272		applyXUILayout(icon_params, this);
 273		lock_panelp->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_params));
 274	}
 275
 276	setSeparatorVisible(true);
 277}
 278
 279LLPanelBodyPartsListItem::~LLPanelBodyPartsListItem()
 280{
 281}
 282
 283BOOL LLPanelBodyPartsListItem::postBuild()
 284{
 285	LLPanelInventoryListItemBase::postBuild();
 286
 287	addWidgetToRightSide("btn_lock");
 288	addWidgetToRightSide("btn_edit_panel");
 289
 290	setWidgetsVisible(false);
 291	reshapeWidgets();
 292
 293	return TRUE;
 294}
 295
 296static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelDeletableWearableListItem(&typeid(LLPanelDeletableWearableListItem::Params), "deletable_wearable_list_item");
 297
 298LLPanelDeletableWearableListItem::Params::Params()
 299:	delete_btn("delete_btn")
 300{}
 301
 302// static
 303LLPanelDeletableWearableListItem* LLPanelDeletableWearableListItem::create(LLViewerInventoryItem* item)
 304{
 305	LLPanelDeletableWearableListItem* list_item = NULL;
 306	if(item)
 307	{
 308		const Params& params = LLUICtrlFactory::getDefaultParams<LLPanelDeletableWearableListItem>();
 309		list_item = new LLPanelDeletableWearableListItem(item, params);
 310		list_item->initFromParams(params);
 311		list_item->postBuild();
 312	}
 313	return list_item;
 314}
 315
 316LLPanelDeletableWearableListItem::LLPanelDeletableWearableListItem(LLViewerInventoryItem* item, const LLPanelDeletableWearableListItem::Params& params)
 317: LLPanelWearableListItem(item, params)
 318{
 319	LLButton::Params button_params = params.delete_btn;
 320	applyXUILayout(button_params, this);
 321	addChild(LLUICtrlFactory::create<LLButton>(button_params));
 322
 323	setSeparatorVisible(true);
 324}
 325
 326BOOL LLPanelDeletableWearableListItem::postBuild()
 327{
 328	LLPanelWearableListItem::postBuild();
 329
 330	addWidgetToLeftSide("btn_delete");
 331
 332	LLButton* delete_btn = getChild<LLButton>("btn_delete");
 333	// Reserve space for 'delete' button event if it is invisible.
 334	setLeftWidgetsWidth(delete_btn->getRect().mRight);
 335
 336	setWidgetsVisible(false);
 337	reshapeWidgets();
 338
 339	return TRUE;
 340}
 341
 342
 343// static
 344LLPanelAttachmentListItem* LLPanelAttachmentListItem::create(LLViewerInventoryItem* item)
 345{
 346	LLPanelAttachmentListItem* list_item = NULL;
 347	if(item)
 348	{
 349		const Params& params = LLUICtrlFactory::getDefaultParams<LLPanelDeletableWearableListItem>();
 350
 351		list_item = new LLPanelAttachmentListItem(item, params);
 352		list_item->initFromParams(params);
 353		list_item->postBuild();
 354	}
 355	return list_item;
 356}
 357
 358void LLPanelAttachmentListItem::updateItem(const std::string& name,
 359										   EItemState item_state)
 360{
 361	std::string title_joint = name;
 362
 363	LLViewerInventoryItem* inv_item = getItem();
 364	if (inv_item && isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(inv_item->getLinkedUUID()))
 365	{
 366		std::string joint = LLTrans::getString(gAgentAvatarp->getAttachedPointName(inv_item->getLinkedUUID()));
 367		title_joint =  title_joint + " (" + joint + ")";
 368	}
 369
 370	LLPanelInventoryListItemBase::updateItem(title_joint, item_state);
 371}
 372
 373//////////////////////////////////////////////////////////////////////////
 374//////////////////////////////////////////////////////////////////////////
 375//////////////////////////////////////////////////////////////////////////
 376static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelDummyClothingListItem(&typeid(LLPanelDummyClothingListItem::Params), "dummy_clothing_list_item");
 377
 378LLPanelDummyClothingListItem::Params::Params()
 379:	add_panel("add_panel"),
 380	add_btn("add_btn")
 381{}
 382
 383LLPanelDummyClothingListItem* LLPanelDummyClothingListItem::create(LLWearableType::EType w_type)
 384{
 385	const Params& params = LLUICtrlFactory::getDefaultParams<LLPanelDummyClothingListItem>();
 386
 387	LLPanelDummyClothingListItem* list_item = new LLPanelDummyClothingListItem(w_type, params);
 388	list_item->initFromParams(params);
 389	list_item->postBuild();
 390	return list_item;
 391}
 392
 393BOOL LLPanelDummyClothingListItem::postBuild()
 394{
 395	addWidgetToRightSide("btn_add_panel");
 396
 397	setIconImage(LLInventoryIcon::getIcon(LLAssetType::AT_CLOTHING, LLInventoryType::IT_NONE, mWearableType, FALSE));
 398	updateItem(wearableTypeToString(mWearableType));
 399
 400	// Make it look loke clothing item - reserve space for 'delete' button
 401	setLeftWidgetsWidth(getChildView("item_icon")->getRect().mLeft);
 402
 403	setWidgetsVisible(false);
 404	reshapeWidgets();
 405
 406	return TRUE;
 407}
 408
 409LLWearableType::EType LLPanelDummyClothingListItem::getWearableType() const
 410{
 411	return mWearableType;
 412}
 413
 414LLPanelDummyClothingListItem::LLPanelDummyClothingListItem(LLWearableType::EType w_type, const LLPanelDummyClothingListItem::Params& params)
 415:	LLPanelWearableListItem(NULL, params), 
 416	mWearableType(w_type)
 417{
 418	LLPanel::Params panel_params(params.add_panel);
 419	applyXUILayout(panel_params, this);
 420	LLPanel* add_panelp = LLUICtrlFactory::create<LLPanel>(panel_params);
 421	addChild(add_panelp);
 422
 423	if (add_panelp)
 424{
 425		LLButton::Params button_params(params.add_btn);
 426		applyXUILayout(button_params, this);
 427		add_panelp->addChild(LLUICtrlFactory::create<LLButton>(button_params));
 428}
 429
 430	setSeparatorVisible(true);
 431}
 432
 433typedef std::map<LLWearableType::EType, std::string> clothing_to_string_map_t;
 434
 435clothing_to_string_map_t init_clothing_string_map()
 436{
 437	clothing_to_string_map_t w_map;
 438	w_map.insert(std::make_pair(LLWearableType::WT_SHIRT, "shirt_not_worn"));
 439	w_map.insert(std::make_pair(LLWearableType::WT_PANTS, "pants_not_worn"));
 440	w_map.insert(std::make_pair(LLWearableType::WT_SHOES, "shoes_not_worn"));
 441	w_map.insert(std::make_pair(LLWearableType::WT_SOCKS, "socks_not_worn"));
 442	w_map.insert(std::make_pair(LLWearableType::WT_JACKET, "jacket_not_worn"));
 443	w_map.insert(std::make_pair(LLWearableType::WT_GLOVES, "gloves_not_worn"));
 444	w_map.insert(std::make_pair(LLWearableType::WT_UNDERSHIRT, "undershirt_not_worn"));
 445	w_map.insert(std::make_pair(LLWearableType::WT_UNDERPANTS, "underpants_not_worn"));
 446	w_map.insert(std::make_pair(LLWearableType::WT_SKIRT, "skirt_not_worn"));
 447	w_map.insert(std::make_pair(LLWearableType::WT_ALPHA, "alpha_not_worn"));
 448	w_map.insert(std::make_pair(LLWearableType::WT_TATTOO, "tattoo_not_worn"));
 449	w_map.insert(std::make_pair(LLWearableType::WT_PHYSICS, "physics_not_worn"));
 450	return w_map;
 451}
 452
 453std::string LLPanelDummyClothingListItem::wearableTypeToString(LLWearableType::EType w_type)
 454{
 455	static const clothing_to_string_map_t w_map = init_clothing_string_map();
 456	static const std::string invalid_str = LLTrans::getString("invalid_not_worn");
 457	
 458	std::string type_str = invalid_str;
 459	clothing_to_string_map_t::const_iterator it = w_map.find(w_type);
 460	if(w_map.end() != it)
 461	{
 462		type_str = LLTrans::getString(it->second);
 463	}
 464	return type_str;
 465}
 466
 467//////////////////////////////////////////////////////////////////////////
 468//////////////////////////////////////////////////////////////////////////
 469//////////////////////////////////////////////////////////////////////////
 470
 471LLWearableItemTypeNameComparator::LLWearableTypeOrder::LLWearableTypeOrder(LLWearableItemTypeNameComparator::ETypeListOrder order_priority, bool sort_asset_by_name, bool sort_wearable_by_name):
 472		mOrderPriority(order_priority),
 473		mSortAssetTypeByName(sort_asset_by_name),
 474		mSortWearableTypeByName(sort_wearable_by_name)
 475{
 476}
 477
 478LLWearableItemTypeNameComparator::LLWearableItemTypeNameComparator()
 479{
 480	// By default the sort order conforms the order by spec of MY OUTFITS items list:
 481	// 1. CLOTHING - sorted by name
 482	// 2. OBJECT   - sorted by type
 483	// 3. BODYPART - sorted by name
 484	mWearableOrder[LLAssetType::AT_CLOTHING] = LLWearableTypeOrder(ORDER_RANK_1, false, false);
 485	mWearableOrder[LLAssetType::AT_OBJECT]   = LLWearableTypeOrder(ORDER_RANK_2, true, true);
 486	mWearableOrder[LLAssetType::AT_BODYPART] = LLWearableTypeOrder(ORDER_RANK_3, false, true);
 487}
 488
 489void LLWearableItemTypeNameComparator::setOrder(LLAssetType::EType items_of_type,  LLWearableItemTypeNameComparator::ETypeListOrder order_priority, bool sort_asset_items_by_name, bool sort_wearable_items_by_name)
 490{
 491	mWearableOrder[items_of_type] = LLWearableTypeOrder(order_priority, sort_asset_items_by_name, sort_wearable_items_by_name);
 492}
 493
 494/*virtual*/
 495bool LLWearableItemNameComparator::doCompare(const LLPanelInventoryListItemBase* wearable_item1, const LLPanelInventoryListItemBase* wearable_item2) const
 496{
 497	std::string name1 = wearable_item1->getItemName();
 498	std::string name2 = wearable_item2->getItemName();
 499
 500	LLStringUtil::toUpper(name1);
 501	LLStringUtil::toUpper(name2);
 502
 503	return name1 < name2;
 504}
 505
 506/*virtual*/
 507bool LLWearableItemTypeNameComparator::doCompare(const LLPanelInventoryListItemBase* wearable_item1, const LLPanelInventoryListItemBase* wearable_item2) const
 508{
 509	const LLAssetType::EType item_type1 = wearable_item1->getType();
 510	const LLAssetType::EType item_type2 = wearable_item2->getType();
 511
 512	LLWearableItemTypeNameComparator::ETypeListOrder item_type_order1 = getTypeListOrder(item_type1);
 513	LLWearableItemTypeNameComparator::ETypeListOrder item_type_order2 = getTypeListOrder(item_type2);
 514
 515	if (item_type_order1 != item_type_order2)
 516	{
 517		// If items are of different asset types we can compare them
 518		// by types order in the list.
 519		return item_type_order1 < item_type_order2;
 520	}
 521
 522	if (sortAssetTypeByName(item_type1))
 523	{
 524		// If both items are of the same asset type except AT_CLOTHING and AT_BODYPART
 525		// we can compare them by name.
 526		return LLWearableItemNameComparator::doCompare(wearable_item1, wearable_item2);
 527	}
 528
 529	const LLWearableType::EType item_wearable_type1 = wearable_item1->getWearableType();
 530	const LLWearableType::EType item_wearable_type2 = wearable_item2->getWearableType();
 531
 532	if (item_wearable_type1 != item_wearable_type2)
 533		// If items are of different LLWearableType::EType types they are compared
 534		// by LLWearableType::EType. types order determined in LLWearableType::EType.
 535	{
 536		// If items are of different LLWearableType::EType types they are compared
 537		// by LLWearableType::EType. types order determined in LLWearableType::EType.
 538		return item_wearable_type1 < item_wearable_type2;
 539	}
 540	else
 541	{
 542		// If both items are of the same clothing type they are compared
 543		// by description and place in reverse order (i.e. outer layer item
 544		// on top) OR by name
 545		if(sortWearableTypeByName(item_type1))
 546		{
 547			return LLWearableItemNameComparator::doCompare(wearable_item1, wearable_item2);
 548		}
 549		return wearable_item1->getDescription() > wearable_item2->getDescription();
 550	}
 551}
 552
 553LLWearableItemTypeNameComparator::ETypeListOrder LLWearableItemTypeNameComparator::getTypeListOrder(LLAssetType::EType item_type) const
 554{
 555	wearable_type_order_map_t::const_iterator const_it = mWearableOrder.find(item_type);
 556
 557
 558	if(const_it == mWearableOrder.end())
 559	{
 560		llwarns<<"Absent information about order rang of items of "<<LLAssetType::getDesc(item_type)<<" type"<<llendl;
 561		return ORDER_RANK_UNKNOWN;
 562	}
 563
 564	return const_it->second.mOrderPriority;
 565}
 566
 567bool LLWearableItemTypeNameComparator::sortAssetTypeByName(LLAssetType::EType item_type) const
 568{
 569	wearable_type_order_map_t::const_iterator const_it = mWearableOrder.find(item_type);
 570
 571
 572	if(const_it == mWearableOrder.end())
 573	{
 574		llwarns<<"Absent information about sorting items of "<<LLAssetType::getDesc(item_type)<<" type"<<llendl;
 575		return true;
 576	}
 577
 578
 579	return const_it->second.mSortAssetTypeByName;
 580	}
 581
 582
 583bool LLWearableItemTypeNameComparator::sortWearableTypeByName(LLAssetType::EType item_type) const
 584{
 585	wearable_type_order_map_t::const_iterator const_it = mWearableOrder.find(item_type);
 586
 587
 588	if(const_it == mWearableOrder.end())
 589	{
 590		llwarns<<"Absent information about sorting items of "<<LLAssetType::getDesc(item_type)<<" type"<<llendl;
 591		return true;
 592}
 593
 594
 595	return const_it->second.mSortWearableTypeByName;
 596}
 597
 598/*virtual*/
 599bool LLWearableItemCreationDateComparator::doCompare(const LLPanelInventoryListItemBase* item1, const LLPanelInventoryListItemBase* item2) const
 600{
 601	time_t date1 = item1->getCreationDate();
 602	time_t date2 = item2->getCreationDate();
 603
 604	if (date1 == date2)
 605	{
 606		return LLWearableItemNameComparator::doCompare(item1, item2);
 607	}
 608
 609	return date1 > date2;
 610}
 611//////////////////////////////////////////////////////////////////////////
 612//////////////////////////////////////////////////////////////////////////
 613//////////////////////////////////////////////////////////////////////////
 614
 615static LLWearableItemTypeNameComparator WEARABLE_TYPE_NAME_COMPARATOR;
 616static const LLWearableItemTypeNameComparator WEARABLE_TYPE_LAYER_COMPARATOR;
 617static const LLWearableItemNameComparator WEARABLE_NAME_COMPARATOR;
 618static const LLWearableItemCreationDateComparator WEARABLE_CREATION_DATE_COMPARATOR;
 619
 620static const LLDefaultChildRegistry::Register<LLWearableItemsList> r("wearable_items_list");
 621
 622LLWearableItemsList::Params::Params()
 623:	standalone("standalone", true)
 624,	worn_indication_enabled("worn_indication_enabled", true)
 625{}
 626
 627LLWearableItemsList::LLWearableItemsList(const LLWearableItemsList::Params& p)
 628:	LLInventoryItemsList(p)
 629{
 630	setSortOrder(E_SORT_BY_TYPE_LAYER, false);
 631	mIsStandalone = p.standalone;
 632	if (mIsStandalone)
 633	{
 634		// Use built-in context menu.
 635		setRightMouseDownCallback(boost::bind(&LLWearableItemsList::onRightClick, this, _2, _3));
 636	}
 637	mWornIndicationEnabled = p.worn_indication_enabled;
 638	setNoItemsCommentText(LLTrans::getString("LoadingData"));
 639}
 640
 641// virtual
 642LLWearableItemsList::~LLWearableItemsList()
 643{}
 644
 645// virtual
 646void LLWearableItemsList::addNewItem(LLViewerInventoryItem* item, bool rearrange /*= true*/)
 647{
 648	if (!item)
 649	{
 650		llwarns << "No inventory item. Couldn't create flat list item." << llendl;
 651		llassert(item != NULL);
 652	}
 653
 654	LLPanelWearableOutfitItem *list_item = LLPanelWearableOutfitItem::create(item, mWornIndicationEnabled);
 655	if (!list_item)
 656		return;
 657
 658	bool is_item_added = addItem(list_item, item->getUUID(), ADD_BOTTOM, rearrange);
 659	if (!is_item_added)
 660	{
 661		llwarns << "Couldn't add flat list item." << llendl;
 662		llassert(is_item_added);
 663	}
 664}
 665
 666void LLWearableItemsList::updateList(const LLUUID& category_id)
 667{
 668	LLInventoryModel::cat_array_t cat_array;
 669	LLInventoryModel::item_array_t item_array;
 670
 671	LLFindOutfitItems collector = LLFindOutfitItems();
 672	// collectDescendentsIf takes non-const reference:
 673	gInventory.collectDescendentsIf(
 674		category_id,
 675		cat_array,
 676		item_array,
 677		LLInventoryModel::EXCLUDE_TRASH,
 678		collector);
 679
 680	if(item_array.empty() && gInventory.isCategoryComplete(category_id))
 681	{
 682		setNoItemsCommentText(LLTrans::getString("EmptyOutfitText"));
 683	}
 684
 685	refreshList(item_array);
 686}
 687
 688void LLWearableItemsList::updateChangedItems(const uuid_vec_t& changed_items_uuids)
 689{
 690	// nothing to update
 691	if (changed_items_uuids.empty()) return;
 692
 693	typedef std::vector<LLPanel*> item_panel_list_t;
 694
 695	item_panel_list_t items;
 696	getItems(items);
 697
 698	for (item_panel_list_t::iterator items_iter = items.begin();
 699			items_iter != items.end();
 700			++items_iter)
 701	{
 702		LLPanelInventoryListItemBase* item = dynamic_cast<LLPanelInventoryListItemBase*>(*items_iter);
 703		if (!item) continue;
 704
 705		LLViewerInventoryItem* inv_item = item->getItem();
 706		if (!inv_item) continue;
 707
 708		LLUUID linked_uuid = inv_item->getLinkedUUID();
 709
 710		for (uuid_vec_t::const_iterator iter = changed_items_uuids.begin();
 711				iter != changed_items_uuids.end();
 712				++iter)
 713		{
 714			if (linked_uuid == *iter)
 715			{
 716				item->setNeedsRefresh(true);
 717				break;
 718			}
 719		}
 720	}
 721}
 722
 723void LLWearableItemsList::onRightClick(S32 x, S32 y)
 724{
 725	uuid_vec_t selected_uuids;
 726
 727	getSelectedUUIDs(selected_uuids);
 728	if (selected_uuids.empty())
 729	{
 730		return;
 731	}
 732
 733	ContextMenu::instance().show(this, selected_uuids, x, y);
 734}
 735
 736void LLWearableItemsList::setSortOrder(ESortOrder sort_order, bool sort_now)
 737{
 738	switch (sort_order)
 739	{
 740	case E_SORT_BY_MOST_RECENT:
 741		setComparator(&WEARABLE_CREATION_DATE_COMPARATOR);
 742		break;
 743	case E_SORT_BY_NAME:
 744		setComparator(&WEARABLE_NAME_COMPARATOR);
 745		break;
 746	case E_SORT_BY_TYPE_LAYER:
 747		setComparator(&WEARABLE_TYPE_LAYER_COMPARATOR);
 748		break;
 749	case E_SORT_BY_TYPE_NAME:
 750	{
 751		WEARABLE_TYPE_NAME_COMPARATOR.setOrder(LLAssetType::AT_CLOTHING, LLWearableItemTypeNameComparator::ORDER_RANK_1, false, true);
 752		setComparator(&WEARABLE_TYPE_NAME_COMPARATOR);
 753		break;
 754	}
 755
 756	// No "default:" to raise compiler warning
 757	// if we're not handling something
 758	}
 759
 760	mSortOrder = sort_order;
 761
 762	if (sort_now)
 763	{
 764		sort();
 765	}
 766}
 767
 768//////////////////////////////////////////////////////////////////////////
 769/// ContextMenu
 770//////////////////////////////////////////////////////////////////////////
 771
 772LLWearableItemsList::ContextMenu::ContextMenu()
 773:	mParent(NULL)
 774{
 775}
 776
 777void LLWearableItemsList::ContextMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y)
 778{
 779	mParent = dynamic_cast<LLWearableItemsList*>(spawning_view);
 780	LLListContextMenu::show(spawning_view, uuids, x, y);
 781	mParent = NULL; // to avoid dereferencing an invalid pointer
 782}
 783
 784// virtual
 785LLContextMenu* LLWearableItemsList::ContextMenu::createMenu()
 786{
 787	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 788	const uuid_vec_t& ids = mUUIDs;		// selected items IDs
 789	LLUUID selected_id = ids.front();	// ID of the first selected item
 790
 791	functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1);
 792
 793	// Register handlers common for all wearable types.
 794	registrar.add("Wearable.Wear", boost::bind(wear_multiple, ids, true));
 795	registrar.add("Wearable.Add", boost::bind(wear_multiple, ids, false));
 796	registrar.add("Wearable.Edit", boost::bind(handleMultiple, LLAgentWearables::editWearable, ids));
 797	registrar.add("Wearable.CreateNew", boost::bind(createNewWearable, selected_id));
 798	registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id));
 799	registrar.add("Wearable.TakeOffDetach", boost::bind(handleMultiple, take_off, ids));
 800
 801	// Register handlers for clothing.
 802	registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, ids));
 803
 804	// Register handlers for body parts.
 805
 806	// Register handlers for attachments.
 807	registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, ids));
 808	registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id));
 809	registrar.add("Object.Attach", boost::bind(LLViewerAttachMenu::attachObjects, ids, _2));
 810
 811	// Create the menu.
 812	LLContextMenu* menu = createFromFile("menu_wearable_list_item.xml");
 813
 814	// Determine which items should be visible/enabled.
 815	updateItemsVisibility(menu);
 816
 817	// Update labels for the items requiring that.
 818	updateItemsLabels(menu);
 819	return menu;
 820}
 821
 822void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu)
 823{
 824	if (!menu)
 825	{
 826		llwarns << "Invalid menu" << llendl;
 827		return;
 828	}
 829
 830	const uuid_vec_t& ids = mUUIDs;	// selected items IDs
 831	U32 mask = 0;					// mask of selected items' types
 832	U32 n_items = ids.size();		// number of selected items
 833	U32 n_worn = 0;					// number of worn items among the selected ones
 834	U32 n_already_worn = 0;			// number of items worn of same type as selected items
 835	U32 n_links = 0;				// number of links among the selected items
 836	U32 n_editable = 0;				// number of editable items among the selected ones
 837
 838	bool can_be_worn = true;
 839
 840	for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
 841	{
 842		LLUUID id = *it;
 843		LLViewerInventoryItem* item = gInventory.getItem(id);
 844
 845		if (!item)
 846		{
 847			llwarns << "Invalid item" << llendl;
 848			// *NOTE: the logic below may not work in this case
 849			continue;
 850		}
 851
 852		updateMask(mask, item->getType());
 853
 854		const LLWearableType::EType wearable_type = item->getWearableType();
 855		const bool is_link = item->getIsLinkType();
 856		const bool is_worn = get_is_item_worn(id);
 857		const bool is_editable = gAgentWearables.isWearableModifiable(id);
 858		const bool is_already_worn = gAgentWearables.selfHasWearable(wearable_type);
 859		if (is_worn)
 860		{
 861			++n_worn;
 862		}
 863		if (is_editable)
 864		{
 865			++n_editable;
 866		}
 867		if (is_link)
 868		{
 869			++n_links;
 870		}
 871		if (is_already_worn)
 872		{
 873			++n_already_worn;
 874		}
 875
 876		if (can_be_worn)
 877		{
 878			can_be_worn = get_can_item_be_worn(item->getLinkedUUID());
 879		}
 880	} // for
 881
 882	bool standalone = mParent ? mParent->isStandalone() : false;
 883	bool wear_add_visible = mask & (MASK_CLOTHING|MASK_ATTACHMENT) && n_worn == 0 && can_be_worn && (n_already_worn != 0 || mask & MASK_ATTACHMENT);
 884
 885	// *TODO: eliminate multiple traversals over the menu items
 886	setMenuItemVisible(menu, "wear_wear", 			n_already_worn == 0 && n_worn == 0 && can_be_worn);
 887	setMenuItemEnabled(menu, "wear_wear", 			n_already_worn == 0 && n_worn == 0);
 888	setMenuItemVisible(menu, "wear_add",			wear_add_visible);
 889	setMenuItemEnabled(menu, "wear_add",			canAddWearables(ids));
 890	setMenuItemVisible(menu, "wear_replace",		n_worn == 0 && n_already_worn != 0 && can_be_worn);
 891	//visible only when one item selected and this item is worn
 892	setMenuItemVisible(menu, "edit",				!standalone && mask & (MASK_CLOTHING|MASK_BODYPART) && n_worn == n_items && n_worn == 1);
 893	setMenuItemEnabled(menu, "edit",				n_editable == 1 && n_worn == 1 && n_items == 1);
 894	setMenuItemVisible(menu, "create_new",			mask & (MASK_CLOTHING|MASK_BODYPART) && n_items == 1);
 895	setMenuItemEnabled(menu, "create_new",			canAddWearables(ids));
 896	setMenuItemVisible(menu, "show_original",		!standalone);
 897	setMenuItemEnabled(menu, "show_original",		n_items == 1 && n_links == n_items);
 898	setMenuItemVisible(menu, "take_off",			mask == MASK_CLOTHING && n_worn == n_items);
 899	setMenuItemVisible(menu, "detach",				mask == MASK_ATTACHMENT && n_worn == n_items);
 900	setMenuItemVisible(menu, "take_off_or_detach",	mask == (MASK_ATTACHMENT|MASK_CLOTHING));
 901	setMenuItemEnabled(menu, "take_off_or_detach",	n_worn == n_items);
 902	setMenuItemVisible(menu, "object_profile",		!standalone);
 903	setMenuItemEnabled(menu, "object_profile",		n_items == 1);
 904	setMenuItemVisible(menu, "--no options--", 		FALSE);
 905	setMenuItemEnabled(menu, "--no options--",		FALSE);
 906
 907	// Populate or hide the "Attach to..." / "Attach to HUD..." submenus.
 908	if (mask == MASK_ATTACHMENT && n_worn == 0)
 909	{
 910		LLViewerAttachMenu::populateMenus("wearable_attach_to", "wearable_attach_to_hud");
 911	}
 912	else
 913	{
 914		setMenuItemVisible(menu, "wearable_attach_to",			false);
 915		setMenuItemVisible(menu, "wearable_attach_to_hud",		false);
 916	}
 917
 918	if (mask & MASK_UNKNOWN)
 919	{
 920		llwarns << "Non-wearable items passed." << llendl;
 921	}
 922
 923	U32 num_visible_items = 0;
 924	for (U32 menu_item_index = 0; menu_item_index < menu->getItemCount(); ++menu_item_index)
 925	{
 926		const LLMenuItemGL* menu_item = menu->getItem(menu_item_index);
 927		if (menu_item && menu_item->getVisible())
 928		{
 929			num_visible_items++;
 930		}
 931	}
 932	if (num_visible_items == 0)
 933	{
 934		setMenuItemVisible(menu, "--no options--", TRUE);
 935	}
 936}
 937
 938void LLWearableItemsList::ContextMenu::updateItemsLabels(LLContextMenu* menu)
 939{
 940	llassert(menu);
 941	if (!menu) return;
 942
 943	// Set proper label for the "Create new <WEARABLE_TYPE>" menu item.
 944	LLViewerInventoryItem* item = gInventory.getLinkedItem(mUUIDs.back());
 945	if (!item || !item->isWearableType()) return;
 946
 947	LLWearableType::EType w_type = item->getWearableType();
 948	std::string new_label = LLTrans::getString("create_new_" + LLWearableType::getTypeName(w_type));
 949
 950	LLMenuItemGL* menu_item = menu->getChild<LLMenuItemGL>("create_new");
 951	menu_item->setLabel(new_label);
 952}
 953
 954// We need this method to convert non-zero BOOL values to exactly 1 (TRUE).
 955// Otherwise code relying on a BOOL value being TRUE may fail
 956// (I experienced a weird assert in LLView::drawChildren() because of that.
 957// static
 958void LLWearableItemsList::ContextMenu::setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val)
 959{
 960	menu->setItemVisible(name, val);
 961}
 962
 963// static
 964void LLWearableItemsList::ContextMenu::setMenuItemEnabled(LLContextMenu* menu, const std::string& name, bool val)
 965{
 966	menu->setItemEnabled(name, val);
 967}
 968
 969// static
 970void LLWearableItemsList::ContextMenu::updateMask(U32& mask, LLAssetType::EType at)
 971{
 972	if (at == LLAssetType::AT_CLOTHING)
 973	{
 974		mask |= MASK_CLOTHING;
 975	}
 976	else if (at == LLAssetType::AT_BODYPART)
 977	{
 978		mask |= MASK_BODYPART;
 979	}
 980	else if (at == LLAssetType::AT_OBJECT)
 981	{
 982		mask |= MASK_ATTACHMENT;
 983	}
 984	else
 985	{
 986		mask |= MASK_UNKNOWN;
 987	}
 988}
 989
 990// static
 991void LLWearableItemsList::ContextMenu::createNewWearable(const LLUUID& item_id)
 992{
 993	LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id);
 994	if (!item || !item->isWearableType()) return;
 995
 996	LLAgentWearables::createWearable(item->getWearableType(), true);
 997}
 998
 999// Returns true if all the given objects and clothes can be added.
1000// static
1001bool LLWearableItemsList::ContextMenu::canAddWearables(const uuid_vec_t& item_ids)
1002{
1003	// TODO: investigate wearables may not be loaded at this point EXT-8231
1004
1005	U32 n_objects = 0;
1006	boost::unordered_map<LLWearableType::EType, U32> clothes_by_type;
1007
1008	// Count given clothes (by wearable type) and objects.
1009	for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it)
1010	{
1011		LLViewerInventoryItem* item = gInventory.getItem(*it);
1012		if (!item)
1013		{
1014			return false;
1015		}
1016
1017		if (item->getType() == LLAssetType::AT_OBJECT)
1018		{
1019			++n_objects;
1020		}
1021		else if (item->getType() == LLAssetType::AT_CLOTHING)
1022		{
1023			++clothes_by_type[item->getWearableType()];
1024		}
1025		else
1026		{
1027			llwarns << "Unexpected wearable type" << llendl;
1028			return false;
1029		}
1030	}
1031
1032	// Check whether we can add all the objects.
1033	if (!isAgentAvatarValid() || !gAgentAvatarp->canAttachMoreObjects(n_objects))
1034	{
1035		return false;
1036	}
1037
1038	// Check whether we can add all the clothes.
1039	boost::unordered_map<LLWearableType::EType, U32>::const_iterator m_it;
1040	for (m_it = clothes_by_type.begin(); m_it != clothes_by_type.end(); ++m_it)
1041	{
1042		LLWearableType::EType w_type	= m_it->first;
1043		U32 n_clothes					= m_it->second;
1044
1045		U32 wearable_count = gAgentWearables.getWearableCount(w_type);
1046		if ((wearable_count > 0) && !LLWearableType::getAllowMultiwear(w_type))
1047		{
1048			return false;
1049		}
1050		if ((wearable_count + n_clothes) > LLAgentWearables::MAX_CLOTHING_PER_TYPE)
1051		{
1052			return false;
1053		}
1054
1055	}
1056
1057	return true;
1058}
1059
1060// EOF