diff options
Diffstat (limited to 'indra/newview/llfavoritesbar.cpp')
-rw-r--r-- | indra/newview/llfavoritesbar.cpp | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp new file mode 100644 index 0000000000..0e5b943dd3 --- /dev/null +++ b/indra/newview/llfavoritesbar.cpp @@ -0,0 +1,579 @@ +/** + * @file llfavoritesbar.cpp + * @brief LLFavoritesBarCtrl class implementation + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#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 "llinventorybridge.h" +#include "llinventorymodel.h" +#include "llviewerinventory.h" +#include "llviewermenu.h" +#include "llviewermenu.h" + +static LLDefaultWidgetRegistry::Register<LLFavoritesBarCtrl> r("favorites_bar"); + +// updateButtons's helper +struct LLFavoritesSort +{ + // Sorting by creation date and name + // TODO - made it customizible using gSavedSettings + bool operator()(const LLViewerInventoryItem* const& a, const LLViewerInventoryItem* const& b) + { + time_t first_create = a->getCreationDate(); + time_t second_create = b->getCreationDate(); + if (first_create == second_create) + { + return (LLStringUtil::compareDict(a->getName(), b->getName()) < 0); + } + else + { + return (first_create > second_create); + } + } +}; + +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()), + 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, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + *accept = ACCEPT_NO; + + switch (cargo_type) + { + + case DAD_LANDMARK: + { + // Copy the item into the favorites folder (if it's not already there). + LLInventoryItem *item = (LLInventoryItem *)cargo_data; + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE); + if (item->getParentUUID() == favorites_id) + { + llwarns << "Attemt to copy a favorite item into the same folder." << llendl; + break; + } + + *accept = ACCEPT_YES_COPY_SINGLE; + + if (drop) + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + favorites_id, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + + llinfos << "Copied inventory item #" << item->getUUID() << " to favorites." << llendl; + } + + } + break; + default: + break; + } + + return TRUE; +} + +//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)) + { + 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(); + + 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) + { + buttons_width += mFont->getWidth(items.get(i)->getName()) + buttonHPad * 2 + buttonHGap; + if (buttons_width > buttons_space) + { + // 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) + { + buttons_width -= mFont->getWidth(items.get(i)->getName()) + buttonHPad * 2 + buttonHGap; + i--; + } + first_drop_down_item = i + 1; // First item behind visible items + + break; + } + } + + // 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; + } + } + + mFirstDropDownItem = first_drop_down_item; + + mItemNamesCache.clear(); + for (S32 i = 0; i < mFirstDropDownItem; i++) + { + mItemNamesCache.put(items.get(i)->getName()); + } + + // 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++) + { + + 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.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())); + + addChildInBack(LLUICtrlFactory::create<LLButton> (bparams)); + + curr_x += buttonWidth + buttonHGap; + } + + // 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()); + } +} + +BOOL LLFavoritesBarCtrl::postBuild() +{ + // make the popup menu available + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_favorites.xml", gMenuHolder); + if (!menu) + { + menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); + } + menu->setBackgroundColor(gSavedSkinSettings.getColor("MenuPopupBgColor")); + mInventoryItemsPopupMenuHandle = menu->getHandle(); + + return TRUE; +} + +BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &items) +{ + if (mFavoriteFolderId.isNull()) + return FALSE; + + LLInventoryModel::cat_array_t cats; + + LLIsType is_type(LLAssetType::AT_LANDMARK); + gInventory.collectDescendentsIf(mFavoriteFolderId, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + std::sort(items.begin(), items.end(), LLFavoritesSort()); + + return TRUE; +} + +void LLFavoritesBarCtrl::showDropDownMenu() +{ + if (mPopupMenuHandle.isDead()) + { + LLMenuGL::Params menu_p; + menu_p.name("favorites menu"); + menu_p.can_tear_off(false); + menu_p.visible(false); + menu_p.scrollable(true); + + LLVisibilityTrackingMenuGL* menu = LLUICtrlFactory::create<LLVisibilityTrackingMenuGL>(menu_p); + + mPopupMenuHandle = menu->getHandle(); + } + + LLVisibilityTrackingMenuGL* menu = (LLVisibilityTrackingMenuGL*)mPopupMenuHandle.get(); + + if(menu) + { + if (menu->getClosedByChevronClick()) + { + menu->resetClosedByChevronClick(); + return; + } + + if (menu->getVisible()) + { + menu->setVisible(FALSE); + menu->resetClosedByChevronClick(); + return; + } + + LLInventoryModel::item_array_t items; + + if (!collectFavoriteItems(items)) + { + return; + } + + S32 count = items.count(); + + // 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()) + { + break; + } + } + + // Check passed, just show the menu + if (i == count) + { + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + + menu->setChevronRect(mChevronRect); + + LLMenuGL::showPopup(this, menu, getRect().getWidth() - menu->getRect().getWidth(), 0); + return; + } + } + + // Add menu items to cache, if there is only names of buttons + if (mItemNamesCache.size() == mFirstDropDownItem) + { + for (S32 i = mFirstDropDownItem; i < count; i++) + { + mItemNamesCache.put(items.get(i)->getName()); + } + } + + 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++) + { + LLInventoryItem* item = items.get(i); + const std::string& item_name = item->getName(); + + 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())); + + LLMenuItemCallGL *menu_item = LLUICtrlFactory::create<LLMenuItemCallGL>(item_params); + + // 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) + + 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 + + menu_item->setLabel(item_name.substr(0, chars_fitted) + "..."); + } + + max_width = llmax(max_width, menu_item->getNominalWidth()); + + menu->addChild(menu_item); + } + + // 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(LLUUID item_id) +{ + LLInventoryModel::item_array_t items; + + if (!collectFavoriteItems(items)) + { + return; + } + + // We only have one Inventory, gInventory. Some day this should be better abstracted. + LLInvFVBridgeAction::doAction(item_id,&gInventory); +} + +void LLFavoritesBarCtrl::onButtonRightClick(LLUUID item_id) +{ + mSelectedItemID = item_id; + + LLMenuGL* menu = (LLMenuGL*)mInventoryItemsPopupMenuHandle.get(); + if (!menu) + { + return; + } + + menu->updateParent(LLMenuGL::sMenuContainer); + + S32 x,y; + LLUI::getCursorPositionLocal(this, &x, &y); + LLMenuGL::showPopup(this, menu, x, y); +} + +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); + } +} |