diff options
Diffstat (limited to 'indra/newview/llfavoritesbar.cpp')
-rw-r--r-- | indra/newview/llfavoritesbar.cpp | 1345 |
1 files changed, 1026 insertions, 319 deletions
diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 7719af2bd7..5796e67618 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -2,123 +2,423 @@ * @file llfavoritesbar.cpp * @brief LLFavoritesBarCtrl class implementation * - * $LicenseInfo:firstyear=2009&license=viewergpl$ - * - * Copyright (c) 2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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 "llbutton.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 "llinventorymodel.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 LLDefaultWidgetRegistry::Register<LLFavoritesBarCtrl> r("favorites_bar"); +static LLDefaultChildRegistry::Register<LLFavoritesBarCtrl> r("favorites_bar"); -// updateButtons's helper -struct LLFavoritesSort +const S32 DROP_DOWN_MENU_WIDTH = 250; + +/** + * Helper for LLFavoriteLandmarkButton and LLFavoriteLandmarkMenuItem. + * Performing requests for SLURL for given Landmark ID + */ +class LLLandmarkInfoGetter { - // Sorting by creation date and name - // TODO - made it customizible using gSavedSettings - bool operator()(const LLViewerInventoryItem* const& a, const LLViewerInventoryItem* const& b) +public: + LLLandmarkInfoGetter() + : mLandmarkID(LLUUID::null), + mName("(Loading...)"), + mPosX(0), + mPosY(0), + mPosZ(0), + mLoaded(false) { - time_t first_create = a->getCreationDate(); - time_t second_create = b->getCreationDate(); - if (first_create == second_create) + 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)) { - return (LLStringUtil::compareDict(a->getName(), b->getName()) < 0); + LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(g_pos, + boost::bind(&LLLandmarkInfoGetter::landmarkNameCallback, static_cast<LLHandle<LLLandmarkInfoGetter> >(mHandle), _1, _2, _3, _4)); } - else + } + + static void landmarkNameCallback(LLHandle<LLLandmarkInfoGetter> handle, const std::string& name, S32 x, S32 y, S32 z) + { + LLLandmarkInfoGetter* getter = handle.get(); + if (getter) { - return (first_create > second_create); + 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; }; -class LLVisibilityTrackingMenuGL : public LLMenuGL +/** + * 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()) + { + LLToolTip::Params params; + std::string extra_message = llformat("%s (%d, %d, %d)", region_name.c_str(), + mLandmarkInfoGetter.getPosX(), mLandmarkInfoGetter.getPosY(), mLandmarkInfoGetter.getPosZ()); + + params.message = llformat("%s\n%s", getLabelSelected().c_str(), extra_message.c_str()); + + LLRect rect = calcScreenRect(); + LLFontGL* standart_font = LLFontGL::getFontSansSerif(); + if(standart_font) + { + S32 w = llmax((S32)(standart_font->getWidthF32(getLabelSelected())+0.5),(S32)(standart_font->getWidthF32(extra_message)+0.5)); + rect.mRight = rect.mLeft + w; + params.max_width = w; + } + + params.sticky_rect = rect; + + 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: - LLVisibilityTrackingMenuGL(const LLMenuGL::Params&); + LLFavoriteLandmarkButton(const LLButton::Params& p) : LLButton(p) {} friend class LLUICtrlFactory; -public: - virtual void onVisibilityChange (BOOL curVisibilityIn); - void setChevronRect(const LLRect& rect) { mChevronRect = rect; } - bool getClosedByChevronClick() { return mClosedByChevronClick; } - void resetClosedByChevronClick() { mClosedByChevronClick = false; } +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: - bool mClosedByChevronClick; - LLRect mChevronRect; + + LLFavoriteLandmarkMenuItem(const LLMenuItemCallGL::Params& p) : LLMenuItemCallGL(p) {} + friend class LLUICtrlFactory; + +private: + LLLandmarkInfoGetter mLandmarkInfoGetter; + LLFavoritesBarCtrl* fb; }; -LLVisibilityTrackingMenuGL::LLVisibilityTrackingMenuGL(const LLMenuGL::Params& p) -: LLMenuGL(p), - mClosedByChevronClick(false) +/** + * 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; + } -//virtual -void LLVisibilityTrackingMenuGL::onVisibilityChange (BOOL curVisibilityIn) + void setVisible(BOOL b) + { + // Overflow menu shouldn't hide when it still has focus. See EXT-4217. + if (!b && hasFocus()) + return; + LLToggleableMenu::setVisible(b); + setFocus(b); + } + + void onFocusLost() + { + setVisible(FALSE); + } + +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 { - S32 x,y; - LLUI::getCursorPositionLocal(LLUI::getRootView(), &x, &y); +public: + LLItemCopiedCallback(S32 sortField): mSortField(sortField) {} - if (!curVisibilityIn && mChevronRect.pointInRect(x, y)) + virtual void fire(const LLUUID& inv_item) { - mClosedByChevronClick = true; + 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() + 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)); + 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() @@ -142,30 +442,74 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, 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; - LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE); - if (item->getParentUUID() == favorites_id) + LLInventoryItem *item = (LLInventoryItem *)cargo_data; + + if (LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(findChildByLocalCoords(x, y))) { - llwarns << "Attemt to copy a favorite item into the same folder." << llendl; - break; + 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); } - *accept = ACCEPT_YES_COPY_SINGLE; + // check if we are dragging an existing item from the favorites bar + if (item && mDragItemId == item->getUUID()) + { + *accept = ACCEPT_YES_SINGLE; - if (drop) + showDragMarker(TRUE); + + if (drop) + { + handleExistingFavoriteDragAndDrop(x, y); + showDragMarker(FALSE); + } + } + else { - 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; + 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: @@ -175,12 +519,105 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, 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(LLAssetType::AT_FAVORITE); + mFavoriteFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); if (mFavoriteFolderId.notNull()) { @@ -189,187 +626,247 @@ void LLFavoritesBarCtrl::changed(U32 mask) } else { - updateButtons(getRect().getWidth()); + updateButtons(); } } //virtual void LLFavoritesBarCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) { - updateButtons(width); - LLUICtrl::reshape(width, height, called_from_parent); + updateButtons(); } -void LLFavoritesBarCtrl::updateButtons(U32 bar_width) +void LLFavoritesBarCtrl::draw() { - LLInventoryModel::item_array_t items; + LLUICtrl::draw(); - if (!collectFavoriteItems(items)) + if (mShowDragMarker) { - 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 w = mImageDragIndication->getWidth(); + S32 h = mImageDragIndication->getHeight(); - S32 count = items.count(); + 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 S32 chevron_button_width = mFont->getWidth(">>") + buttonHPad * 2; +LLXMLNodePtr LLFavoritesBarCtrl::getButtonXMLNode() +{ + LLXMLNodePtr buttonXMLNode = NULL; + bool success = LLUICtrlFactory::getLayeredXMLNode("favorites_bar_button.xml", buttonXMLNode); + if (!success) + { + llwarns << "Failed to create Favorites Bar button from favorites_bar_button.xml" << llendl; + buttonXMLNode = NULL; + } + return buttonXMLNode; +} - S32 buttons_space = bar_width - curr_x; +void LLFavoritesBarCtrl::updateButtons() +{ + mItems.clear(); - S32 first_drop_down_item = count; + if (!collectFavoriteItems(mItems)) + { + return; + } - // Calculating, how much buttons can fit in the bar - S32 buttons_width = 0; - for (S32 i = 0; i < count; ++i) + static LLXMLNodePtr buttonXMLNode = getButtonXMLNode(); + if (buttonXMLNode.isNull()) + { + return; + } + 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()) { - buttons_width += mFont->getWidth(items.get(i)->getName()) + buttonHPad * 2 + buttonHGap; - if (buttons_width > buttons_space) + LLFavoriteLandmarkButton* button = dynamic_cast<LLFavoriteLandmarkButton*> (*child_it); + if (button) { - // 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) + const LLViewerInventoryItem *item = mItems[first_changed_item_index].get(); + if (item) { - buttons_width -= mFont->getWidth(items.get(i)->getName()) + buttonHPad * 2 + buttonHGap; - i--; + // 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_drop_down_item = i + 1; // First item behind visible items - - break; + first_changed_item_index++; } + child_it++; } + // now first_changed_item_index should contains a number of button that need to change - // 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)) + if (first_changed_item_index <= mItems.count()) { - S32 i; - for (i = 0; i < mFirstDropDownItem; ++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 + + while (child_it != childs->end()) { - if (mItemNamesCache.get(i) != items.get(i)->getName()) + //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) { - break; + removeChild(button); + delete button; } } - if (i == mFirstDropDownItem) + // 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) { - // Chevron button should stay right aligned - LLView *chevron_button = getChildView(std::string(">>"), FALSE, FALSE); - if (chevron_button) + 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()) { - 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()); + last_right_edge = (*last_visible_it)->getRect().mRight; } - return; } - } + //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], buttonXMLNode, last_right_edge); + if (!last_new_button) + { + break; + } + sendChildToBack(last_new_button); + last_right_edge = last_new_button->getRect().mRight; - mFirstDropDownItem = first_drop_down_item; + 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 = 2; // default value + buttonXMLNode->getAttributeS32("left", buttonHGap); + LLRect rect; + // Chevron button should stay right aligned + rect.setOriginAndSize(getRect().mRight - mChevronButton->getRect().getWidth() - buttonHGap, 0, + mChevronButton->getRect().getWidth(), + mChevronButton->getRect().getHeight()); - mItemNamesCache.clear(); - for (S32 i = 0; i < mFirstDropDownItem; i++) + 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->setFocus(FALSE); + overflow_menu->setVisible(FALSE); + if (mUpdateDropDownItems) + showDropDownMenu(); + } + } + else { - mItemNamesCache.put(items.get(i)->getName()); + mUpdateDropDownItems = false; } +} - // 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++) +LLButton* LLFavoritesBarCtrl::createButton(const LLPointer<LLViewerInventoryItem> item, LLXMLNodePtr &buttonXMLNode, S32 x_offset) +{ + S32 def_button_width = 120; + buttonXMLNode->getAttributeS32("width", def_button_width); + S32 button_x_delta = 2; // default value + buttonXMLNode->getAttributeS32("left", button_x_delta); + 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 ) { - - 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; + return NULL; } - - // Chevron button - if (mFirstDropDownItem != count) + fav_btn = LLUICtrlFactory::defaultBuilder<LLFavoriteLandmarkButton>(buttonXMLNode, this, NULL); + if (NULL == fav_btn) { - 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()); + llwarns << "Unable to create LLFavoriteLandmarkButton widget: " << item->getName() << llendl; + return NULL; } + + 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); + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_favorites.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if (!menu) { - menu = LLUICtrlFactory::createDummyWidget<LLMenuGL>("inventory_menu"); + menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); } - menu->setBackgroundColor(gSavedSkinSettings.getColor("MenuPopupBgColor")); + menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); mInventoryItemsPopupMenuHandle = menu->getHandle(); return TRUE; @@ -387,6 +884,15 @@ BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &it 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; } @@ -394,127 +900,83 @@ void LLFavoritesBarCtrl::showDropDownMenu() { if (mPopupMenuHandle.isDead()) { - LLMenuGL::Params menu_p; + 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; - LLVisibilityTrackingMenuGL* menu = LLUICtrlFactory::create<LLVisibilityTrackingMenuGL>(menu_p); - + LLToggleableMenu* menu = LLUICtrlFactory::create<LLFavoriteLandmarkToggleableMenu>(menu_p); mPopupMenuHandle = menu->getHandle(); } - LLVisibilityTrackingMenuGL* menu = (LLVisibilityTrackingMenuGL*)mPopupMenuHandle.get(); + LLToggleableMenu* menu = (LLToggleableMenu*)mPopupMenuHandle.get(); - if(menu) + if (menu) { - if (menu->getClosedByChevronClick()) - { - menu->resetClosedByChevronClick(); + // Release focus to allow changing of visibility. + menu->setFocus(FALSE); + if (!menu->toggleVisibility()) return; - } - - if (menu->getVisible()) - { - menu->setVisible(FALSE); - menu->resetClosedByChevronClick(); - return; - } - LLInventoryModel::item_array_t items; - - if (!collectFavoriteItems(items)) + U32 max_width = llmin(DROP_DOWN_MENU_WIDTH, getRect().getWidth()); + if (mUpdateDropDownItems) { - return; - } + menu->empty(); - S32 count = items.count(); + U32 widest_item = 0; - // Check it there are changed items, since last call - if (mItemNamesCache.size() == count) - { - S32 i; - for (i = mFirstDropDownItem; i < count; i++) + for (S32 i = mFirstDropDownItem; i < mItems.count(); i++) { - if (mItemNamesCache.get(i) != items.get(i)->getName()) + 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) { - break; + 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) + + "..."); } - } - - // Check passed, just show the menu - if (i == count) - { - menu->buildDrawLabels(); - menu->updateParent(LLMenuGL::sMenuContainer); + widest_item = llmax(widest_item, menu_item->getNominalWidth()); - menu->setChevronRect(mChevronRect); - - LLMenuGL::showPopup(this, menu, getRect().getWidth() - menu->getRect().getWidth(), 0); - return; + menu->addChild(menu_item); } + mUpdateDropDownItems = false; } - // 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); + menu->setButtonRect(mChevronButton->getRect(), this); LLMenuGL::showPopup(this, menu, getRect().getWidth() - max_width, 0); } @@ -522,18 +984,11 @@ void LLFavoritesBarCtrl::showDropDownMenu() 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) +void LLFavoritesBarCtrl::onButtonRightClick( LLUUID item_id,LLView* fav_button,S32 x,S32 y,MASK mask) { mSelectedItemID = item_id; @@ -543,11 +998,45 @@ void LLFavoritesBarCtrl::onButtonRightClick(LLUUID item_id) 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); +} - S32 x,y; - LLUI::getCursorPositionLocal(this, &x, &y); - LLMenuGL::showPopup(this, 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) @@ -561,19 +1050,237 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) if (action == "open") { - teleport_via_landmark(item->getAssetUUID()); + onButtonClick(item->getUUID()); } else if (action == "about") { - LLFloaterReg::showInstance("preview_landmark", LLSD(mSelectedItemID), TAKE_FOCUS_YES); + 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 == "rename") + else if (action == "copy") { - // Would need to re-implement this: - // folder->startRenamingSelectedItem(); + LLInventoryClipboard::instance().store(mSelectedItemID); + } + else if (action == "paste") + { + pastFromClipboard(); } else if (action == "delete") { gInventory.removeItem(mSelectedItemID); } } + +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 |