diff options
Diffstat (limited to 'indra/newview/llfavoritesbar.cpp')
| -rw-r--r-- | indra/newview/llfavoritesbar.cpp | 1272 |
1 files changed, 1272 insertions, 0 deletions
diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp new file mode 100644 index 0000000000..3981b887ad --- /dev/null +++ b/indra/newview/llfavoritesbar.cpp @@ -0,0 +1,1272 @@ +/** + * @file llfavoritesbar.cpp + * @brief LLFavoritesBarCtrl class implementation + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llfavoritesbar.h" + +#include "llfloaterreg.h" +#include "llfocusmgr.h" +#include "llinventory.h" +#include "lllandmarkactions.h" +#include "lltrans.h" +#include "lluictrlfactory.h" +#include "llmenugl.h" +#include "lltooltip.h" + +#include "llagent.h" +#include "llclipboard.h" +#include "llinventoryclipboard.h" +#include "llinventorybridge.h" +#include "llinventoryfunctions.h" +#include "llfloaterworldmap.h" +#include "lllandmarkactions.h" +#include "llnotificationsutil.h" +#include "llsidetray.h" +#include "lltoggleablemenu.h" +#include "llviewerinventory.h" +#include "llviewermenu.h" +#include "llviewermenu.h" +#include "lltooldraganddrop.h" + +static LLDefaultChildRegistry::Register<LLFavoritesBarCtrl> r("favorites_bar"); + +const S32 DROP_DOWN_MENU_WIDTH = 250; + +/** + * Helper for LLFavoriteLandmarkButton and LLFavoriteLandmarkMenuItem. + * Performing requests for SLURL for given Landmark ID + */ +class LLLandmarkInfoGetter +{ +public: + LLLandmarkInfoGetter() + : mLandmarkID(LLUUID::null), + mName("(Loading...)"), + mPosX(0), + mPosY(0), + mPosZ(0), + mLoaded(false) + { + mHandle.bind(this); + } + + void setLandmarkID(const LLUUID& id) { mLandmarkID = id; } + const LLUUID& getLandmarkId() const { return mLandmarkID; } + + const std::string& getName() + { + if(!mLoaded) + requestNameAndPos(); + + return mName; + } + + S32 getPosX() + { + if (!mLoaded) + requestNameAndPos(); + return mPosX; + } + + S32 getPosY() + { + if (!mLoaded) + requestNameAndPos(); + return mPosY; + } + + S32 getPosZ() + { + if (!mLoaded) + requestNameAndPos(); + return mPosZ; + } + +private: + /** + * Requests landmark data from server. + */ + void requestNameAndPos() + { + if (mLandmarkID.isNull()) + return; + + LLVector3d g_pos; + if(LLLandmarkActions::getLandmarkGlobalPos(mLandmarkID, g_pos)) + { + LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(g_pos, + boost::bind(&LLLandmarkInfoGetter::landmarkNameCallback, static_cast<LLHandle<LLLandmarkInfoGetter> >(mHandle), _1, _2, _3, _4)); + } + } + + static void landmarkNameCallback(LLHandle<LLLandmarkInfoGetter> handle, const std::string& name, S32 x, S32 y, S32 z) + { + LLLandmarkInfoGetter* getter = handle.get(); + if (getter) + { + getter->mPosX = x; + getter->mPosY = y; + getter->mPosZ = z; + getter->mName = name; + getter->mLoaded = true; + } + } + + LLUUID mLandmarkID; + std::string mName; + S32 mPosX; + S32 mPosY; + S32 mPosZ; + bool mLoaded; + LLRootHandle<LLLandmarkInfoGetter> mHandle; +}; + +/** + * This class is needed to override LLButton default handleToolTip function and + * show SLURL as button tooltip. + * *NOTE: dzaporozhan: This is a workaround. We could set tooltips for buttons + * in createButtons function but landmark data is not available when Favorites Bar is + * created. Thats why we are requesting landmark data after + */ +class LLFavoriteLandmarkButton : public LLButton +{ +public: + + BOOL handleToolTip(S32 x, S32 y, MASK mask) + { + std::string region_name = mLandmarkInfoGetter.getName(); + + if (!region_name.empty()) + { + std::string extra_message = llformat("%s (%d, %d, %d)", region_name.c_str(), + mLandmarkInfoGetter.getPosX(), mLandmarkInfoGetter.getPosY(), mLandmarkInfoGetter.getPosZ()); + + LLToolTip::Params params; + params.message = llformat("%s\n%s", getLabelSelected().c_str(), extra_message.c_str()); + params.max_width = 1000; + params.sticky_rect = calcScreenRect(); + + LLToolTipMgr::instance().show(params); + } + return TRUE; + } + + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) + { + LLFavoritesBarCtrl* fb = dynamic_cast<LLFavoritesBarCtrl*>(getParent()); + + if (fb) + { + fb->handleHover(x, y, mask); + } + + return LLButton::handleHover(x, y, mask); + } + + void setLandmarkID(const LLUUID& id){ mLandmarkInfoGetter.setLandmarkID(id); } + const LLUUID& getLandmarkId() const { return mLandmarkInfoGetter.getLandmarkId(); } + + void onMouseEnter(S32 x, S32 y, MASK mask) + { + if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) + { + LLUICtrl::onMouseEnter(x, y, mask); + } + else + { + LLButton::onMouseEnter(x, y, mask); + } + } + +protected: + LLFavoriteLandmarkButton(const LLButton::Params& p) : LLButton(p) {} + friend class LLUICtrlFactory; + +private: + LLLandmarkInfoGetter mLandmarkInfoGetter; +}; + +/** + * This class is needed to override LLMenuItemCallGL default handleToolTip function and + * show SLURL as button tooltip. + * *NOTE: dzaporozhan: This is a workaround. We could set tooltips for buttons + * in showDropDownMenu function but landmark data is not available when Favorites Bar is + * created. Thats why we are requesting landmark data after + */ +class LLFavoriteLandmarkMenuItem : public LLMenuItemCallGL +{ +public: + BOOL handleToolTip(S32 x, S32 y, MASK mask) + { + std::string region_name = mLandmarkInfoGetter.getName(); + if (!region_name.empty()) + { + LLToolTip::Params params; + params.message = llformat("%s\n%s (%d, %d)", getLabel().c_str(), region_name.c_str(), mLandmarkInfoGetter.getPosX(), mLandmarkInfoGetter.getPosY()); + params.sticky_rect = calcScreenRect(); + LLToolTipMgr::instance().show(params); + } + return TRUE; + } + + void setLandmarkID(const LLUUID& id){ mLandmarkInfoGetter.setLandmarkID(id); } + + virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) + { + if (mMouseDownSignal) + (*mMouseDownSignal)(this, x, y, mask); + return LLMenuItemCallGL::handleMouseDown(x, y, mask); + } + + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask) + { + if (mMouseUpSignal) + (*mMouseUpSignal)(this, x, y, mask); + return LLMenuItemCallGL::handleMouseUp(x, y, mask); + } + + virtual BOOL handleHover(S32 x, S32 y, MASK mask) + { + if (fb) + { + fb->handleHover(x, y, mask); + } + + return TRUE; + } + + void initFavoritesBarPointer(LLFavoritesBarCtrl* fb) { this->fb = fb; } + +protected: + + LLFavoriteLandmarkMenuItem(const LLMenuItemCallGL::Params& p) : LLMenuItemCallGL(p) {} + friend class LLUICtrlFactory; + +private: + LLLandmarkInfoGetter mLandmarkInfoGetter; + LLFavoritesBarCtrl* fb; +}; + +/** + * This class was introduced just for fixing the following issue: + * EXT-836 Nav bar: Favorites overflow menu passes left-mouse click through. + * We must explicitly handle drag and drop event by returning TRUE + * because otherwise LLToolDragAndDrop will initiate drag and drop operation + * with the world. + */ +class LLFavoriteLandmarkToggleableMenu : public LLToggleableMenu +{ +public: + virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) + { + *accept = ACCEPT_NO; + return TRUE; + } + +protected: + LLFavoriteLandmarkToggleableMenu(const LLToggleableMenu::Params& p): + LLToggleableMenu(p) + { + } + + friend class LLUICtrlFactory; +}; + +/** + * This class is needed to update an item being copied to the favorites folder + * with a sort field value (required to save favorites bar's tabs order). + * See method handleNewFavoriteDragAndDrop for more details on how this class is used. + */ +class LLItemCopiedCallback : public LLInventoryCallback +{ +public: + LLItemCopiedCallback(S32 sortField): mSortField(sortField) {} + + virtual void fire(const LLUUID& inv_item) + { + LLViewerInventoryItem* item = gInventory.getItem(inv_item); + + if (item) + { + item->setSortField(mSortField); + item->setComplete(TRUE); + item->updateServer(FALSE); + + gInventory.updateItem(item); + gInventory.notifyObservers(); + } + + LLView::getWindow()->setCursor(UI_CURSOR_ARROW); + } + +private: + S32 mSortField; +}; + +// 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) + { + S32 sortField1 = a->getSortField(); + S32 sortField2 = b->getSortField(); + + if (!(sortField1 < 0 && sortField2 < 0)) + { + return sortField2 > sortField1; + } + + 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); + } + } +}; + +LLFavoritesBarCtrl::Params::Params() +: image_drag_indication("image_drag_indication"), + chevron_button("chevron_button"), + label("label") +{ +} + +LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) +: LLUICtrl(p), + mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), + mPopupMenuHandle(), + mInventoryItemsPopupMenuHandle(), + mImageDragIndication(p.image_drag_indication), + mShowDragMarker(FALSE), + mLandingTab(NULL), + mLastTab(NULL), + mTabsHighlightEnabled(TRUE) + , mUpdateDropDownItems(true) +{ + // 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); + + //make chevron button + LLButton::Params chevron_button_params(p.chevron_button); + chevron_button_params.click_callback.function(boost::bind(&LLFavoritesBarCtrl::showDropDownMenu, this)); + mChevronButton = LLUICtrlFactory::create<LLButton> (chevron_button_params); + addChild(mChevronButton); + + LLTextBox::Params label_param(p.label); + mBarLabel = LLUICtrlFactory::create<LLTextBox> (label_param); + addChild(mBarLabel); +} + +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: + { + /* + * add a callback to the end drag event. + * the callback will disconnet itself immediately after execution + * this is done because LLToolDragAndDrop is a common tool so it shouldn't + * be overloaded with redundant callbacks. + */ + if (!mEndDragConnection.connected()) + { + mEndDragConnection = LLToolDragAndDrop::getInstance()->setEndDragCallback(boost::bind(&LLFavoritesBarCtrl::onEndDrag, this)); + } + + // Copy the item into the favorites folder (if it's not already there). + LLInventoryItem *item = (LLInventoryItem *)cargo_data; + + if (LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(findChildByLocalCoords(x, y))) + { + setLandingTab(dest); + } + /* + * the condition dest == NULL can be satisfied not only in the case + * of dragging to the right from the last tab of the favbar. there is a + * small gap between each tab. if the user drags something exactly there + * then mLandingTab will be set to NULL and the dragged item will be pushed + * to the end of the favorites bar. this is incorrect behavior. that's why + * we need an additional check which excludes the case described previously + * making sure that the mouse pointer is beyond the last tab. + */ + else if (mLastTab && x >= mLastTab->getRect().mRight) + { + setLandingTab(NULL); + } + + // check if we are dragging an existing item from the favorites bar + if (item && mDragItemId == item->getUUID()) + { + *accept = ACCEPT_YES_SINGLE; + + showDragMarker(TRUE); + + if (drop) + { + handleExistingFavoriteDragAndDrop(x, y); + showDragMarker(FALSE); + } + } + else + { + const LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + if (item->getParentUUID() == favorites_id) + { + llwarns << "Attemt to copy a favorite item into the same folder." << llendl; + break; + } + + *accept = ACCEPT_YES_COPY_MULTI; + + showDragMarker(TRUE); + + if (drop) + { + if (mItems.empty()) + { + setLandingTab(NULL); + } + handleNewFavoriteDragAndDrop(item, favorites_id, x, y); + showDragMarker(FALSE); + } + } + } + break; + default: + break; + } + + return TRUE; +} + +void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) +{ + LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(mLandingTab); + + // there is no need to handle if an item was dragged onto itself + if (dest && dest->getLandmarkId() == mDragItemId) + { + return; + } + + if (dest) + { + LLInventoryModel::updateItemsOrder(mItems, mDragItemId, dest->getLandmarkId()); + } + else + { + mItems.push_back(gInventory.getItem(mDragItemId)); + } + + gInventory.saveItemsOrder(mItems); + + LLToggleableMenu* menu = (LLToggleableMenu*) mPopupMenuHandle.get(); + + if (menu && menu->getVisible()) + { + menu->setVisible(FALSE); + showDropDownMenu(); + } +} + +void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, const LLUUID& favorites_id, S32 x, S32 y) +{ + LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(mLandingTab); + + // there is no need to handle if an item was dragged onto itself + if (dest && dest->getLandmarkId() == mDragItemId) + { + return; + } + + LLPointer<LLViewerInventoryItem> viewer_item = new LLViewerInventoryItem(item); + + if (dest) + { + insertBeforeItem(mItems, dest->getLandmarkId(), viewer_item); + } + else + { + mItems.push_back(viewer_item); + } + + int sortField = 0; + LLPointer<LLItemCopiedCallback> cb; + + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (LLInventoryModel::item_array_t::iterator i = mItems.begin(); i != mItems.end(); ++i) + { + LLViewerInventoryItem* currItem = *i; + + if (currItem->getUUID() == item->getUUID()) + { + cb = new LLItemCopiedCallback(++sortField); + } + else + { + currItem->setSortField(++sortField); + currItem->setComplete(TRUE); + currItem->updateServer(FALSE); + + gInventory.updateItem(currItem); + } + } + + LLToolDragAndDrop* tool_dad = LLToolDragAndDrop::getInstance(); + if (tool_dad->getSource() == LLToolDragAndDrop::SOURCE_NOTECARD) + { + viewer_item->setType(LLAssetType::AT_LANDMARK); + copy_inventory_from_notecard(tool_dad->getObjectID(), tool_dad->getSourceID(), viewer_item.get(), gInventoryCallbacks.registerCB(cb)); + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + favorites_id, + std::string(), + cb); + } + + llinfos << "Copied inventory item #" << item->getUUID() << " to favorites." << llendl; +} + +//virtual +void LLFavoritesBarCtrl::changed(U32 mask) +{ + if (mFavoriteFolderId.isNull()) + { + mFavoriteFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + + if (mFavoriteFolderId.notNull()) + { + gInventory.fetchDescendentsOf(mFavoriteFolderId); + } + } + else + { + updateButtons(); + } +} + +//virtual +void LLFavoritesBarCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLUICtrl::reshape(width, height, called_from_parent); + updateButtons(); +} + +void LLFavoritesBarCtrl::draw() +{ + LLUICtrl::draw(); + + if (mShowDragMarker) + { + S32 w = mImageDragIndication->getWidth(); + S32 h = mImageDragIndication->getHeight(); + + if (mLandingTab) + { + // mouse pointer hovers over an existing tab + LLRect rect = mLandingTab->getRect(); + mImageDragIndication->draw(rect.mLeft - w/2, rect.getHeight(), w, h); + } + else if (mLastTab) + { + // mouse pointer hovers over the favbar empty space (right to the last tab) + LLRect rect = mLastTab->getRect(); + mImageDragIndication->draw(rect.mRight, rect.getHeight(), w, h); + } + } +} + +const LLButton::Params& LLFavoritesBarCtrl::getButtonParams() +{ + static LLButton::Params button_params; + static bool params_initialized = false; + + if (!params_initialized) + { + LLXMLNodePtr button_xml_node; + if(LLUICtrlFactory::getLayeredXMLNode("favorites_bar_button.xml", button_xml_node)) + { + LLXUIParser parser; + parser.readXUI(button_xml_node, button_params, "favorites_bar_button.xml"); + } + params_initialized = true; + } + + return button_params; +} + +void LLFavoritesBarCtrl::updateButtons() +{ + mItems.clear(); + + if (!collectFavoriteItems(mItems)) + { + return; + } + + const LLButton::Params& button_params = getButtonParams(); + + if(mItems.empty()) + { + mBarLabel->setVisible(TRUE); + } + else + { + mBarLabel->setVisible(FALSE); + } + const child_list_t* childs = getChildList(); + child_list_const_iter_t child_it = childs->begin(); + int first_changed_item_index = 0; + int rightest_point = getRect().mRight - mChevronButton->getRect().getWidth(); + //lets find first changed button + while (child_it != childs->end() && first_changed_item_index < mItems.count()) + { + LLFavoriteLandmarkButton* button = dynamic_cast<LLFavoriteLandmarkButton*> (*child_it); + if (button) + { + const LLViewerInventoryItem *item = mItems[first_changed_item_index].get(); + if (item) + { + // an child's order and mItems should be same + if (button->getLandmarkId() != item->getUUID() // sort order has been changed + || button->getLabelSelected() != item->getName() // favorite's name has been changed + || button->getRect().mRight < rightest_point) // favbar's width has been changed + { + break; + } + } + first_changed_item_index++; + } + child_it++; + } + // now first_changed_item_index should contains a number of button that need to change + + if (first_changed_item_index <= mItems.count()) + { + // Rebuild the buttons only + // child_list_t is a linked list, so safe to erase from the middle if we pre-incrament the iterator + + while (child_it != childs->end()) + { + //lets remove other landmarks button and rebuild it + child_list_const_iter_t cur_it = child_it++; + LLFavoriteLandmarkButton* button = + dynamic_cast<LLFavoriteLandmarkButton*> (*cur_it); + if (button) + { + removeChild(button); + delete button; + } + } + // we have to remove ChevronButton to make sure that the last item will be LandmarkButton to get the right aligning + // keep in mind that we are cutting all buttons in space between the last visible child of favbar and ChevronButton + if (mChevronButton->getParent() == this) + { + removeChild(mChevronButton); + } + int last_right_edge = 0; + //calculate new buttons offset + if (getChildList()->size() > 0) + { + //find last visible child to get the rightest button offset + child_list_const_reverse_iter_t last_visible_it = std::find_if(childs->rbegin(), childs->rend(), + std::mem_fun(&LLView::getVisible)); + if(last_visible_it != childs->rend()) + { + last_right_edge = (*last_visible_it)->getRect().mRight; + } + } + //last_right_edge is saving coordinates + LLButton* last_new_button = NULL; + int j = first_changed_item_index; + for (; j < mItems.count(); j++) + { + last_new_button = createButton(mItems[j], button_params, last_right_edge); + if (!last_new_button) + { + break; + } + sendChildToBack(last_new_button); + last_right_edge = last_new_button->getRect().mRight; + + mLastTab = last_new_button; + } + mFirstDropDownItem = j; + // Chevron button + if (mFirstDropDownItem < mItems.count()) + { + // if updateButton had been called it means: + //or there are some new favorites, or width had been changed + // so if we need to display chevron button, we must update dropdown items too. + mUpdateDropDownItems = true; + S32 buttonHGap = button_params.rect.left; // default value + LLRect rect; + // Chevron button should stay right aligned + rect.setOriginAndSize(getRect().mRight - mChevronButton->getRect().getWidth() - buttonHGap, 0, + mChevronButton->getRect().getWidth(), + mChevronButton->getRect().getHeight()); + + addChild(mChevronButton); + mChevronButton->setRect(rect); + mChevronButton->setVisible(TRUE); + } + // Update overflow menu + LLToggleableMenu* overflow_menu = static_cast <LLToggleableMenu*> (mPopupMenuHandle.get()); + if (overflow_menu && overflow_menu->getVisible()) + { + overflow_menu->setVisible(FALSE); + if (mUpdateDropDownItems) + showDropDownMenu(); + } + } + else + { + mUpdateDropDownItems = false; + } +} + +LLButton* LLFavoritesBarCtrl::createButton(const LLPointer<LLViewerInventoryItem> item, const LLButton::Params& button_params, S32 x_offset) +{ + S32 def_button_width = button_params.rect.width; + S32 button_x_delta = button_params.rect.left; // default value + S32 curr_x = x_offset; + + /** + * WORKAROUND: + * there are some problem with displaying of fonts in buttons. + * Empty space (or ...) is displaying instead of last symbols, even though the width of the button is enough. + * Problem will gone, if we stretch out the button. For that reason I have to put additional 20 pixels. + */ + int required_width = mFont->getWidth(item->getName()) + 20; + int width = required_width > def_button_width? def_button_width : required_width; + LLFavoriteLandmarkButton* fav_btn = NULL; + + // do we have a place for next button + double buttonHGap + mChevronButton ? + if(curr_x + width + 2*button_x_delta + mChevronButton->getRect().getWidth() > getRect().mRight ) + { + return NULL; + } + LLButton::Params fav_btn_params(button_params); + fav_btn = LLUICtrlFactory::create<LLFavoriteLandmarkButton>(fav_btn_params); + if (NULL == fav_btn) + { + llwarns << "Unable to create LLFavoriteLandmarkButton widget: " << item->getName() << llendl; + return NULL; + } + + addChild(fav_btn); + + LLRect butt_rect (fav_btn->getRect()); + fav_btn->setLandmarkID(item->getUUID()); + butt_rect.setOriginAndSize(curr_x + button_x_delta, fav_btn->getRect().mBottom, width, fav_btn->getRect().getHeight()); + + fav_btn->setRect(butt_rect); + // change only left and save bottom + fav_btn->setFont(mFont); + fav_btn->setName(item->getName()); + fav_btn->setLabel(item->getName()); + fav_btn->setToolTip(item->getName()); + fav_btn->setCommitCallback(boost::bind(&LLFavoritesBarCtrl::onButtonClick, this, item->getUUID())); + fav_btn->setRightMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this, item->getUUID(), _1, _2, _3,_4 )); + + fav_btn->LLUICtrl::setMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseDown, this, item->getUUID(), _1, _2, _3, _4)); + fav_btn->LLUICtrl::setMouseUpCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseUp, this, item->getUUID(), _1, _2, _3, _4)); + + return fav_btn; +} + + +BOOL LLFavoritesBarCtrl::postBuild() +{ + // make the popup menu available + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_favorites.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if (!menu) + { + menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); + } + menu->setBackgroundColor(LLUIColorTable::instance().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()); + + if (needToSaveItemsOrder(items)) + { + S32 sortField = 0; + for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) + { + (*i)->setSortField(++sortField); + } + } + + return TRUE; +} + +void LLFavoritesBarCtrl::showDropDownMenu() +{ + if (mPopupMenuHandle.isDead()) + { + LLToggleableMenu::Params menu_p; + menu_p.name("favorites menu"); + menu_p.can_tear_off(false); + menu_p.visible(false); + menu_p.scrollable(true); + menu_p.max_scrollable_items = 10; + menu_p.preferred_width = DROP_DOWN_MENU_WIDTH; + + LLToggleableMenu* menu = LLUICtrlFactory::create<LLFavoriteLandmarkToggleableMenu>(menu_p); + mPopupMenuHandle = menu->getHandle(); + } + + LLToggleableMenu* menu = (LLToggleableMenu*)mPopupMenuHandle.get(); + + if (menu) + { + if (!menu->toggleVisibility()) + return; + + U32 max_width = llmin(DROP_DOWN_MENU_WIDTH, getRect().getWidth()); + if (mUpdateDropDownItems) + { + menu->empty(); + + U32 widest_item = 0; + + for (S32 i = mFirstDropDownItem; i < mItems.count(); i++) + { + LLViewerInventoryItem* item = mItems.get(i); + const std::string& item_name = item->getName(); + + LLFavoriteLandmarkMenuItem::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())); + LLFavoriteLandmarkMenuItem *menu_item = LLUICtrlFactory::create<LLFavoriteLandmarkMenuItem>(item_params); + menu_item->initFavoritesBarPointer(this); + menu_item->setRightMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this, item->getUUID(), _1, _2, _3, _4)); + menu_item->LLUICtrl::setMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseDown, this, item->getUUID(), _1, _2, _3, _4)); + menu_item->LLUICtrl::setMouseUpCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseUp, this, item->getUUID(), _1, _2, _3, _4)); + menu_item->setLandmarkID(item->getUUID()); + + // Check whether item name wider than menu + if (menu_item->getNominalWidth() > max_width) + { + S32 chars_total = item_name.length(); + S32 chars_fitted = 1; + menu_item->setLabel(LLStringExplicit("")); + S32 label_space = max_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) + + "..."); + } + widest_item = llmax(widest_item, menu_item->getNominalWidth()); + + menu->addChild(menu_item); + } + mUpdateDropDownItems = false; + } + + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + + menu->setButtonRect(mChevronButton->getRect(), this); + + LLMenuGL::showPopup(this, menu, getRect().getWidth() - max_width, 0); + } +} + +void LLFavoritesBarCtrl::onButtonClick(LLUUID item_id) +{ + // We only have one Inventory, gInventory. Some day this should be better abstracted. + LLInvFVBridgeAction::doAction(item_id,&gInventory); +} + +void LLFavoritesBarCtrl::onButtonRightClick( LLUUID item_id,LLView* fav_button,S32 x,S32 y,MASK mask) +{ + mSelectedItemID = item_id; + + LLMenuGL* menu = (LLMenuGL*)mInventoryItemsPopupMenuHandle.get(); + if (!menu) + { + return; + } + + // Release mouse capture so hover events go to the popup menu + // because this is happening during a mouse down. + gFocusMgr.setMouseCapture(NULL); + + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(fav_button, menu, x, y); +} + +BOOL LLFavoritesBarCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = childrenHandleRightMouseDown( x, y, mask) != NULL; + if(!handled && !gMenuHolder->hasVisibleMenu()) + { + show_navbar_context_menu(this,x,y); + handled = true; + } + + return handled; +} +void copy_slurl_to_clipboard_cb(std::string& slurl) +{ + gClipboard.copyFromString(utf8str_to_wstring(slurl)); + + LLSD args; + args["SLURL"] = slurl; + LLNotificationsUtil::add("CopySLURL", args); +} + + +bool LLFavoritesBarCtrl::enableSelected(const LLSD& userdata) +{ + std::string param = userdata.asString(); + + if (param == std::string("can_paste")) + { + return isClipboardPasteable(); + } + + return false; +} + +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") + { + onButtonClick(item->getUUID()); + } + else if (action == "about") + { + LLSD key; + key["type"] = "landmark"; + key["id"] = mSelectedItemID; + + LLSideTray::getInstance()->showPanel("panel_places", key); + } + else if (action == "copy_slurl") + { + LLVector3d posGlobal; + LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); + + if (!posGlobal.isExactlyZero()) + { + LLLandmarkActions::getSLURLfromPosGlobal(posGlobal, copy_slurl_to_clipboard_cb); + } + } + else if (action == "show_on_map") + { + LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance(); + + LLVector3d posGlobal; + LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); + + if (!posGlobal.isExactlyZero() && worldmap_instance) + { + worldmap_instance->trackLocation(posGlobal); + LLFloaterReg::showInstance("world_map", "center"); + } + } + else if (action == "cut") + { + } + else if (action == "copy") + { + LLInventoryClipboard::instance().store(mSelectedItemID); + } + else if (action == "paste") + { + pastFromClipboard(); + } + else if (action == "delete") + { + gInventory.removeItem(mSelectedItemID); + } + + // Pop-up the overflow menu again (it gets hidden whenever the user clicks a context menu item). + // See EXT-4217 and STORM-207. + LLToggleableMenu* menu = (LLToggleableMenu*) mPopupMenuHandle.get(); + if (menu && !menu->getVisible()) + { + showDropDownMenu(); + } +} + +BOOL LLFavoritesBarCtrl::isClipboardPasteable() const +{ + if (!LLInventoryClipboard::instance().hasContents()) + { + return FALSE; + } + + LLDynamicArray<LLUUID> objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + for(S32 i = 0; i < count; i++) + { + const LLUUID &item_id = objects.get(i); + + // Can't paste folders + const LLInventoryCategory *cat = gInventory.getCategory(item_id); + if (cat) + { + return FALSE; + } + + const LLInventoryItem *item = gInventory.getItem(item_id); + if (item && LLAssetType::AT_LANDMARK != item->getType()) + { + return FALSE; + } + } + return TRUE; +} + +void LLFavoritesBarCtrl::pastFromClipboard() const +{ + LLInventoryModel* model = &gInventory; + if(model && isClipboardPasteable()) + { + LLInventoryItem* item = NULL; + LLDynamicArray<LLUUID> objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + LLUUID parent_id(mFavoriteFolderId); + for(S32 i = 0; i < count; i++) + { + item = model->getItem(objects.get(i)); + if (item) + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + parent_id, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + } + } + } +} + +void LLFavoritesBarCtrl::onButtonMouseDown(LLUUID id, LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + // EXT-6997 (Fav bar: Pop-up menu for LM in overflow dropdown is kept after LM was dragged away) + // mInventoryItemsPopupMenuHandle.get() - is a pop-up menu (of items) in already opened dropdown menu. + // We have to check and set visibility of pop-up menu in such a way instead of using + // LLMenuHolderGL::hideMenus() because it will close both menus(dropdown and pop-up), but + // we need to close only pop-up menu while dropdown one should be still opened. + LLMenuGL* menu = (LLMenuGL*)mInventoryItemsPopupMenuHandle.get(); + if(menu && menu->getVisible()) + { + menu->setVisible(FALSE); + } + + mDragItemId = id; + mStartDrag = TRUE; + + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + + LLToolDragAndDrop::getInstance()->setDragStart(screenX, screenY); +} + +void LLFavoritesBarCtrl::onButtonMouseUp(LLUUID id, LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + mStartDrag = FALSE; + mDragItemId = LLUUID::null; +} + +void LLFavoritesBarCtrl::onEndDrag() +{ + mEndDragConnection.disconnect(); + + showDragMarker(FALSE); + mDragItemId = LLUUID::null; + LLView::getWindow()->setCursor(UI_CURSOR_ARROW); +} + +BOOL LLFavoritesBarCtrl::handleHover(S32 x, S32 y, MASK mask) +{ + if (mDragItemId != LLUUID::null && mStartDrag) + { + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + + if(LLToolDragAndDrop::getInstance()->isOverThreshold(screenX, screenY)) + { + LLToolDragAndDrop::getInstance()->beginDrag( + DAD_LANDMARK, mDragItemId, + LLToolDragAndDrop::SOURCE_LIBRARY); + + mStartDrag = FALSE; + + return LLToolDragAndDrop::getInstance()->handleHover(x, y, mask); + } + } + + return TRUE; +} + +LLUICtrl* LLFavoritesBarCtrl::findChildByLocalCoords(S32 x, S32 y) +{ + LLUICtrl* ctrl = 0; + S32 screenX, screenY; + const child_list_t* list = getChildList(); + + localPointToScreen(x, y, &screenX, &screenY); + + // look for a child which contains the point (screenX, screenY) in it's rectangle + for (child_list_const_iter_t i = list->begin(); i != list->end(); ++i) + { + LLRect rect; + localRectToScreen((*i)->getRect(), &rect); + + if (rect.pointInRect(screenX, screenY)) + { + ctrl = dynamic_cast<LLUICtrl*>(*i); + break; + } + } + + return ctrl; +} + +BOOL LLFavoritesBarCtrl::needToSaveItemsOrder(const LLInventoryModel::item_array_t& items) +{ + BOOL result = FALSE; + + // if there is an item without sort order field set, we need to save items order + for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) + { + if ((*i)->getSortField() < 0) + { + result = TRUE; + break; + } + } + + return result; +} + +LLInventoryModel::item_array_t::iterator LLFavoritesBarCtrl::findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id) +{ + LLInventoryModel::item_array_t::iterator result = items.end(); + + for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) + { + if ((*i)->getUUID() == id) + { + result = i; + break; + } + } + + return result; +} + +void LLFavoritesBarCtrl::insertBeforeItem(LLInventoryModel::item_array_t& items, const LLUUID& beforeItemId, LLViewerInventoryItem* insertedItem) +{ + LLViewerInventoryItem* beforeItem = gInventory.getItem(beforeItemId); + llassert(beforeItem); + if (beforeItem) + { + items.insert(findItemByUUID(items, beforeItem->getUUID()), insertedItem); + } +} + +// EOF |
