diff options
Diffstat (limited to 'indra/newview/llfavoritesbar.cpp')
-rw-r--r-- | indra/newview/llfavoritesbar.cpp | 481 |
1 files changed, 366 insertions, 115 deletions
diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 45bbea7e2a..0e5b943dd3 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -33,16 +33,21 @@ #include "llviewerprecompiledheaders.h" #include "llfavoritesbar.h" + +#include "llbutton.h" +#include "llfloaterreg.h" +#include "llinventory.h" #include "lluictrlfactory.h" +#include "llmenugl.h" #include "llagent.h" -#include "llviewerinventory.h" -#include "llinventory.h" -#include "llinventorymodel.h" -#include "llbutton.h" #include "llinventorybridge.h" +#include "llinventorymodel.h" +#include "llviewerinventory.h" +#include "llviewermenu.h" +#include "llviewermenu.h" -static LLRegisterWidget<LLFavoritesBarCtrl> r("favorites_bar"); +static LLDefaultWidgetRegistry::Register<LLFavoritesBarCtrl> r("favorites_bar"); // updateButtons's helper struct LLFavoritesSort @@ -64,16 +69,64 @@ struct LLFavoritesSort } }; +class LLVisibilityTrackingMenuGL : public LLMenuGL +{ +protected: + LLVisibilityTrackingMenuGL(const LLMenuGL::Params&); + friend class LLUICtrlFactory; +public: + virtual void onVisibilityChange (BOOL curVisibilityIn); + void setChevronRect(const LLRect& rect) { mChevronRect = rect; } + + bool getClosedByChevronClick() { return mClosedByChevronClick; } + void resetClosedByChevronClick() { mClosedByChevronClick = false; } + +protected: + bool mClosedByChevronClick; + LLRect mChevronRect; +}; + +LLVisibilityTrackingMenuGL::LLVisibilityTrackingMenuGL(const LLMenuGL::Params& p) +: LLMenuGL(p), + mClosedByChevronClick(false) +{ +} + +//virtual +void LLVisibilityTrackingMenuGL::onVisibilityChange (BOOL curVisibilityIn) +{ + S32 x,y; + LLUI::getCursorPositionLocal(LLUI::getRootView(), &x, &y); + + if (!curVisibilityIn && mChevronRect.pointInRect(x, y)) + { + mClosedByChevronClick = true; + } +} + LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) : LLUICtrl(p), - mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()) + mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), + mPopupMenuHandle(), + mInventoryItemsPopupMenuHandle() { + // Register callback for menus with current registrar (will be parent panel's registrar) + LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Favorites.DoToSelected", + boost::bind(&LLFavoritesBarCtrl::doToSelected, this, _2)); + + // Add this if we need to selectively enable items + //LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("Favorites.EnableSelected", + // boost::bind(&LLFavoritesBarCtrl::enableSelected, this, _2)); + gInventory.addObserver(this); } LLFavoritesBarCtrl::~LLFavoritesBarCtrl() { gInventory.removeObserver(this); + + LLView::deleteViewByHandle(mPopupMenuHandle); + LLView::deleteViewByHandle(mInventoryItemsPopupMenuHandle); } BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, @@ -87,9 +140,7 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, switch (cargo_type) { - // IAN BUG: did the spec ask for calling cards here? case DAD_LANDMARK: - case DAD_CALLINGCARD: { // Copy the item into the favorites folder (if it's not already there). LLInventoryItem *item = (LLInventoryItem *)cargo_data; @@ -127,6 +178,31 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, //virtual void LLFavoritesBarCtrl::changed(U32 mask) { + if (mFavoriteFolderId.isNull()) + { + mFavoriteFolderId = gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE); + + if (mFavoriteFolderId.notNull()) + { + gInventory.fetchDescendentsOf(mFavoriteFolderId); + } + } + else + { + updateButtons(getRect().getWidth()); + } +} + +//virtual +void LLFavoritesBarCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + updateButtons(width); + + LLUICtrl::reshape(width, height, called_from_parent); +} + +void LLFavoritesBarCtrl::updateButtons(U32 bar_width) +{ LLInventoryModel::item_array_t items; if (!collectFavoriteItems(items)) @@ -134,170 +210,317 @@ void LLFavoritesBarCtrl::changed(U32 mask) return; } + const S32 buttonHPad = LLUI::sSettingGroups["config"]->getS32("ButtonHPad"); + const S32 buttonHGap = 2; + const S32 buttonVGap = 2; + static LLButton::Params default_button_params(LLUICtrlFactory::getDefaultParams<LLButton::Params>()); + std::string flat_icon = "transparent.j2c"; + std::string hover_icon = default_button_params.image_unselected.name; + std::string hover_icon_selected = default_button_params.image_selected.name; + + S32 curr_x = buttonHGap; + S32 count = items.count(); - if (getChildCount() == count) + + const S32 chevron_button_width = mFont->getWidth(">>") + buttonHPad * 2; + + S32 buttons_space = bar_width - curr_x; + + S32 first_drop_down_item = count; + + // Calculating, how much buttons can fit in the bar + S32 buttons_width = 0; + for (S32 i = 0; i < count; ++i) { - // Check whether buttons are reflecting state of favorite inventory folder - const LLView::child_list_t *buttons_list = getChildList(); - S32 i = 0; - for(LLView::child_list_const_iter_t iter = buttons_list->begin(); iter != buttons_list->end(); iter++) + buttons_width += mFont->getWidth(items.get(i)->getName()) + buttonHPad * 2 + buttonHGap; + if (buttons_width > buttons_space) { - LLButton *button = (LLButton *)*iter; - LLInventoryItem* item = items.get(i); - // tooltip contains full name of item, label can be cutted - if (button->getToolTip() != item->getName()) + // There is no space for all buttons. + // Calculating the number of buttons, that are fit with chevron button + buttons_space -= chevron_button_width + buttonHGap; + while (i >= 0 && buttons_width > buttons_space) { - break; + buttons_width -= mFont->getWidth(items.get(i)->getName()) + buttonHPad * 2 + buttonHGap; + i--; } - - - i++; + first_drop_down_item = i + 1; // First item behind visible items + + break; } + } - // Check passed, nothing is changed - if (i < items.count()) + // If inventory items are not changed up to mFirstDropDownItem, no need to recreate them + if (mFirstDropDownItem == first_drop_down_item && (mItemNamesCache.size() == count || mItemNamesCache.size() == mFirstDropDownItem)) + { + S32 i; + for (i = 0; i < mFirstDropDownItem; ++i) { + if (mItemNamesCache.get(i) != items.get(i)->getName()) + { + break; + } + } + if (i == mFirstDropDownItem) + { + // Chevron button should stay right aligned + LLView *chevron_button = getChildView(std::string(">>"), FALSE, FALSE); + if (chevron_button) + { + LLRect rect; + rect.setOriginAndSize(bar_width - chevron_button_width - buttonHGap, buttonVGap, chevron_button_width, getRect().getHeight()-buttonVGap); + chevron_button->setRect(rect); + + S32 chevron_root_x, chevron_root_y; + localPointToOtherView(rect.mLeft, rect.mBottom, &chevron_root_x, &chevron_root_y, LLUI::getRootView()); + mChevronRect.setOriginAndSize(chevron_root_x, chevron_root_y, rect.getWidth(), rect.getHeight()); + } return; } } - //FIXME: optimize this - deleteAllChildren(); - - LLButton::Params bparams; + mFirstDropDownItem = first_drop_down_item; - const S32 buttonWidth = LLUI::sSettingGroups["config"]->getS32("ButtonHPad") * 2; - LLRect rect; + mItemNamesCache.clear(); + for (S32 i = 0; i < mFirstDropDownItem; i++) + { + mItemNamesCache.put(items.get(i)->getName()); + } - for(S32 i = 0; i < count; ++i) + // Rebuild the buttons only + // child_list_t is a linked list, so safe to erase from the middle if we pre-incrament the iterator + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ) + { + child_list_const_iter_t cur_it = child_it++; + LLView* viewp = *cur_it; + LLButton* button = dynamic_cast<LLButton*>(viewp); + if (button) + { + removeChild(button); + delete button; + } + } + + // Adding buttons + for(S32 i = 0; i < mFirstDropDownItem; i++) { - rect.setOriginAndSize(0, 0, buttonWidth, llround(mFont->getLineHeight())); + + LLInventoryItem* item = items.get(i); + S32 buttonWidth = mFont->getWidth(item->getName()) + buttonHPad * 2; + + LLRect rect; + rect.setOriginAndSize(curr_x, buttonVGap, buttonWidth, getRect().getHeight()-buttonVGap); + + LLButton::Params bparams; + bparams.image_unselected.name(flat_icon); + bparams.image_disabled.name(flat_icon); + bparams.image_selected.name(hover_icon_selected); + bparams.image_hover_selected.name(hover_icon_selected); + bparams.image_disabled_selected.name(hover_icon_selected); + bparams.image_hover_unselected.name(hover_icon); bparams.follows.flags (FOLLOWS_LEFT | FOLLOWS_BOTTOM); bparams.rect (rect); bparams.tab_stop(false); bparams.font(mFont); - bparams.click_callback.function(boost::bind(&LLFavoritesBarCtrl::onButtonClick, this, 0)); + bparams.name(item->getName()); + bparams.tool_tip(item->getName()); + bparams.click_callback.function(boost::bind(&LLFavoritesBarCtrl::onButtonClick, this, item->getUUID())); + bparams.rightclick_callback.function(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this, item->getUUID())); - LLButton *button = LLUICtrlFactory::create<LLButton> (bparams); + addChildInBack(LLUICtrlFactory::create<LLButton> (bparams)); - addChildInBack(button); + curr_x += buttonWidth + buttonHGap; } - updateButtons(getRect().getWidth(), items); + // Chevron button + if (mFirstDropDownItem != count) + { + LLButton::Params bparams; + + LLRect rect; + rect.setOriginAndSize(bar_width - chevron_button_width - buttonHGap, buttonVGap, chevron_button_width, getRect().getHeight()-buttonVGap); + + bparams.follows.flags (FOLLOWS_LEFT | FOLLOWS_BOTTOM); + bparams.image_unselected.name(flat_icon); + bparams.image_disabled.name(flat_icon); + bparams.image_selected.name(hover_icon_selected); + bparams.image_hover_selected.name(hover_icon_selected); + bparams.image_disabled_selected.name(hover_icon_selected); + bparams.image_hover_unselected.name(hover_icon); + bparams.rect (rect); + bparams.tab_stop(false); + bparams.font(mFont); + bparams.name(">>"); + bparams.click_callback.function(boost::bind(&LLFavoritesBarCtrl::showDropDownMenu, this)); + + addChildInBack(LLUICtrlFactory::create<LLButton> (bparams)); + + S32 chevron_root_x, chevron_root_y; + localPointToOtherView(rect.mLeft, rect.mBottom, &chevron_root_x, &chevron_root_y, LLUI::getRootView()); + mChevronRect.setOriginAndSize(chevron_root_x, chevron_root_y, rect.getWidth(), rect.getHeight()); + } } -//virtual -void LLFavoritesBarCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) +BOOL LLFavoritesBarCtrl::postBuild() { - LLInventoryModel::item_array_t items; - if (collectFavoriteItems(items)) + // make the popup menu available + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_favorites.xml", gMenuHolder); + if (!menu) { - updateButtons(width, items); + menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); } + menu->setBackgroundColor(gSavedSkinSettings.getColor("MenuPopupBgColor")); + mInventoryItemsPopupMenuHandle = menu->getHandle(); - LLUICtrl::reshape(width, height, called_from_parent); + return TRUE; } -void LLFavoritesBarCtrl::updateButtons(U32 barWidth, LLInventoryModel::item_array_t items) +BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &items) { - std::sort(items.begin(), items.end(), LLFavoritesSort()); + if (mFavoriteFolderId.isNull()) + return FALSE; + + LLInventoryModel::cat_array_t cats; - const S32 buttonHPad = LLUI::sSettingGroups["config"]->getS32("ButtonHPad"); - const S32 buttonHGap = 2; + LLIsType is_type(LLAssetType::AT_LANDMARK); + gInventory.collectDescendentsIf(mFavoriteFolderId, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); - S32 curr_x = buttonHGap; + std::sort(items.begin(), items.end(), LLFavoritesSort()); - S32 count = items.count(); - S32 labelsWidth = 0; + return TRUE; +} - for (S32 i = 0; i < count; ++i) +void LLFavoritesBarCtrl::showDropDownMenu() +{ + if (mPopupMenuHandle.isDead()) { - labelsWidth += mFont->getWidth(items.get(i)->getName()); - } - - const S32 ellipsisWIdth = mFont->getWidth("..."); + LLMenuGL::Params menu_p; + menu_p.name("favorites menu"); + menu_p.can_tear_off(false); + menu_p.visible(false); + menu_p.scrollable(true); - F32 shrinkFactor = 1.0f; + LLVisibilityTrackingMenuGL* menu = LLUICtrlFactory::create<LLVisibilityTrackingMenuGL>(menu_p); - S32 labelsSpace = barWidth - buttonHGap * (count + 1) - buttonHPad * 2 * count; // There is leading buttonHGap in front of first button, - // one buttonHGap at the end of each button and 2 buttonHPad's - if (labelsWidth >= labelsSpace) - { - shrinkFactor = (float)labelsSpace / labelsWidth; + mPopupMenuHandle = menu->getHandle(); } - const LLView::child_list_t *buttons_list = getChildList(); + LLVisibilityTrackingMenuGL* menu = (LLVisibilityTrackingMenuGL*)mPopupMenuHandle.get(); - S32 i = 0; - for(LLView::child_list_const_iter_t iter = buttons_list->begin(); iter != buttons_list->end(); iter++) + if(menu) { - LLButton *button = (LLButton *)*iter; - - LLInventoryItem* item = items.get(i); + if (menu->getClosedByChevronClick()) + { + menu->resetClosedByChevronClick(); + return; + } - LLRect rect; + if (menu->getVisible()) + { + menu->setVisible(FALSE); + menu->resetClosedByChevronClick(); + return; + } - S32 labelWidth = mFont->getWidth(item->getName()); + LLInventoryModel::item_array_t items; - if (shrinkFactor < 1.0f) + if (!collectFavoriteItems(items)) { - labelWidth = (S32) ((float)labelWidth * shrinkFactor); + return; + } - if (labelWidth > ellipsisWIdth) - { - labelWidth -= ellipsisWIdth; + S32 count = items.count(); - S32 charsTotal = item->getName().length(); - S32 charsFitted = 1; - while (charsFitted < charsTotal && mFont->getWidth(item->getName(), 0, charsFitted) < labelWidth) + // Check it there are changed items, since last call + if (mItemNamesCache.size() == count) + { + S32 i; + for (i = mFirstDropDownItem; i < count; i++) + { + if (mItemNamesCache.get(i) != items.get(i)->getName()) { - charsFitted++; + break; } + } + + // Check passed, just show the menu + if (i == count) + { + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); - charsFitted--; - labelWidth += ellipsisWIdth; + menu->setChevronRect(mChevronRect); - button->setLabel(item->getName().substr(0, charsFitted) + "..."); + LLMenuGL::showPopup(this, menu, getRect().getWidth() - menu->getRect().getWidth(), 0); + return; } - else + } + + // Add menu items to cache, if there is only names of buttons + if (mItemNamesCache.size() == mFirstDropDownItem) + { + for (S32 i = mFirstDropDownItem; i < count; i++) { - button->setLabel((LLStringExplicit)""); - labelWidth = 0; + mItemNamesCache.put(items.get(i)->getName()); } } - else + + menu->empty(); + + U32 max_width = 0; + + // Menu will not be wider, than bar + S32 bar_width = getRect().getWidth(); + + for(S32 i = mFirstDropDownItem; i < count; i++) { - button->setLabel(item->getName()); - } + LLInventoryItem* item = items.get(i); + const std::string& item_name = item->getName(); - S32 buttonWidth = labelWidth + buttonHPad * 2; - rect.setOriginAndSize(curr_x, 2, buttonWidth, llround(mFont->getLineHeight())); - button->setRect(rect); + LLMenuItemCallGL::Params item_params; + item_params.name(item_name); + item_params.label(item_name); + + item_params.on_click.function(boost::bind(&LLFavoritesBarCtrl::onButtonClick, this, item->getUUID())); - button->setToolTip(item->getName()); + LLMenuItemCallGL *menu_item = LLUICtrlFactory::create<LLMenuItemCallGL>(item_params); - curr_x += buttonWidth + buttonHGap; + // Check whether item name wider than menu + if ((S32) menu_item->getNominalWidth() > bar_width) + { + S32 chars_total = item_name.length(); + S32 chars_fitted = 1; + menu_item->setLabel(LLStringExplicit("")); + S32 label_space = bar_width - menu_item->getFont()->getWidth("...") - + menu_item->getNominalWidth(); // This returns width of menu item with empty label (pad pixels) - i++; - } -} + while (chars_fitted < chars_total && menu_item->getFont()->getWidth(item_name, 0, chars_fitted) < label_space) + { + chars_fitted++; + } + chars_fitted--; // Rolling back one char, that doesn't fit -BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &items) -{ - LLUUID favorite_folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE); + menu_item->setLabel(item_name.substr(0, chars_fitted) + "..."); + } - if (!favorite_folder_id.notNull()) - return FALSE; - - LLInventoryModel::cat_array_t cats; + max_width = llmax(max_width, menu_item->getNominalWidth()); - gInventory.collectDescendents(favorite_folder_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + menu->addChild(menu_item); + } - return TRUE; + // Menu will not be wider, than bar + max_width = llmin((S32)max_width, bar_width); + + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + + menu->setChevronRect(mChevronRect); + + LLMenuGL::showPopup(this, menu, getRect().getWidth() - max_width, 0); + } } -void LLFavoritesBarCtrl::onButtonClick(int idx) +void LLFavoritesBarCtrl::onButtonClick(LLUUID item_id) { LLInventoryModel::item_array_t items; @@ -306,23 +529,51 @@ void LLFavoritesBarCtrl::onButtonClick(int idx) return; } - S32 count = items.count(); + // We only have one Inventory, gInventory. Some day this should be better abstracted. + LLInvFVBridgeAction::doAction(item_id,&gInventory); +} - if (idx < 0 || idx >= count) +void LLFavoritesBarCtrl::onButtonRightClick(LLUUID item_id) +{ + mSelectedItemID = item_id; + + LLMenuGL* menu = (LLMenuGL*)mInventoryItemsPopupMenuHandle.get(); + if (!menu) { - llwarns << "Invalid favorites bar index" << llendl; return; } + + menu->updateParent(LLMenuGL::sMenuContainer); - LLInventoryItem* item = items.get(idx); - if(item) - { - LLUUID item_id = item->getUUID(); - LLAssetType::EType item_type = item->getType(); + S32 x,y; + LLUI::getCursorPositionLocal(this, &x, &y); + LLMenuGL::showPopup(this, menu, x, y); +} - //TODO - donno but may be we must use InventoryModel from InventoryView - //think for now there is only one model...but still - LLInvFVBridgeAction::doAction(item_type,item_id,&gInventory); +void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) +{ + std::string action = userdata.asString(); + llinfos << "Action = " << action << " Item = " << mSelectedItemID.asString() << llendl; + + LLViewerInventoryItem* item = gInventory.getItem(mSelectedItemID); + if (!item) + return; + + if (action == "open") + { + teleport_via_landmark(item->getAssetUUID()); + } + else if (action == "about") + { + LLFloaterReg::showInstance("preview_landmark", LLSD(mSelectedItemID), TAKE_FOCUS_YES); + } + else if (action == "rename") + { + // Would need to re-implement this: + // folder->startRenamingSelectedItem(); + } + else if (action == "delete") + { + gInventory.removeItem(mSelectedItemID); } } - |