/indra/newview/llwearableitemslist.cpp
C++ | 1060 lines | 765 code | 182 blank | 113 comment | 145 complexity | 949dcde6c929dde8f4470431c6cb2e68 MD5 | raw file
Possible License(s): LGPL-2.1
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