diff options
72 files changed, 2594 insertions, 817 deletions
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index e9df361472..bba5464b00 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -44,6 +44,9 @@ const LLSD UNSELECTED_EVENT = LLSD().insert("selected", false); static const std::string COMMENT_TEXTBOX = "comment_text"; +//forward declaration +bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2); + LLFlatListView::Params::Params() : item_pad("item_pad"), allow_select("allow_select"), @@ -333,6 +336,17 @@ void LLFlatListView::sort() rearrangeItems(); } +bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value) +{ + if (old_value.isUndefined() || new_value.isUndefined()) return false; + if (llsds_are_equal(old_value, new_value)) return false; + + item_pair_t* item_pair = getItemPair(old_value); + if (!item_pair) return false; + + item_pair->second = new_value; + return true; +} ////////////////////////////////////////////////////////////////////////// // PROTECTED STUFF diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index af5a9cfa9b..888258efdc 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -273,6 +273,8 @@ public: void setComparator(const ItemComparator* comp) { mItemComparator = comp; } void sort(); + bool updateValue(const LLSD& old_value, const LLSD& new_value); + protected: /** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */ diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 47ca4899df..44c5f8c0de 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1262,6 +1262,12 @@ BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons inde return FALSE; } +BOOL LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + LLPanel::handleScrollWheel(x,y,clicks); + return TRUE;//always +} + // virtual BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask) { diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 7a6c3f6863..123de12398 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -206,6 +206,9 @@ public: virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); + + virtual BOOL handleScrollWheel(S32 x, S32 y, S32 mask); + virtual void draw(); virtual void onOpen(const LLSD& key) {} diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index a75a0c6979..0d674528dc 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -409,8 +409,13 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) BOOL LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks) { - changeLine( clicks * mStepSize, TRUE ); - return TRUE; + S32 pos = llclamp(mDocPos + clicks * mStepSize, 0, getDocPosMax()); + if (pos != mDocPos) + { + setDocPos(pos, TRUE); + return TRUE; + } + return FALSE; } BOOL LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 5597d494fe..d8606c6889 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -235,6 +235,8 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask) BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) { + if(LLUICtrl::handleScrollWheel(x,y,clicks)) + return TRUE; for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) { // Note: tries vertical and then horizontal @@ -246,9 +248,7 @@ BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) return TRUE; } } - - // Eat scroll wheel event (to avoid scrolling nested containers?) - return TRUE; + return FALSE; } BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index ff6fe5a7ea..31f12fe312 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -672,6 +672,26 @@ LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask) } +LLView* LLView::childFromPoint(S32 x, S32 y) +{ + if (!getVisible() ) + return false; + for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + { + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() ) + { + continue; + } + return viewp; + + } + return 0; +} + BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -901,12 +921,6 @@ LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks) handled_view = viewp; break; } - - if (viewp->blockMouseEvent(local_x, local_y)) - { - handled_view = viewp; - break; - } } } return handled_view; diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 5e35068733..73146b2c1f 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -445,6 +445,8 @@ public: /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; + virtual LLView* childFromPoint(S32 x, S32 y); + // view-specific handlers virtual void onMouseEnter(S32 x, S32 y, MASK mask); virtual void onMouseLeave(S32 x, S32 y, MASK mask); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c630a56c8d..51840bf696 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -94,6 +94,7 @@ set(viewer_SOURCE_FILES llchannelmanager.cpp llchatbar.cpp llchatitemscontainerctrl.cpp + llchathistory.cpp llchatmsgbox.cpp llchiclet.cpp llclassifiedinfo.cpp @@ -246,6 +247,7 @@ set(viewer_SOURCE_FILES llinventoryclipboard.cpp llinventoryfilter.cpp llinventorymodel.cpp + llinventorysubtreepanel.cpp lljoystickbutton.cpp lllandmarkactions.cpp lllandmarklist.cpp @@ -344,6 +346,7 @@ set(viewer_SOURCE_FILES llpanelvolume.cpp llparcelselection.cpp llpatchvertexarray.cpp + llplacesinventorybridge.cpp llpolymesh.cpp llpolymorph.cpp llpreviewanim.cpp @@ -564,6 +567,7 @@ set(viewer_HEADER_FILES llchannelmanager.h llchatbar.h llchatitemscontainerctrl.h + llchathistory.h llchatmsgbox.h llchiclet.h llclassifiedinfo.h @@ -716,6 +720,7 @@ set(viewer_HEADER_FILES llinventoryclipboard.h llinventoryfilter.h llinventorymodel.h + llinventorysubtreepanel.h lljoystickbutton.h lllandmarkactions.h lllandmarklist.h @@ -811,6 +816,7 @@ set(viewer_HEADER_FILES llpanelvolume.h llparcelselection.h llpatchvertexarray.h + llplacesinventorybridge.h llpolymesh.h llpolymorph.h llpreview.h diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp index 3e411583ac..7ae1b5cd4a 100644 --- a/indra/newview/llavatariconctrl.cpp +++ b/indra/newview/llavatariconctrl.cpp @@ -181,16 +181,6 @@ LLAvatarIconCtrl::LLAvatarIconCtrl(const LLAvatarIconCtrl::Params& p) rect.setOriginAndSize(left, bottom, llavatariconctrl_symbol_size, llavatariconctrl_symbol_size); - LLIconCtrl::Params icparams; - icparams.name ("Status Symbol"); - icparams.follows.flags (FOLLOWS_RIGHT | FOLLOWS_BOTTOM); - icparams.rect (rect); - mStatusSymbol = LLUICtrlFactory::create<LLIconCtrl> (icparams); - mStatusSymbol->setValue("circle.tga"); - mStatusSymbol->setColor(LLColor4::grey); - - addChild(mStatusSymbol); - if (p.avatar_id.isProvided()) { LLSD value(p.avatar_id); @@ -239,16 +229,13 @@ void LLAvatarIconCtrl::setValue(const LLSD& value) mAvatarId = value.asUUID(); // *BUG: This will return stale icons if a user changes their - // profile picture. Also, the online/offline tooltips will be - // out of date. However, otherwise we send too many upstream + // profile picture. However, otherwise we send too many upstream // AvatarPropertiesRequest messages. - // - // *TODO: Implement a timeout on the icon cache, perhaps a day?, - // and make the cache update if a user views the full-profile for - // an avatar. + + // to get fresh avatar icon use + // LLAvatarIconIDCache::getInstance()->remove(avatar_id); // Check if cache already contains image_id for that avatar - if (!updateFromCache()) { app->addObserver(mAvatarId, this); @@ -282,36 +269,6 @@ bool LLAvatarIconCtrl::updateFromCache() LLIconCtrl::setValue("default_profile_picture.j2c"); } - // Can only see online status of friends - if (LLAvatarTracker::instance().isBuddy(mAvatarId)) - { - if (LLAvatarTracker::instance().isBuddyOnline(mAvatarId)) - { - // Update color of status symbol and tool tip - mStatusSymbol->setColor(LLColor4::green); - if (mDrawTooltip) - { - setToolTip((LLStringExplicit)"Online"); - } - } - else - { - mStatusSymbol->setColor(LLColor4::grey); - if (mDrawTooltip) - { - setToolTip((LLStringExplicit)"Offline"); - } - } - } - else - { - // Not a buddy, no information - mStatusSymbol->setColor(LLColor4::grey); - if (mDrawTooltip) - { - setToolTip((LLStringExplicit)""); - } - } return true; } @@ -370,6 +327,11 @@ void LLAvatarIconCtrl::nameUpdatedCallback( { mFirstName = first; mLastName = last; + + if (mDrawTooltip) + { + setToolTip(mFirstName + " " + mLastName); + } } } diff --git a/indra/newview/llavatariconctrl.h b/indra/newview/llavatariconctrl.h index 77390eb233..426fcec514 100644 --- a/indra/newview/llavatariconctrl.h +++ b/indra/newview/llavatariconctrl.h @@ -104,7 +104,6 @@ public: const std::string& getLastName() const { return mLastName; } protected: - LLIconCtrl* mStatusSymbol; LLUUID mAvatarId; std::string mFirstName; std::string mLastName; diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 36f9780ad0..ef48420490 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -202,6 +202,7 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is item->showSpeakingIndicator(true); item->setName(name); item->setAvatarId(id); + item->setOnline(is_bold); item->setContextMenu(mContextMenu); item->childSetVisible("info_btn", false); diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 4179d7a58d..90408beca0 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -52,11 +52,17 @@ LLAvatarListItem::LLAvatarListItem() mInfoBtn(NULL), mProfileBtn(NULL), mContextMenu(NULL), - mAvatarId(LLUUID::null) + mOnlineStatus(E_UNKNOWN) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_list_item.xml"); } +LLAvatarListItem::~LLAvatarListItem() +{ + if (mAvatarId.notNull()) + LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarId, this); +} + BOOL LLAvatarListItem::postBuild() { mAvatarIcon = getChild<LLAvatarIconCtrl>("avatar_icon"); @@ -138,6 +144,36 @@ void LLAvatarListItem::setStatus(const std::string& status) mStatus->setValue(status); } +// virtual, called by LLAvatarTracker +void LLAvatarListItem::changed(U32 mask) +{ + // no need to check mAvatarId for null in this case + setOnline(LLAvatarTracker::instance().isBuddyOnline(mAvatarId)); +} + +void LLAvatarListItem::setOnline(bool online) +{ + // *FIX: setName() overrides font style set by setOnline(). Not an issue ATM. + // *TODO: Make the colors configurable via XUI. + + if (mOnlineStatus != E_UNKNOWN && (bool) mOnlineStatus == online) + return; + + mOnlineStatus = (EOnlineStatus) online; + + // Change avatar name font style depending on the new online status. + LLStyle::Params style_params; + style_params.color = online ? LLColor4::white : LLColor4::grey; + + // Rebuild the text to change its style. + std::string text = mAvatarName->getText(); + mAvatarName->setText(LLStringUtil::null); + mAvatarName->appendText(text, false, style_params); + + // Make the icon fade if the avatar goes offline. + mAvatarIcon->setColor(online ? LLColor4::white : LLColor4::smoke); +} + void LLAvatarListItem::setName(const std::string& name) { mAvatarName->setValue(name); @@ -146,10 +182,17 @@ void LLAvatarListItem::setName(const std::string& name) void LLAvatarListItem::setAvatarId(const LLUUID& id) { + if (mAvatarId.notNull()) + LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarId, this); + mAvatarId = id; mAvatarIcon->setValue(id); mSpeakingIndicator->setSpeakerId(id); + // We'll be notified on avatar online status changes + if (mAvatarId.notNull()) + LLAvatarTracker::instance().addParticularFriendObserver(mAvatarId, this); + // Set avatar name. gCacheName->get(id, FALSE, boost::bind(&LLAvatarListItem::onNameCache, this, _2, _3)); } diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index 871441b2d3..2330db5249 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -38,9 +38,11 @@ #include "llbutton.h" #include "lltextbox.h" +#include "llcallingcard.h" // for LLFriendObserver + class LLAvatarIconCtrl; -class LLAvatarListItem : public LLPanel +class LLAvatarListItem : public LLPanel, public LLFriendObserver { public: class ContextMenu @@ -50,15 +52,17 @@ public: }; LLAvatarListItem(); - virtual ~LLAvatarListItem() {}; + virtual ~LLAvatarListItem(); virtual BOOL postBuild(); virtual void onMouseLeave(S32 x, S32 y, MASK mask); virtual void onMouseEnter(S32 x, S32 y, MASK mask); virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual void setValue(const LLSD& value); + virtual void changed(U32 mask); // from LLFriendObserver void setStatus(const std::string& status); + void setOnline(bool online); void setName(const std::string& name); void setAvatarId(const LLUUID& id); @@ -75,6 +79,13 @@ public: void setContextMenu(ContextMenu* menu) { mContextMenu = menu; } private: + + typedef enum e_online_status { + E_OFFLINE, + E_ONLINE, + E_UNKNOWN, + } EOnlineStatus; + void onNameCache(const std::string& first_name, const std::string& last_name); LLAvatarIconCtrl*mAvatarIcon; @@ -87,6 +98,7 @@ private: ContextMenu* mContextMenu; LLUUID mAvatarId; + EOnlineStatus mOnlineStatus; }; #endif //LL_LLAVATARLISTITEM_H diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 0ff8ca7d26..8987f14e97 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -185,6 +185,18 @@ void LLBottomTray::sessionRemoved(const LLUUID& session_id) } } +void LLBottomTray::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) +{ + //this is only needed in case of outgoing ad-hoc/group chat sessions + LLChicletPanel* chiclet_panel = getChicletPanel(); + if (chiclet_panel) + { + //it should be ad-hoc im chiclet or group im chiclet + LLChiclet* chiclet = chiclet_panel->findChiclet<LLChiclet>(old_session_id); + if (chiclet) chiclet->setSessionId(new_session_id); + } +} + //virtual void LLBottomTray::onFocusLost() { diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 4724c5ecef..cc35e63524 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -67,6 +67,7 @@ public: // LLIMSessionObserver observe triggers virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); virtual void sessionRemoved(const LLUUID& session_id); + void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); virtual void onFocusLost(); virtual void setVisible(BOOL visible); diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 2b7bd83ca3..359bb23f05 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -260,7 +260,7 @@ S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds) ++new_buddy_count; mBuddyInfo[agent_id] = (*itr).second; gCacheName->getName(agent_id, first, last); - mModifyMask |= LLFriendObserver::ADD; + addChangedMask(LLFriendObserver::ADD, agent_id); lldebugs << "Added buddy " << agent_id << ", " << (mBuddyInfo[agent_id]->isOnline() ? "Online" : "Offline") << ", TO: " << mBuddyInfo[agent_id]->getRightsGrantedTo() @@ -333,7 +333,7 @@ void LLAvatarTracker::setBuddyOnline(const LLUUID& id, bool is_online) if(info) { info->online(is_online); - mModifyMask |= LLFriendObserver::ONLINE; + addChangedMask(LLFriendObserver::ONLINE, id); lldebugs << "Set buddy " << id << (is_online ? " Online" : " Offline") << llendl; } else @@ -488,10 +488,52 @@ void LLAvatarTracker::notifyObservers() { (*it)->changed(mModifyMask); } + + for (changed_buddy_t::iterator it = mChangedBuddyIDs.begin(); it != mChangedBuddyIDs.end(); it++) + { + notifyParticularFriendObservers(*it); + } + mModifyMask = LLFriendObserver::NONE; mChangedBuddyIDs.clear(); } +void LLAvatarTracker::addParticularFriendObserver(const LLUUID& buddy_id, LLFriendObserver* observer) +{ + if (buddy_id.notNull() && observer) + mParticularFriendObserverMap[buddy_id].insert(observer); +} + +void LLAvatarTracker::removeParticularFriendObserver(const LLUUID& buddy_id, LLFriendObserver* observer) +{ + if (buddy_id.isNull() || !observer) + return; + + observer_map_t::iterator obs_it = mParticularFriendObserverMap.find(buddy_id); + if(obs_it == mParticularFriendObserverMap.end()) + return; + + obs_it->second.erase(observer); + + // purge empty sets from the map + if (obs_it->second.size() == 0) + mParticularFriendObserverMap.erase(obs_it); +} + +void LLAvatarTracker::notifyParticularFriendObservers(const LLUUID& buddy_id) +{ + observer_map_t::iterator obs_it = mParticularFriendObserverMap.find(buddy_id); + if(obs_it == mParticularFriendObserverMap.end()) + return; + + // Notify observers interested in buddy_id. + observer_set_t& obs = obs_it->second; + for (observer_set_t::iterator ob_it = obs.begin(); ob_it != obs.end(); ob_it++) + { + (*ob_it)->changed(mModifyMask); + } +} + // store flag for change // and id of object change applies to void LLAvatarTracker::addChangedMask(U32 mask, const LLUUID& referent) @@ -610,8 +652,8 @@ void LLAvatarTracker::processChange(LLMessageSystem* msg) } } } - mModifyMask |= LLFriendObserver::POWERS; + addChangedMask(LLFriendObserver::POWERS, agent_id); notifyObservers(); } diff --git a/indra/newview/llcallingcard.h b/indra/newview/llcallingcard.h index 228239b5ba..bd58b2fbe6 100644 --- a/indra/newview/llcallingcard.h +++ b/indra/newview/llcallingcard.h @@ -150,6 +150,12 @@ public: void removeObserver(LLFriendObserver* observer); void notifyObservers(); + // Observers interested in updates of a particular avatar. + // On destruction these observers are NOT deleted. + void addParticularFriendObserver(const LLUUID& buddy_id, LLFriendObserver* observer); + void removeParticularFriendObserver(const LLUUID& buddy_id, LLFriendObserver* observer); + void notifyParticularFriendObservers(const LLUUID& buddy_id); + /** * Stores flag for change and id of object change applies to * @@ -199,6 +205,10 @@ protected: typedef std::vector<LLFriendObserver*> observer_list_t; observer_list_t mObservers; + typedef std::set<LLFriendObserver*> observer_set_t; + typedef std::map<LLUUID, observer_set_t> observer_map_t; + observer_map_t mParticularFriendObserverMap; + private: // do not implement LLAvatarTracker(const LLAvatarTracker&); diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp new file mode 100644 index 0000000000..80f3867a80 --- /dev/null +++ b/indra/newview/llchathistory.cpp @@ -0,0 +1,126 @@ +/** + * @file llchathistory.cpp + * @brief LLTextEditor base class + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-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 "llchathistory.h" +#include "llpanel.h" +#include "lltextbox.h" +#include "lluictrlfactory.h" +#include "llscrollcontainer.h" +#include "llavatariconctrl.h" + +static LLDefaultChildRegistry::Register<LLChatHistory> r("chat_history"); +static const std::string MESSAGE_USERNAME_DATE_SEPARATOR(" ----- "); + +LLChatHistory::LLChatHistory(const LLChatHistory::Params& p) +: LLTextEditor(p), +mMessageHeaderFilename(p.message_header), +mMessageSeparatorFilename(p.message_separator), +mLeftTextPad(p.left_text_pad), +mRightTextPad(p.right_text_pad), +mLeftWidgetPad(p.left_widget_pad), +mRightWidgetPad(p.rigth_widget_pad) +{ +} + +LLChatHistory::~LLChatHistory() +{ + this->clear(); +} + +/*void LLChatHistory::updateTextRect() +{ + static LLUICachedControl<S32> texteditor_border ("UITextEditorBorder", 0); + + LLRect old_text_rect = mTextRect; + mTextRect = mScroller->getContentWindowRect(); + mTextRect.stretch(-texteditor_border); + mTextRect.mLeft += mLeftTextPad; + mTextRect.mRight -= mRightTextPad; + if (mTextRect != old_text_rect) + { + needsReflow(); + } +}*/ + +LLView* LLChatHistory::getSeparator() +{ + LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance()); + return separator; +} + +LLView* LLChatHistory::getHeader(const LLUUID& avatar_id, std::string& from, std::string& time) +{ + LLPanel* header = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>(mMessageHeaderFilename, NULL, LLPanel::child_registry_t::instance()); + LLTextBox* userName = header->getChild<LLTextBox>("user_name"); + userName->setValue(from); + LLTextBox* timeBox = header->getChild<LLTextBox>("time_box"); + timeBox->setValue(time); + if(!avatar_id.isNull()) + { + LLAvatarIconCtrl* icon = header->getChild<LLAvatarIconCtrl>("avatar_icon"); + icon->setValue(avatar_id); + } + return header; +} + +void LLChatHistory::appendWidgetMessage(const LLUUID& avatar_id, std::string& from, std::string& time, std::string& message, LLStyle::Params& style_params) +{ + LLView* view = NULL; + std::string view_text; + + if (mLastFromName == from) + { + view = getSeparator(); + view_text = "\n"; + } + else + { + view = getHeader(avatar_id, from, time); + view_text = from + MESSAGE_USERNAME_DATE_SEPARATOR + time; + } + //Prepare the rect for the view + LLRect target_rect = mScroller->getContentWindowRect(); + target_rect.mLeft += mLeftWidgetPad; + target_rect.mRight -= mRightWidgetPad; + view->reshape(target_rect.getWidth(), view->getRect().getHeight()); + view->setOrigin(target_rect.mLeft, view->getRect().mBottom); + + this->appendWidget(view, view_text, FALSE, TRUE); + + //Append the text message + this->appendText(message, TRUE, style_params); + + mLastFromName = from; + this->blockUndo(); + this->setCursorAndScrollToEnd(); +} diff --git a/indra/newview/llchathistory.h b/indra/newview/llchathistory.h new file mode 100644 index 0000000000..d6eccf896a --- /dev/null +++ b/indra/newview/llchathistory.h @@ -0,0 +1,112 @@ +/** + * @file llchathistory.h + * @brief LLTextEditor base class + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-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$ + */ + +#ifndef LLCHATHISTORY_H_ +#define LLCHATHISTORY_H_ + +#include "lltexteditor.h" + +//Chat log widget allowing addition of a message as a widget +class LLChatHistory : public LLTextEditor +{ + public: + struct Params : public LLInitParam::Block<Params, LLTextEditor::Params> + { + //Message header filename + Optional<std::string> message_header; + //Message separator filename + Optional<std::string> message_separator; + //Text left padding from the scroll rect + Optional<S32> left_text_pad; + //Text right padding from the scroll rect + Optional<S32> right_text_pad; + //Widget left padding from the scroll rect + Optional<S32> left_widget_pad; + //Widget right padding from the scroll rect + Optional<S32> rigth_widget_pad; + + Params() + : message_header("message_header"), + message_separator("message_separator"), + left_text_pad("left_text_pad"), + right_text_pad("right_text_pad"), + left_widget_pad("left_widget_pad"), + rigth_widget_pad("rigth_widget_pad") + { + } + + }; + protected: + LLChatHistory(const Params&); + friend class LLUICtrlFactory; + + /** + * Redefinition of LLTextEditor::updateTextRect() to considerate text + * left/right padding params. + */ + //virtual void updateTextRect(); + /** + * Builds a message separator. + * @return pointer to LLView separator object. + */ + LLView* getSeparator(); + /** + * Builds a message header. + * @param from owner of a message. + * @param time time of a message. + * @return pointer to LLView header object. + */ + LLView* getHeader(const LLUUID& avatar_id, std::string& from, std::string& time); + + public: + ~LLChatHistory(); + + /** + * Appends a widget message. + * If last user appended message, concurs with current user, + * separator is added before the message, otherwise header is added. + * @param from owner of a message. + * @param time time of a message. + * @param message message itself. + */ + void appendWidgetMessage(const LLUUID& avatar_id, std::string& from, std::string& time, std::string& message, LLStyle::Params& style_params); + + private: + std::string mLastFromName; + std::string mMessageHeaderFilename; + std::string mMessageSeparatorFilename; + S32 mLeftTextPad; + S32 mRightTextPad; + S32 mLeftWidgetPad; + S32 mRightWidgetPad; +}; +#endif /* LLCHATHISTORY_H_ */ diff --git a/indra/newview/llfloaterinventory.cpp b/indra/newview/llfloaterinventory.cpp index a8ab4303ba..a33a605f50 100644 --- a/indra/newview/llfloaterinventory.cpp +++ b/indra/newview/llfloaterinventory.cpp @@ -1161,6 +1161,7 @@ LLUIImagePtr get_item_icon(LLAssetType::EType asset_type, const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder"); const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("RecentItemsSortOrder"); const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string(""); +static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER; LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : LLPanel(p), @@ -1172,7 +1173,12 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) mAllowMultiSelect(p.allow_multi_select), mHasInventoryConnection(false), mStartFolderString(p.start_folder) +, mBuildDefaultHierarchy(true) +, mRootInventoryItemUUID(LLUUID::null) +, mInvFVBridgeBuilder(NULL) { + mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER; + // contex menu callbacks mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryPanel::doToSelected, this, _2)); mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLAssetType::AT_TRASH)); @@ -1237,8 +1243,8 @@ BOOL LLInventoryPanel::postBuild() const LLAssetType::EType preferred_type = LLAssetType::lookupHumanReadable(mStartFolderString); mStartFolderID = (preferred_type != LLAssetType::AT_NONE ? gInventory.findCategoryUUIDForType(preferred_type) : LLUUID::null); - // build view of inventory if inventory ready, otherwise wait for modelChanged() callback - if (mInventory->isInventoryUsable() && !mHasInventoryConnection) + // build view of inventory if we need default full hierarchy and inventory ready, otherwise wait for modelChanged() callback + if (mBuildDefaultHierarchy && mInventory->isInventoryUsable() && !mHasInventoryConnection) { rebuildViewsFor(mStartFolderID); mHasInventoryConnection = true; @@ -1456,6 +1462,25 @@ void LLInventoryPanel::modelChanged(U32 mask) } } +void LLInventoryPanel::setInvFVBridgeBuilder(const LLInventoryFVBridgeBuilder* bridge_builder) +{ + if (NULL == bridge_builder) + { + llwarns << "NULL is passed as Inventory Bridge Builder. Default will be used." << llendl; + } + else + { + mInvFVBridgeBuilder = bridge_builder; + } + + if (mInventory->isInventoryUsable() && !mHasInventoryConnection) + { + rebuildViewsFor(mRootInventoryItemUUID); + mHasInventoryConnection = true; + } +} + + void LLInventoryPanel::rebuildViewsFor(const LLUUID& id) { LLFolderViewItem* old_view = NULL; @@ -1493,11 +1518,11 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) else if (objectp->getType() == LLAssetType::AT_CATEGORY && objectp->getActualType() != LLAssetType::AT_LINK_FOLDER) { - LLInvFVBridge* new_listener = LLInvFVBridge::createBridge(objectp->getType(), - objectp->getType(), - LLInventoryType::IT_CATEGORY, - this, - objectp->getUUID()); + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), + objectp->getType(), + LLInventoryType::IT_CATEGORY, + this, + objectp->getUUID()); if (new_listener) { @@ -1516,12 +1541,12 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) { // Build new view for item LLInventoryItem* item = (LLInventoryItem*)objectp; - LLInvFVBridge* new_listener = LLInvFVBridge::createBridge(item->getType(), - item->getActualType(), - item->getInventoryType(), - this, - item->getUUID(), - item->getFlags()); + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), + item->getActualType(), + item->getInventoryType(), + this, + item->getUUID(), + item->getFlags()); if (new_listener) { diff --git a/indra/newview/llfloaterinventory.h b/indra/newview/llfloaterinventory.h index 33b1a3b6c9..1666f18c05 100644 --- a/indra/newview/llfloaterinventory.h +++ b/indra/newview/llfloaterinventory.h @@ -57,6 +57,7 @@ class LLFolderViewItem; class LLInventoryFilter; class LLInventoryModel; class LLInvFVBridge; +class LLInventoryFVBridgeBuilder; class LLMenuBarGL; class LLCheckBoxCtrl; class LLSpinCtrl; @@ -110,7 +111,7 @@ protected: friend class LLUICtrlFactory; public: - ~LLInventoryPanel(); + virtual ~LLInventoryPanel(); LLInventoryModel* getModel() { return mInventory; } @@ -172,7 +173,10 @@ public: protected: // Given the id and the parent, build all of the folder views. void rebuildViewsFor(const LLUUID& id); - void buildNewViews(const LLUUID& id); + virtual void buildNewViews(const LLUUID& id); // made virtual to support derived classes. EXT-719 + + // Be sure that passed pointer will be destroyed where it was created. + void setInvFVBridgeBuilder(const LLInventoryFVBridgeBuilder* bridge_builder); protected: LLInventoryModel* mInventory; @@ -180,12 +184,34 @@ protected: BOOL mAllowMultiSelect; std::string mSortOrderSetting; -private: +//private: // Can not make these private - needed by llinventorysubtreepanel LLFolderView* mFolders; std::string mStartFolderString; LLUUID mStartFolderID; LLScrollContainer* mScroller; bool mHasInventoryConnection; + + /** + * Flag specified if default inventory hierarchy should be created in postBuild() + */ + bool mBuildDefaultHierarchy; + + /** + * Contains UUID of Inventory item from which hierarchy should be built. + * Should be set by derived class before modelChanged() is called. + * Default is LLUUID::null that means total Inventory hierarchy. + */ + LLUUID mRootInventoryItemUUID; + + /** + * Pointer to LLInventoryFVBridgeBuilder. + * + * It is set in LLInventoryPanel's constructor and can be overridden in derived classes with + * another implementation. + * Take into account it will not be deleted by LLInventoryPanel itself. + */ + const LLInventoryFVBridgeBuilder* mInvFVBridgeBuilder; + }; class LLFloaterInventory; diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 3fd0875709..155262ee13 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -190,6 +190,8 @@ LLFolderView::LLFolderView(const Params& p) mDragAndDropThisFrame(FALSE), mCallbackRegistrar(NULL), mParentPanel(p.parent_panel) +, mUseEllipses(false) +, mDraggingOverItem(NULL) { LLRect rect = p.rect; LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom); @@ -481,6 +483,11 @@ void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) scroll_rect = mScrollContainer->getContentWindowRect(); } width = llmax(mMinWidth, scroll_rect.getWidth()); + + // restrict width with scroll container's width + if (mUseEllipses) + width = scroll_rect.getWidth(); + LLView::reshape(width, height, called_from_parent); mReshapeSignal(mSelectedItems, FALSE); @@ -1224,12 +1231,42 @@ void LLFolderView::copy() BOOL LLFolderView::canCut() const { - return FALSE; + if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0))) + { + return FALSE; + } + + for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) + { + const LLFolderViewItem* item = *selected_it; + const LLFolderViewEventListener* listener = item->getListener(); + if (!listener || !listener->isItemMovable()) + { + return FALSE; + } + } + return TRUE; } void LLFolderView::cut() { - // implement Windows-style cut-and-leave + // clear the inventory clipboard + LLInventoryClipboard::instance().reset(); + S32 count = mSelectedItems.size(); + if(getVisible() && getEnabled() && (count > 0)) + { + LLFolderViewEventListener* listener = NULL; + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + { + listener = (*item_it)->getListener(); + if(listener) + { + listener->cutToClipboard(); + } + } + } + mSearchString.clear(); } BOOL LLFolderView::canPaste() const diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index c95dc6e5bd..ebfb4efde2 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -180,6 +180,9 @@ public: BOOL startDrag(LLToolDragAndDrop::ESource source); void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; } + void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } + LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } + // deletion functionality void removeSelectedItems(); @@ -248,6 +251,8 @@ public: void setShowSingleSelection(BOOL show); BOOL getShowSingleSelection() { return mShowSingleSelection; } F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } + void setUseEllipses(bool use_ellipses) { mUseEllipses = use_ellipses; } + bool getUseEllipses() { return mUseEllipses; } void addItemID(const LLUUID& id, LLFolderViewItem* itemp); void removeItemID(const LLUUID& id); @@ -327,6 +332,17 @@ protected: LLPanel* mParentPanel; + /** + * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. + * NOTE: For now it uses only to cut LLFolderViewItem::mLabel text to be used for Landmarks in Places Panel. + */ + bool mUseEllipses; // See EXT-719 + + /** + * Contains item under mouse pointer while dragging + */ + LLFolderViewItem* mDraggingOverItem; // See EXT-719 + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar; public: diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h index 254ce4062a..ff38da279a 100644 --- a/indra/newview/llfoldervieweventlistener.h +++ b/indra/newview/llfoldervieweventlistener.h @@ -68,7 +68,7 @@ public: virtual void showProperties(void) = 0; virtual BOOL isItemRenameable() const = 0; virtual BOOL renameItem(const std::string& new_name) = 0; - virtual BOOL isItemMovable( void ) = 0; // Can be moved to another folder + virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder virtual BOOL isItemRemovable( void ) = 0; // Can be destroyed virtual BOOL removeItem() = 0; virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) = 0; diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 2b1dd83d72..0d74641c5f 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -36,6 +36,7 @@ // viewer includes #include "llfolderview.h" // Items depend extensively on LLFolderViews #include "llfoldervieweventlistener.h" +#include "llinventorybridge.h" // for LLItemBridge in LLInventorySort::operator() #include "llinventoryfilter.h" #include "llinventorymodel.h" // *TODO: make it take a pointer to an inventory-model interface #include "llviewercontrol.h" // gSavedSettings @@ -130,6 +131,7 @@ LLFolderViewItem::LLFolderViewItem(LLFolderViewItem::Params p) mListener(p.listener), mArrowImage(p.folder_arrow_image), mBoxImage(p.selection_image) +, mDontShowInHierarhy(false) { refresh(); } @@ -312,7 +314,12 @@ void LLFolderViewItem::arrangeFromRoot() S32 height = 0; S32 width = 0; - root->arrange( &width, &height, 0 ); + S32 total_height = root->arrange( &width, &height, 0 ); + + LLSD params; + params["action"] = "size_changes"; + params["height"] = total_height; + getParent()->notifyParent(params); } // Utility function for LLFolderView @@ -385,12 +392,22 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation) } *width = llmax(*width, mLabelWidth + mIndentation); + + // determine if we need to use ellipses to avoid horizontal scroll. EXT-719 + bool use_ellipses = getRoot()->getUseEllipses(); + if (use_ellipses) + { + // limit to set rect to avoid horizontal scrollbar + *width = llmin(*width, getRoot()->getRect().getWidth()); + } *height = getItemHeight(); return *height; } S32 LLFolderViewItem::getItemHeight() { + if (mDontShowInHierarhy) return 0; + S32 icon_height = mIcon->getHeight(); S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight()); return llmax( icon_height, label_height ) + ICON_PAD; @@ -781,7 +798,10 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, } if(mParentFolder && !handled) { + // store this item to get it in LLFolderBridge::dragItemIntoFolder on drop event. + mRoot->setDraggingOverItem(this); handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); + mRoot->setDraggingOverItem(NULL); } if (handled) { @@ -794,6 +814,8 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, void LLFolderViewItem::draw() { + if (mDontShowInHierarhy) return; + static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); @@ -948,7 +970,8 @@ void LLFolderViewItem::draw() font->renderUTF8( mLabel, 0, text_left, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); + S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE); + if (!mLabelSuffix.empty()) { font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, @@ -2456,6 +2479,28 @@ bool LLInventorySort::updateSort(U32 order) bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b) { + // ignore sort order for landmarks in the Favorites folder. + // they should be always sorted as in Favorites bar. See EXT-719 + if (a->getSortGroup() == SG_ITEM && b->getSortGroup() == SG_ITEM + && a->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK + && b->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + { + + static LLUUID favorites_folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE); + + LLUUID a_uuid = a->getParentFolder()->getListener()->getUUID(); + LLUUID b_uuid = b->getParentFolder()->getListener()->getUUID(); + + if (a_uuid == favorites_folder_id && b_uuid == favorites_folder_id) + { + // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem + // or to LLInvFVBridge + S32 a_sort = (static_cast<const LLItemBridge*>(a->getListener()))->getItem()->getSortField(); + S32 b_sort = (static_cast<const LLItemBridge*>(b->getListener()))->getItem()->getSortField(); + return a_sort < b_sort; + } + } + // We sort by name if we aren't sorting by date // OR if these are folders and we are sorting folders by name. bool by_name = (!mByDate diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index 09c97662d9..90c346b381 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -158,6 +158,7 @@ protected: LLUIImagePtr mBoxImage; BOOL mIsLoading; LLTimer mTimeSinceRequestStart; + bool mDontShowInHierarhy; // helper function to change the selection from the root. void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected); @@ -200,6 +201,7 @@ public: // makes sure that this view and it's children are the right size. virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); virtual S32 getItemHeight(); + void setDontShowInHierarchy(bool dont_show) { mDontShowInHierarhy = dont_show; } // applies filters to control visibility of inventory items virtual void filter( LLInventoryFilter& filter); diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index ae869d9ac4..27e31d4edd 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -315,7 +315,7 @@ void LLGroupListItem::setActive(bool active) // rebuild the text. This will cause problems if the text contains // hyperlinks, as their styles will be wrong. std::string text = mGroupNameBox->getText(); - mGroupNameBox->setText(LLStringUtil::null);// *HACK: replace with clear() when it's fixed. + mGroupNameBox->setText(LLStringUtil::null); mGroupNameBox->appendText(text, false, style_params); } diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 680106c7bb..a35c04440b 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -63,13 +63,15 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mDialog(IM_NOTHING_SPECIAL), mHistoryEditor(NULL), mInputEditor(NULL), - mPositioned(false) + mPositioned(false), + mSessionInitialized(false) { - EInstantMessage type = LLIMModel::getInstance()->getType(session_id); - if(IM_COUNT != type) + LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionID); + if (im_session) { - mDialog = type; - + mSessionInitialized = im_session->mSessionInitialized; + + mDialog = im_session->mType; if (IM_NOTHING_SPECIAL == mDialog || IM_SESSION_P2P_INVITE == mDialog) { mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); @@ -139,10 +141,16 @@ void LLIMFloater::sendMsg() std::string utf8_text = wstring_to_utf8str(text); utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); - LLIMModel::sendMessage(utf8_text, - mSessionID, - mOtherParticipantUUID, - mDialog); + if (mSessionInitialized) + { + LLIMModel::sendMessage(utf8_text, mSessionID, + mOtherParticipantUUID,mDialog); + } + else + { + //queue up the message to send once the session is initialized + mQueuedMsgsForInit.append(utf8_text); + } mInputEditor->setText(LLStringUtil::null); @@ -200,6 +208,8 @@ BOOL LLIMFloater::postBuild() LLLogChat::loadHistory(getTitle(), &chatFromLogFile, (void *)this); } + //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla" + //see LLFloaterIMPanel for how it is done (IB) return LLDockableFloater::postBuild(); } @@ -337,6 +347,37 @@ bool LLIMFloater::toggle(const LLUUID& session_id) } } +//static +LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) +{ + return LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); +} + +void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) +{ + mSessionInitialized = true; + + if (mSessionID != im_session_id) + { + mSessionID = im_session_id; + setKey(im_session_id); + } + + //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) + + + //need to send delayed messaged collected while waiting for session initialization + if (!mQueuedMsgsForInit.size()) return; + LLSD::array_iterator iter; + for ( iter = mQueuedMsgsForInit.beginArray(); + iter != mQueuedMsgsForInit.endArray(); + ++iter) + { + LLIMModel::sendMessage(iter->asString(), mSessionID, + mOtherParticipantUUID, mDialog); + } +} + void LLIMFloater::updateMessages() { std::list<LLSD> messages = LLIMModel::instance().getMessages(mSessionID, mLastMessageIndex+1); @@ -457,3 +498,4 @@ void LLIMFloater::chatFromLogFile(LLLogChat::ELogLineType type, std::string line self->mHistoryEditor->appendText(message, true, LLStyle::Params().color(LLUIColorTable::instance().getColor("ChatHistoryTextColor"))); self->mHistoryEditor->blockUndo(); } + diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 5276013568..9b519ee7e3 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -68,6 +68,10 @@ public: // Returns true iff panel became visible static bool toggle(const LLUUID& session_id); + static LLIMFloater* findInstance(const LLUUID& session_id); + + void sessionInitReplyReceived(const LLUUID& im_session_id); + // get new messages from LLIMModel void updateMessages(); static void onSendMsg( LLUICtrl*, void*); @@ -108,6 +112,9 @@ private: LLViewerTextEditor* mHistoryEditor; LLLineEditor* mInputEditor; bool mPositioned; + + bool mSessionInitialized; + LLSD mQueuedMsgsForInit; }; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 2ecd3eb448..6aa6c3f461 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -144,6 +144,8 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& // All participants will be added to the list of people we've recently interacted with. mSpeakers->addListener(&LLRecentPeople::instance(), "add"); + //we need to wait for session initialization for outgoing ad-hoc and group chat session + //correct session id for initiated ad-hoc chat will be received from the server if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, mInitialTargetIDs, mType)) { @@ -181,26 +183,44 @@ LLIMModel::LLIMSession::~LLIMSession() mVoiceChannel = NULL; } +void LLIMModel::LLIMSession::sessionInitReplyReceived(const LLUUID& new_session_id) +{ + mSessionInitialized = true; + + if (new_session_id != mSessionID) + { + mSessionID = new_session_id; + mVoiceChannel->updateSessionID(new_session_id); + } +} + LLIMModel::LLIMSession* LLIMModel::findIMSession(const LLUUID& session_id) const { return get_if_there(LLIMModel::instance().sSessionsMap, session_id, (LLIMModel::LLIMSession*) NULL); } +//*TODO change name to represent session initialization aspect (IB) void LLIMModel::updateSessionID(const LLUUID& old_session_id, const LLUUID& new_session_id) { - if (new_session_id == old_session_id) return; - LLIMSession* session = findIMSession(old_session_id); if (session) { - session->mSessionID = new_session_id; - session->mVoiceChannel->updateSessionID(new_session_id); + session->sessionInitReplyReceived(new_session_id); - session->mSessionInitialized = true; + if (old_session_id != new_session_id) + { + sSessionsMap.erase(old_session_id); + sSessionsMap[new_session_id] = session; - sSessionsMap.erase(old_session_id); - sSessionsMap[new_session_id] = session; + gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id); + } + + LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id); + if (im_floater) + { + im_floater->sessionInitReplyReceived(new_session_id); + } } //*TODO remove this "floater" stuff when Communicate Floater is gone @@ -736,18 +756,10 @@ bool LLIMModel::sendStartSession( temp_session_id, other_participant_id, dialog); - - switch(dialog) - { - case IM_SESSION_GROUP_START: - gMessageSystem->addBinaryDataFast( + gMessageSystem->addBinaryDataFast( _PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE); - break; - default: - break; - } gAgent.sendReliableMessage(); return true; @@ -789,6 +801,9 @@ bool LLIMModel::sendStartSession( other_participant_id, agents); } + + //we also need to wait for reply from the server in case of ad-hoc chat (we'll get new session id) + return true; } return false; @@ -1291,9 +1306,16 @@ void LLIMMgr::addMessage( new_session_id = computeSessionID(dialog, other_participant_id); } + //*NOTE session_name is empty in case of incoming P2P sessions + std::string fixed_session_name = from; + if(!session_name.empty() && session_name.size()>1) + { + fixed_session_name = session_name; + } + if (!LLIMModel::getInstance()->findIMSession(new_session_id)) { - LLIMModel::instance().newSession(session_id, session_name, dialog, other_participant_id); + LLIMModel::getInstance()->newSession(session_id, fixed_session_name, dialog, other_participant_id); } floater = findFloaterBySession(new_session_id); @@ -1310,17 +1332,12 @@ void LLIMMgr::addMessage( // create IM window as necessary if(!floater) { - std::string name = from; - if(!session_name.empty() && session_name.size()>1) - { - name = session_name; - } floater = createFloater( new_session_id, other_participant_id, - name, + fixed_session_name, dialog, FALSE); @@ -1869,6 +1886,15 @@ void LLIMMgr::notifyObserverSessionRemoved(const LLUUID& session_id) } } +void LLIMMgr::notifyObserverSessionIDUpdated( const LLUUID& old_session_id, const LLUUID& new_session_id ) +{ + for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) + { + (*it)->sessionIDUpdated(old_session_id, new_session_id); + } + +} + void LLIMMgr::addSessionObserver(LLIMSessionObserver *observer) { mSessionObservers.push_back(observer); diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index df28c16bb1..84646a9a6f 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -56,6 +56,8 @@ public: const EInstantMessage& type, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids); virtual ~LLIMSession(); + void sessionInitReplyReceived(const LLUUID& new_session_id); + LLUUID mSessionID; std::string mName; EInstantMessage mType; @@ -153,6 +155,7 @@ public: virtual ~LLIMSessionObserver() {} virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) = 0; virtual void sessionRemoved(const LLUUID& session_id) = 0; + virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) = 0; }; @@ -304,6 +307,7 @@ private: void notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); void notifyObserverSessionRemoved(const LLUUID& session_id); + void notifyObserverSessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); private: std::set<LLHandle<LLFloater> > mFloaters; diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 44dd11dd86..14dd0cdbce 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -54,6 +54,8 @@ #include "lltooltip.h" // positionViewNearMouse() #include "lluictrl.h" +#include "llavatariconctrl.h" + class LLFetchAvatarData; @@ -347,6 +349,10 @@ void LLInspectAvatar::requestUpdate() // and this may result in the image being visible sooner. // *NOTE: This may generate a duplicate avatar properties request, but that // will be suppressed internally in the avatar properties processor. + + //remove avatar id from cache to get fresh info + LLAvatarIconIDCache::getInstance()->remove(mAvatarID); + childSetValue("avatar_icon", LLSD(mAvatarID) ); gCacheName->get(mAvatarID, FALSE, diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index e807240e4e..db7d4f4c8f 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -257,11 +257,22 @@ void LLInvFVBridge::renameLinkedItems(const LLUUID &item_id, const std::string& } // Can be moved to another folder -BOOL LLInvFVBridge::isItemMovable() +BOOL LLInvFVBridge::isItemMovable() const { return TRUE; } +/*virtual*/ +/** + * @brief Adds this item into clipboard storage + */ +void LLInvFVBridge::cutToClipboard() +{ + if(isItemMovable()) + { + LLInventoryClipboard::instance().cut(mUUID); + } +} // *TODO: make sure this does the right thing void LLInvFVBridge::showProperties() { @@ -912,6 +923,24 @@ void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) } // +=================================================+ +// | InventoryFVBridgeBuilder | +// +=================================================+ +LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + const LLUUID& uuid, + U32 flags /* = 0x00 */) const +{ + return LLInvFVBridge::createBridge(asset_type, + actual_asset_type, + inv_type, + inventory, + uuid, + flags); +} + +// +=================================================+ // | LLItemBridge | // +=================================================+ @@ -1321,7 +1350,7 @@ BOOL LLItemBridge::isItemPermissive() const LLFolderBridge* LLFolderBridge::sSelf=NULL; // Can be moved to another folder -BOOL LLFolderBridge::isItemMovable() +BOOL LLFolderBridge::isItemMovable() const { LLInventoryObject* obj = getInventoryObject(); if(obj) @@ -2184,19 +2213,28 @@ void LLFolderBridge::pasteFromClipboard() LLDynamicArray<LLUUID> objects; LLInventoryClipboard::instance().retrieve(objects); S32 count = objects.count(); - LLUUID parent_id(mUUID); + const LLUUID parent_id(mUUID); 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)); + if(LLInventoryClipboard::instance().isCutMode()) + { + // move_inventory_item() is not enough, + //we have to update inventory locally too + changeItemParent(model, dynamic_cast<LLViewerInventoryItem*>(item), parent_id, FALSE); + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + parent_id, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + } } } } @@ -2668,6 +2706,56 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response return false; } +/* +Next functions intended to reorder items in the inventory folder and save order on server +Is now used for Favorites folder. + +*TODO: refactoring is needed with Favorites Bar functionality. Probably should be moved in LLInventoryModel +*/ +void saveItemsOrder(LLInventoryModel::item_array_t& items) +{ + int sortField = 0; + + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) + { + LLViewerInventoryItem* item = *i; + + item->setSortField(++sortField); + item->setComplete(TRUE); + item->updateServer(FALSE); + + gInventory.updateItem(item); + } + + gInventory.notifyObservers(); +} + +LLInventoryModel::item_array_t::iterator 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 updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& srcItemId, const LLUUID& destItemId) +{ + LLViewerInventoryItem* srcItem = gInventory.getItem(srcItemId); + LLViewerInventoryItem* destItem = gInventory.getItem(destItemId); + + items.erase(findItemByUUID(items, srcItem->getUUID())); + items.insert(findItemByUUID(items, destItem->getUUID()), srcItem); +} + BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop) { @@ -2726,7 +2814,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } } - accept = is_movable && (mUUID != inv_item->getParentUUID()); + LLUUID favorites_id = model->findCategoryUUIDForType(LLAssetType::AT_FAVORITE); + + // we can move item inside a folder only if this folder is Favorites. See EXT-719 + accept = is_movable && ((mUUID != inv_item->getParentUUID()) || (mUUID == favorites_id)); if(accept && drop) { if (inv_item->getType() == LLAssetType::AT_GESTURE @@ -2748,8 +2839,28 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } } - LLUUID favorites_id = model->findCategoryUUIDForType(LLAssetType::AT_FAVORITE); - if (favorites_id == mUUID) // if target is the favorites folder we use copy + // if dragging from/into favorites folder only reorder items + if ((mUUID == inv_item->getParentUUID()) && (favorites_id == mUUID)) + { + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLIsType is_type(LLAssetType::AT_LANDMARK); + model->collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); + LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; + if (itemp) + { + LLUUID srcItemId = inv_item->getUUID(); + LLUUID destItemId = itemp->getListener()->getUUID(); + + // update order + updateItemsOrder(items, srcItemId, destItemId); + + saveItemsOrder(items); + } + } + else if (favorites_id == mUUID) // if target is the favorites folder we use copy { copy_inventory_item( gAgent.getID(), @@ -3023,14 +3134,14 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } items.push_back(std::string("Landmark Separator")); - items.push_back(std::string("Teleport To Landmark")); + items.push_back(std::string("About Landmark")); // Disable "About Landmark" menu item for // multiple landmarks selected. Only one landmark // info panel can be shown at a time. if ((flags & FIRST_SELECTED_ITEM) == 0) { - disabled_items.push_back(std::string("Teleport To Landmark")); + disabled_items.push_back(std::string("About Landmark")); } hideContextEntries(menu, items, disabled_items); diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index bc950520aa..d1d2c57f07 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -120,6 +120,11 @@ protected: LLInventoryPanel* mIP; }; +const std::string safe_inv_type_lookup(LLInventoryType::EType inv_type); +void hideContextEntries(LLMenuGL& menu, + const std::vector<std::string> &entries_to_show, + const std::vector<std::string> &disabled_entries); + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInvFVBridge (& it's derived classes) // @@ -168,13 +173,13 @@ public: virtual BOOL isItemRenameable() const { return TRUE; } //virtual BOOL renameItem(const std::string& new_name) {} virtual BOOL isItemRemovable(); - virtual BOOL isItemMovable(); + virtual BOOL isItemMovable() const; //virtual BOOL removeItem() = 0; virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch); virtual void move(LLFolderViewEventListener* new_parent_bridge) {} virtual BOOL isItemCopyable() const { return FALSE; } virtual BOOL copyToClipboard() const { return FALSE; } - virtual void cutToClipboard() {} + virtual void cutToClipboard(); virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; virtual void pasteFromClipboard() {} @@ -221,6 +226,22 @@ protected: void purgeItem(LLInventoryModel *model, const LLUUID &uuid); }; +/** + * This class intended to build Folder View Bridge via LLInvFVBridge::createBridge. + * It can be overridden with another way of creation necessary Inventory-Folder-View-Bridge. + */ +class LLInventoryFVBridgeBuilder +{ +public: + virtual ~LLInventoryFVBridgeBuilder(){} + virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + const LLUUID& uuid, + U32 flags = 0x00) const; +}; + class LLItemBridge : public LLInvFVBridge { @@ -291,7 +312,7 @@ public: void* cargo_data); virtual BOOL isItemRemovable(); - virtual BOOL isItemMovable(); + virtual BOOL isItemMovable() const ; virtual BOOL isUpToDate() const; virtual BOOL isItemCopyable() const; virtual BOOL isClipboardPasteableAsLink() const; diff --git a/indra/newview/llinventoryclipboard.cpp b/indra/newview/llinventoryclipboard.cpp index 94ffcbd457..71a5e12332 100644 --- a/indra/newview/llinventoryclipboard.cpp +++ b/indra/newview/llinventoryclipboard.cpp @@ -47,6 +47,7 @@ LLInventoryClipboard LLInventoryClipboard::sInstance; ///---------------------------------------------------------------------------- LLInventoryClipboard::LLInventoryClipboard() +: mCutMode(false) { } @@ -77,6 +78,16 @@ void LLInventoryClipboard::store(const LLDynamicArray<LLUUID>& inv_objects) } } +void LLInventoryClipboard::cut(const LLUUID& object) +{ + if(!mCutMode && !mObjects.empty()) + { + //looks like there are some stored items, reset clipboard state + reset(); + } + mCutMode = true; + add(object); +} void LLInventoryClipboard::retrieve(LLDynamicArray<LLUUID>& inv_objects) const { inv_objects.reset(); @@ -90,6 +101,7 @@ void LLInventoryClipboard::retrieve(LLDynamicArray<LLUUID>& inv_objects) const void LLInventoryClipboard::reset() { mObjects.reset(); + mCutMode = false; } // returns true if the clipboard has something pasteable in it. diff --git a/indra/newview/llinventoryclipboard.h b/indra/newview/llinventoryclipboard.h index 7a2cf15d62..7e221d650c 100644 --- a/indra/newview/llinventoryclipboard.h +++ b/indra/newview/llinventoryclipboard.h @@ -60,6 +60,7 @@ public: // this method stores an array of objects void store(const LLDynamicArray<LLUUID>& inventory_objects); + void cut(const LLUUID& object); // this method gets the objects in the clipboard by copying them // into the array provided. void retrieve(LLDynamicArray<LLUUID>& inventory_objects) const; @@ -69,11 +70,13 @@ public: // returns true if the clipboard has something pasteable in it. BOOL hasContents() const; + bool isCutMode() const { return mCutMode; } protected: static LLInventoryClipboard sInstance; LLDynamicArray<LLUUID> mObjects; + bool mCutMode; public: // please don't actually call these diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp index 7c759e22a8..2e6615dd91 100644 --- a/indra/newview/lllandmarkactions.cpp +++ b/indra/newview/lllandmarkactions.cpp @@ -42,16 +42,18 @@ #include "llnotifications.h" #include "llagent.h" +#include "llagentui.h" #include "llinventorymodel.h" #include "lllandmarklist.h" #include "llslurl.h" +#include "llstring.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" +#include "llviewerwindow.h" +#include "llwindow.h" #include "llworldmap.h" -#include "lllandmark.h" -#include "llinventorymodel.h" -#include "llagentui.h" +void copy_slurl_to_clipboard_callback(const std::string& slurl); class LLFetchlLandmarkByPos : public LLInventoryCollectFunctor { @@ -303,15 +305,39 @@ void LLLandmarkActions::onRegionResponse(slurl_callback_t cb, bool LLLandmarkActions::getLandmarkGlobalPos(const LLUUID& landmarkInventoryItemID, LLVector3d& posGlobal) { + LLLandmark* landmark = LLLandmarkActions::getLandmark(landmarkInventoryItemID); + + if (NULL == landmark) + return false; + + return landmark->getGlobalPos(posGlobal); +} + +LLLandmark* LLLandmarkActions::getLandmark(const LLUUID& landmarkInventoryItemID) +{ LLViewerInventoryItem* item = gInventory.getItem(landmarkInventoryItemID); if (NULL == item) return false; const LLUUID& asset_id = item->getAssetUUID(); - LLLandmark* landmark = gLandmarkList.getAsset(asset_id, NULL); + return gLandmarkList.getAsset(asset_id, NULL); +} - if (NULL == landmark) - return false; +void LLLandmarkActions::copySLURLtoClipboard(const LLUUID& landmarkInventoryItemID) +{ + LLLandmark* landmark = LLLandmarkActions::getLandmark(landmarkInventoryItemID); + if(landmark) + { + LLVector3d global_pos; + landmark->getGlobalPos(global_pos); + LLLandmarkActions::getSLURLfromPosGlobal(global_pos,©_slurl_to_clipboard_callback,true); + } +} - return landmark->getGlobalPos(posGlobal); +void copy_slurl_to_clipboard_callback(const std::string& slurl) +{ + gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(slurl)); + LLSD args; + args["SLURL"] = slurl; + LLNotifications::instance().add("CopySLURL", args); } diff --git a/indra/newview/lllandmarkactions.h b/indra/newview/lllandmarkactions.h index ab8cc4d6a5..e882db0a92 100644 --- a/indra/newview/lllandmarkactions.h +++ b/indra/newview/lllandmarkactions.h @@ -35,6 +35,7 @@ #include "llinventorymodel.h" +class LLLandmark; /** * @brief Provides helper functions to manage landmarks */ @@ -101,6 +102,20 @@ public: // *TODO: mantipov: profide callback for cases, when Landmark is not loaded yet. static bool getLandmarkGlobalPos(const LLUUID& landmarkInventoryItemID, LLVector3d& posGlobal); + /** + * @brief Retrieve a landmark from gLandmarkList by inventory item's id + * + * @return pointer to loaded landmark from gLandmarkList or NULL if landmark does not exist. + */ + static LLLandmark* getLandmark(const LLUUID& landmarkInventoryItemID); + + /** + * @brief Performs standard action of copying of SLURL from landmark to user's clipboard. + * This action requires additional server request. The user will be notified by info message, + * when URL is copied . + */ + static void copySLURLtoClipboard(const LLUUID& landmarkInventoryItemID); + private: LLLandmarkActions(); LLLandmarkActions(const LLLandmarkActions&); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 91862112a0..7160cce5cb 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -48,11 +48,9 @@ #include "llnearbychathandler.h" #include "llchannelmanager.h" -//for LLViewerTextEditor support #include "llagent.h" // gAgent #include "llfloaterscriptdebug.h" -#include "llslurl.h" -#include "llviewertexteditor.h" +#include "llchathistory.h" #include "llstylemap.h" #include "lldraghandle.h" @@ -64,7 +62,7 @@ LLNearbyChat::LLNearbyChat(const LLSD& key) : LLFloater(key), mEChatTearofState(CHAT_PINNED), mChatCaptionPanel(NULL), - mChatHistoryEditor(NULL) + mChatHistory(NULL) { m_isDirty = false; } @@ -110,7 +108,7 @@ BOOL LLNearbyChat::postBuild() gSavedSettings.declareS32("nearbychat_showicons_and_names",2,"NearByChat header settings",true); mChatCaptionPanel = getChild<LLPanel>("chat_caption", false); - mChatHistoryEditor = getChild<LLViewerTextEditor>("Chat History Editor"); + mChatHistory = getChild<LLChatHistory>("chat_history"); reshape(getRect().getWidth(), getRect().getHeight(), FALSE); @@ -185,44 +183,6 @@ LLColor4 nearbychat_get_text_color(const LLChat& chat) void LLNearbyChat::add_timestamped_line(const LLChat& chat, const LLColor4& color) { - std::string line = chat.mText; - - //chat.mText starts with Avatar Name if entered message was "/me <action>". - // In this case output chat message should be "<Avatar Name> <action>". See EXT-656 - // See also process_chat_from_simulator() in the llviewermessage.cpp where ircstyle = TRUE; - if (CHAT_STYLE_IRC != chat.mChatStyle) - line = chat.mFromName + ": " + line; - - bool prepend_newline = true; - if (gSavedSettings.getBOOL("ChatShowTimestamps")) - { - mChatHistoryEditor->appendTime(prepend_newline); - prepend_newline = false; - } - - // If the msg is from an agent (not yourself though), - // extract out the sender name and replace it with the hotlinked name. - - std::string str_URL = chat.mURL; - - if (chat.mSourceType == CHAT_SOURCE_AGENT && - chat.mFromID != LLUUID::null) - { - str_URL = LLSLURL::buildCommand("agent", chat.mFromID, "inspect"); - } - - // If the chat line has an associated url, link it up to the name. - if (!str_URL.empty() - && (line.length() > chat.mFromName.length() && line.find(chat.mFromName,0) == 0)) - { - std::string start_line = line.substr(0, chat.mFromName.length() + 1); - line = line.substr(chat.mFromName.length() + 1); - mChatHistoryEditor->appendText(start_line, prepend_newline, - LLStyleMap::instance().lookup(chat.mFromID,str_URL)); - mChatHistoryEditor->blockUndo(); - prepend_newline = false; - } - S32 font_size = gSavedSettings.getS32("ChatFontSize"); const LLFontGL* fontp = NULL; @@ -240,8 +200,14 @@ void LLNearbyChat::add_timestamped_line(const LLChat& chat, const LLColor4& colo break; } - mChatHistoryEditor->appendText(line, prepend_newline, LLStyle::Params().color(color).font(fontp)); - mChatHistoryEditor->blockUndo(); + LLStyle::Params style_params; + style_params.color(color); + style_params.font(fontp); + LLUUID uuid = chat.mFromID; + std::string from = chat.mFromName; + std::string time = ""; + std::string message = chat.mText; + mChatHistory->appendWidgetMessage(uuid, from, time, message, style_params); } void LLNearbyChat::addMessage(const LLChat& chat) @@ -315,7 +281,7 @@ void LLNearbyChat::reshape(S32 width, S32 height, BOOL called_from_parent) mResizeBar[LLResizeBar::RIGHT]->setRect(resize_rect); } - // *NOTE: we must check mChatCaptionPanel and mChatHistoryEditor against NULL because reshape is called from the + // *NOTE: we must check mChatCaptionPanel and mChatHistory against NULL because reshape is called from the // LLView::initFromParams BEFORE postBuild is called and child controls are not exist yet LLRect caption_rect; if (NULL != mChatCaptionPanel) @@ -326,12 +292,12 @@ void LLNearbyChat::reshape(S32 width, S32 height, BOOL called_from_parent) mChatCaptionPanel->setRect(caption_rect); } - if (NULL != mChatHistoryEditor) + if (NULL != mChatHistory) { - LLRect scroll_rect = mChatHistoryEditor->getRect(); + LLRect scroll_rect = mChatHistory->getRect(); scroll_rect.setLeftTopAndSize( 2, height - caption_rect.getHeight() - RESIZE_BAR_THICKNESS, width - 4, height - caption_rect.getHeight() - RESIZE_BAR_THICKNESS*2); - mChatHistoryEditor->reshape( width - 4, height - caption_rect.getHeight() - RESIZE_BAR_THICKNESS*2, 1); - mChatHistoryEditor->setRect(scroll_rect); + mChatHistory->reshape( width - 4, height - caption_rect.getHeight() - RESIZE_BAR_THICKNESS*2, 1); + mChatHistory->setRect(scroll_rect); } // diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 7c8ffa3b94..47cae8ed0d 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -38,7 +38,7 @@ #include "llchat.h" class LLResizeBar; -class LLViewerTextEditor; +class LLChatHistory; class LLNearbyChat: public LLFloater { @@ -89,7 +89,7 @@ private: LLHandle<LLView> mPopupMenuHandle; LLPanel* mChatCaptionPanel; - LLViewerTextEditor* mChatHistoryEditor; + LLChatHistory* mChatHistory; bool m_isDirty; }; diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index 5186a93569..c08f92b983 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -33,6 +33,8 @@ #include "llviewerprecompiledheaders.h" // must be first include +#include "llfloaterreg.h" +#include "llnearbychat.h" #include "llnotificationhandler.h" #include "lltoastnotifypanel.h" #include "llviewercontrol.h" @@ -96,6 +98,14 @@ bool LLTipHandler::processNotification(const LLSD& notify) LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel); if(channel) channel->addToast(p); + + // archive message in nearby chat + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); + if(nearby_chat) + { + LLChat chat_msg(notification->getMessage()); + nearby_chat->addMessage(chat_msg); + } } else if (notify["sigtype"].asString() == "delete") { diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 3a3ea1fc3c..7670a5120c 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -43,6 +43,7 @@ #include "lltexturectrl.h" #include "lltooldraganddrop.h" #include "llscrollcontainer.h" +#include "llavatariconctrl.h" #include "llweb.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -447,6 +448,10 @@ void LLPanelAvatarProfile::processGroupProperties(const LLAvatarGroups* avatar_g void LLPanelAvatarProfile::fillCommonData(const LLAvatarData* avatar_data) { + //remove avatar id from cache to get fresh info + LLAvatarIconIDCache::getInstance()->remove(avatar_data->avatar_id); + + childSetValue("register_date", avatar_data->born_on ); childSetValue("sl_description_edit", avatar_data->about_text); childSetValue("fl_description_edit",avatar_data->fl_about_text); diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 1d79ea4a21..5f96407d7d 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -35,32 +35,50 @@ #include "llbutton.h" #include "llfloaterreg.h" -#include "lllandmark.h" +#include "llsdutil.h" +#include "llaccordionctrltab.h" +#include "llagent.h" +#include "llagentui.h" #include "llfloaterworldmap.h" -#include "llfloaterinventory.h" -#include "llfoldervieweventlistener.h" -#include "lllandmarklist.h" +#include "llfolderviewitem.h" +#include "llinventorysubtreepanel.h" +#include "lllandmarkactions.h" +#include "llplacesinventorybridge.h" #include "llsidetray.h" -#include "lltabcontainer.h" -#include "llworldmap.h" +#include "llviewermenu.h" +#include "llviewerregion.h" // Not yet implemented; need to remove buildPanel() from constructor when we switch //static LLRegisterPanelClassWrapper<LLLandmarksPanel> t_landmarks("panel_landmarks"); +static const std::string OPTIONS_BUTTON_NAME = "options_gear_btn"; +static const std::string ADD_LANDMARK_BUTTON_NAME = "add_landmark_btn"; +static const std::string ADD_FOLDER_BUTTON_NAME = "add_folder_btn"; +static const std::string TRASH_BUTTON_NAME = "trash_btn"; + +static const LLPlacesInventoryBridgeBuilder PLACES_INVENTORY_BUILDER; + +// helper functions +static void filter_list(LLInventorySubTreePanel* inventory_list, const std::string& string); + + LLLandmarksPanel::LLLandmarksPanel() - : LLPanelPlacesTab(), - mInventoryPanel(NULL) + : LLPanelPlacesTab() + , mFavoritesInventoryPanel(NULL) + , mLandmarksInventoryPanel(NULL) + , mMyInventoryPanel(NULL) + , mLibraryInventoryPanel(NULL) + , mCurrentSelectedList(NULL) + , mListCommands(NULL) + , mGearFolderMenu(NULL) + , mGearLandmarkMenu(NULL) { - mSavedFolderState = new LLSaveFolderState(); - mSavedFolderState->setApply(FALSE); - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_landmarks.xml"); } LLLandmarksPanel::~LLLandmarksPanel() { - delete mSavedFolderState; } BOOL LLLandmarksPanel::postBuild() @@ -68,19 +86,13 @@ BOOL LLLandmarksPanel::postBuild() if (!gInventory.isInventoryUsable()) return FALSE; - mInventoryPanel = getChild<LLInventoryPanel>("landmarks_list"); - mInventoryPanel->setFilterTypes(0x1 << LLInventoryType::IT_LANDMARK); - mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - mInventoryPanel->openDefaultFolderForType(LLAssetType::AT_LANDMARK); - mInventoryPanel->setSelectCallback(boost::bind(&LLLandmarksPanel::onSelectionChange, this, _1, _2)); + // mast be called before any other initXXX methods to init Gear menu + initListCommandsHandlers(); - LLFolderView* root_folder = mInventoryPanel->getRootFolder(); - root_folder->setReshapeCallback(boost::bind(&LLLandmarksPanel::onSelectionChange, this, _1, _2)); - - mActionBtn = getChild<LLButton>("selector"); - root_folder->addChild(mActionBtn); - mActionBtn->setEnabled(TRUE); - childSetAction("selector", boost::bind(&LLLandmarksPanel::onSelectorButtonClicked, this), this); + initFavoritesInventroyPanel(); + initLandmarksInventroyPanel(); + initMyInventroyPanel(); + initLibraryInventroyPanel(); return TRUE; } @@ -88,53 +100,21 @@ BOOL LLLandmarksPanel::postBuild() // virtual void LLLandmarksPanel::onSearchEdit(const std::string& string) { - if (string == "") - { - mInventoryPanel->setFilterSubString(LLStringUtil::null); - - // re-open folders that were initially open - mSavedFolderState->setApply(TRUE); - mInventoryPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); - LLOpenFoldersWithSelection opener; - mInventoryPanel->getRootFolder()->applyFunctorRecursively(opener); - mInventoryPanel->getRootFolder()->scrollToShowSelection(); - } - - gInventory.startBackgroundFetch(); - - if (mInventoryPanel->getFilterSubString().empty() && string.empty()) - { - // current filter and new filter empty, do nothing - return; - } - - // save current folder open state if no filter currently applied - if (mInventoryPanel->getRootFolder()->getFilterSubString().empty()) - { - mSavedFolderState->setApply(FALSE); - mInventoryPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); - } - - // set new filter string - mInventoryPanel->setFilterSubString(string); + filter_list(mFavoritesInventoryPanel, string); + filter_list(mLandmarksInventoryPanel, string); + filter_list(mMyInventoryPanel, string); + filter_list(mLibraryInventoryPanel, string); } // virtual void LLLandmarksPanel::onShowOnMap() { - LLFolderViewItem* current_item = mInventoryPanel->getRootFolder()->getCurSelectedItem(); - if (!current_item) - return; - - LLFolderViewEventListener* listenerp = current_item->getListener(); - if (listenerp->getInventoryType() != LLInventoryType::IT_LANDMARK) - return; - - LLInventoryItem* inventory_item = gInventory.getItem(listenerp->getUUID()); - if (!inventory_item) + if (NULL == mCurrentSelectedList) + { + llwarns << "There are no selected list. No actions are performed." << llendl; return; - - LLLandmark* landmark = gLandmarkList.getAsset(inventory_item->getAssetUUID()); + } + LLLandmark* landmark = getCurSelectedLandmark(); if (!landmark) return; @@ -153,9 +133,12 @@ void LLLandmarksPanel::onShowOnMap() // virtual void LLLandmarksPanel::onTeleport() { - LLFolderViewItem* current_item = mInventoryPanel->getRootFolder()->getCurSelectedItem(); + LLFolderViewItem* current_item = getCurSelectedItem(); if (!current_item) + { + llwarns << "There are no selected list. No actions are performed." << llendl; return; + } LLFolderViewEventListener* listenerp = current_item->getListener(); if (listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) @@ -164,117 +147,546 @@ void LLLandmarksPanel::onTeleport() } } -/* // virtual -void LLLandmarksPanel::onCopySLURL() +void LLLandmarksPanel::updateVerbs() { - LLFolderViewItem* current_item = mInventoryPanel->getRootFolder()->getCurSelectedItem(); - if (!current_item) + if (!isTabVisible()) return; - LLFolderViewEventListener* listenerp = current_item->getListener(); - if (listenerp->getInventoryType() != LLInventoryType::IT_LANDMARK) - return; + BOOL enabled = isLandmarkSelected(); + mTeleportBtn->setEnabled(enabled); + mShowOnMapBtn->setEnabled(enabled); - LLInventoryItem* inventory_item = gInventory.getItem(listenerp->getUUID()); - if (!inventory_item) - return; + // TODO: mantipov: Uncomment when mShareBtn is supported + // Share button should be enabled when neither a folder nor a landmark is selected + //mShareBtn->setEnabled(NULL != current_item); - LLLandmark* landmark = gLandmarkList.getAsset(inventory_item->getAssetUUID()); - if (!landmark) - return; + updateListCommands(); +} - LLVector3d landmark_global_pos; - if (!landmark->getGlobalPos(landmark_global_pos)) +void LLLandmarksPanel::onSelectionChange(LLInventorySubTreePanel* inventory_list, const std::deque<LLFolderViewItem*> &items, BOOL user_action) +{ + if (user_action && (items.size() > 0)) + { + deselectOtherThan(inventory_list); + mCurrentSelectedList = inventory_list; + } + + LLFolderViewItem* current_item = inventory_list->getRootFolder()->getCurSelectedItem(); + if (!current_item) return; - U64 new_region_handle = to_region_handle(landmark_global_pos); + updateVerbs(); +} + +void LLLandmarksPanel::onSelectorButtonClicked() +{ + // TODO: mantipov: update getting of selected item + // TODO: bind to "i" button + LLFolderViewItem* cur_item = mFavoritesInventoryPanel->getRootFolder()->getCurSelectedItem(); - LLWorldMap::url_callback_t cb = boost::bind( - &LLPanelPlacesTab::onRegionResponse, this, - landmark_global_pos, _1, _2, _3, _4); + LLFolderViewEventListener* listenerp = cur_item->getListener(); + if (listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) + { + LLSD key; + key["type"] = "landmark"; + key["id"] = listenerp->getUUID(); - LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, cb, std::string("unused"), false); + LLSideTray::getInstance()->showPanel("panel_places", key); + } +} + +////////////////////////////////////////////////////////////////////////// +// PROTECTED METHODS +////////////////////////////////////////////////////////////////////////// + +bool LLLandmarksPanel::isLandmarkSelected() const +{ + LLFolderViewItem* current_item = getCurSelectedItem(); + if(current_item && current_item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + { + return true; + } + + return false; +} + +LLLandmark* LLLandmarksPanel::getCurSelectedLandmark() const +{ + + LLFolderViewItem* cur_item = getCurSelectedItem(); + if(cur_item && cur_item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + { + return LLLandmarkActions::getLandmark(cur_item->getListener()->getUUID()); + } + return NULL; +} + +LLFolderViewItem* LLLandmarksPanel::getCurSelectedItem () const +{ + return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->getCurSelectedItem() : NULL; } -*/ // virtual -void LLLandmarksPanel::updateVerbs() +void LLLandmarksPanel::processParcelInfo(const LLParcelData& parcel_data) { - if (!isTabVisible()) - return; + //this function will be called after user will try to create a pick for selected landmark. + // We have to make request to sever to get parcel_id and snaption_id. + if(isLandmarkSelected()) + { + LLLandmark* landmark = getCurSelectedLandmark(); + LLFolderViewItem* cur_item = getCurSelectedItem(); + LLUUID id = cur_item->getListener()->getUUID(); + LLInventoryItem* inv_item = mCurrentSelectedList->getModel()->getItem(id); + if(landmark) + { + LLPanelPick* panel_pick = new LLPanelPick(TRUE); + LLSD params; + LLVector3d landmark_global_pos; + landmark->getGlobalPos(landmark_global_pos); + panel_pick->prepareNewPick(landmark_global_pos,cur_item->getName(),inv_item->getDescription(), + parcel_data.snapshot_id,parcel_data.parcel_id); + // by default save button should be enabled + panel_pick->childSetEnabled("save_changes_btn", TRUE); + // let's toggle pick panel into panel places + LLPanel* panel_places = LLSideTray::getInstance()->getChild<LLPanel>("panel_places");//-> sidebar_places + panel_places->addChild(panel_pick); + LLRect paren_rect(panel_places->getRect()); + panel_pick->reshape(paren_rect.getWidth(),paren_rect.getHeight(), TRUE); + panel_pick->setRect(paren_rect); + params["parcel_id"] =parcel_data.parcel_id; + /* set exit callback to get back onto panel places + in callback we will make cleaning up( delete pick_panel instance, + remove landmark panel from observer list + */ + panel_pick->setExitCallback(boost::bind(&LLLandmarksPanel::onPickPanelExit,this, + panel_pick, panel_places,params)); + } + } +} + +// virtual +void LLLandmarksPanel::setParcelID(const LLUUID& parcel_id) +{ + if (!parcel_id.isNull()) + { + LLRemoteParcelInfoProcessor::getInstance()->addObserver(parcel_id, this); + LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(parcel_id); + } +} + +// virtual +void LLLandmarksPanel::setErrorStatus(U32 status, const std::string& reason) +{ + llerrs<< "Can't handle remote parcel request."<< " Http Status: "<< status << ". Reason : "<< reason<<llendl; +} + + +////////////////////////////////////////////////////////////////////////// +// PRIVATE METHODS +////////////////////////////////////////////////////////////////////////// + +void LLLandmarksPanel::initFavoritesInventroyPanel() +{ + mFavoritesInventoryPanel = getChild<LLInventorySubTreePanel>("favorites_list"); + + LLUUID start_folder_id = mFavoritesInventoryPanel->getModel()->findCategoryUUIDForType(LLAssetType::AT_FAVORITE); + + initLandmarksPanel(mFavoritesInventoryPanel, start_folder_id); + + initAccordion("tab_favorites", mFavoritesInventoryPanel); +} + +void LLLandmarksPanel::initLandmarksInventroyPanel() +{ + mLandmarksInventoryPanel = getChild<LLInventorySubTreePanel>("landmarks_list"); + + LLUUID start_folder_id = mLandmarksInventoryPanel->getModel()->findCategoryUUIDForType(LLAssetType::AT_LANDMARK); + + initLandmarksPanel(mLandmarksInventoryPanel, start_folder_id); + mLandmarksInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); + + // subscribe to have auto-rename functionality while creating New Folder + mLandmarksInventoryPanel->setSelectCallback(boost::bind(&LLInventoryPanel::onSelectionChange, mLandmarksInventoryPanel, _1, _2)); + + initAccordion("tab_landmarks", mLandmarksInventoryPanel); +} + +void LLLandmarksPanel::initMyInventroyPanel() +{ + mMyInventoryPanel= getChild<LLInventorySubTreePanel>("my_inventory_list"); + + LLUUID start_folder_id = mMyInventoryPanel->getModel()->getRootFolderID(); + + initLandmarksPanel(mMyInventoryPanel, start_folder_id); + + initAccordion("tab_inventory", mMyInventoryPanel); +} + +void LLLandmarksPanel::initLibraryInventroyPanel() +{ + mLibraryInventoryPanel = getChild<LLInventorySubTreePanel>("library_list"); + + LLUUID start_folder_id = mLibraryInventoryPanel->getModel()->getLibraryRootFolderID(); + + initLandmarksPanel(mLibraryInventoryPanel, start_folder_id); + + initAccordion("tab_library", mLibraryInventoryPanel); +} + + +void LLLandmarksPanel::initLandmarksPanel(LLInventorySubTreePanel* inventory_list, const LLUUID& start_folder_id) +{ + inventory_list->buildSubtreeViewsFor(start_folder_id, &PLACES_INVENTORY_BUILDER); + + inventory_list->setFilterTypes(0x1 << LLInventoryType::IT_LANDMARK); + inventory_list->setSelectCallback(boost::bind(&LLLandmarksPanel::onSelectionChange, this, inventory_list, _1, _2)); + + inventory_list->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); + + LLPlacesFolderView* root_folder = dynamic_cast<LLPlacesFolderView*>(inventory_list->getRootFolder()); + if (root_folder) + { + root_folder->setupMenuHandle(LLInventoryType::IT_CATEGORY, mGearFolderMenu->getHandle()); + root_folder->setupMenuHandle(LLInventoryType::IT_LANDMARK, mGearLandmarkMenu->getHandle()); + } +} + +void LLLandmarksPanel::initAccordion(const std::string& accordion_tab_name, LLInventorySubTreePanel* inventory_list) +{ + LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>(accordion_tab_name); + accordion_tab->setDropDownStateChangedCallback( + boost::bind(&LLLandmarksPanel::onAccordionExpandedCollapsed, this, _2, inventory_list)); +} + +void LLLandmarksPanel::onAccordionExpandedCollapsed(const LLSD& param, LLInventorySubTreePanel* inventory_list) +{ + bool expanded = param.asBoolean(); + + if(!expanded && (mCurrentSelectedList == inventory_list)) + { + inventory_list->getRootFolder()->clearSelection(); + + mCurrentSelectedList = NULL; + updateVerbs(); + } +} + +void LLLandmarksPanel::deselectOtherThan(const LLInventorySubTreePanel* inventory_list) +{ + if (inventory_list != mFavoritesInventoryPanel) + { + mFavoritesInventoryPanel->getRootFolder()->clearSelection(); + } + + if (inventory_list != mLandmarksInventoryPanel) + { + mLandmarksInventoryPanel->getRootFolder()->clearSelection(); + } + if (inventory_list != mMyInventoryPanel) + { + mMyInventoryPanel->getRootFolder()->clearSelection(); + } + if (inventory_list != mLibraryInventoryPanel) + { + mLibraryInventoryPanel->getRootFolder()->clearSelection(); + } +} + +// List Commands Handlers +void LLLandmarksPanel::initListCommandsHandlers() +{ + mListCommands = getChild<LLPanel>("bottom_panel"); - BOOL enabled = FALSE; + mListCommands->childSetAction(OPTIONS_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onActionsButtonClick, this)); + mListCommands->childSetAction(ADD_LANDMARK_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onAddLandmarkButtonClick, this)); + mListCommands->childSetAction(ADD_FOLDER_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onAddFolderButtonClick, this)); + mListCommands->childSetAction(TRASH_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onTrashButtonClick, this)); + + + mCommitCallbackRegistrar.add("Places.LandmarksGear.Add.Action", boost::bind(&LLLandmarksPanel::onAddAction, this, _2)); + mCommitCallbackRegistrar.add("Places.LandmarksGear.CopyPaste.Action", boost::bind(&LLLandmarksPanel::onCopyPasteAction, this, _2)); + mCommitCallbackRegistrar.add("Places.LandmarksGear.Custom.Action", boost::bind(&LLLandmarksPanel::onCustomAction, this, _2)); + mCommitCallbackRegistrar.add("Places.LandmarksGear.Folding.Action", boost::bind(&LLLandmarksPanel::onFoldingAction, this, _2)); + mEnableCallbackRegistrar.add("Places.LandmarksGear.Enable", boost::bind(&LLLandmarksPanel::isActionEnabled, this, _2)); + mGearLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_places_gear_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mGearFolderMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_places_gear_folder.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +} + + +void LLLandmarksPanel::updateListCommands() +{ + // TODO: should be false when "Received" folder is selected + bool add_folder_enabled = mCurrentSelectedList == mLandmarksInventoryPanel; + bool trash_enabled = false; // TODO: should be false when "Received" folder is selected + + LLFolderViewItem* current_item = getCurSelectedItem(); - LLFolderViewItem* current_item = mInventoryPanel->getRootFolder()->getCurSelectedItem(); if (current_item) { LLFolderViewEventListener* listenerp = current_item->getListener(); if (listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) { - enabled = TRUE; + trash_enabled = mCurrentSelectedList != mLibraryInventoryPanel; } } - mTeleportBtn->setEnabled(enabled); - mShowOnMapBtn->setEnabled(enabled); + // keep Options & Add Landmark buttons always enabled + mListCommands->childSetEnabled(ADD_FOLDER_BUTTON_NAME, add_folder_enabled); + mListCommands->childSetEnabled(TRASH_BUTTON_NAME, trash_enabled); } -void LLLandmarksPanel::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action) +void LLLandmarksPanel::onActionsButtonClick() { - LLFolderViewItem* current_item = mInventoryPanel->getRootFolder()->getCurSelectedItem(); - if (!current_item) + LLFolderViewItem* cur_item = NULL; + if(mCurrentSelectedList) + cur_item = mCurrentSelectedList->getRootFolder()->getCurSelectedItem(); + + if(!cur_item) return; - - LLFolderViewEventListener* listenerp = current_item->getListener(); + + LLFolderViewEventListener* listenerp = cur_item->getListener(); + + LLMenuGL* menu =NULL; if (listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) { - S32 bottom = 0; - LLFolderViewItem* folder = current_item->getParentFolder(); + menu = mGearLandmarkMenu; + } + else if (listenerp->getInventoryType() == LLInventoryType::IT_CATEGORY) + { + mGearFolderMenu->getChild<LLMenuItemCallGL>("expand")->setVisible(!cur_item->isOpen()); + mGearFolderMenu->getChild<LLMenuItemCallGL>("collapse")->setVisible(cur_item->isOpen()); + menu = mGearFolderMenu; + } + if(menu) + { + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + LLView* actions_btn = getChild<LLView>(OPTIONS_BUTTON_NAME); + S32 menu_x, menu_y; + actions_btn->localPointToOtherView(0,actions_btn->getRect().getHeight(),&menu_x,&menu_y, this); + menu_y += menu->getRect().getHeight(); + LLMenuGL::showPopup(this, menu, menu_x,menu_y); + } +} - while ( folder->getParentFolder() != NULL ) +void LLLandmarksPanel::onAddLandmarkButtonClick() const +{ + if(LLLandmarkActions::landmarkAlreadyExists()) + { + std::string location; + LLAgentUI::buildLocationString(location, LLAgentUI::LOCATION_FORMAT_FULL); + llwarns<<" Landmark already exists at location: "<< location<<llendl; + return; + } + LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); +} + +void LLLandmarksPanel::onAddFolderButtonClick() const +{ + LLFolderViewItem* item = getCurSelectedItem(); + if(item && mCurrentSelectedList == mLandmarksInventoryPanel) + { + LLFolderBridge *parentBridge = NULL; + if(item-> getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + { + parentBridge = dynamic_cast<LLFolderBridge*>(item->getParentFolder()->getListener()); + /*WORKAROUND:* + LLFolderView::doIdle() is calling in each frame, + it changes selected items before LLFolderView::startRenamingSelectedItem. + To avoid it we have to change keyboardFocus. + */ + gFocusMgr.setKeyboardFocus(item->getParentFolder()); + } + else if (item-> getListener()->getInventoryType() == LLInventoryType::IT_CATEGORY) { - bottom += folder->getRect().mBottom; - folder = folder->getParentFolder(); + parentBridge = dynamic_cast<LLFolderBridge*>(item->getListener()); + gFocusMgr.setKeyboardFocus(item); } + menu_create_inventory_item(mCurrentSelectedList->getRootFolder(),parentBridge, LLSD("category")); + } +} - LLRect rect = current_item->getRect(); - LLRect btn_rect( - rect.mRight - mActionBtn->getRect().getWidth(), - bottom + rect.mTop, - rect.mRight, - bottom + rect.mBottom); +void LLLandmarksPanel::onTrashButtonClick() const +{ + if(!mCurrentSelectedList) return; - mActionBtn->setRect(btn_rect); + mCurrentSelectedList->getRootFolder()->doToSelected(mCurrentSelectedList->getModel(), "delete"); +} - if (!mActionBtn->getVisible()) - mActionBtn->setVisible(TRUE); +void LLLandmarksPanel::onAddAction(const LLSD& userdata) const +{ + std::string command_name = userdata.asString(); + if("add_landmark" == command_name) + { + onAddLandmarkButtonClick(); + } + else if ("category" == command_name) + { + onAddFolderButtonClick(); + } +} + +void LLLandmarksPanel::onCopyPasteAction(const LLSD& userdata) const +{ + if(!mCurrentSelectedList) + return; + std::string command_name = userdata.asString(); + if("copy_slurl" == command_name) + { + LLFolderViewItem* cur_item = getCurSelectedItem(); + if(cur_item) + LLLandmarkActions::copySLURLtoClipboard(cur_item->getListener()->getUUID()); + } + else if ( "paste" == command_name) + { + mCurrentSelectedList->getRootFolder()->paste(); + } + else if ( "cut" == command_name) + { + mCurrentSelectedList->getRootFolder()->cut(); } else { - if (mActionBtn->getVisible()) - mActionBtn->setVisible(FALSE); + mCurrentSelectedList->getRootFolder()->doToSelected(mCurrentSelectedList->getModel(),command_name); } - - updateVerbs(); } -void LLLandmarksPanel::onSelectorButtonClicked() +void LLLandmarksPanel::onFoldingAction(const LLSD& userdata) const { - LLFolderViewItem* cur_item = mInventoryPanel->getRootFolder()->getCurSelectedItem(); + if(!mCurrentSelectedList) return; - LLFolderViewEventListener* listenerp = cur_item->getListener(); - if (listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderView* root_folder = mCurrentSelectedList->getRootFolder(); + std::string command_name = userdata.asString(); + + if ("expand_all" == command_name) { - LLSD key; - key["type"] = "landmark"; - key["id"] = listenerp->getUUID(); + root_folder->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_DOWN); + root_folder->arrangeAll(); + } + else if ("collapse_all" == command_name) + { + root_folder->closeAllFolders(); + } + else + { + root_folder->doToSelected(&gInventory, userdata); + } +} - LLSideTray::getInstance()->showPanel("panel_places", key); +bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const +{ + std::string command_name = userdata.asString(); + if("category" == command_name) + { + return mCurrentSelectedList == mLandmarksInventoryPanel; + } + else if("paste" == command_name) + { + return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->canPaste() : false; + } + return true; +} + +void LLLandmarksPanel::onCustomAction(const LLSD& userdata) +{ + LLFolderViewItem* cur_item = getCurSelectedItem(); + if(!cur_item) + return ; + std::string command_name = userdata.asString(); + if("more_info" == command_name) + { + cur_item->getListener()->performAction(mCurrentSelectedList->getRootFolder(),mCurrentSelectedList->getModel(),"about"); } + else if ("teleport" == command_name) + { + onTeleport(); + } + else if ("show_on_map" == command_name) + { + onShowOnMap(); + } + else if ("create_pick" == command_name) + { + LLLandmark* landmark = getCurSelectedLandmark(); + if(!landmark) return; + + LLViewerRegion* region = gAgent.getRegion(); + if (!region) return; + + LLGlobalVec pos_global; + LLUUID region_id; + landmark->getGlobalPos(pos_global); + landmark->getRegionID(region_id); + LLVector3 region_pos((F32)fmod(pos_global.mdV[VX], (F64)REGION_WIDTH_METERS), + (F32)fmod(pos_global.mdV[VY], (F64)REGION_WIDTH_METERS), + (F32)pos_global.mdV[VZ]); + + LLSD body; + std::string url = region->getCapability("RemoteParcelRequest"); + if (!url.empty()) + { + body["location"] = ll_sd_from_vector3(region_pos); + if (!region_id.isNull()) + { + body["region_id"] = region_id; + } + if (!pos_global.isExactlyZero()) + { + U64 region_handle = to_region_handle(pos_global); + body["region_handle"] = ll_sd_from_U64(region_handle); + } + LLHTTPClient::post(url, body, new LLRemoteParcelRequestResponder(getObserverHandle())); + } + else + { + llwarns << "Can't create pick for landmark for region" << region_id + << ". Region: " << region->getName() + << " does not support RemoteParcelRequest" << llendl; + } + } +} + +void LLLandmarksPanel::onPickPanelExit( LLPanelPick* pick_panel, LLView* owner, const LLSD& params) +{ + pick_panel->setVisible(FALSE); + owner->removeChild(pick_panel); + //we need remove observer to avoid processParcelInfo in the future. + LLRemoteParcelInfoProcessor::getInstance()->removeObserver(params["parcel_id"].asUUID(), this); + + delete pick_panel; + pick_panel = NULL; } -void LLLandmarksPanel::setSelectedItem(const LLUUID& obj_id) + +////////////////////////////////////////////////////////////////////////// +// HELPER FUNCTIONS +////////////////////////////////////////////////////////////////////////// +static void filter_list(LLInventorySubTreePanel* inventory_list, const std::string& string) { - mInventoryPanel->setSelection(obj_id, FALSE); + if (string == "") + { + inventory_list->setFilterSubString(LLStringUtil::null); + + // re-open folders that were initially open + inventory_list->restoreFolderState(); + } + + gInventory.startBackgroundFetch(); + + if (inventory_list->getFilterSubString().empty() && string.empty()) + { + // current filter and new filter empty, do nothing + return; + } + + // save current folder open state if no filter currently applied + if (inventory_list->getRootFolder()->getFilterSubString().empty()) + { + inventory_list->saveFolderState(); + } + + // set new filter string + inventory_list->setFilterSubString(string); } +// EOF diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index 14cbbd6123..e74a7fdc88 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -33,14 +33,20 @@ #ifndef LL_LLPANELLANDMARKS_H #define LL_LLPANELLANDMARKS_H +#include "lllandmark.h" + +// newview #include "llinventorymodel.h" #include "llpanelplacestab.h" +#include "llpanelpick.h" +#include "llremoteparcelrequest.h" class LLFolderViewItem; +class LLMenuGL; class LLInventoryPanel; -class LLSaveFolderState; +class LLInventorySubTreePanel; -class LLLandmarksPanel : public LLPanelPlacesTab +class LLLandmarksPanel : public LLPanelPlacesTab, LLRemoteParcelInfoObserver { public: LLLandmarksPanel(); @@ -50,17 +56,58 @@ public: /*virtual*/ void onSearchEdit(const std::string& string); /*virtual*/ void onShowOnMap(); /*virtual*/ void onTeleport(); - ///*virtual*/ void onCopySLURL(); /*virtual*/ void updateVerbs(); - void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action); + void onSelectionChange(LLInventorySubTreePanel* inventory_list, const std::deque<LLFolderViewItem*> &items, BOOL user_action); void onSelectorButtonClicked(); - void setSelectedItem(const LLUUID& obj_id); + +protected: + /** + * @return true - if current selected panel is not null and selected item is a landmark + */ + bool isLandmarkSelected() const; + LLLandmark* getCurSelectedLandmark() const; + LLFolderViewItem* getCurSelectedItem () const; + //LLRemoteParcelInfoObserver interface + /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); + /*virtual*/ void setParcelID(const LLUUID& parcel_id); + /*virtual*/ void setErrorStatus(U32 status, const std::string& reason); + private: - LLInventoryPanel* mInventoryPanel; - LLSaveFolderState* mSavedFolderState; - LLButton* mActionBtn; + void initFavoritesInventroyPanel(); + void initLandmarksInventroyPanel(); + void initMyInventroyPanel(); + void initLibraryInventroyPanel(); + void initLandmarksPanel(LLInventorySubTreePanel* inventory_list, const LLUUID& start_folder_id); + void initAccordion(const std::string& accordion_tab_name, LLInventorySubTreePanel* inventory_list); + void onAccordionExpandedCollapsed(const LLSD& param, LLInventorySubTreePanel* inventory_list); + void deselectOtherThan(const LLInventorySubTreePanel* inventory_list); + + // List Commands Handlers + void initListCommandsHandlers(); + void updateListCommands(); + void onActionsButtonClick(); + void onAddLandmarkButtonClick() const; + void onAddFolderButtonClick() const; + void onTrashButtonClick() const; + void onAddAction(const LLSD& command_name) const; + void onCopyPasteAction(const LLSD& command_name) const; + void onFoldingAction(const LLSD& command_name) const; + bool isActionEnabled(const LLSD& command_name) const; + void onCustomAction(const LLSD& command_name); + void onPickPanelExit( LLPanelPick* pick_panel, LLView* owner, const LLSD& params); + +private: + LLInventorySubTreePanel* mFavoritesInventoryPanel; + LLInventorySubTreePanel* mLandmarksInventoryPanel; + LLInventorySubTreePanel* mMyInventoryPanel; + LLInventorySubTreePanel* mLibraryInventoryPanel; + LLMenuGL* mGearLandmarkMenu; + LLMenuGL* mGearFolderMenu; + LLInventorySubTreePanel* mCurrentSelectedList; + + LLPanel* mListCommands; }; #endif //LL_LLPANELLANDMARKS_H diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h index 82cba72bc4..7cc2a04c53 100644 --- a/indra/newview/llpanelpick.h +++ b/indra/newview/llpanelpick.h @@ -39,6 +39,7 @@ #include "llpanel.h" #include "llremoteparcelrequest.h" +#include "llavatarpropertiesprocessor.h" class LLTextureCtrl; class LLMessageSystem; diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index d415b17538..b2541ac1b0 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -650,6 +650,23 @@ void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param) mPlaceInfo->createPick(mPosGlobal, mPickPanel); } + else if (item == "add_to_favbar") + { + if ( mItem.notNull() ) + { + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE); + if ( favorites_id.notNull() ) + { + copy_inventory_item(gAgent.getID(), + mItem->getPermissions().getOwner(), + mItem->getUUID(), + favorites_id, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + llinfos << "Copied inventory item #" << mItem->getUUID() << " to favorites." << llendl; + } + } + } } void LLPanelPlaces::onBackButtonClicked() diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index e1e3fe4677..96efb885dd 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -47,6 +47,10 @@ #include "lllandmarkactions.h" #include "llclipboard.h" +// Maximum number of items that can be added to a list in one pass. +// Used to limit time spent for items list update per frame. +static const U32 ADD_LIMIT = 50; + class LLTeleportHistoryFlatItem : public LLPanel { public: @@ -56,6 +60,7 @@ public: virtual BOOL postBuild(); S32 getIndex() { return mIndex; } + const std::string& getRegionName() { return mRegionName;} /*virtual*/ void setValue(const LLSD& value); @@ -211,9 +216,10 @@ void LLTeleportHistoryPanel::ContextMenu::onCopy() LLTeleportHistoryPanel::LLTeleportHistoryPanel() : LLPanelPlacesTab(), mFilterSubString(LLStringUtil::null), + mDirty(true), + mCurrentItem(0), mTeleportHistory(NULL), mHistoryAccordion(NULL), - mStarButton(NULL), mAccordionTabMenu(NULL), mLastSelectedScrollList(NULL) { @@ -277,13 +283,19 @@ BOOL LLTeleportHistoryPanel::postBuild() if(gear_menu) mGearMenuHandle = gear_menu->getHandle(); - mStarButton = getChild<LLButton>("star_btn"); - mStarButton->setCommitCallback(boost::bind(&LLTeleportHistoryPanel::onStarButtonCommit, this)); - return TRUE; } // virtual +void LLTeleportHistoryPanel::draw() +{ + if (mDirty) + refresh(); + + LLPanelPlacesTab::draw(); +} + +// virtual void LLTeleportHistoryPanel::onSearchEdit(const std::string& string) { if (mFilterSubString != string) @@ -361,8 +373,6 @@ void LLTeleportHistoryPanel::updateVerbs() { mTeleportBtn->setEnabled(false); mShowOnMapBtn->setEnabled(false); - mStarButton->setEnabled(false); - mStarButton->setToolTip(LLStringExplicit("")); return; } @@ -370,141 +380,138 @@ void LLTeleportHistoryPanel::updateVerbs() mTeleportBtn->setEnabled(NULL != itemp && itemp->getIndex() < (S32)mTeleportHistory->getItems().size() - 1); mShowOnMapBtn->setEnabled(NULL != itemp); +} + +void LLTeleportHistoryPanel::getNextTab(const LLDate& item_date, S32& tab_idx, LLDate& tab_date) +{ + const U32 seconds_in_day = 24 * 60 * 60; + + S32 tabs_cnt = mItemContainers.size(); + S32 curr_year = 0, curr_month = 0, curr_day = 0; + + tab_date = LLDate::now(); + tab_date.split(&curr_year, &curr_month, &curr_day); + tab_date.fromYMDHMS(curr_year, curr_month, curr_day); // Set hour, min, and sec to 0 + tab_date.secondsSinceEpoch(tab_date.secondsSinceEpoch() + seconds_in_day); - if (NULL != itemp) + tab_idx = -1; + + while (tab_idx < tabs_cnt - 1 && item_date < tab_date) { - LLViewerInventoryItem *landmark = LLLandmarkActions::findLandmarkForGlobalPos( - mTeleportHistory->getItems()[itemp->getIndex()].mGlobalPos); + tab_idx++; - mStarButton->setEnabled(true); - if (!landmark || landmark->getUUID().isNull()) + if (tab_idx <= tabs_cnt - 4) { - mStarButton->setToggleState(true); - // Landmark can be created only for current agent positon, which is most recent (last) item in teleport history. - // mTeleportBtn is disabled only for that item. - mStarButton->setToolTip(mTeleportBtn->getEnabled() ? getString("cant_create_lm_here") : getString("create_landmark")); + tab_date.secondsSinceEpoch(tab_date.secondsSinceEpoch() - seconds_in_day); } - else + else if (tab_idx == tabs_cnt - 3) // 6 day and older, low boundary is 1 month { - mStarButton->setToggleState(false); - mStarButton->setToolTip(getString("open_landmark")); + tab_date = LLDate::now(); + tab_date.split(&curr_year, &curr_month, &curr_day); + curr_month--; + if (0 == curr_month) + { + curr_month = 12; + curr_year--; + } + tab_date.fromYMDHMS(curr_year, curr_month, curr_day); + } + else if (tab_idx == tabs_cnt - 2) // 1 month and older, low boundary is 6 months + { + tab_date = LLDate::now(); + tab_date.split(&curr_year, &curr_month, &curr_day); + if (curr_month > 6) + { + curr_month -= 6; + } + else + { + curr_month += 6; + curr_year--; + } + tab_date.fromYMDHMS(curr_year, curr_month, curr_day); + } + else // 6 months and older + { + tab_date.secondsSinceEpoch(0); } - } - else - { - mStarButton->setEnabled(false); - mStarButton->setToolTip(LLStringExplicit("")); } } -void LLTeleportHistoryPanel::showTeleportHistory() +void LLTeleportHistoryPanel::refresh() { if (!mHistoryAccordion) + { + mDirty = false; return; + } - const LLTeleportHistoryStorage::slurl_list_t& hist_items = mTeleportHistory->getItems(); - - const U32 seconds_in_day = 24 * 60 * 60; - LLDate curr_date = LLDate::now(); - - S32 curr_tab = -1; - S32 tabs_cnt = mItemContainers.size(); - S32 curr_year = 0, curr_month = 0, curr_day = 0; - - curr_date.split(&curr_year, &curr_month, &curr_day); - curr_date.fromYMDHMS(curr_year, curr_month, curr_day); // Set hour, min, and sec to 0 - curr_date.secondsSinceEpoch(curr_date.secondsSinceEpoch() + seconds_in_day); + const LLTeleportHistoryStorage::slurl_list_t& items = mTeleportHistory->getItems(); + LLDate tab_boundary_date = LLDate::now(); LLFlatListView* curr_flat_view = NULL; - S32 index = hist_items.size() - 1; - - for (LLTeleportHistoryStorage::slurl_list_t::const_reverse_iterator iter = hist_items.rbegin(); - iter != hist_items.rend(); ++iter, --index) + U32 added_items = 0; + while (mCurrentItem >= 0) { - std::string landmark_title = (*iter).mTitle; + std::string landmark_title = items[mCurrentItem].mTitle; LLStringUtil::toUpper(landmark_title); std::string::size_type match_offset = mFilterSubString.size() ? landmark_title.find(mFilterSubString) : std::string::npos; bool passed = mFilterSubString.size() == 0 || match_offset != std::string::npos; if (!passed) + { + mCurrentItem--; continue; + } - if (curr_tab < tabs_cnt - 1) - { - const LLDate &date = (*iter).mDate; + const LLDate &date = items[mCurrentItem].mDate; - if (date < curr_date) - { - LLAccordionCtrlTab* tab = NULL; - while (curr_tab < tabs_cnt - 1 && date < curr_date) - { - curr_tab++; - - tab = mItemContainers.get(mItemContainers.size() - 1 - curr_tab); - tab->setVisible(false); - - if (curr_tab <= tabs_cnt - 4) - { - curr_date.secondsSinceEpoch(curr_date.secondsSinceEpoch() - seconds_in_day); - } - else if (curr_tab == tabs_cnt - 3) // 6 day and older, low boundary is 1 month - { - curr_date = LLDate::now(); - curr_date.split(&curr_year, &curr_month, &curr_day); - curr_month--; - if (0 == curr_month) - { - curr_month = 12; - curr_year--; - } - curr_date.fromYMDHMS(curr_year, curr_month, curr_day); - } - else if (curr_tab == tabs_cnt - 2) // 1 month and older, low boundary is 6 months - { - curr_date = LLDate::now(); - curr_date.split(&curr_year, &curr_month, &curr_day); - if (curr_month > 6) - { - curr_month -= 6; - } - else - { - curr_month += 6; - curr_year--; - } - curr_date.fromYMDHMS(curr_year, curr_month, curr_day); - } - else // 6 months and older - { - curr_date.secondsSinceEpoch(0); - } - } + if (date < tab_boundary_date) + { + S32 tab_idx = 0; + getNextTab(date, tab_idx, tab_boundary_date); - tab->setVisible(true); + LLAccordionCtrlTab* tab = mItemContainers.get(mItemContainers.size() - 1 - tab_idx); + tab->setVisible(true); - curr_flat_view = getFlatListViewFromTab(tab); - if (curr_flat_view) - { - curr_flat_view->clear(); - } - } + curr_flat_view = getFlatListViewFromTab(tab); } if (curr_flat_view) - { - curr_flat_view->addItem(new LLTeleportHistoryFlatItem(index, &mContextMenu, (*iter).mTitle)); - } - } + curr_flat_view->addItem(new LLTeleportHistoryFlatItem(mCurrentItem, &mContextMenu, items[mCurrentItem].mTitle)); + + mCurrentItem--; - // Hide empty tabs from current to bottom - for (curr_tab++; curr_tab < tabs_cnt; curr_tab++) - mItemContainers.get(mItemContainers.size() - 1 - curr_tab)->setVisible(false); + if (++added_items >= ADD_LIMIT) + break; + } mHistoryAccordion->arrange(); updateVerbs(); + + if (mCurrentItem < 0) + mDirty = false; +} + +void LLTeleportHistoryPanel::showTeleportHistory() +{ + mDirty = true; + mCurrentItem = mTeleportHistory->getItems().size() - 1; + + for (S32 n = mItemContainers.size() - 1; n >= 0; --n) + { + LLAccordionCtrlTab* tab = mItemContainers.get(n); + tab->setVisible(false); + + LLFlatListView* fv = getFlatListViewFromTab(tab); + if (fv) + fv->clear(); + } + + refresh(); } void LLTeleportHistoryPanel::handleItemSelect(LLFlatListView* selected) @@ -667,28 +674,3 @@ void LLTeleportHistoryPanel::onGearButtonClicked() LLMenuGL::showPopup(this, menu, menu_x, menu_y); } -void LLTeleportHistoryPanel::onStarButtonCommit() -{ - if (!mLastSelectedScrollList) - return; - - LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedScrollList->getSelectedItem()); - if(!itemp) - return; - - if (itemp->getIndex() < (S32)mTeleportHistory->getItems().size() - 1) - { - LLTeleportHistoryFlatItem::showPlaceInfoPanel(itemp->getIndex()); - } - else - { - LLViewerInventoryItem *landmark = LLLandmarkActions::findLandmarkForGlobalPos( - mTeleportHistory->getItems()[itemp->getIndex()].mGlobalPos); - - if (!landmark || landmark->getUUID().isNull()) - LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); - else - LLTeleportHistoryFlatItem::showPlaceInfoPanel(itemp->getIndex()); - } -} - diff --git a/indra/newview/llpanelteleporthistory.h b/indra/newview/llpanelteleporthistory.h index f487c92836..bc3595e66d 100644 --- a/indra/newview/llpanelteleporthistory.h +++ b/indra/newview/llpanelteleporthistory.h @@ -69,6 +69,8 @@ public: virtual ~LLTeleportHistoryPanel(); /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ void onSearchEdit(const std::string& string); /*virtual*/ void onShowOnMap(); /*virtual*/ void onTeleport(); @@ -86,17 +88,19 @@ private: void onClearTeleportHistory(); bool onClearTeleportHistoryDialog(const LLSD& notification, const LLSD& response); + void refresh(); + void getNextTab(const LLDate& item_date, S32& curr_tab, LLDate& tab_date); void showTeleportHistory(); void handleItemSelect(LLFlatListView* ); LLFlatListView* getFlatListViewFromTab(LLAccordionCtrlTab *); void onGearButtonClicked(); - void onStarButtonCommit(); LLTeleportHistoryStorage* mTeleportHistory; LLAccordionCtrl* mHistoryAccordion; - LLButton * mStarButton; LLFlatListView* mLastSelectedScrollList; + bool mDirty; + S32 mCurrentItem; std::string mFilterSubString; typedef LLDynamicArray<LLAccordionCtrlTab*> item_containers_t; diff --git a/indra/newview/llplacesinventorybridge.cpp b/indra/newview/llplacesinventorybridge.cpp new file mode 100644 index 0000000000..ef02882c0d --- /dev/null +++ b/indra/newview/llplacesinventorybridge.cpp @@ -0,0 +1,212 @@ +/** + * @file llplacesinventorybridge.cpp + * @brief Implementation of the Inventory-Folder-View-Bridge classes for Places Panel. + * + * $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 "llmenugl.h" + +#include "llplacesinventorybridge.h" + +#include "llfloaterinventory.h" // for LLInventoryPanel +#include "llfolderview.h" // for FIRST_SELECTED_ITEM + + +static const std::string LANDMARKS_INVENTORY_LIST_NAME("landmarks_list"); + +bool is_landmarks_panel(const LLInventoryPanel* inv_panel) +{ + if (NULL == inv_panel) + return false; + return inv_panel->getName() == LANDMARKS_INVENTORY_LIST_NAME; +} + +void fill_items_with_menu_items(std::vector<std::string>& items, LLMenuGL& menu) +{ + LLView::child_list_const_iter_t itor; + for (itor = menu.beginChild(); itor != menu.endChild(); ++itor) + { + std::string name = (*itor)->getName(); + items.push_back(name); + } +} + +// virtual +void LLPlacesLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + std::vector<std::string> items; + std::vector<std::string> disabled_items; + + if(isInTrash()) + { + items.push_back(std::string("Purge Item")); + if (!isItemRemovable()) + { + disabled_items.push_back(std::string("Purge Item")); + } + + items.push_back(std::string("Restore Item")); + } + else + { + fill_items_with_menu_items(items, menu); + + // Disable "Landmark More Information" menu item for + // multiple landmarks selected. Only one landmark + // info panel can be shown at a time. + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("more_info")); + } + } + + hideContextEntries(menu, items, disabled_items); +} + + + +void LLPlacesFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + { + std::vector<std::string> items; + std::vector<std::string> disabled_items; + + LLInventoryPanel* inv_panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); + bool is_open = false; + bool disable_changing = true; + if (inv_panel) + { + LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getRootFolder()->getItemByID(mUUID)); + is_open = (NULL != folder) && folder->isOpen(); + + disable_changing = !is_landmarks_panel(inv_panel); + } + + // collect all items' names + fill_items_with_menu_items(items, menu); + + // remove expand or collapse menu item depend on folder state + std::string collapse_expand_item_to_hide(is_open ? "expand" : "collapse"); + std::vector<std::string>::iterator it = std::find(items.begin(), items.end(), collapse_expand_item_to_hide); + if (it != items.end()) items.erase(it); + + if (disable_changing) + { + disabled_items.push_back(std::string("add_folder")); + disabled_items.push_back(std::string("rename")); + disabled_items.push_back(std::string("delete")); + } + + // repeat parent functionality + sSelf = this; // necessary for "New Folder" functionality + + hideContextEntries(menu, items, disabled_items); + } +} + +//virtual +void LLPlacesFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +{ + if ("expand" == action) + { + LLFolderViewFolder* act_folder = getFolder(); + act_folder->toggleOpen(); + } + else if ("collapse" == action) + { + LLFolderViewFolder* act_folder = getFolder(); + act_folder->toggleOpen(); + } + else + { + LLFolderBridge::performAction(folder, model, action); + } +} + +LLFolderViewFolder* LLPlacesFolderBridge::getFolder() +{ + LLFolderViewFolder* folder = NULL; + LLInventoryPanel* inv_panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); + if (inv_panel) + { + folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getRootFolder()->getItemByID(mUUID)); + } + + return folder; +} + +// virtual +LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( + LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + const LLUUID& uuid, + U32 flags/* = 0x00*/) const +{ + LLInvFVBridge* new_listener = NULL; + switch(asset_type) + { + case LLAssetType::AT_LANDMARK: + if(!(inv_type == LLInventoryType::IT_LANDMARK)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLPlacesLandmarkBridge(inv_type, inventory, uuid, flags); + break; + case LLAssetType::AT_CATEGORY: + if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) + { + // *TODO: Create a link folder handler instead if it is necessary + new_listener = LLInventoryFVBridgeBuilder::createBridge( + asset_type, + actual_asset_type, + inv_type, + inventory, + uuid, + flags); + break; + } + new_listener = new LLPlacesFolderBridge(inv_type, inventory, uuid); + break; + default: + new_listener = LLInventoryFVBridgeBuilder::createBridge( + asset_type, + actual_asset_type, + inv_type, + inventory, + uuid, + flags); + } + return new_listener; +} + +// EOF diff --git a/indra/newview/llplacesinventorybridge.h b/indra/newview/llplacesinventorybridge.h new file mode 100644 index 0000000000..66a8e8e54d --- /dev/null +++ b/indra/newview/llplacesinventorybridge.h @@ -0,0 +1,91 @@ +/** + * @file llplacesinventorybridge.h + * @brief Declaration of the Inventory-Folder-View-Bridge classes for Places Panel. + * + * $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$ + */ + +#ifndef LL_LLPLACESINVENTORYBRIDGE_H +#define LL_LLPLACESINVENTORYBRIDGE_H + +#include "llinventorybridge.h" + +class LLFolderViewFolder; + +/** + * Overridden version of the Inventory-Folder-View-Bridge for Places Panel (Landmarks Tab) + */ +class LLPlacesLandmarkBridge : public LLLandmarkBridge +{ + friend class LLPlacesInventoryBridgeBuilder; + +public: + /*virtual*/ void buildContextMenu(LLMenuGL& menu, U32 flags); + +protected: + LLPlacesLandmarkBridge(LLInventoryType::EType type, LLInventoryPanel* inventory, const LLUUID& uuid, U32 flags = 0x00) + : LLLandmarkBridge(inventory, uuid, flags) {mInvType = type;} +}; + +/** + * Overridden version of the Inventory-Folder-View-Bridge for Folders + */ +class LLPlacesFolderBridge : public LLFolderBridge +{ + friend class LLPlacesInventoryBridgeBuilder; + +public: + /*virtual*/ void buildContextMenu(LLMenuGL& menu, U32 flags); + /*virtual*/ void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + +protected: + LLPlacesFolderBridge(LLInventoryType::EType type, LLInventoryPanel* inventory, const LLUUID& uuid) + : LLFolderBridge(inventory, uuid) {mInvType = type;} + + LLFolderViewFolder* getFolder(); +}; + + +/** + * This class intended to override default InventoryBridgeBuilder for Inventory Panel. + * + * It builds Bridges for Landmarks and Folders in Places Landmarks Panel + */ +class LLPlacesInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder +{ +public: + /*virtual*/ LLInvFVBridge* createBridge( + LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + const LLUUID& uuid, + U32 flags = 0x00) const; +}; + +#endif // LL_LLPLACESINVENTORYBRIDGE_H diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp index 4db849c159..ff95f8adce 100644 --- a/indra/newview/llsidetray.cpp +++ b/indra/newview/llsidetray.cpp @@ -559,6 +559,15 @@ void LLSideTray::highlightFocused() } +BOOL LLSideTray::handleScrollWheel(S32 x, S32 y, S32 mask) +{ + BOOL ret = LLPanel::handleScrollWheel(x,y,mask); + + if(!ret && childFromPoint(x,y) != 0 ) + return TRUE;//mouse wheel over sidetray buttons, eat mouse wheel + return ret; +} + //virtual BOOL LLSideTray::handleMouseDown (S32 x, S32 y, MASK mask) { @@ -641,7 +650,9 @@ LLPanel* LLSideTray::showPanel (const std::string& panel_name, const LLSD& para LLView* view = (*child_it)->findChildView(panel_name,true); if(view) { - onTabButtonClick((*child_it)->getName()); + selectTabByName ((*child_it)->getName()); + if(mCollapsed) + expandSideBar(); LLSideTrayPanelContainer* container = dynamic_cast<LLSideTrayPanelContainer*>(view->getParent()); if(container) diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h index 13acbbb659..6ea6bafac9 100644 --- a/indra/newview/llsidetray.h +++ b/indra/newview/llsidetray.h @@ -203,6 +203,7 @@ public: bool addChild (LLView* view, S32 tab_group); BOOL handleMouseDown (S32 x, S32 y, MASK mask); + BOOL handleScrollWheel(S32 x, S32 y, S32 mask); void reshape (S32 width, S32 height, BOOL called_from_parent = TRUE); S32 getTrayWidth(); diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 67a0528a06..6714fe908f 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -45,29 +45,6 @@ #include "llnotificationmanager.h" -// IM session ID can be the same as Avatar UUID. (See LLIMMgr::computeSessionID) -// Probably notification ID also can be the same as Avatar UUID. -// In case when session ID & notification ID are the same it will be impossible to add both -// appropriate Items into Flat List. -// Functions below are intended to wrap passed LLUUID into LLSD value with different "type". -// Use them anywhere you need to add, get, remove items via the list -inline -LLSD get_notification_value(const LLUUID& notification_id) -{ - return LLSD() - .insert("type", "notification") - .insert("uuid", notification_id); -} - -inline -LLSD get_session_value(const LLUUID& session_id) -{ - return LLSD() - .insert("type", "im_chiclet") - .insert("uuid", session_id); -} - - //--------------------------------------------------------------------------------- LLSysWellWindow::LLSysWellWindow(const LLSD& key) : LLDockableFloater(NULL, key), mChannel(NULL), @@ -157,7 +134,7 @@ LLSysWellWindow::~LLSysWellWindow() //--------------------------------------------------------------------------------- void LLSysWellWindow::addItem(LLSysWellItem::Params p) { - LLSD value = get_notification_value(p.notification_id); + LLSD value = p.notification_id; // do not add clones if( mMessageList->getItemByValue(value)) return; @@ -191,7 +168,7 @@ void LLSysWellWindow::clear() //--------------------------------------------------------------------------------- void LLSysWellWindow::removeItemByID(const LLUUID& id) { - if(mMessageList->removeItemByValue(get_notification_value(id))) + if(mMessageList->removeItemByValue(id)) { handleItemRemoved(IT_NOTIFICATION); reshapeWindow(); @@ -357,7 +334,7 @@ void LLSysWellWindow::reshapeWindow() LLChiclet* LLSysWellWindow::findIMChiclet(const LLUUID& sessionId) { LLChiclet* res = NULL; - RowPanel* panel = mMessageList->getTypedItemByValue<RowPanel>(get_session_value(sessionId)); + RowPanel* panel = mMessageList->getTypedItemByValue<RowPanel>(sessionId); if (panel != NULL) { res = panel->mChiclet; @@ -371,7 +348,7 @@ void LLSysWellWindow::addIMRow(const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) { RowPanel* item = new RowPanel(this, sessionId, chicletCounter, name, otherParticipantId); - if (mMessageList->insertItemAfter(mSeparator, item, get_session_value(sessionId))) + if (mMessageList->insertItemAfter(mSeparator, item, sessionId)) { handleItemAdded(IT_INSTANT_MESSAGE); } @@ -389,7 +366,7 @@ void LLSysWellWindow::addIMRow(const LLUUID& sessionId, S32 chicletCounter, //--------------------------------------------------------------------------------- void LLSysWellWindow::delIMRow(const LLUUID& sessionId) { - if (mMessageList->removeItemByValue(get_session_value(sessionId))) + if (mMessageList->removeItemByValue(sessionId)) { handleItemRemoved(IT_INSTANT_MESSAGE); } @@ -423,7 +400,7 @@ void LLSysWellWindow::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) { //*TODO get rid of get_session_value, session_id's are unique, cause performance degradation with lots chiclets (IB) - if (mMessageList->getItemByValue(get_session_value(session_id)) == NULL) + if (mMessageList->getItemByValue(session_id) == NULL) { S32 chicletCounter = LLIMModel::getInstance()->getNumUnread(session_id); if (chicletCounter > -1) @@ -443,6 +420,17 @@ void LLSysWellWindow::sessionRemoved(const LLUUID& sessionId) LLBottomTray::getInstance()->getSysWell()->updateUreadIMNotifications(); } +void LLSysWellWindow::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) +{ + //for outgoing ad-hoc and group im sessions only + LLChiclet* chiclet = findIMChiclet(old_session_id); + if (chiclet) + { + chiclet->setSessionId(new_session_id); + mMessageList->updateValue(old_session_id, new_session_id); + } +} + void LLSysWellWindow::handleItemAdded(EItemType added_item_type) { bool should_be_shown = ++mTypedItemsCount[added_item_type] == 1 && anotherTypeExists(added_item_type); diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 0c3f4d0587..fa6a1abea4 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -108,6 +108,7 @@ private: // LLIMSessionObserver observe triggers virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); virtual void sessionRemoved(const LLUUID& session_id); + void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); // pointer to a corresponding channel's instance LLNotificationsUI::LLScreenChannel* mChannel; diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index 86b162247a..0c23947a8c 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -36,178 +36,106 @@ #include "llviewercontrol.h" #include "lluiconstants.h" #include "llrect.h" -#include "lliconctrl.h" -#include "lltexteditor.h" -#include "lltextbox.h" -#include "lldbstrings.h" -#include "llchat.h" -#include "llfloaterchat.h" #include "lltrans.h" -#include "lloverlaybar.h" - const S32 BOTTOM_PAD = VPAD * 3; +const S32 BUTTON_WIDTH = 90; //static const LLFontGL* LLToastNotifyPanel::sFont = NULL; const LLFontGL* LLToastNotifyPanel::sFontSmall = NULL; -LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : LLToastPanel(notification) { - mIsTip = notification->getType() == "notifytip"; - mNumOptions = 0; - mNumButtons = 0; - mIsScriptDialog = (notification->getName() == "ScriptDialog" - || notification->getName() == "ScriptDialogGroup"); - mAddedDefaultBtn = false; +LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : +LLToastPanel(notification), +mTextBox(NULL), +mIcon(NULL), +mInfoPanel(NULL), +mControlPanel(NULL), +mNumOptions(0), +mNumButtons(0), +mAddedDefaultBtn(false) +{ + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_notification.xml"); + mInfoPanel = getChild<LLPanel>("info_panel"); + mControlPanel = getChild<LLPanel>("control_panel"); + mIcon = getChild<LLIconCtrl>("info_icon"); - // clicking on a button does not steal current focus - setIsChrome(TRUE); + // customize panel's attributes + // is it intended for displaying a tip + mIsTip = notification->getType() == "notifytip"; + // is it a script dialog + mIsScriptDialog = (notification->getName() == "ScriptDialog" || notification->getName() == "ScriptDialogGroup"); + // is it a caution + // + // caution flag can be set explicitly by specifying it in the notification payload, or it can be set implicitly if the + // notify xml template specifies that it is a caution + // tip-style notification handle 'caution' differently -they display the tip in a different color + mIsCaution = notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH; - // class init + // setup parameters + // get a notification message + mMessage = notification->getMessage(); + // init font variables if (!sFont) { sFont = LLFontGL::getFontSansSerif(); sFontSmall = LLFontGL::getFontSansSerifSmall(); } - - // setup paramaters - mMessage = notification->getMessage(); - + // clicking on a button does not steal current focus + setIsChrome(TRUE); // initialize setFocusRoot(!mIsTip); - - // caution flag can be set explicitly by specifying it in the - // notification payload, or it can be set implicitly if the - // notify xml template specifies that it is a caution - // - // tip-style notification handle 'caution' differently - - // they display the tip in a different color - mIsCaution = notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH; - + // get a form for the notification LLNotificationFormPtr form(notification->getForm()); - + // get number of elements mNumOptions = form->getNumElements(); - LLRect rect = mIsTip ? getNotifyTipRect(mMessage) - : getNotifyRect(mNumOptions, mIsScriptDialog, mIsCaution); - setRect(rect); - setFollows(mIsTip ? (FOLLOWS_BOTTOM|FOLLOWS_RIGHT) : (FOLLOWS_TOP|FOLLOWS_RIGHT)); - setBackgroundVisible(FALSE); - setBackgroundOpaque(TRUE); - - LLIconCtrl* icon; - LLTextEditor* text; - - const S32 TOP = getRect().getHeight() - (mIsTip ? (S32)sFont->getLineHeight() : 32); - const S32 BOTTOM = (S32)sFont->getLineHeight(); - S32 x = HPAD + HPAD; - S32 y = TOP; - - LLIconCtrl::Params common_params; - common_params.rect(LLRect(x, y, x+32, TOP-32)); - common_params.mouse_opaque(false); - common_params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); + // customize panel's outfit + // preliminary adjust panel's layout + mIsTip ? adjustPanelForTipNotice() : adjustPanelForScriptNotice(form); + // choose a right icon if (mIsTip) { // use the tip notification icon - common_params.image(LLUI::getUIImage("notify_tip_icon.tga")); - icon = LLUICtrlFactory::create<LLIconCtrl> (common_params); + mIcon->setValue("notify_tip_icon.tga"); + LLRect icon_rect = mIcon->getRect(); + icon_rect.setLeftTopAndSize(icon_rect.mLeft, getRect().getHeight() - VPAD, icon_rect.getWidth(), icon_rect.getHeight()); + mIcon->setRect(icon_rect); } else if (mIsCaution) { // use the caution notification icon - common_params.image(LLUI::getUIImage("notify_caution_icon.tga")); - icon = LLUICtrlFactory::create<LLIconCtrl> (common_params); + mIcon->setValue("notify_caution_icon.tga"); } else { // use the default notification icon - common_params.image(LLUI::getUIImage("notify_box_icon.tga")); - icon = LLUICtrlFactory::create<LLIconCtrl> (common_params); + mIcon->setValue("notify_box_icon.tga"); } - icon->setMouseOpaque(FALSE); - addChild(icon); - - x += HPAD + HPAD + 32; - + // adjust text options according to the notification type // add a caution textbox at the top of a caution notification - LLTextBox* caution_box = NULL; if (mIsCaution && !mIsTip) { - S32 caution_height = ((S32)sFont->getLineHeight() * 2) + VPAD; - LLTextBox::Params params; - params.name("caution_box"); - params.rect(LLRect(x, y, getRect().getWidth() - 2, caution_height)); - params.font(sFont); - params.mouse_opaque(false); - params.font.style("BOLD"); - params.text_color(LLUIColorTable::instance().getColor("NotifyCautionWarnColor")); - params.bg_readonly_color(LLUIColorTable::instance().getColor("NotifyCautionBoxColor")); - params.border_visible(false); - params.wrap(true); - caution_box = LLUICtrlFactory::create<LLTextBox> (params); - caution_box->setValue(notification->getMessage()); - - addChild(caution_box); - - // adjust the vertical position of the next control so that - // it appears below the caution textbox - y = y - caution_height; + mTextBox = getChild<LLTextBox>("caution_text_box"); } else { - - const S32 BTN_TOP = BOTTOM_PAD + (((mNumOptions-1+2)/3)) * (BTN_HEIGHT+VPAD); - - // Tokenization on \n is handled by LLTextBox - - const S32 MAX_LENGTH = 512 + 20 + - DB_FIRST_NAME_BUF_SIZE + - DB_LAST_NAME_BUF_SIZE + - DB_INV_ITEM_NAME_BUF_SIZE; // For script dialogs: add space for title. - - LLTextEditor::Params params; - params.name("box"); - params.rect(LLRect(x, y, getRect().getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16)); - params.max_text_length(MAX_LENGTH); - params.read_only(true); - params.default_text(mMessage); - params.font(sFont); - params.embedded_items(false); - params.wrap(true); - params.tab_stop(false); - params.mouse_opaque(false); - params.bg_readonly_color(LLColor4::transparent); - params.text_readonly_color(LLUIColorTable::instance().getColor("NotifyTextColor")); - params.enabled(false); - params.border_visible(false); - text = LLUICtrlFactory::create<LLTextEditor> (params); - addChild(text); + mTextBox = getChild<LLTextEditor>("text_editor_box"); } - if (mIsTip) - { - // TODO: Make a separate archive for these. - LLChat chat(mMessage); - chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLFloaterChat::addChatHistory(chat); - } - else - { - LLButton::Params p; - p.name(std::string("next")); - p.rect(LLRect(getRect().getWidth()-26, BOTTOM_PAD + 20, getRect().getWidth()-2, BOTTOM_PAD)); - p.image_selected.name("notify_next.png"); - p.image_unselected.name("notify_next.png"); - p.font(sFont); - p.scale_image(true); - p.tool_tip(LLTrans::getString("next").c_str()); + // *TODO: magic numbers(???) - copied from llnotify.cpp(250) + const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; + + mTextBox->setVisible(TRUE); + mTextBox->setValue(notification->getMessage()); + // add buttons for a script notification + if (!mIsTip) + { for (S32 i = 0; i < mNumOptions; i++) { - LLSD form_element = form->getElement(i); if (form_element["type"].asString() != "button") { @@ -222,137 +150,63 @@ LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : LLToas addButton("OK", LLTrans::getString("ok"), FALSE, TRUE); mAddedDefaultBtn = true; } - - } + + // adjust panel's height to the text size + mInfoPanel->setFollowsAll(); + snapToMessageHeight(mTextBox, MAX_LENGTH); } -LLToastNotifyPanel::~LLToastNotifyPanel() { +LLToastNotifyPanel::~LLToastNotifyPanel() +{ std::for_each(mBtnCallbackData.begin(), mBtnCallbackData.end(), DeletePointer()); } -LLRect LLToastNotifyPanel::getNotifyRect(S32 num_options, BOOL mIsScriptDialog, BOOL is_caution) +void LLToastNotifyPanel::adjustPanelForScriptNotice(const LLNotificationFormPtr form) { - S32 notify_height = gSavedSettings.getS32("NotifyBoxHeight"); - if (is_caution) + F32 buttons_num = 0; + S32 button_rows = 0; + + // calculate number of buttons + for (S32 i = 0; i < mNumOptions; i++) { - // make caution-style dialog taller to accomodate extra text, - // as well as causing the accept/decline buttons to be drawn - // in a different position, to help prevent "quick-click-through" - // of many permissions prompts - notify_height = gSavedSettings.getS32("PermissionsCautionNotifyBoxHeight"); + if (form->getElement(i)["type"].asString() == "button") + { + buttons_num++; + } } - const S32 NOTIFY_WIDTH = gSavedSettings.getS32("NotifyBoxWidth"); - const S32 TOP = getRect().getHeight(); - const S32 RIGHT =getRect().getWidth(); - const S32 LEFT = RIGHT - NOTIFY_WIDTH; - - if (num_options < 1) + // calculate necessary height for the button panel + // if notification form contains no buttons - reserve a place for OK button + // script notifications have extra line for an IGNORE button + if(mIsScriptDialog) { - num_options = 1; + button_rows = llceil((buttons_num - 1) / 3.0f) + 1; } - - // Add two "blank" option spaces. - if (mIsScriptDialog) + else { - num_options += 2; + button_rows = llmax( 1, llceil(buttons_num / 3.0f)); } - S32 additional_lines = (num_options-1) / 3; - - notify_height += additional_lines * (BTN_HEIGHT + VPAD); + S32 button_panel_height = button_rows * BTN_HEIGHT + (button_rows + 1) * VPAD + BOTTOM_PAD; - return LLRect(LEFT, TOP, RIGHT, TOP-notify_height); + //adjust layout + LLRect button_rect = mControlPanel->getRect(); + reshape(getRect().getWidth(), mInfoPanel->getRect().getHeight() + button_panel_height); + mControlPanel->reshape(button_rect.getWidth(), button_panel_height); } // static -LLRect LLToastNotifyPanel::getNotifyTipRect(const std::string &utf8message) +void LLToastNotifyPanel::adjustPanelForTipNotice() { - S32 line_count = 1; - LLWString message = utf8str_to_wstring(utf8message); - S32 message_len = message.length(); - - const S32 NOTIFY_WIDTH = gSavedSettings.getS32("NotifyBoxWidth"); - // Make room for the icon area. - const S32 text_area_width = NOTIFY_WIDTH - HPAD * 4 - 32; - - const llwchar* wchars = message.c_str(); - const llwchar* start = wchars; - const llwchar* end; - S32 total_drawn = 0; - BOOL done = FALSE; - - do - { - line_count++; - - for (end=start; *end != 0 && *end != '\n'; end++) - ; - - if( *end == 0 ) - { - end = wchars + message_len; - done = TRUE; - } - - S32 remaining = end - start; - while( remaining ) - { - S32 drawn = sFont->maxDrawableChars( start, (F32)text_area_width, remaining, TRUE ); - - if( 0 == drawn ) - { - drawn = 1; // Draw at least one character, even if it doesn't all fit. (avoids an infinite loop) - } - - total_drawn += drawn; - start += drawn; - remaining -= drawn; - - if( total_drawn < message_len ) - { - if( (wchars[ total_drawn ] != '\n') ) - { - // wrap because line was too long - line_count++; - } - } - else - { - done = TRUE; - } - } - - total_drawn++; // for '\n' - end++; - start = end; - } while( !done ); + LLRect info_rect = mInfoPanel->getRect(); + LLRect this_rect = getRect(); - const S32 MIN_NOTIFY_HEIGHT = 72; - const S32 MAX_NOTIFY_HEIGHT = 600; - S32 notify_height = llceil((F32) (line_count+1) * sFont->getLineHeight()); - if(gOverlayBar) - { - notify_height += gOverlayBar->getBoundingRect().mTop; - } - else - { - // *FIX: this is derived from the padding caused by the - // rounded rects, shouldn't be a const here. - notify_height += 10; - } - notify_height += VPAD; - notify_height = llclamp(notify_height, MIN_NOTIFY_HEIGHT, MAX_NOTIFY_HEIGHT); - - const S32 RIGHT = getRect().getWidth(); - const S32 LEFT = RIGHT - NOTIFY_WIDTH; - - return LLRect(LEFT, notify_height, RIGHT, 0); + mControlPanel->setVisible(FALSE); + reshape(getRect().getWidth(), mInfoPanel->getRect().getHeight()); } - // static void LLToastNotifyPanel::onClickButton(void* data) { @@ -371,10 +225,6 @@ void LLToastNotifyPanel::onClickButton(void* data) // virtual LLButton* LLToastNotifyPanel::addButton(const std::string& name, const std::string& label, BOOL is_option, BOOL is_default) { - // make caution notification buttons slightly narrower - // so that 3 of them can fit without overlapping the "next" button - S32 btn_width = mIsCaution? 84 : 90; - LLRect btn_rect; LLButton* btn; S32 btn_height= BTN_HEIGHT; @@ -397,9 +247,9 @@ LLButton* LLToastNotifyPanel::addButton(const std::string& name, const std::stri } } - btn_rect.setOriginAndSize(x + (index % 3) * (btn_width+HPAD+HPAD) + ignore_pad, + btn_rect.setOriginAndSize(x + (index % 3) * (BUTTON_WIDTH+HPAD+HPAD) + ignore_pad, BOTTOM_PAD + (index / 3) * (BTN_HEIGHT+VPAD), - btn_width - 2*ignore_pad, + BUTTON_WIDTH - 2*ignore_pad, btn_height); InstanceAndS32* userdata = new InstanceAndS32; @@ -422,7 +272,7 @@ LLButton* LLToastNotifyPanel::addButton(const std::string& name, const std::stri btn = LLUICtrlFactory::create<LLButton>(p); - addChild(btn, -1); + mControlPanel->addChild(btn, -1); if (is_default) { diff --git a/indra/newview/lltoastnotifypanel.h b/indra/newview/lltoastnotifypanel.h index df58c06f25..66534edcdf 100644 --- a/indra/newview/lltoastnotifypanel.h +++ b/indra/newview/lltoastnotifypanel.h @@ -38,6 +38,9 @@ #include "llnotifications.h" #include "llbutton.h" #include "lltoastpanel.h" +#include "lliconctrl.h" +#include "lltexteditor.h" +#include "lltextbox.h" /** @@ -46,15 +49,15 @@ * * Replaces class LLNotifyBox. */ -class LLToastNotifyPanel: public LLToastPanel { +class LLToastNotifyPanel: public LLToastPanel +{ public: LLToastNotifyPanel(LLNotificationPtr&); virtual ~LLToastNotifyPanel(); - bool isTip() {return mIsTip;} - static LLToastNotifyPanel * buildNotifyPanel(LLNotificationPtr notification); protected: LLButton* addButton(std::string const &name, const std::string& label, BOOL is_option, BOOL is_default); + // Used for callbacks struct InstanceAndS32 { @@ -65,16 +68,23 @@ protected: private: - // Returns the rect, relative to gNotifyView, where this - // notify box should be placed. - LLRect getNotifyRect(S32 num_options, BOOL layout_script_dialog, BOOL is_caution); - LLRect getNotifyTipRect(const std::string &message); + void adjustPanelForScriptNotice(const LLNotificationFormPtr form); + void adjustPanelForTipNotice(); + + // panel elements + LLTextBase* mTextBox; + LLIconCtrl* mIcon; + LLPanel* mInfoPanel; // a panel, that contains an information + LLPanel* mControlPanel; // a panel, that contains buttons (if present) + // internal handler for button being clicked static void onClickButton(void* data); + bool mIsTip; bool mAddedDefaultBtn; bool mIsScriptDialog; - bool mIsCaution; // is this a caution notification? + bool mIsCaution; + std::string mMessage; S32 mNumOptions; S32 mNumButtons; diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp index 7b477470aa..ef75e06047 100644 --- a/indra/newview/lltoastpanel.cpp +++ b/indra/newview/lltoastpanel.cpp @@ -34,6 +34,9 @@ #include "lltoastpanel.h" +//static +const S32 LLToastPanel::MIN_PANEL_HEIGHT = 40; // VPAD(4)*2 + ICON_HEIGHT(32) + LLToastPanel::LLToastPanel(LLNotificationPtr& notification) { mNotification = notification; @@ -50,8 +53,13 @@ std::string LLToastPanel::getTitle() } //snap to the message height if it is visible -void LLToastPanel::snapToMessageHeight(LLTextBox* message, S32 maxLineCount) +void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount) { + if(!message) + { + return; + } + //Add message height if it is visible if (message->getVisible()) { @@ -61,22 +69,16 @@ void LLToastPanel::snapToMessageHeight(LLTextBox* message, S32 maxLineCount) LLRect messageRect = message->getRect(); S32 oldTextHeight = messageRect.getHeight(); - //Reshape the toast to give the message max height. - //This needed to calculate lines count according to specified text - heightDelta = maxTextHeight - oldTextHeight; - reshape( getRect().getWidth(), getRect().getHeight() + heightDelta); - //Knowing the height is set to max allowed, getTextPixelHeight returns needed text height //Perhaps we need to pass maxLineCount as parameter to getTextPixelHeight to avoid previous reshape. - S32 requiredTextHeight = message->getTextPixelHeight(); + S32 requiredTextHeight = message->getContentsRect().getHeight(); S32 newTextHeight = llmin(requiredTextHeight, maxTextHeight); //Calculate last delta height deducting previous heightDelta heightDelta = newTextHeight - oldTextHeight - heightDelta; //reshape the panel with new height - reshape( getRect().getWidth(), getRect().getHeight() + heightDelta); + reshape( getRect().getWidth(), llmax(getRect().getHeight() + heightDelta, MIN_PANEL_HEIGHT)); } - } diff --git a/indra/newview/lltoastpanel.h b/indra/newview/lltoastpanel.h index 418373e8c6..a88127b008 100644 --- a/indra/newview/lltoastpanel.h +++ b/indra/newview/lltoastpanel.h @@ -58,9 +58,11 @@ public: virtual std::string getTitle(); virtual const LLUUID& getID() { return mNotification->id();} + + static const S32 MIN_PANEL_HEIGHT; protected: LLNotificationPtr mNotification; - void snapToMessageHeight(LLTextBox* message, S32 maxLineCount); + void snapToMessageHeight(LLTextBase* message, S32 maxLineCount); }; #endif /* LL_TOASTPANEL_H */ diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 273ca8bd1a..5f95e9ccf1 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2293,7 +2293,9 @@ void LLViewerWindow::handleScrollWheel(S32 clicks) } // Zoom the camera in and out behavior - gAgent.handleScrollWheel(clicks); + + if(top_ctrl == 0 && mWorldViewRect.pointInRect(mCurrentMousePoint.mX, mCurrentMousePoint.mY) ) + gAgent.handleScrollWheel(clicks); return; } @@ -4168,8 +4170,9 @@ void LLViewerWindow::drawMouselookInstructions() instructions, 0, getVirtualWorldViewRect().getCenterX(), getVirtualWorldViewRect().mBottom + INSTRUCTIONS_PAD, - LLColor4( 0.0f, 0.0f, 0.0f, 0.6f ), - LLFontGL::HCENTER, LLFontGL::TOP); + LLColor4( 1.0f, 1.0f, 1.0f, 0.5f ), + LLFontGL::HCENTER, LLFontGL::TOP, + LLFontGL::NORMAL,LLFontGL::DROP_SHADOW); } S32 LLViewerWindow::getWindowHeight() const diff --git a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml index f1976afb4a..6fbfed5f60 100644 --- a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml @@ -30,23 +30,18 @@ color="1 1 1 1" enabled="true" image_name="closebox.tga" name="close_btn"/> </panel> - <text_editor + <chat_history allow_html="true" bg_readonly_color="ChatHistoryBgColor" bg_writeable_color="ChatHistoryBgColor" - follows="left|top|right|bottom" + follows="left|top|right" font="SansSerif" layout="topleft" - height="320" - max_length="2147483647" - name="Chat History Editor" + height="320" + name="chat_history" parse_highlights="true" - read_only="true" text_color="ChatHistoryTextColor" text_readonly_color="ChatHistoryTextColor" - bottom="0" - track_bottom="true" - width="250" - word_wrap="true" /> + width="250"/> </floater> diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 6dc08ad3ef..7785492651 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -478,11 +478,12 @@ parameter="open" /> </menu_item_call> <menu_item_separator - layout="topleft" /> + layout="topleft" + name="Landmark Separator" /> <menu_item_call label="About Landmark" layout="topleft" - name="Teleport To Landmark"> + name="About Landmark"> <menu_item_call.on_click function="Inventory.DoToSelected" parameter="about" /> diff --git a/indra/newview/skins/default/xui/en/menu_landmark.xml b/indra/newview/skins/default/xui/en/menu_landmark.xml index 64fec3ab40..54a4095967 100644 --- a/indra/newview/skins/default/xui/en/menu_landmark.xml +++ b/indra/newview/skins/default/xui/en/menu_landmark.xml @@ -29,4 +29,12 @@ function="Places.OverflowMenu.Action" parameter="pick" /> </menu_item_call> + <menu_item_call + label="Add to Favorites Bar" + layout="topleft" + name="add_to_favbar"> + <menu_item_call.on_click + function="Places.OverflowMenu.Action" + parameter="add_to_favbar" /> + </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml b/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml new file mode 100644 index 0000000000..b31a0d88a4 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml @@ -0,0 +1,45 @@ +<menu name="menu_folder_gear" + left="0" bottom="0" visible="false" + mouse_opaque="false" opaque="true" + color="MenuDefaultBgColor" drop_shadow="true"> + <menu_item_call name="add_landmark" label="Add Landmark"> + <menu_item_call.on_click function="Places.LandmarksGear.Add.Action" userdata="add_landmark" /> + </menu_item_call> + <menu_item_call name="add_folder" label="Add Folder"> + <menu_item_call.on_click function="Places.LandmarksGear.Add.Action" userdata="category" /> + <menu_item_call.on_enable function="Places.LandmarksGear.Enable" userdata="category" /> + </menu_item_call> + <menu_item_separator layout="topleft" /> + <menu_item_call name="cut" label="Cut"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="cut" /> + </menu_item_call> + <menu_item_call name="copy_folder" label="Copy"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="copy" /> + </menu_item_call> + <menu_item_call name="paste" label="Paste"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="paste" /> + <menu_item_call.on_enable function="Places.LandmarksGear.Enable" userdata="paste" /> + </menu_item_call> + <menu_item_call name="rename" label="Rename"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="rename" /> + </menu_item_call> + <menu_item_call name="delete" label="Delete"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="delete" /> + </menu_item_call> + <menu_item_separator layout="topleft" /> + <menu_item_call name="expand" label="Expand"> + <menu_item_call.on_click function="Places.LandmarksGear.Folding.Action" userdata="expand" /> + </menu_item_call> + <menu_item_call name="collapse" label="Collapse"> + <menu_item_call.on_click function="Places.LandmarksGear.Folding.Action" userdata="collapse" /> + </menu_item_call> + <menu_item_call name="expand_all" label="Expand all folders"> + <menu_item_call.on_click function="Places.LandmarksGear.Folding.Action" userdata="expand_all" /> + </menu_item_call> + <menu_item_call name="collapse_all" label="Collapse all folders"> + <menu_item_call.on_click function="Places.LandmarksGear.Folding.Action" userdata="collapse_all" /> + </menu_item_call> + <menu_item_call name="sort_by_date" label="Sort by Date"> + <menu_item_call.on_click function="Places.LandmarksGear.Folding.Action" userdata="sort_by_date" /> + </menu_item_call> +</menu>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml b/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml new file mode 100644 index 0000000000..2d8bb0dcb9 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml @@ -0,0 +1,56 @@ +<menu name="menu_ladmark_gear" + left="0" bottom="0" visible="false" + mouse_opaque="false" opaque="true" + color="MenuDefaultBgColor" drop_shadow="true"> + <menu_item_call name="teleport" label="Teleport"> + <menu_item_call.on_click function="Places.LandmarksGear.Custom.Action" userdata="teleport" /> + </menu_item_call> + <menu_item_call name="more_info" label="More Information"> + <menu_item_call.on_click function="Places.LandmarksGear.Custom.Action" userdata="more_info" /> + </menu_item_call> + <menu_item_call name="show_on_map" label="Show on Map"> + <menu_item_call.on_click function="Places.LandmarksGear.Custom.Action" userdata="show_on_map" /> + </menu_item_call> + <menu_item_separator layout="topleft" /> + <menu_item_call name="add_landmark" label="Add Landmark"> + <menu_item_call.on_click function="Places.LandmarksGear.Add.Action" userdata="add_landmark" /> + </menu_item_call> + <menu_item_call name="add_folder" label="Add Folder"> + <menu_item_call.on_click function="Places.LandmarksGear.Add.Action" userdata="category" /> + <menu_item_call.on_enable function="Places.LandmarksGear.Enable" userdata="category" /> + </menu_item_call> + <menu_item_separator layout="topleft" /> + <menu_item_call name="cut" label="Cut"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="cut" /> + </menu_item_call> + <menu_item_call name="copy_landmark" label="Copy Landmark"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="copy" /> + </menu_item_call> + <menu_item_call name="copy_slurl" label="Copy SLURL"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="copy_slurl" /> + </menu_item_call> + <menu_item_call name="paste" label="Paste"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="paste" /> + <menu_item_call.on_enable function="Places.LandmarksGear.Enable" userdata="paste" /> + </menu_item_call> + <menu_item_call name="rename" label="Rename"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="rename" /> + </menu_item_call> + <menu_item_call name="delete" label="Delete"> + <menu_item_call.on_click function="Places.LandmarksGear.CopyPaste.Action" userdata="delete" /> + </menu_item_call> + <menu_item_separator layout="topleft" /> + <menu_item_call name="expand_all" label="Expand all folders"> + <menu_item_call.on_click function="Places.LandmarksGear.Folding.Action" userdata="expand_all" /> + </menu_item_call> + <menu_item_call name="collapse_all" label="Collapse all folders"> + <menu_item_call.on_click function="Places.LandmarksGear.Folding.Action" userdata="collapse_all" /> + </menu_item_call> + <menu_item_call name="sort_by_date" label="Sort by Date"> + <menu_item_call.on_click function="Places.LandmarksGear.Folding.Action" userdata="sort_by_date" /> + </menu_item_call> + <menu_item_separator layout="topleft" /> + <menu_item_call name="create_pick" label="Create Pick"> + <menu_item_call.on_click function="Places.LandmarksGear.Custom.Action" userdata="create_pick" /> + </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml index f7f08b9b6a..69d90e4c7d 100644 --- a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml @@ -38,12 +38,13 @@ width="20" /> <text follows="left|right" - font="SansSerifSmallBold" + font="SansSerifSmall" + font.style="BOLD" height="20" layout="topleft" left_pad="5" name="avatar_name" - text_color="white" + text_color="grey" top="4" use_ellipses="true" value="Unknown" diff --git a/indra/newview/skins/default/xui/en/panel_chat_header.xml b/indra/newview/skins/default/xui/en/panel_chat_header.xml new file mode 100644 index 0000000000..a9f622e018 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_chat_header.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="left|top|right" + height="57" + label="im_header_container" + layout="topleft" + left="8" + name="im_header_container"> + <panel + background_visible="true" + bevel_style="in" + bg_alpha_color="black" + follows="left|top|right" + height="30" + label="im_header" + layout="topleft" + name="im_header" + top_pad="17"> + <avatar_icon + follows="left" + height="20" + image_name="icon_avatar_online.tga" + layout="topleft" + left="5" + mouse_opaque="true" + name="avatar_icon" + top="5" + width="20" /> + <text + follows="left|right" + font="SansSerifBigBold" + height="20" + layout="topleft" + left_pad="10" + right="-50" + name="user_name" + text_color="white" + top="5" + value="Darth Vader" + use_ellipses="true" /> + <text + follows="right" + font="SansSerifBig" + height="20" + layout="topleft" + name="time_box" + right="0" + text_color="white" + top="5" + value="23:30" + width="50" /> + </panel> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_chat_separator.xml b/indra/newview/skins/default/xui/en/panel_chat_separator.xml new file mode 100644 index 0000000000..dd27595cdb --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_chat_separator.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="left|right|top" + height="10" + layout="topleft" + left="8" + name="chat_separator_container"> + <panel + background_visible="true" + bg_alpha_color="black" + follows="left|right|top" + height="1" + layout="topleft" + name="chat_separator_panel" + top_pad="5" /> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_landmarks.xml b/indra/newview/skins/default/xui/en/panel_landmarks.xml index ae33d6da3e..7c7561f922 100644 --- a/indra/newview/skins/default/xui/en/panel_landmarks.xml +++ b/indra/newview/skins/default/xui/en/panel_landmarks.xml @@ -1,35 +1,143 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel name="Landmarks" - bottom="0" - height="326" + top="0" + height="400" + layout="topleft" left="0" width="380" border="true" background_visible="true" bg_alpha_color="DkGray2" follows="left|top|right|bottom"> - <inventory_panel - allow_multi_select="true" - border="true" - bottom="0" - follows="left|top|right|bottom" - height="326" - left="0" - mouse_opaque="true" - name="landmarks_list" - start_folder="landmark" - width="380"/> - <button - bottom="0" - halign="center" - height="16" - label=">" - enabled="false" - mouse_opaque="false" - name="selector" - width="20" - left="0" - follows="right|bottom" - tool_tip="View landmark properties"/> + <accordion + follows="left|top|right|bottom" + height="368" + layout="topleft" + left="0" + name="landmarks_accordion" + top="2" + width="380"> + <accordion_tab + layout="topleft" + name="tab_favorites" + title="Favorites bar"> + <inventory_subtree_panel + allow_multi_select="true" + border="true" + bottom="0" + follows="left|top|right|bottom" + height="126" + left="0" + mouse_opaque="true" + name="favorites_list" + width="380"/> + </accordion_tab> + <accordion_tab + layout="topleft" + name="tab_landmarks" + title="Landmarks"> + <inventory_subtree_panel + allow_multi_select="true" + border="true" + bottom="0" + follows="left|top|right|bottom" + height="126" + left="0" + mouse_opaque="true" + name="landmarks_list" + start_folder="landmark" + width="380"/> + </accordion_tab> + <accordion_tab + layout="topleft" + name="tab_inventory" + title="My Inventory"> + <inventory_subtree_panel + allow_multi_select="true" + border="true" + bottom="0" + follows="left|top|right|bottom" + height="126" + left="0" + mouse_opaque="true" + name="my_inventory_list" + width="380"/> + </accordion_tab> + <accordion_tab + layout="topleft" + name="tab_library" + title="Library"> + <inventory_subtree_panel + allow_multi_select="true" + border="true" + bottom="0" + follows="left|top|right|bottom" + height="120" + left="0" + mouse_opaque="true" + name="library_list" + width="380"/> + </accordion_tab> + </accordion> + <panel + background_visible="true" + bevel_style="none" + bottom="0" + follows="left|right|bottom" + height="30" + layout="bottomleft" + left="0" + name="bottom_panel" + width="380"> + <button + follows="bottom|left" + tool_tip="Show additional options" + height="18" + image_disabled="OptionsMenu_Disabled" + image_selected="OptionsMenu_Press" + image_unselected="OptionsMenu_Off" + layout="topleft" + left="10" + name="options_gear_btn" + picture_style="true" + top="6" + width="18" /> + <button + follows="bottom|left" + height="18" + image_selected="AddItem_Press" + image_unselected="AddItem_Off" + image_disabled="AddItem_Disabled" + layout="topleft" + left_pad="5" + name="add_landmark_btn" + picture_style="true" + tool_tip="Add New Landmark" + width="18" /> + <button + follows="bottom|left" + height="18" + image_selected="AddItem_Press" + image_unselected="AddItem_Off" + image_disabled="AddItem_Disabled" + layout="topleft" + left_pad="5" + name="add_folder_btn" + picture_style="true" + tool_tip="Add New Folder" + width="18" /> + <button + follows="bottom|right" + height="18" + image_selected="TrashItem_Press" + image_unselected="TrashItem_Off" + layout="topleft" + right="-5" + name="trash_btn" + picture_style="true" + tool_tip="Remove selected Landmark" + top="6" + width="18" /> + </panel> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_notification.xml b/indra/newview/skins/default/xui/en/panel_notification.xml new file mode 100644 index 0000000000..4d890b1d46 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_notification.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + background_opaque="true" + background_visible="false" + bg_alpha_color="0.3 0.3 0.3 0" + height="140" + label="notification_panel" + layout="topleft" + left="0" + name="notification_panel" + top="0" + width="350"> + <panel + background_visible="true" + follows="left|right|top" + height="100" + label="info_panel" + layout="topleft" + left="0" + name="info_panel" + top="0" + width="350"> + <text + border_visible="false" + follows="left|right|top|bottom" + font="SansSerif" + height="90" + layout="topleft" + left="45" + name="text_box" + read_only="true" + text_color="white" + top="5" + visible="false" + width="300" + wrap="true"/> + <text + border_visible="false" + follows="left|right|top|bottom" + font="SansSerifBold" + height="90" + layout="topleft" + left="45" + name="caution_text_box" + text_color="1 0.82 0.46 1" + top="5" + visible="false" + width="300" + wrap="true"/> + <text_editor + bg_readonly_color="0.0 0.0 0.0 0" + border_visible="false" + embedded_items="false" + enabled="false" + follows="left|right|top|bottom" + font="SansSerif" + height="90" + layout="topleft" + left="45" + mouse_opaque="false" + name="text_editor_box" + read_only="true" + tab_stop="false" + text_color="white" + text_readonly_color="white" + top="5" + visible="false" + width="300" + wrap="true"/> + </panel> + <panel + background_visible="true" + follows="left|right|bottom" + height="40" + label="control_panel" + layout="topleft" + left="0" + name="control_panel" + top_pad="0" + width="350"> + </panel> + <icon + follows="left|top" + height="32" + image_name="notify_tip_icon.tga" + layout="topleft" + left="8" + mouse_opaque="false" + name="info_icon" + top="20" + width="32" /> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_teleport_history.xml b/indra/newview/skins/default/xui/en/panel_teleport_history.xml index b0cd75117f..4169c6245b 100644 --- a/indra/newview/skins/default/xui/en/panel_teleport_history.xml +++ b/indra/newview/skins/default/xui/en/panel_teleport_history.xml @@ -1,15 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel name="Teleport History" bottom="0" height="326" left="0" width="380" border="true" follows="left|top|right|bottom"> - <string - name="cant_create_lm_here" - value="Please teleport to selected location before creating Landmark. " /> - <string - name="create_landmark" - value="Create Landmark" /> - <string - name="open_landmark" - value="Open Landmark panel" /> <accordion follows="left|top|right|bottom" height="300" @@ -188,19 +179,5 @@ picture_style="true" top="5" width="18" /> - <button - follows="bottom|left" - font="SansSerifBigBold" - height="18" - image_selected="Favorite_Star_Active" - image_disabled="Favorite_Star_Off" - image_unselected="Favorite_Star_Press" - layout="topleft" - left_pad="5" - name="star_btn" - picture_style="true" - tool_tip="" - top_delta="0" - width="18" /> </panel> </panel> diff --git a/indra/newview/skins/default/xui/en/widgets/chat_history.xml b/indra/newview/skins/default/xui/en/widgets/chat_history.xml new file mode 100644 index 0000000000..b72d59524e --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/chat_history.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<chat_history + message_header="panel_chat_header.xml" + message_separator="panel_chat_separator.xml" + left_text_pad="10" + right_text_pad="15" + left_widget_pad="5" + rigth_widget_pad="10" + max_length="2147483647" + enabled="false" + track_bottom="true" + name="chat_history" + type="string" + word_wrap="true" />
\ No newline at end of file |