diff options
author | Steven Bennetts <steve@lindenlab.com> | 2009-09-29 19:37:05 +0000 |
---|---|---|
committer | Steven Bennetts <steve@lindenlab.com> | 2009-09-29 19:37:05 +0000 |
commit | 606b381c9fbc43c214afd26fb2e2598eec656b66 (patch) | |
tree | 422a6d5d94d50bd97ac5bcbdb52f0f6de083c6e7 | |
parent | 751cc7cf68bb4d766e8ecaaf76af054dcfbbe9dc (diff) |
merge https://svn.aws.productengine.com/secondlife/export-from-ll/viewer-2-0@1830 https://svn.aws.productengine.com/secondlife/pe/stable-2@1839 -> viewer-2.0.0-3
JIRAS:
EXT-96 EXT-204 EXT-312 EXT-334 EXT-479 EXT-498 EXT-514 EXT-637 EXT-647 EXT-746 EXT-748 EXT-749 EXT-757 EXT-789 EXT-794 EXT-808 EXT-817 EXT-823 EXT-831 EXT-834 EXT-837 EXT-844 EXT-848 EXT-862 EXT-876 EXT-896 EXT-897 EXT-898 EXT-899 EXT-910 EXT-912 EXT-918 EXT-921 EXT-925 EXT-926 EXT-928 EXT-930 EXT-931 EXT-935 EXT-938 EXT-939 EXT-952 EXT-985 EXT-986 EXT-992 EXT-994 EXT-995 EXT-996 EXT-997 EXT-998 EXT-1001 EXT-1004 EXT-1010 EXT-1012 EXT-1016 EXT-1018 EXT-1020 EXT-1028 EXT-1041 EXT-1044 EXT-1051 EXT-1052 EXT-1061 EXT-1069 EXT-1071 EXT-1074 EXT-1075 EXT-1076 EXT-1078 EXT-1080 EXT-1081 EXT-1082 EXT-1083 EXT-1085 EXT-1092 EXT-1093 EXT-1099 EXT-1100 EXT-1101 EXT-1104 EXT-1106 EXT-1111 EXT-1113 EXT-1114 EXT-1115 EXT-1116 EXT-1118 EXT-1119 EXT-1129 EXT-1132 EXT-1135 EXT-1138 EXT-1142 EXT-1161 EXT-1162 EXT-1178 EXT-1180
* NEW DEVELOPMENT:
* EXT-898 - Add dock/undock support for camera and movement controls
* Avatar list changes
* Bottom bar changes: menu, docking, visibility
* Camera changes
* Camera & Movement Floaters
* Dockable Floaters (LLDockableFloater)
* Removed LLListCtrl
* Toast / Notification changes: signal / destruction changes, ordering
* Nearby chat input should display active voice indicator
QA NOTES:
* Message Well Window is ready to be tested for regression & matching the spec.
* Verify Group List Item L&F
* Verify All tabs in People Panel
* Verify that Picks behavior is not changed
152 files changed, 5521 insertions, 2297 deletions
diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp index 7c0ac6c554..ca7e471bf2 100644 --- a/indra/llcommon/lldate.cpp +++ b/indra/llcommon/lldate.cpp @@ -254,6 +254,36 @@ bool LLDate::fromStream(std::istream& s) return true; } +bool LLDate::fromYMDHMS(S32 year, S32 month, S32 day, S32 hour, S32 min, S32 sec) +{ + struct apr_time_exp_t exp_time; + + exp_time.tm_year = year - 1900; + exp_time.tm_mon = month - 1; + exp_time.tm_mday = day; + exp_time.tm_hour = hour; + exp_time.tm_min = min; + exp_time.tm_sec = sec; + + // zero out the unused fields + exp_time.tm_usec = 0; + exp_time.tm_wday = 0; + exp_time.tm_yday = 0; + exp_time.tm_isdst = 0; + exp_time.tm_gmtoff = 0; + + // generate a time_t from that + apr_time_t time; + if (apr_time_exp_gmt_get(&time, &exp_time) != APR_SUCCESS) + { + return false; + } + + mSecondsSinceEpoch = time / LL_APR_USEC_PER_SEC; + + return true; +} + F64 LLDate::secondsSinceEpoch() const { return mSecondsSinceEpoch; diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h index 38c4c8cb60..2655a2d283 100644 --- a/indra/llcommon/lldate.h +++ b/indra/llcommon/lldate.h @@ -101,6 +101,7 @@ public: */ bool fromString(const std::string& iso8601_date); bool fromStream(std::istream&); + bool fromYMDHMS(S32 year, S32 month = 1, S32 day = 0, S32 hour = 0, S32 min = 0, S32 sec = 0); /** * @brief Return the date in seconds since epoch. diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index fce6b759a4..1e6b216a61 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -50,7 +50,6 @@ set(llui_SOURCE_FILES llkeywords.cpp lllayoutstack.cpp lllineeditor.cpp - lllistctrl.cpp lllocalcliprect.cpp llmenugl.cpp llmodaldialog.cpp @@ -136,7 +135,6 @@ set(llui_HEADER_FILES lllayoutstack.h lllazyvalue.h lllineeditor.h - lllistctrl.h lllocalcliprect.h llmenugl.h llmodaldialog.h diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp index ed15d9d922..93d62fd7c2 100644 --- a/indra/llui/lldockablefloater.cpp +++ b/indra/llui/lldockablefloater.cpp @@ -35,12 +35,21 @@ #include "lldockablefloater.h" //static -LLHandle<LLFloater> LLDockableFloater::instanceHandle; +LLHandle<LLFloater> LLDockableFloater::sInstanceHandle; LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, const LLSD& key, const Params& params) : - LLFloater(key, params), mDockControl(dockControl) + LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(true) { + setDocked(mDockControl.get() != NULL && mDockControl.get()->isDockVisible()); + resetInstance(); +} + +LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, + const LLSD& key, const Params& params) : + LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(uniqueDocking) +{ + setDocked(mDockControl.get() != NULL && mDockControl.get()->isDockVisible()); resetInstance(); } @@ -57,22 +66,14 @@ BOOL LLDockableFloater::postBuild() void LLDockableFloater::resetInstance() { - if (instanceHandle.get() != this) + if (mUniqueDocking && sInstanceHandle.get() != this) { - if (instanceHandle.get() != NULL && instanceHandle.get()->isDocked()) + if (sInstanceHandle.get() != NULL && sInstanceHandle.get()->isDocked()) { - //closeFloater() is not virtual - if (instanceHandle.get()->canClose()) - { - instanceHandle.get()->closeFloater(); + sInstanceHandle.get()->setVisible(FALSE); } - else - { - instanceHandle.get()->setVisible(FALSE); + sInstanceHandle = getHandle(); } - } - instanceHandle = getHandle(); - } } void LLDockableFloater::setVisible(BOOL visible) @@ -81,12 +82,18 @@ void LLDockableFloater::setVisible(BOOL visible) { resetInstance(); } + + if (visible && mDockControl.get() != NULL) + { + mDockControl.get()->repositionDockable(); + } + LLFloater::setVisible(visible); } void LLDockableFloater::setDocked(bool docked, bool pop_on_undock) { - if (mDockControl.get() != NULL) + if (mDockControl.get() != NULL && mDockControl.get()->isDockVisible()) { if (docked) { @@ -97,13 +104,17 @@ void LLDockableFloater::setDocked(bool docked, bool pop_on_undock) { mDockControl.get()->off(); } - } if (!docked && pop_on_undock) { // visually pop up a little bit to emphasize the undocking translate(0, UNDOCK_LEAP_HEIGHT); } + } + else + { + docked = false; + } LLFloater::setDocked(docked, pop_on_undock); } @@ -113,15 +124,20 @@ void LLDockableFloater::draw() if (mDockControl.get() != NULL) { mDockControl.get()->repositionDockable(); + if (isDocked()) + { mDockControl.get()->drawToungue(); } + } LLFloater::draw(); } void LLDockableFloater::setDockControl(LLDockControl* dockControl) { mDockControl.reset(dockControl); + setDocked(mDockControl.get() != NULL && mDockControl.get()->isDockVisible()); } + const LLUIImagePtr& LLDockableFloater::getDockTongue() { return mDockTongue; diff --git a/indra/llui/lldockablefloater.h b/indra/llui/lldockablefloater.h index 1d0e89cef5..ed90567ad3 100644 --- a/indra/llui/lldockablefloater.h +++ b/indra/llui/lldockablefloater.h @@ -46,12 +46,26 @@ class LLDockableFloater : public LLFloater static const U32 UNDOCK_LEAP_HEIGHT = 12; public: LOG_CLASS(LLDockableFloater); - LLDockableFloater(LLDockControl* dockControl, const LLSD& key, const Params& params = getDefaultParams()); + LLDockableFloater(LLDockControl* dockControl, const LLSD& key, + const Params& params = getDefaultParams()); + LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, + const LLSD& key, const Params& params = getDefaultParams()); virtual ~LLDockableFloater(); + static LLHandle<LLFloater> getInstanceHandle() { return sInstanceHandle; } + + /** + * If descendant class overrides postBuild() in order to perform specific + * construction then it must still invoke its superclass' implementation. + */ /* virtula */BOOL postBuild(); /* virtual */void setDocked(bool docked, bool pop_on_undock = true); /* virtual */void draw(); + + /** + * If descendant class overrides setVisible() then it must still invoke its + * superclass' implementation. + */ /*virtual*/ void setVisible(BOOL visible); private: @@ -69,7 +83,12 @@ protected: private: std::auto_ptr<LLDockControl> mDockControl; LLUIImagePtr mDockTongue; - static LLHandle<LLFloater> instanceHandle; + static LLHandle<LLFloater> sInstanceHandle; + /** + * Provides possibility to define that dockable floaters can be docked + * non exclusively. + */ + bool mUniqueDocking; }; #endif /* LL_DOCKABLEFLOATER_H */ diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp index d666f2be56..0b16b2554c 100644 --- a/indra/llui/lldockcontrol.cpp +++ b/indra/llui/lldockcontrol.cpp @@ -35,12 +35,12 @@ #include "lldockcontrol.h" LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater, - const LLUIImagePtr& dockTongue, DocAt dockAt, bool enabled) : - mDockWidget(dockWidget), mDockableFloater(dockableFloater), mDockTongue( - dockTongue) + const LLUIImagePtr& dockTongue, DocAt dockAt, get_rect_callback_t get_rect_callback) : + mDockWidget(dockWidget), mDockableFloater(dockableFloater), mDockTongue(dockTongue) { mDockAt = dockAt; - if (enabled) + + if (dockableFloater->isDocked()) { on(); } @@ -49,7 +49,17 @@ LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater, off(); } - if (dockWidget != NULL) { + if (!(get_rect_callback)) + { + mGetRectCallback = boost::bind(&LLDockControl::getEnabledRect, this, _1); + } + else + { + mGetRectCallback = get_rect_callback; + } + + if (dockWidget != NULL) + { repositionDockable(); } } @@ -67,24 +77,80 @@ void LLDockControl::setDock(LLView* dockWidget) } } +void LLDockControl::getEnabledRect(LLRect& rect) +{ + rect = mDockableFloater->getRootView()->getRect(); +} + void LLDockControl::repositionDockable() { - if (mEnabled) + LLRect dockRect = mDockWidget->calcScreenRect(); + LLRect rootRect; + mGetRectCallback(rootRect); + static BOOL prev_visibility = !mDockWidget->getVisible(); + + // recalculate dockable position if dock position changed, dock visibility changed, + // root view rect changed or recalculation is forced + if (mEnabled && (mPrevDockRect != dockRect || prev_visibility != mDockWidget->getVisible() + || mRootRect != rootRect || mRecalculateDocablePosition)) { - calculateDockablePosition(); + // undock dockable and off() if dock not visible + if (!isDockVisible()) + { + mDockableFloater->setDocked(false); + // force off() since dockable may not have dockControll at this time + off(); + } + else + { + moveDockable(); + } + + mPrevDockRect = dockRect; + mRootRect = rootRect; + mRecalculateDocablePosition = false; + prev_visibility = mDockWidget->getVisible(); } } -void LLDockControl::calculateDockablePosition() +bool LLDockControl::isDockVisible() { + bool res = true; + + if (mDockWidget != NULL) + { + //we should check all hierarchy + res = mDockWidget->isInVisibleChain(); + if (res) + { LLRect dockRect = mDockWidget->calcScreenRect(); - LLRect rootRect = mDockableFloater->getRootView()->getRect(); - // recalculate dockable position if dock position changed - // or root view rect changed or recalculation is forced - if (mPrevDockRect != dockRect || mRootRect != rootRect - || mRecalculateDocablePosition) + switch (mDockAt) { + case TOP: + // check is dock inside parent rect + LLRect dockParentRect = + mDockWidget->getParent()->calcScreenRect(); + if (dockRect.mRight <= dockParentRect.mLeft + || dockRect.mLeft >= dockParentRect.mRight) + { + res = false; + } + break; + } + } + } + + return res; +} + +void LLDockControl::moveDockable() +{ + // calculate new dockable position + LLRect dockRect = mDockWidget->calcScreenRect(); + LLRect rootRect; + mGetRectCallback(rootRect); + LLRect dockableRect = mDockableFloater->calcScreenRect(); S32 x = 0; S32 y = 0; @@ -92,8 +158,8 @@ void LLDockControl::calculateDockablePosition() { case TOP: x = dockRect.getCenterX() - dockableRect.getWidth() / 2; - y = dockRect.mTop + mDockTongue->getHeight() - + dockableRect.getHeight(); + y = dockRect.mTop + mDockTongue->getHeight() + dockableRect.getHeight(); + // check is dockable inside root view rect if (x < rootRect.mLeft) { x = rootRect.mLeft; @@ -102,10 +168,29 @@ void LLDockControl::calculateDockablePosition() { x = rootRect.mRight - dockableRect.getWidth(); } + + + // calculate dock tongue position + LLRect dockParentRect = + mDockWidget->getParent()->calcScreenRect(); + if (dockRect.getCenterX() < dockParentRect.mLeft) + { + mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2; + } + else if (dockRect.getCenterX() > dockParentRect.mRight) + { + mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;; + } + else + { mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2; + } mDockTongueY = dockRect.mTop; + break; } + + // move dockable dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), dockableRect.getHeight()); LLRect localDocableParentRect; @@ -115,17 +200,17 @@ void LLDockControl::calculateDockablePosition() mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY, &mDockTongueX, &mDockTongueY); - mPrevDockRect = dockRect; - mRootRect = rootRect; - mRecalculateDocablePosition = false; - } + } void LLDockControl::on() { + if (isDockVisible()) + { mDockableFloater->setCanDrag(false); mEnabled = true; mRecalculateDocablePosition = true; + } } void LLDockControl::off() @@ -141,3 +226,4 @@ void LLDockControl::drawToungue() mDockTongue->draw(mDockTongueX, mDockTongueY); } } + diff --git a/indra/llui/lldockcontrol.h b/indra/llui/lldockcontrol.h index 7d8d5c7653..219ddfd092 100644 --- a/indra/llui/lldockcontrol.h +++ b/indra/llui/lldockcontrol.h @@ -51,10 +51,12 @@ public: }; public: + // callback for a function getting a rect valid for control's position + typedef boost::function<void (LLRect& )> get_rect_callback_t; + LOG_CLASS(LLDockControl); LLDockControl(LLView* dockWidget, LLFloater* dockableFloater, - const LLUIImagePtr& dockTongue, DocAt dockAt, - bool enabled); + const LLUIImagePtr& dockTongue, DocAt dockAt, get_rect_callback_t get_rect_callback = NULL); virtual ~LLDockControl(); public: @@ -63,9 +65,15 @@ public: void setDock(LLView* dockWidget); void repositionDockable(); void drawToungue(); -protected: - virtual void calculateDockablePosition(); + bool isDockVisible(); + + // gets a rect that bounds possible positions for a dockable control + void getEnabledRect(LLRect& rect); + +private: + virtual void moveDockable(); private: + get_rect_callback_t mGetRectCallback; bool mEnabled; bool mRecalculateDocablePosition; DocAt mDockAt; diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 75334acb39..9fcd386c19 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -33,6 +33,7 @@ #include "linden_common.h" #include "llpanel.h" +#include "lltextbox.h" #include "llflatlistview.h" @@ -41,6 +42,8 @@ static const LLDefaultChildRegistry::Register<LLFlatListView> flat_list_view("fl const LLSD SELECTED_EVENT = LLSD().insert("selected", true); const LLSD UNSELECTED_EVENT = LLSD().insert("selected", false); +static const std::string COMMENT_TEXTBOX = "comment_text"; + LLFlatListView::Params::Params() : item_pad("item_pad"), allow_select("allow_select"), @@ -55,7 +58,12 @@ void LLFlatListView::reshape(S32 width, S32 height, BOOL called_from_parent /* = rearrangeItems(); } -bool LLFlatListView::addItem(LLPanel* item, LLSD value /* = LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/) +const LLRect& LLFlatListView::getItemsRect() const +{ + return mItemsPanel->getRect(); +} + +bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/) { if (!item) return false; if (value.isUndefined()) return false; @@ -84,11 +92,12 @@ bool LLFlatListView::addItem(LLPanel* item, LLSD value /* = LLUUID::null*/, EAdd item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4)); rearrangeItems(); + notifyParentItemsRectChanged(); return true; } -bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, LLSD value /*= LLUUID::null*/) +bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value /*= LLUUID::null*/) { if (!after_item) return false; if (!item_to_add) return false; @@ -111,11 +120,11 @@ bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, else { pairs_iterator_t it = mItemPairs.begin(); - ++it; - while (it != mItemPairs.end()) + for (; it != mItemPairs.end(); ++it) { if (*it == after_pair) { + // insert new elements before the element at position of passed iterator. mItemPairs.insert(++it, new_pair); mItemsPanel->addChild(item_to_add); break; @@ -128,6 +137,7 @@ bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, item_to_add->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4)); rearrangeItems(); + notifyParentItemsRectChanged(); return true; } @@ -153,14 +163,14 @@ bool LLFlatListView::removeItemByValue(const LLSD& value) return removeItemPair(item_pair); } -bool LLFlatListView::removeItemByUUID(LLUUID& uuid) +bool LLFlatListView::removeItemByUUID(const LLUUID& uuid) { return removeItemByValue(LLSD(uuid)); } -LLPanel* LLFlatListView::getItemByValue(LLSD& value) const +LLPanel* LLFlatListView::getItemByValue(const LLSD& value) const { - if (value.isDefined()) return NULL; + if (value.isUndefined()) return NULL; item_pair_t* pair = getItemPair(value); if (pair) return pair->first; @@ -188,7 +198,7 @@ bool LLFlatListView::selectItemByValue(const LLSD& value, bool select /*= true*/ return selectItemPair(item_pair, select); } -bool LLFlatListView::selectItemByUUID(LLUUID& uuid, bool select /* = true*/) +bool LLFlatListView::selectItemByUUID(const LLUUID& uuid, bool select /* = true*/) { return selectItemByValue(LLSD(uuid), select); } @@ -252,7 +262,7 @@ void LLFlatListView::getSelectedItems(std::vector<LLPanel*>& selected_items) con } } -void LLFlatListView::resetSelection() +void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/) { if (mSelectedItemPairs.empty()) return; @@ -264,6 +274,29 @@ void LLFlatListView::resetSelection() } mSelectedItemPairs.clear(); + + if (mCommitOnSelectionChange && !no_commit_on_deselection) + { + onCommit(); + } +} + +void LLFlatListView::setNoItemsCommentText(const std::string& comment_text) +{ + if (NULL == mNoItemsCommentTextbox) + { + LLRect comment_rect = getRect(); + comment_rect.setOriginAndSize(0, 0, comment_rect.getWidth(), comment_rect.getHeight()); + comment_rect.stretch(-getBorderWidth()); + LLTextBox::Params text_p; + text_p.name(COMMENT_TEXTBOX); + text_p.border_visible(false); + text_p.rect(comment_rect); + text_p.follows.flags(FOLLOWS_ALL); + mNoItemsCommentTextbox = LLUICtrlFactory::create<LLTextBox>(text_p, this); + } + + mNoItemsCommentTextbox->setValue(comment_text); } void LLFlatListView::clear() @@ -272,11 +305,32 @@ void LLFlatListView::clear() for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) { mItemsPanel->removeChild((*it)->first); - delete (*it)->first; + (*it)->first->die(); delete *it; } mItemPairs.clear(); mSelectedItemPairs.clear(); + + // also set items panel height to zero. Reshape it to allow reshaping of non-item children + LLRect rc = mItemsPanel->getRect(); + rc.mBottom = rc.mTop; + mItemsPanel->reshape(rc.getWidth(), rc.getHeight()); + mItemsPanel->setRect(rc); + + setNoItemsCommentVisible(true); + notifyParentItemsRectChanged(); +} + +void LLFlatListView::sort() +{ + if (!mItemComparator) + { + llwarns << "No comparator specified for sorting FlatListView items." << llendl; + return; + } + + mItemPairs.sort(ComparatorAdaptor(*mItemComparator)); + rearrangeItems(); } @@ -286,12 +340,16 @@ void LLFlatListView::clear() LLFlatListView::LLFlatListView(const LLFlatListView::Params& p) -: LLScrollContainer(p), - mItemsPanel(NULL), - mItemPad(p.item_pad), - mAllowSelection(p.allow_select), - mMultipleSelection(p.multi_select), - mKeepOneItemSelected(p.keep_one_selected) +: LLScrollContainer(p) + , mItemComparator(NULL) + , mItemsPanel(NULL) + , mItemPad(p.item_pad) + , mAllowSelection(p.allow_select) + , mMultipleSelection(p.multi_select) + , mKeepOneItemSelected(p.keep_one_selected) + , mCommitOnSelectionChange(false) + , mPrevNotifyParentRect(LLRect()) + , mNoItemsCommentTextbox(NULL) { mBorderThickness = getBorderWidth(); @@ -315,19 +373,32 @@ void LLFlatListView::rearrangeItems() { static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); + setNoItemsCommentVisible(mItemPairs.empty()); + if (mItemPairs.empty()) return; //calculating required height - assuming items can be of different height //list should accommodate all its items S32 height = 0; + S32 invisible_children_count = 0; pairs_iterator_t it = mItemPairs.begin(); for (; it != mItemPairs.end(); ++it) { LLPanel* item = (*it)->first; + + // skip invisible child + if (!item->getVisible()) + { + ++invisible_children_count; + continue; + } + height += item->getRect().getHeight(); } - height += mItemPad * (mItemPairs.size() - 1); + + // add paddings between items, excluding invisible ones + height += mItemPad * (mItemPairs.size() - invisible_children_count - 1); LLRect rc = mItemsPanel->getRect(); S32 width = mItemsNoScrollWidth; @@ -346,14 +417,18 @@ void LLFlatListView::rearrangeItems() for (it2 = first_it; it2 != mItemPairs.end(); ++it2) { LLPanel* item = (*it2)->first; + + // skip invisible child + if (!item->getVisible()) + continue; + LLRect rc = item->getRect(); - if(it2 != first_it) - { - item_new_top -= (rc.getHeight() + mItemPad); - } rc.setLeftTopAndSize(rc.mLeft, item_new_top, width, rc.getHeight()); item->reshape(rc.getWidth(), rc.getHeight()); item->setRect(rc); + + // move top for next item in list + item_new_top -= (rc.getHeight() + mItemPad); } } @@ -443,6 +518,12 @@ bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select) //a way of notifying panel of selection state changes LLPanel* item = item_pair->first; item->setValue(select ? SELECTED_EVENT : UNSELECTED_EVENT); + + if (mCommitOnSelectionChange) + { + onCommit(); + } + return true; } @@ -483,12 +564,82 @@ bool LLFlatListView::removeItemPair(item_pair_t* item_pair) } mItemsPanel->removeChild(item_pair->first); - delete item_pair->first; + item_pair->first->die(); delete item_pair; rearrangeItems(); + notifyParentItemsRectChanged(); return true; } +void LLFlatListView::notifyParentItemsRectChanged() +{ + S32 comment_height = 0; + + // take into account comment text height if exists + if (mNoItemsCommentTextbox && mNoItemsCommentTextbox->getVisible()) + { + comment_height = mNoItemsCommentTextbox->getTextPixelHeight(); + } + + LLRect req_rect = getItemsRect(); + + // get maximum of items total height and comment text height + req_rect.setOriginAndSize(req_rect.mLeft, req_rect.mBottom, req_rect.getWidth(), llmax(req_rect.getHeight(), comment_height)); + + // take into account border size. + req_rect.stretch(getBorderWidth()); + + if (req_rect == mPrevNotifyParentRect) + return; + + mPrevNotifyParentRect = req_rect; + + LLSD params; + params["action"] = "size_changes"; + params["width"] = req_rect.getWidth(); + params["height"] = req_rect.getHeight(); + + getParent()->notifyParent(params); +} + +void LLFlatListView::setNoItemsCommentVisible(bool visible) const +{ + if (mNoItemsCommentTextbox) + { + if (visible) + { + // We have to update child rect here because of issues with rect after reshaping while creating LLTextbox + // It is possible to have invalid LLRect if Flat List is in LLAccordionTab + LLRect comment_rect = getLocalRect(); + comment_rect.stretch(-getBorderWidth()); + mNoItemsCommentTextbox->setRect(comment_rect); + } + mNoItemsCommentTextbox->setVisible(visible); + } +} + +void LLFlatListView::getItems(std::vector<LLPanel*>& items) const +{ + if (mItemPairs.empty()) return; + + items.clear(); + for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + items.push_back((*it)->first); + } +} + +void LLFlatListView::getValues(std::vector<LLSD>& values) const +{ + if (mItemPairs.empty()) return; + + values.clear(); + for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) + { + values.push_back((*it)->second); + } +} +//EOF diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index bd0b419f4f..af5a9cfa9b 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -33,10 +33,10 @@ #ifndef LL_LLFLATLISTVIEW_H #define LL_LLFLATLISTVIEW_H +#include "llpanel.h" #include "llscrollcontainer.h" - -class LLPanel; +class LLTextBox; /** * LLFlatListView represents a flat list ui control that operates on items in a form of LLPanel's. @@ -62,6 +62,38 @@ class LLFlatListView : public LLScrollContainer { public: + /** + * Abstract comparator for comparing flat list items in a form of LLPanel + */ + class ItemComparator + { + public: + ItemComparator() {}; + virtual ~ItemComparator() {}; + + /** Returns true if item1 < item2, false otherwise */ + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const = 0; + }; + + /** + * Represents reverse comparator which acts as a decorator for a comparator that need to be reversed + */ + class ItemReverseComparator : public ItemComparator + { + public: + ItemReverseComparator(const ItemComparator& comparator) : mComparator(comparator) {}; + virtual ~ItemReverseComparator() {}; + + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const + { + return mComparator.compare(item2, item1); + } + + private: + const ItemComparator& mComparator; + }; + + struct Params : public LLInitParam::Block<Params, LLScrollContainer::Params> { /** turning on/off selection support */ @@ -85,18 +117,23 @@ public: /** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /** Returns full rect of child panel */ + const LLRect& getItemsRect() const; + + /** Returns distance between items */ + const S32 getItemsPad() { return mItemPad; } /** * Adds and item and LLSD value associated with it to the list at specified position * @return true if the item was added, false otherwise */ - virtual bool addItem(LLPanel* item, LLSD value = LLUUID::null, EAddPosition pos = ADD_BOTTOM); + virtual bool addItem(LLPanel * item, const LLSD& value = LLUUID::null, EAddPosition pos = ADD_BOTTOM); /** * Insert item_to_add along with associated value to the list right after the after_item. * @return true if the item was successfully added, false otherwise */ - virtual bool insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, LLSD value = LLUUID::null); + virtual bool insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value = LLUUID::null); /** * Remove specified item @@ -114,13 +151,19 @@ public: * Remove an item specified by uuid * @return true if the item was removed, false otherwise */ - virtual bool removeItemByUUID(LLUUID& uuid); + virtual bool removeItemByUUID(const LLUUID& uuid); /** * Get an item by value * @return the item as LLPanel if associated with value, NULL otherwise */ - virtual LLPanel* getItemByValue(LLSD& value) const; + virtual LLPanel* getItemByValue(const LLSD& value) const; + + template<class T> + T* getTypedItemByValue(const LLSD& value) const + { + return dynamic_cast<T*>(getItemByValue(value)); + } /** * Select or deselect specified item based on select @@ -138,9 +181,17 @@ public: * Select or deselect an item by associated uuid based on select * @return true if succeed, false otherwise */ - virtual bool selectItemByUUID(LLUUID& uuid, bool select = true); + virtual bool selectItemByUUID(const LLUUID& uuid, bool select = true); + /** + * Get all panels stored in the list. + */ + virtual void getItems(std::vector<LLPanel*>& items) const; + /** + * Get all items values. + */ + virtual void getValues(std::vector<LLSD>& values) const; /** * Get LLSD associated with the first selected item @@ -176,9 +227,23 @@ public: virtual void getSelectedItems(std::vector<LLPanel*>& selected_items) const; - /** Resets selection of items */ - virtual void resetSelection(); + /** + * Resets selection of items. + * + * It calls onCommit callback if setCommitOnSelectionChange(bool b) was called with "true" + * argument for current Flat List. + * @param no_commit_on_deselection - if true onCommit callback will not be called + */ + virtual void resetSelection(bool no_commit_on_deselection = false); + /** + * Sets comment text which will be shown in the list is it is empty. + * + * Textbox to hold passed text is created while this method is called at the first time. + * + * @param comment_text - string to be shown as a comment. + */ + void setNoItemsCommentText( const std::string& comment_text); /** Turn on/off multiple selection support */ void setAllowMultipleSelection(bool allow) { mMultipleSelection = allow; } @@ -186,6 +251,8 @@ public: /** Turn on/off selection support */ void setAllowSelection(bool can_select) { mAllowSelection = can_select; } + /** Sets flag whether onCommit should be fired if selection was changed */ + void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; } /** Get number of selected items in the list */ U32 numSelected() const {return mSelectedItemPairs.size(); } @@ -197,6 +264,14 @@ public: /** Removes all items from the list */ virtual void clear(); + /** + * Set comparator to use for future sorts. + * + * This class does NOT manage lifetime of the comparator + * but assumes that the comparator is always alive. + */ + void setComparator(const ItemComparator* comp) { mItemComparator = comp; } + void sort(); protected: @@ -207,6 +282,19 @@ protected: typedef pairs_list_t::iterator pairs_iterator_t; typedef pairs_list_t::const_iterator pairs_const_iterator_t; + /** An adapter for a ItemComparator */ + struct ComparatorAdaptor + { + ComparatorAdaptor(const ItemComparator& comparator) : mComparator(comparator) {}; + + bool operator()(const item_pair_t* item_pair1, const item_pair_t* item_pair2) + { + return mComparator.compare(item_pair1->first, item_pair2->first); + } + + const ItemComparator& mComparator; + }; + friend class LLUICtrlFactory; LLFlatListView(const LLFlatListView::Params& p); @@ -214,7 +302,10 @@ protected: /** Manage selection on mouse events */ void onItemMouseClick(item_pair_t* item_pair, MASK mask); - /** Updates position of items */ + /** + * Updates position of items. + * It does not take into account invisible items. + */ virtual void rearrangeItems(); virtual item_pair_t* getItemPair(LLPanel* item) const; @@ -227,14 +318,27 @@ protected: virtual bool removeItemPair(item_pair_t* item_pair); + /** + * Notify parent about changed size of internal controls with "size_changes" action + * + * Size includes Items Rect width and either Items Rect height or comment text height. + * Comment text height is included if comment text is set and visible. + * List border size is also included into notified size. + */ + void notifyParentItemsRectChanged(); + private: void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;} + void setNoItemsCommentVisible(bool visible) const; private: + /** Comparator to use when sorting the list. */ + const ItemComparator* mItemComparator; + LLPanel* mItemsPanel; S32 mItemsNoScrollWidth; @@ -242,7 +346,7 @@ private: S32 mBorderThickness; /** Items padding */ - U32 mItemPad; + S32 mItemPad; /** Selection support flag */ bool mAllowSelection; @@ -250,6 +354,14 @@ private: /** Multiselection support flag, ignored if selection is not supported */ bool mMultipleSelection; + /** + * Flag specified whether onCommit be called if selection is changed in the list. + * + * Can be ignored in the resetSelection() method. + * @see resetSelection() + */ + bool mCommitOnSelectionChange; + bool mKeepOneItemSelected; /** All pairs of the list */ @@ -257,6 +369,14 @@ private: /** Selected pairs for faster access */ pairs_list_t mSelectedItemPairs; + + /** + * Rectangle contained previous size of items parent notified last time. + * Is used to reduce amount of parentNotify() calls if size was not changed. + */ + LLRect mPrevNotifyParentRect; + + LLTextBox* mNoItemsCommentTextbox; }; #endif diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 60ddbc6cb3..ab9b59e252 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -216,6 +216,10 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL { mCachedKeyboardFocusList.pop_front(); old_focus_view->onFocusLost(); + + // part of fix of EXT-996 + // this need to handle event when user click inside in-world area + mFocusChangeSignal(); } } diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index d0adadd6d3..2c2dae216a 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -124,6 +124,11 @@ public: void unlockFocus(); BOOL focusLocked() const { return mLockedView != NULL; } + void addFocusChangeCallback(const boost::signals2::signal<void ()>::slot_type& cb) + { + mFocusChangeSignal.connect(cb); + } + private: LLUICtrl* mLockedView; @@ -150,6 +155,8 @@ private: typedef std::map<LLHandle<LLView>, LLHandle<LLView> > focus_history_map_t; focus_history_map_t mFocusHistory; + boost::signals2::signal<void()> mFocusChangeSignal; + #ifdef _DEBUG std::string mMouseCaptorName; std::string mKeyboardFocusName; diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 9845b7e2ce..25e2475f59 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1443,8 +1443,8 @@ void LLNotifications::cancel(LLNotificationPtr pNotif) { llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl; } - updateItem(LLSD().insert("sigtype", "delete").insert("id", pNotif->id()), pNotif); pNotif->cancel(); + updateItem(LLSD().insert("sigtype", "delete").insert("id", pNotif->id()), pNotif); } void LLNotifications::update(const LLNotificationPtr pNotif) diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 172c4a9c65..ed150ac50c 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -66,7 +66,9 @@ LLScrollbar::Params::Params() up_button("up_button"), down_button("down_button"), left_button("left_button"), - right_button("right_button") + right_button("right_button"), + bg_visible("bg_visible", false), + bg_color("bg_color", LLColor4::black) { tab_stop = false; } @@ -92,7 +94,9 @@ LLScrollbar::LLScrollbar(const Params & p) mThumbImageH(p.thumb_image_horizontal), mTrackImageV(p.track_image_vertical), mTrackImageH(p.track_image_horizontal), - mThickness(p.thickness.isProvided() ? p.thickness : LLUI::sSettingGroups["config"]->getS32("UIScrollbarSize")) + mThickness(p.thickness.isProvided() ? p.thickness : LLUI::sSettingGroups["config"]->getS32("UIScrollbarSize")), + mBGVisible(p.bg_visible), + mBGColor(p.bg_color) { updateThumbRect(); @@ -482,6 +486,11 @@ void LLScrollbar::draw() { if (!getRect().isValid()) return; + if(mBGVisible) + { + gl_rect_2d(getLocalRect(), mBGColor.get(), TRUE); + } + S32 local_mouse_x; S32 local_mouse_y; LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 30d906e04c..7e72331a3f 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -66,8 +66,11 @@ public: track_image_horizontal, track_image_vertical; + Optional<bool> bg_visible; + Optional<LLUIColor> track_color, - thumb_color; + thumb_color, + bg_color; Optional<LLButton::Params> up_button; Optional<LLButton::Params> down_button; @@ -127,6 +130,12 @@ public: void onLineUpBtnPressed(const LLSD& data); void onLineDownBtnPressed(const LLSD& data); + void setBGColor(const LLUIColor& color) { mBGColor = color; } + const LLUIColor& getBGColor() const { return mBGColor; } + + void setBGVisible() { mBGVisible = true; } + bool getBGVisible() const { return mBGVisible; } + private: void updateThumbRect(); void changeLine(S32 delta, BOOL update_thumb ); @@ -151,6 +160,9 @@ private: LLUIColor mTrackColor; LLUIColor mThumbColor; + LLUIColor mBGColor; + + bool mBGVisible; LLUIImagePtr mThumbImageV; LLUIImagePtr mThumbImageH; diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 30a042cff1..cd5926fb6b 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -67,12 +67,10 @@ static LLDefaultChildRegistry::Register<LLScrollContainer> r("scroll_container") #include "llscrollingpanellist.h" #include "llcontainerview.h" #include "llpanel.h" -#include "lllistctrl.h" static ScrollContainerRegistry::Register<LLScrollingPanelList> r1("scrolling_panel_list"); static ScrollContainerRegistry::Register<LLContainerView> r2("container_view"); static ScrollContainerRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML); -static ScrollContainerRegistry::Register<LLListCtrl> r4("list"); LLScrollContainer::Params::Params() : is_opaque("opaque"), diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 483106e857..54e42bf642 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -139,7 +139,9 @@ LLScrollListCtrl::Params::Params() bg_stripe_color("bg_stripe_color"), hovered_color("hovered_color"), highlighted_color("highlighted_color"), - contents("") + contents(""), + scroll_bar_bg_visible("scroll_bar_bg_visible"), + scroll_bar_bg_color("scroll_bar_bg_color") { name = "scroll_list"; mouse_opaque = true; @@ -220,6 +222,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) sbparams.change_callback(boost::bind(&LLScrollListCtrl::onScrollChange, this, _1, _2)); sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); sbparams.visible(false); + sbparams.bg_visible(p.scroll_bar_bg_visible); + sbparams.bg_color(p.scroll_bar_bg_color); mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams); addChild(mScrollbar); diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 49a49499ef..83b2f71037 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -83,7 +83,8 @@ public: Optional<bool> has_border, draw_heading, draw_stripes, - background_visible; + background_visible, + scroll_bar_bg_visible; // layout Optional<S32> column_padding, @@ -104,7 +105,8 @@ public: bg_readonly_color, bg_stripe_color, hovered_color, - highlighted_color; + highlighted_color, + scroll_bar_bg_color; Optional<Contents> contents; diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index 291d1dc517..0517325e70 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -109,6 +109,7 @@ public: void setClickedCallback( boost::function<void (void*)> cb, void* userdata = NULL ){ mClickedCallback = boost::bind(cb, userdata); } // mouse down and up within button const LLFontGL* getFont() const { return mDefaultFont; } + void setFont(const LLFontGL* font) { mDefaultFont = font; } void reshapeToFitText(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 8a59b34332..e4e4d8a2fa 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -128,6 +128,7 @@ set(viewer_SOURCE_FILES lleventinfo.cpp lleventnotifier.cpp lleventpoll.cpp + llexpandabletextbox.cpp llface.cpp llfasttimerview.cpp llfavoritesbar.cpp @@ -233,6 +234,7 @@ set(viewer_SOURCE_FILES llhudrender.cpp llhudtext.cpp llhudview.cpp + llimfloater.cpp llimhandler.cpp llimpanel.cpp llimview.cpp @@ -276,8 +278,9 @@ set(viewer_SOURCE_FILES llnetmap.cpp llnotificationalerthandler.cpp llnotificationgrouphandler.cpp - llnotificationinfohandler.cpp llnotificationmanager.cpp + llnotificationscripthandler.cpp + llnotificationtiphandler.cpp llnotify.cpp lloutputmonitorctrl.cpp lloverlaybar.cpp @@ -320,6 +323,7 @@ set(viewer_SOURCE_FILES llpanelmovetip.cpp llpanelobject.cpp llpanelpeople.cpp + llpanelpeoplemenus.cpp llpanelpermissions.cpp llpanelpick.cpp llpanelpicks.cpp @@ -587,6 +591,7 @@ set(viewer_HEADER_FILES lleventinfo.h lleventnotifier.h lleventpoll.h + llexpandabletextbox.h llface.h llfasttimerview.h llfavoritesbar.h @@ -692,6 +697,7 @@ set(viewer_HEADER_FILES llhudrender.h llhudtext.h llhudview.h + llimfloater.h llimpanel.h llimview.h llimcontrolpanel.h @@ -777,6 +783,7 @@ set(viewer_HEADER_FILES llpanelmovetip.h llpanelobject.h llpanelpeople.h + llpanelpeoplemenus.h llpanelpermissions.h llpanelpick.h llpanelpicks.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c794d7d319..19d503390c 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4884,18 +4884,7 @@ <key>Type</key> <string>S32</string> <key>Value</key> - <integer>10</integer> - </map> - <key>NavBarMargin</key> - <map> - <key>Comment</key> - <string>Width of notification messages</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>S32</string> - <key>Value</key> - <integer>60</integer> + <integer>35</integer> </map> <key>OverflowToastHeight</key> <map> @@ -7520,10 +7509,10 @@ <key>Value</key> <integer>1</integer> </map> - <key>ShowCameraAndMoveControls</key> + <key>ShowCameraButton</key> <map> <key>Comment</key> - <string>Show/Hide Camera and Move controls in the bottom tray</string> + <string>Show/Hide Camera button in the bottom tray</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -7531,6 +7520,28 @@ <key>Value</key> <integer>1</integer> </map> + <key>ShowMoveButton</key> + <map> + <key>Comment</key> + <string>Show/Hide Move button in the bottom tray</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>ShowGestureButton</key> + <map> + <key>Comment</key> + <string>Show/Hide Gesture button in the bottom tray</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>ShowNavbarFavoritesPanel</key> <map> <key>Comment</key> @@ -7553,6 +7564,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>GroupListShowIcons</key> + <map> + <key>Comment</key> + <string>Show/hide group icons in the group list</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>ShowPGSearchAll</key> <map> <key>Comment</key> diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 4819703e72..1676bb1d44 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -43,8 +43,11 @@ #include "llappviewer.h" // for gLastVersionChannel #include "llcachename.h" #include "llcallingcard.h" // for LLAvatarTracker +#include "llgivemoney.h" // foe LLFloaterPay #include "llinventorymodel.h" // for gInventory.findCategoryUUIDForType #include "llimview.h" // for gIMMgr +#include "llmutelist.h" +#include "llrecentpeople.h" #include "llsidetray.h" #include "llviewermessage.h" // for handle_lure #include "llviewerregion.h" @@ -206,6 +209,41 @@ void LLAvatarActions::showProfile(const LLUUID& id) } } +// static +void LLAvatarActions::pay(const LLUUID& id) +{ + LLNotification::Params params("BusyModePay"); + params.functor.function(boost::bind(&LLAvatarActions::handlePay, _1, _2, id)); + + if (gAgent.getBusy()) + { + // warn users of being in busy mode during a transaction + LLNotifications::instance().add(params); + } + else + { + LLNotifications::instance().forceResponse(params, 1); + } +} + +// static +void LLAvatarActions::toggleBlock(const LLUUID& id) +{ + std::string name; + + gCacheName->getFullName(id, name); + LLMute mute(id, name, LLMute::AGENT); + + if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) + { + LLMuteList::getInstance()->remove(mute); + } + else + { + LLMuteList::getInstance()->add(mute); + } +} + //== private methods ======================================================================================== // static @@ -243,6 +281,19 @@ bool LLAvatarActions::handleRemove(const LLSD& notification, const LLSD& respons } // static +bool LLAvatarActions::handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + if (option == 0) + { + gAgent.clearBusy(); + } + + LLFloaterPay::payDirectly(&give_money, avatar_id, /*is_group=*/FALSE); + return false; +} + +// static bool LLAvatarActions::callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response) { S32 option = LLNotification::getSelectedOption(notification, response); @@ -290,3 +341,11 @@ bool LLAvatarActions::isFriend(const LLUUID& id) { return ( NULL != LLAvatarTracker::instance().getBuddyInfo(id) ); } + +// static +bool LLAvatarActions::isBlocked(const LLUUID& id) +{ + std::string name; + gCacheName->getFullName(id, name); + return LLMuteList::getInstance()->isMuted(id, name); +} diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index f3c411e033..e911715c70 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -77,14 +77,30 @@ public: static void showProfile(const LLUUID& id); /** + * Give money to the avatar. + */ + static void pay(const LLUUID& id); + + /** + * Block/unblock the avatar. + */ + static void toggleBlock(const LLUUID& id); + + /** * Return true if avatar with "id" is a friend */ static bool isFriend(const LLUUID& id); + /** + * @return true if the avatar is blocked + */ + static bool isBlocked(const LLUUID& id); + private: static bool callbackAddFriend(const LLSD& notification, const LLSD& response); static bool callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response); static bool handleRemove(const LLSD& notification, const LLSD& response); + static bool handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id); // Just request friendship, no dialog. static void requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message); diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 2e64c10bb2..ee14a2ff86 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -37,144 +37,9 @@ // newview #include "llcallingcard.h" // for LLAvatarTracker #include "llcachename.h" -#include "lloutputmonitorctrl.h" #include "llvoiceclient.h" static LLDefaultChildRegistry::Register<LLAvatarList> r("avatar_list"); -static LLDefaultChildRegistry::Register<LLAvatarListTmp> r_tmp("avatar_list_tmp"); - -static const std::string COMMENT_TEXTBOX = "comment_text"; - -LLAvatarList::Params::Params() -: - volume_column_width("volume_column_width", 0) - , online_go_first("online_go_first", true) -{ - draw_heading = true; - draw_stripes = false; - multi_select = false; - column_padding = 0; - search_column = COL_NAME; - sort_column = COL_NAME; -} - -LLAvatarList::LLAvatarList(const Params& p) -: LLScrollListCtrl(p) - , mHaveVolumeColumn(p.volume_column_width > 0) - , mOnlineGoFirst(p.online_go_first) -{ - setCommitOnSelectionChange(TRUE); // there's no such param in LLScrollListCtrl::Params - - // display a context menu appropriate for a list of avatar names - setContextMenu(LLScrollListCtrl::MENU_AVATAR); - - // "volume" column - { - LLScrollListColumn::Params col_params; - col_params.name = "volume"; - col_params.header.label = "Volume"; // *TODO: localize or remove the header - col_params.width.pixel_width = p.volume_column_width; - addColumn(col_params); - } - - // "name" column - { - LLScrollListColumn::Params col_params; - col_params.name = "name"; - col_params.header.label = "Name"; // *TODO: localize or remove the header - col_params.width.dynamic_width = true; - addColumn(col_params); - } - - // "online status" column - { - LLScrollListColumn::Params col_params; - col_params.name = "online"; - col_params.header.label = "Online"; // *TODO: localize or remove the header - col_params.width.pixel_width = 0; // invisible column - addColumn(col_params); - } - - - // invisible "id" column - { - LLScrollListColumn::Params col_params; - col_params.name = "id"; - col_params.header.label = "ID"; // *TODO: localize or remove the header - col_params.width.pixel_width = 0; - addColumn(col_params); - } - - // Primary sort = online status, secondary sort = name - // The corresponding parameters don't work because we create columns dynamically. - sortByColumnIndex(COL_NAME, TRUE); - if (mOnlineGoFirst) - sortByColumnIndex(COL_ONLINE, FALSE); - setSearchColumn(COL_NAME); -} - -// virtual -void LLAvatarList::draw() -{ - LLScrollListCtrl::draw(); - if (mHaveVolumeColumn) - { - updateVolume(); - } -} - -std::vector<LLUUID> LLAvatarList::getSelectedIDs() -{ - LLUUID selected_id; - std::vector<LLUUID> avatar_ids; - std::vector<LLScrollListItem*> selected = getAllSelected(); - for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr) - { - avatar_ids.push_back((*itr)->getUUID()); - } - return avatar_ids; -} - -void LLAvatarList::addItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos) -{ - std::string fullname; - - // Populate list item. - LLSD element; - element["id"] = id; - - // Update volume column (if we have one) - { - std::string icon = mHaveVolumeColumn ? getVolumeIcon(id) : ""; - LLSD& volume_column = element["columns"][COL_VOLUME]; - volume_column["column"] = "volume"; - volume_column["type"] = "icon"; - volume_column["value"] = icon; - } - - LLSD& friend_column = element["columns"][COL_NAME]; - friend_column["column"] = "name"; - friend_column["value"] = name; - - LLSD& online_column = element["columns"][COL_ONLINE]; - online_column["column"] = "online"; - online_column["value"] = is_bold ? "1" : "0"; - - LLScrollListItem* new_itemp = addElement(element, pos); - - // Indicate buddy online status. - // (looks like parsing font parameters from LLSD is broken) - if (is_bold) - { - LLScrollListText* name_textp = dynamic_cast<LLScrollListText*>(new_itemp->getColumn(COL_NAME)); - if (name_textp) - name_textp->setFontStyle(LLFontGL::BOLD); - else - { - llwarns << "Name column not found" << llendl; - } - } -} static bool findInsensitive(std::string haystack, const std::string& needle_upper) { @@ -182,136 +47,12 @@ static bool findInsensitive(std::string haystack, const std::string& needle_uppe return haystack.find(needle_upper) != std::string::npos; } -BOOL LLAvatarList::update(const std::vector<LLUUID>& all_buddies, const std::string& name_filter) -{ - BOOL have_names = TRUE; - - // Save selection. - std::vector<LLUUID> selected_ids = getSelectedIDs(); - LLUUID current_id = getCurrentID(); - S32 pos = getScrollPos(); - - std::vector<LLUUID>::const_iterator buddy_it = all_buddies.begin(); - deleteAllItems(); - for(; buddy_it != all_buddies.end(); ++buddy_it) - { - std::string name; - const LLUUID& buddy_id = *buddy_it; - have_names &= gCacheName->getFullName(buddy_id, name); - if (name_filter != LLStringUtil::null && !findInsensitive(name, name_filter)) - continue; - addItem(buddy_id, name, LLAvatarTracker::instance().isBuddyOnline(buddy_id)); - } - - // Changed item in place, need to request sort and update columns - // because we might have changed data in a column on which the user - // has already sorted. JC - updateSort(); - - // re-select items - selectMultiple(selected_ids); - setCurrentByID(current_id); -#if 0 - // Restore selection. - if(selected_ids.size() > 0) - { - // only non-null if friends was already found. This may fail, - // but we don't really care here, because refreshUI() will - // clean up the interface. - for(std::vector<LLUUID>::iterator itr = selected_ids.begin(); itr != selected_ids.end(); ++itr) - { - setSelectedByValue(*itr, true); - } - } -#endif - setScrollPos(pos); - - updateLineHeight(); - LLRect rect = getRequiredRect(); - - LLSD params; - params["action"] = "size_changes"; - params["width"] = rect.getWidth(); - params["height"] = llmax(rect.getHeight(),20) + 5; - - getParent()->notifyParent(params); - - return have_names; -} -// static -std::string LLAvatarList::getVolumeIcon(const LLUUID& id) -{ - // - // Determine icon appropriate for the current avatar volume. - // - // *TODO: remove this in favor of LLOutputMonitorCtrl - // when ListView widget is implemented - // which is capable of containing arbitrary widgets. - // - static LLOutputMonitorCtrl::Params default_monitor_params(LLUICtrlFactory::getDefaultParams<LLOutputMonitorCtrl>()); - bool muted = gVoiceClient->getIsModeratorMuted(id) || gVoiceClient->getOnMuteList(id); - F32 power = gVoiceClient->getCurrentPower(id); - std::string icon; - - if (muted) - { - icon = default_monitor_params.image_mute.name; - } - else if (power == 0.f) - { - icon = default_monitor_params.image_off.name; - } - else if (power < LLVoiceClient::OVERDRIVEN_POWER_LEVEL) - { - S32 icon_image_idx = llmin(2, llfloor((power / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f)); - switch(icon_image_idx) - { - default: - case 0: - icon = default_monitor_params.image_on.name; - break; - case 1: - icon = default_monitor_params.image_level_1.name; - break; - case 2: - icon = default_monitor_params.image_level_2.name; - break; - } - } - else - { - // overdriven - icon = default_monitor_params.image_level_3.name; - } - - return icon; -} - -// Update volume column for all list rows. -void LLAvatarList::updateVolume() -{ - item_list& items = getItemList(); +//comparators +static const LLAvatarItemNameComparator NAME_COMPARATOR; +static const LLFlatListView::ItemReverseComparator REVERSE_NAME_COMPARATOR(NAME_COMPARATOR); - for (item_list::iterator item_it = items.begin(); - item_it != items.end(); - ++item_it) - { - LLScrollListItem* itemp = (*item_it); - LLUUID speaker_id = itemp->getUUID(); - - LLScrollListCell* icon_cell = itemp->getColumn(COL_VOLUME); - if (icon_cell) - icon_cell->setValue(getVolumeIcon(speaker_id)); - } -} - - - - -#include "llavatarlistitem.h" - -LLAvatarListTmp::Params::Params() +LLAvatarList::Params::Params() : volume_column_width("volume_column_width", 0) , online_go_first("online_go_first", true) @@ -320,198 +61,163 @@ volume_column_width("volume_column_width", 0) -LLAvatarListTmp::LLAvatarListTmp(const Params& p) +LLAvatarList::LLAvatarList(const Params& p) : LLFlatListView(p) -, mHaveVolumeColumn(p.volume_column_width > 0) , mOnlineGoFirst(p.online_go_first) +, mContextMenu(NULL) { - LLRect item_list_rect = getLocalRect(); - item_list_rect.stretch( -getBorderWidth()); - - LLTextBox::Params text_p; - text_p.name(COMMENT_TEXTBOX); - text_p.border_visible(false); - text_p.rect(item_list_rect); - text_p.follows.flags(FOLLOWS_ALL); - addChild(LLUICtrlFactory::create<LLTextBox>(text_p)); -} + setCommitOnSelectionChange(true); -// virtual -void LLAvatarListTmp::draw() -{ - LLFlatListView::draw(); - if (mHaveVolumeColumn) - { - updateVolume(); - } + // Set default sort order. + setComparator(&NAME_COMPARATOR); } -std::vector<LLUUID> LLAvatarListTmp::getSelectedIDs() +void LLAvatarList::computeDifference( + const std::vector<LLUUID>& vnew_unsorted, + std::vector<LLUUID>& vadded, + std::vector<LLUUID>& vremoved) { - LLUUID selected_id; - std::vector<LLUUID> avatar_ids; + std::vector<LLUUID> vcur; + std::vector<LLUUID> vnew = vnew_unsorted; - getSelectedUUIDs(avatar_ids); + // Convert LLSDs to LLUUIDs. + { + std::vector<LLSD> vcur_values; + getValues(vcur_values); - return avatar_ids; -} + for (size_t i=0; i<vcur_values.size(); i++) + vcur.push_back(vcur_values[i].asUUID()); + } -void LLAvatarListTmp::addNewItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos) -{ - LLAvatarListItem* item = new LLAvatarListItem(); - item->showStatus(true); - item->showInfoBtn(true); - item->showSpeakingIndicator(true); - item->setName(name); - item->setAvatarId(id); + std::sort(vcur.begin(), vcur.end()); + std::sort(vnew.begin(), vnew.end()); - item->childSetVisible("info_btn", false); + std::vector<LLUUID>::iterator it; + size_t maxsize = llmax(vcur.size(), vnew.size()); + vadded.resize(maxsize); + vremoved.resize(maxsize); - addItem(item, id, pos); + // what to remove + it = set_difference(vcur.begin(), vcur.end(), vnew.begin(), vnew.end(), vremoved.begin()); + vremoved.erase(it, vremoved.end()); - setCommentVisible(false); + // what to add + it = set_difference(vnew.begin(), vnew.end(), vcur.begin(), vcur.end(), vadded.begin()); + vadded.erase(it, vadded.end()); } -BOOL LLAvatarListTmp::update(const std::vector<LLUUID>& all_buddies, const std::string& name_filter) +BOOL LLAvatarList::update(const std::vector<LLUUID>& all_buddies, const std::string& name_filter) { BOOL have_names = TRUE; + bool have_filter = name_filter != LLStringUtil::null; // Save selection. - std::vector<LLUUID> selected_ids = getSelectedIDs(); + std::vector<LLUUID> selected_ids; + getSelectedUUIDs(selected_ids); LLUUID current_id = getSelectedUUID(); - LLRect pos = getScrolledViewRect(); - std::vector<LLUUID>::const_iterator buddy_it = all_buddies.begin(); - clear(); - for(; buddy_it != all_buddies.end(); ++buddy_it) + // Determine what to add and what to remove. + std::vector<LLUUID> added, removed; + LLAvatarList::computeDifference(all_buddies, added, removed); + + // Handle added items. + for (std::vector<LLUUID>::const_iterator it=added.begin(); it != added.end(); it++) { std::string name; - const LLUUID& buddy_id = *buddy_it; + const LLUUID& buddy_id = *it; have_names &= gCacheName->getFullName(buddy_id, name); - if (name_filter != LLStringUtil::null && !findInsensitive(name, name_filter)) - continue; + if (!have_filter || findInsensitive(name, name_filter)) addNewItem(buddy_id, name, LLAvatarTracker::instance().isBuddyOnline(buddy_id)); } + // Handle removed items. + for (std::vector<LLUUID>::const_iterator it=removed.begin(); it != removed.end(); it++) + { + removeItemByUUID(*it); + } + + // Handle filter. + if (have_filter) + { + std::vector<LLSD> cur_values; + getValues(cur_values); + + for (std::vector<LLSD>::const_iterator it=cur_values.begin(); it != cur_values.end(); it++) + { + std::string name; + const LLUUID& buddy_id = it->asUUID(); + have_names &= gCacheName->getFullName(buddy_id, name); + if (!findInsensitive(name, name_filter)) + removeItemByUUID(buddy_id); + } + } + // Changed item in place, need to request sort and update columns // because we might have changed data in a column on which the user // has already sorted. JC - // updateSort(); // TODO: implement sorting + sort(); // re-select items // selectMultiple(selected_ids); // TODO: implement in LLFlatListView if need selectItemByUUID(current_id); - scrollToShowRect(pos); - - - setCommentVisible(false); - - return have_names; -} - - -const LLUUID LLAvatarListTmp::getCurrentID() const -{ - return getSelectedUUID(); + // If the name filter is specified and the names are incomplete, + // we need to re-update when the names are complete so that + // the filter can be applied correctly. + // + // Otherwise, if we have no filter then no need to update again + // because the items will update their names. + return !have_filter || have_names; } -void LLAvatarListTmp::setCommentText(const std::string& comment_text) +void LLAvatarList::sortByName() { - getChild<LLTextBox>(COMMENT_TEXTBOX)->setValue(comment_text); + setComparator(&NAME_COMPARATOR); + sort(); } - ////////////////////////////////////////////////////////////////////////// // PROTECTED SECTION ////////////////////////////////////////////////////////////////////////// - -// virtual overridden -bool LLAvatarListTmp::removeItemPair(item_pair_t* item_pair) +void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos) { - bool removed = LLFlatListView::removeItemPair(item_pair); - setCommentVisible(size() == 0); - return removed; + LLAvatarListItem* item = new LLAvatarListItem(); + item->showStatus(false); + item->showInfoBtn(true); + item->showSpeakingIndicator(true); + item->setName(name); + item->setAvatarId(id); + item->setContextMenu(mContextMenu); + + item->childSetVisible("info_btn", false); + + addItem(item, id, pos); } -////////////////////////////////////////////////////////////////////////// -// PRIVATE SECTION -////////////////////////////////////////////////////////////////////////// -// static -std::string LLAvatarListTmp::getVolumeIcon(const LLUUID& id) -{ - // - // Determine icon appropriate for the current avatar volume. - // - // *TODO: remove this in favor of LLOutputMonitorCtrl - // when ListView widget is implemented - // which is capable of containing arbitrary widgets. - // - static LLOutputMonitorCtrl::Params default_monitor_params(LLUICtrlFactory::getDefaultParams<LLOutputMonitorCtrl>()); - bool muted = gVoiceClient->getIsModeratorMuted(id) || gVoiceClient->getOnMuteList(id); - F32 power = gVoiceClient->getCurrentPower(id); - std::string icon; - if (muted) - { - icon = default_monitor_params.image_mute.name; - } - else if (power == 0.f) - { - icon = default_monitor_params.image_off.name; - } - else if (power < LLVoiceClient::OVERDRIVEN_POWER_LEVEL) - { - S32 icon_image_idx = llmin(2, llfloor((power / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f)); - switch(icon_image_idx) - { - default: - case 0: - icon = default_monitor_params.image_on.name; - break; - case 1: - icon = default_monitor_params.image_level_1.name; - break; - case 2: - icon = default_monitor_params.image_level_2.name; - break; - } - } - else +bool LLAvatarItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const +{ + const LLAvatarListItem* avatar_item1 = dynamic_cast<const LLAvatarListItem*>(item1); + const LLAvatarListItem* avatar_item2 = dynamic_cast<const LLAvatarListItem*>(item2); + + if (!avatar_item1 || !avatar_item2) { - // overdriven - icon = default_monitor_params.image_level_3.name; + llerror("item1 and item2 cannot be null", 0); + return true; } - return icon; + return doCompare(avatar_item1, avatar_item2); } -// Update volume column for all list rows. -void LLAvatarListTmp::updateVolume() +bool LLAvatarItemNameComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const { - // TODO: implement via Listener - /* - item_list& items = getItemList(); + std::string name1 = avatar_item1->getAvatarName(); + std::string name2 = avatar_item2->getAvatarName(); - for (item_list::iterator item_it = items.begin(); - item_it != items.end(); - ++item_it) - { - LLScrollListItem* itemp = (*item_it); - LLUUID speaker_id = itemp->getUUID(); + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); - LLScrollListCell* icon_cell = itemp->getColumn(COL_VOLUME); - if (icon_cell) - icon_cell->setValue(getVolumeIcon(speaker_id)); - } - */ + return name1 < name2; } - -void LLAvatarListTmp::setCommentVisible(bool visible) const -{ - getChildView(COMMENT_TEXTBOX)->setVisible(visible); -} - -// EOF diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index 639ed83ada..8d79e073d2 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -33,88 +33,77 @@ #ifndef LL_LLAVATARLIST_H #define LL_LLAVATARLIST_H -#include <llscrolllistctrl.h> +#include "llflatlistview.h" + +#include "llavatarlistitem.h" -// *TODO: derive from ListView when it's ready. -class LLAvatarList : public LLScrollListCtrl +class LLAvatarList : public LLFlatListView { LOG_CLASS(LLAvatarList); public: - struct Params : public LLInitParam::Block<Params, LLScrollListCtrl::Params> + struct Params : public LLInitParam::Block<Params, LLFlatListView::Params> { Optional<S32> volume_column_width; Optional<bool> online_go_first; Params(); }; - enum EColumnOrder - { - COL_VOLUME, - COL_NAME, - COL_ONLINE, - COL_ID, - }; - LLAvatarList(const Params&); virtual ~LLAvatarList() {} - /*virtual*/ void draw(); - BOOL update(const std::vector<LLUUID>& all_buddies, const std::string& name_filter = LLStringUtil::null); + void setContextMenu(LLAvatarListItem::ContextMenu* menu) { mContextMenu = menu; } + + void sortByName(); + protected: - std::vector<LLUUID> getSelectedIDs(); - void addItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos = ADD_BOTTOM); + void addNewItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos = ADD_BOTTOM); + void computeDifference( + const std::vector<LLUUID>& vnew, + std::vector<LLUUID>& vadded, + std::vector<LLUUID>& vremoved); private: - static std::string getVolumeIcon(const LLUUID& id); /// determine volume icon from current avatar volume - void updateVolume(); // update volume for all avatars - bool mHaveVolumeColumn; bool mOnlineGoFirst; -}; - -#include "llflatlistview.h" + LLAvatarListItem::ContextMenu* mContextMenu; +}; -class LLAvatarListTmp : public LLFlatListView +/** Abstract comparator for avatar items */ +class LLAvatarItemComparator : public LLFlatListView::ItemComparator { - LOG_CLASS(LLAvatarListTmp); -public: - struct Params : public LLInitParam::Block<Params, LLFlatListView::Params> - { - Optional<S32> volume_column_width; - Optional<bool> online_go_first; - Params(); - }; - - LLAvatarListTmp(const Params&); - virtual ~LLAvatarListTmp() {} - - /*virtual*/ void draw(); + LOG_CLASS(LLAvatarItemComparator); - BOOL update(const std::vector<LLUUID>& all_buddies, - const std::string& name_filter = LLStringUtil::null); +public: + LLAvatarItemComparator() {}; + virtual ~LLAvatarItemComparator() {}; - const LLUUID getCurrentID() const; - void setCommentText( const std::string& comment_text); + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const; protected: - std::vector<LLUUID> getSelectedIDs(); - void addNewItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos = ADD_BOTTOM); - /*virtual*/ bool removeItemPair(item_pair_t* item_pair); -private: - static std::string getVolumeIcon(const LLUUID& id); /// determine volume icon from current avatar volume - void updateVolume(); // update volume for all avatars - void setCommentVisible(bool visible) const; + /** + * Returns true if avatar_item1 < avatar_item2, false otherwise + * Implement this method in your particular comparator. + * In Linux a compiler failed to build it using the name "compare", so it was renamed to doCompare + */ + virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const = 0; +}; - bool mHaveVolumeColumn; - bool mOnlineGoFirst; -}; +class LLAvatarItemNameComparator : public LLAvatarItemComparator +{ + LOG_CLASS(LLAvatarItemNameComparator); +public: + LLAvatarItemNameComparator() {}; + virtual ~LLAvatarItemNameComparator() {}; +protected: + virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const; +}; #endif // LL_LLAVATARLIST_H diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index feae8202bc..665dffc8c6 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -48,7 +48,9 @@ LLAvatarListItem::LLAvatarListItem() mAvatarName(NULL), mStatus(NULL), mSpeakingIndicator(NULL), - mInfoBtn(NULL) + mInfoBtn(NULL), + mContextMenu(NULL), + mAvatarId(LLUUID::null) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_list_item.xml"); } @@ -114,6 +116,15 @@ void LLAvatarListItem::onMouseLeave(S32 x, S32 y, MASK mask) LLPanel::onMouseLeave(x, y, mask); } +// virtual +BOOL LLAvatarListItem::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (mContextMenu) + mContextMenu->show(this, const_cast<const LLUUID&>(mAvatarId), x, y); + + return LLPanel::handleRightMouseDown(x, y, mask); +} + void LLAvatarListItem::setStatus(const std::string& status) { mStatus->setValue(status); @@ -127,13 +138,17 @@ void LLAvatarListItem::setName(const std::string& name) void LLAvatarListItem::setAvatarId(const LLUUID& id) { + mAvatarId = id; mAvatarIcon->setValue(id); mSpeakingIndicator->setSpeakerId(id); + + // Set avatar name. + gCacheName->get(id, FALSE, boost::bind(&LLAvatarListItem::onNameCache, this, _2, _3)); } void LLAvatarListItem::onInfoBtnClick() { - LLFloaterReg::showInstance("inspect_avatar", mAvatarIcon->getValue()); + LLFloaterReg::showInstance("inspect_avatar", mAvatarId); /* TODO fix positioning of inspector localPointToScreen(mXPos, mYPos, &mXPos, &mYPos); @@ -156,6 +171,21 @@ void LLAvatarListItem::onInfoBtnClick() */ } +void LLAvatarListItem::showStatus(bool show_status) +{ + // *HACK: dirty hack until we can determine correct avatar status (EXT-1076). + + if (show_status) + return; + + LLRect name_rect = mAvatarName->getRect(); + LLRect status_rect = mStatus->getRect(); + + mStatus->setVisible(show_status); + name_rect.mRight += (status_rect.mRight - name_rect.mRight); + mAvatarName->setRect(name_rect); +} + void LLAvatarListItem::setValue( const LLSD& value ) { if (!value.isMap()) return;; @@ -163,3 +193,19 @@ void LLAvatarListItem::setValue( const LLSD& value ) childSetVisible("selected_icon", value["selected"]); } +const LLUUID& LLAvatarListItem::getAvatarId() const +{ + return mAvatarId; +} + +const std::string LLAvatarListItem::getAvatarName() const +{ + return mAvatarName->getValue(); +} + +void LLAvatarListItem::onNameCache(const std::string& first_name, const std::string& last_name) +{ + std::string name = first_name + " " + last_name; + mAvatarName->setValue(name); + mAvatarName->setToolTip(name); +} diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index dc5606e4c2..bde9250e4a 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -43,32 +43,48 @@ class LLAvatarIconCtrl; class LLAvatarListItem : public LLPanel { public: + class ContextMenu + { + public: + virtual void show(LLView* spawning_view, const LLUUID& id, S32 x, S32 y) = 0; + }; + 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); void setStatus(const std::string& status); void setName(const std::string& name); void setAvatarId(const LLUUID& id); + + const LLUUID& getAvatarId() const; + const std::string getAvatarName() const; void onInfoBtnClick(); void showSpeakingIndicator(bool show) { mSpeakingIndicator->setVisible(show); } void showInfoBtn(bool show_info_btn) {mInfoBtn->setVisible(show_info_btn); } - void showStatus(bool show_status) {mStatus->setVisible(show_status); } + void showStatus(bool show_status); + void setContextMenu(ContextMenu* menu) { mContextMenu = menu; } private: + void onNameCache(const std::string& first_name, const std::string& last_name); + LLAvatarIconCtrl*mAvatarIcon; LLTextBox* mAvatarName; LLTextBox* mStatus; LLOutputMonitorCtrl* mSpeakingIndicator; LLButton* mInfoBtn; + ContextMenu* mContextMenu; + + LLUUID mAvatarId; }; #endif //LL_LLAVATARLISTITEM_H diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp index edf6e84b68..e568b9c526 100644 --- a/indra/newview/llavatarpropertiesprocessor.cpp +++ b/indra/newview/llavatarpropertiesprocessor.cpp @@ -428,27 +428,10 @@ void LLAvatarPropertiesProcessor::processPickInfoReply(LLMessageSystem* msg, voi msg->getString(_PREHASH_Data, _PREHASH_Desc, pick_data.desc); msg->getUUID(_PREHASH_Data, _PREHASH_SnapshotID, pick_data.snapshot_id); - // "Location text" is actually the owner name, the original - // name that owner gave the parcel, and the location. - msg->getString(_PREHASH_Data, _PREHASH_User, pick_data.location_text); - pick_data.location_text.append(", "); - + msg->getString(_PREHASH_Data, _PREHASH_User, pick_data.user_name); msg->getString(_PREHASH_Data, _PREHASH_OriginalName, pick_data.original_name); - if (!pick_data.original_name.empty()) - { - pick_data.location_text.append(pick_data.original_name); - pick_data.location_text.append(", "); - } - msg->getString(_PREHASH_Data, _PREHASH_SimName, pick_data.sim_name); - pick_data.location_text.append(pick_data.sim_name); - pick_data.location_text.append(" "); - msg->getVector3d(_PREHASH_Data, _PREHASH_PosGlobal, pick_data.pos_global); - S32 region_x = llround((F32)pick_data.pos_global.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)pick_data.pos_global.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)pick_data.pos_global.mdV[VZ]); - pick_data.location_text.append(llformat("(%d, %d, %d)", region_x, region_y, region_z)); msg->getS32(_PREHASH_Data, _PREHASH_SortOrder, pick_data.sort_order); msg->getBOOL(_PREHASH_Data, _PREHASH_Enabled, pick_data.enabled); diff --git a/indra/newview/llavatarpropertiesprocessor.h b/indra/newview/llavatarpropertiesprocessor.h index 24675c44c0..79d109f1db 100644 --- a/indra/newview/llavatarpropertiesprocessor.h +++ b/indra/newview/llavatarpropertiesprocessor.h @@ -96,7 +96,7 @@ struct LLPickData BOOL enabled; //used only in read requests - std::string location_text; + std::string user_name; std::string original_name; std::string sim_name; diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 06f9a86d8d..46151b469f 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -37,7 +37,7 @@ #include "llchiclet.h" #include "llfloaterreg.h" #include "llflyoutbutton.h" -#include "llimpanel.h" // for LLIMFloater +#include "llimfloater.h" // for LLIMFloater #include "lllayoutstack.h" #include "llnearbychatbar.h" #include "llsplitbutton.h" @@ -59,13 +59,13 @@ LLBottomTray::LLBottomTray(const LLSD&) mChicletPanel = getChild<LLChicletPanel>("chiclet_list"); mSysWell = getChild<LLNotificationChiclet>("sys_well"); - mSysWell->setNotificationChicletWindow(LLFloaterReg::getInstance("syswell_window")); + // init mSysWell + // set handler for a Click operation + mSysWell->setClickCallback(boost::bind(&LLSysWellWindow::onChicletClick, LLFloaterReg::getTypedInstance<LLSysWellWindow>("syswell_window"))); mChicletPanel->setChicletClickedCallback(boost::bind(&LLBottomTray::onChicletClick,this,_1)); - LLSplitButton* presets = getChild<LLSplitButton>("presets"); - presets->setSelectionCallback(LLFloaterCamera::onClickCameraPresets); - + LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("CameraPresets.ChangeView",&LLFloaterCameraPresets::onClickCameraPresets); LLIMMgr::getInstance()->addSessionObserver(this); //this is to fix a crash that occurs because LLBottomTray is a singleton @@ -79,16 +79,15 @@ LLBottomTray::LLBottomTray(const LLSD&) BOOL LLBottomTray::postBuild() { - mCommitCallbackRegistrar.add("ShowCamMoveCtrls.Action", boost::bind(&LLBottomTray::onShowCamMoveCtrlsContextMenuItemClicked, this, _2)); - mEnableCallbackRegistrar.add("ShowCamMoveCtrls.EnableMenuItem", boost::bind(&LLBottomTray::onShowCamMoveCtrlsContextMenuItemEnabled, this, _2)); - - mShowCamMoveCtrlsContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_hide_camera_move_controls.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - gMenuHolder->addChild(mShowCamMoveCtrlsContextMenu); + mBottomTrayContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_bottomtray.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + gMenuHolder->addChild(mBottomTrayContextMenu); mNearbyChatBar = getChild<LLNearbyChatBar>("chat_bar"); mToolbarStack = getChild<LLLayoutStack>("toolbar_stack"); mMovementPanel = getChild<LLPanel>("movement_panel"); + mGestureCombo = getChild<LLComboBox>("Gesture"); mCamPanel = getChild<LLPanel>("cam_panel"); + setRightMouseDownCallback(boost::bind(&LLBottomTray::showBottomTrayContextMenu,this, _2, _3,_4)); return TRUE; } @@ -222,54 +221,47 @@ void LLBottomTray::setVisible(BOOL visible) } } -BOOL LLBottomTray::handleRightMouseDown(S32 x, S32 y, MASK mask) +void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask) { - if (!LLPanel::handleRightMouseDown(x, y, mask)) + // We should show BottomTrayContextMenu in last turn + if (mBottomTrayContextMenu && !LLMenuGL::sMenuContainer->getVisibleMenu()) { - if (mShowCamMoveCtrlsContextMenu) - { - mShowCamMoveCtrlsContextMenu->buildDrawLabels(); - mShowCamMoveCtrlsContextMenu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, mShowCamMoveCtrlsContextMenu, x, y); + //there are no other context menu (IM chiclet etc ), so we can show BottomTrayContextMenu + mBottomTrayContextMenu->buildDrawLabels(); + mBottomTrayContextMenu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, mBottomTrayContextMenu, x, y); + } - } - - return TRUE; } -bool LLBottomTray::onShowCamMoveCtrlsContextMenuItemEnabled(const LLSD& userdata) +void LLBottomTray::showGestureButton(BOOL visible) { - std::string item = userdata.asString(); - - if (item == "show_camera_move_controls") + if (visible != mGestureCombo->getVisible()) { - return gSavedSettings.getBOOL("ShowCameraAndMoveControls"); - } + LLRect r = mNearbyChatBar->getRect(); - return FALSE; -} - -void LLBottomTray::onShowCamMoveCtrlsContextMenuItemClicked(const LLSD& userdata) -{ - std::string item = userdata.asString(); + mGestureCombo->setVisible(visible); - if (item == "show_camera_move_controls") + if (!visible) { - BOOL state = !gSavedSettings.getBOOL("ShowCameraAndMoveControls"); + LLFloaterReg::hideFloaterInstance("gestures"); + r.mRight -= mGestureCombo->getRect().getWidth(); + } + else + { + r.mRight += mGestureCombo->getRect().getWidth(); + } - showCameraAndMoveControls(state); - gSavedSettings.setBOOL("ShowCameraAndMoveControls", state); + mNearbyChatBar->setRect(r); } } -void LLBottomTray::showCameraAndMoveControls(BOOL visible) +void LLBottomTray::showMoveButton(BOOL visible) { - mCamPanel->setVisible(visible); mMovementPanel->setVisible(visible); +} - if (!visible) - { - LLFloaterReg::hideFloaterInstance("moveview"); - LLFloaterReg::hideFloaterInstance("camera"); - } +void LLBottomTray::showCameraButton(BOOL visible) +{ + mCamPanel->setVisible(visible); } diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index c3c840ede0..b25dec7b92 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -37,6 +37,7 @@ #include "llpanel.h" #include "llimview.h" +#include "llcombobox.h" class LLChicletPanel; class LLLineEditor; @@ -70,9 +71,11 @@ public: virtual void onFocusLost(); virtual void setVisible(BOOL visible); - virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + void showBottomTrayContextMenu(S32 x, S32 y, MASK mask); - void showCameraAndMoveControls(BOOL visible); + void showGestureButton(BOOL visible); + void showMoveButton(BOOL visible); + void showCameraButton(BOOL visible); private: @@ -82,9 +85,6 @@ protected: void onChicletClick(LLUICtrl* ctrl); - bool onShowCamMoveCtrlsContextMenuItemEnabled(const LLSD& userdata); - void onShowCamMoveCtrlsContextMenuItemClicked(const LLSD& userdata); - static void* createNearbyChatBar(void* userdata); /** @@ -97,9 +97,10 @@ protected: LLTalkButton* mTalkBtn; LLNearbyChatBar* mNearbyChatBar; LLLayoutStack* mToolbarStack; - LLMenuGL* mShowCamMoveCtrlsContextMenu; + LLMenuGL* mBottomTrayContextMenu; LLPanel* mMovementPanel; LLPanel* mCamPanel; + LLComboBox* mGestureCombo; }; #endif // LL_LLBOTTOMPANEL_H diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index a8373491cf..7ae9976338 100644 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -37,6 +37,8 @@ #include "llappviewer.h" #include "llviewercontrol.h" #include "llimview.h" +#include "llbottomtray.h" +#include "llviewerwindow.h" #include <algorithm> @@ -48,12 +50,34 @@ LLChannelManager::LLChannelManager() LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLChannelManager::onLoginCompleted, this)); mChannelList.clear(); mStartUpChannel = NULL; + + if(!gViewerWindow) + { + llerrs << "LLChannelManager::LLChannelManager() - viwer window is not initialized yet" << llendl; + } } //-------------------------------------------------------------------------- LLChannelManager::~LLChannelManager() { - //All channels are being deleted by Parent View + for(std::vector<ChannelElem>::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) + { + delete (*it).channel; + } + + mChannelList.clear(); +} + +//-------------------------------------------------------------------------- +LLScreenChannel* LLChannelManager::createNotificationChannel() +{ + // creating params for a channel + LLChannelManager::Params p; + p.id = LLUUID(gSavedSettings.getString("NotificationChannelUUID")); + p.channel_align = CA_RIGHT; + + // Getting a Channel for our notifications + return LLChannelManager::getInstance()->getChannel(p); } //-------------------------------------------------------------------------- @@ -61,20 +85,22 @@ void LLChannelManager::onLoginCompleted() { S32 away_notifications = 0; + // calc a number of all offline notifications for(std::vector<ChannelElem>::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) { + // don't calc notifications for Nearby Chat if((*it).channel->getChannelID() == LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))) { continue; } + // don't calc notifications for channels that always show their notifications if(!(*it).channel->getDisplayToastsAlways()) { away_notifications +=(*it).channel->getNumberOfHiddenToasts(); } } - // *TODO: calculate IM notifications away_notifications += gIMMgr->getNumberOfUnreadIM(); if(!away_notifications) @@ -83,11 +109,11 @@ void LLChannelManager::onLoginCompleted() return; } + // create a channel for the StartUp Toast LLChannelManager::Params p; p.id = LLUUID(gSavedSettings.getString("StartUpChannelUUID")); - p.channel_right_bound = getRootView()->getRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); - p.channel_width = gSavedSettings.getS32("NotifyBoxWidth"); - mStartUpChannel = createChannel(p); + p.channel_align = CA_RIGHT; + mStartUpChannel = getChannel(p); if(!mStartUpChannel) { @@ -95,8 +121,13 @@ void LLChannelManager::onLoginCompleted() return; } + // init channel's position and size + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound); mStartUpChannel->setShowToasts(true); - static_cast<LLUICtrl*>(mStartUpChannel)->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this)); + + mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this)); mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("ChannelBottomPanelMargin"), gSavedSettings.getS32("StartUpToastTime")); } @@ -107,76 +138,56 @@ void LLChannelManager::onStartUpToastClose() { mStartUpChannel->setVisible(FALSE); mStartUpChannel->closeStartUpToast(); - getRootView()->removeChild(mStartUpChannel); removeChannelByID(LLUUID(gSavedSettings.getString("StartUpChannelUUID"))); delete mStartUpChannel; mStartUpChannel = NULL; } - // set StartUp Toast Flag + // set StartUp Toast Flag to allow all other channels to show incoming toasts LLScreenChannel::setStartUpToastShown(); - // allow all other channels to show incoming toasts - for(std::vector<ChannelElem>::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) - { - (*it).channel->setShowToasts(true); - } - // force NEARBY CHAT CHANNEL to repost all toasts if present - LLScreenChannel* nearby_channel = getChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + LLScreenChannel* nearby_channel = findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); nearby_channel->loadStoredToastsToChannel(); nearby_channel->setCanStoreToasts(false); } //-------------------------------------------------------------------------- -LLScreenChannel* LLChannelManager::createChannel(LLChannelManager::Params& p) +LLScreenChannel* LLChannelManager::getChannel(LLChannelManager::Params& p) { LLScreenChannel* new_channel = NULL; - if(!p.chiclet) - { - new_channel = getChannelByID(p.id); - } - else - { - new_channel = getChannelByChiclet(p.chiclet); - } + new_channel = findChannelByID(p.id); if(new_channel) return new_channel; new_channel = new LLScreenChannel(p.id); - getRootView()->addChild(new_channel); - new_channel->init(p.channel_right_bound - p.channel_width, p.channel_right_bound); - new_channel->setToastAlignment(p.align); + + if(!new_channel) + { + llerrs << "LLChannelManager::getChannel(LLChannelManager::Params& p) - can't create a channel!" << llendl; + } + else + { + new_channel->setToastAlignment(p.toast_align); + new_channel->setChannelAlignment(p.channel_align); new_channel->setDisplayToastsAlways(p.display_toasts_always); ChannelElem new_elem; new_elem.id = p.id; - new_elem.chiclet = p.chiclet; new_elem.channel = new_channel; - - mChannelList.push_back(new_elem); //TODO: remove chiclet from ScreenChannel? - - return new_channel; -} -//-------------------------------------------------------------------------- -LLScreenChannel* LLChannelManager::getChannelByID(const LLUUID id) -{ - std::vector<ChannelElem>::iterator it = find(mChannelList.begin(), mChannelList.end(), id); - if(it != mChannelList.end()) - { - return (*it).channel; + mChannelList.push_back(new_elem); } - return NULL; + return new_channel; } //-------------------------------------------------------------------------- -LLScreenChannel* LLChannelManager::getChannelByChiclet(const LLChiclet* chiclet) +LLScreenChannel* LLChannelManager::findChannelByID(const LLUUID id) { - std::vector<ChannelElem>::iterator it = find(mChannelList.begin(), mChannelList.end(), chiclet); + std::vector<ChannelElem>::iterator it = find(mChannelList.begin(), mChannelList.end(), id); if(it != mChannelList.end()) { return (*it).channel; @@ -186,22 +197,6 @@ LLScreenChannel* LLChannelManager::getChannelByChiclet(const LLChiclet* chiclet) } //-------------------------------------------------------------------------- -void LLChannelManager::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - for(std::vector<ChannelElem>::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) - { - if((*it).channel->getToastAlignment() == NA_CENTRE) - { - LLRect channel_rect = (*it).channel->getRect(); - S32 screen_width = getRootView()->getRect().getWidth(); - channel_rect.setLeftTopAndSize(screen_width/2, channel_rect.mTop, channel_rect.getWidth(), channel_rect.getHeight()); - (*it).channel->setRect(channel_rect); - (*it).channel->showToasts(); - } - } -} - -//-------------------------------------------------------------------------- void LLChannelManager::removeChannelByID(const LLUUID id) { std::vector<ChannelElem>::iterator it = find(mChannelList.begin(), mChannelList.end(), id); @@ -212,18 +207,5 @@ void LLChannelManager::removeChannelByID(const LLUUID id) } //-------------------------------------------------------------------------- -void LLChannelManager::removeChannelByChiclet(const LLChiclet* chiclet) -{ - std::vector<ChannelElem>::iterator it = find(mChannelList.begin(), mChannelList.end(), chiclet); - if(it != mChannelList.end()) - { - mChannelList.erase(it); - } -} - -//-------------------------------------------------------------------------- - - - diff --git a/indra/newview/llchannelmanager.h b/indra/newview/llchannelmanager.h index e26c96b62e..811fa06d2b 100644 --- a/indra/newview/llchannelmanager.h +++ b/indra/newview/llchannelmanager.h @@ -34,7 +34,6 @@ #define LL_LLCHANNELMANAGER_H -#include "llchiclet.h" #include "llscreenchannel.h" #include "lluuid.h" @@ -48,36 +47,30 @@ namespace LLNotificationsUI * Manager for screen channels. * Responsible for instantiating and retrieving screen channels. */ -class LLChannelManager : public LLUICtrl, public LLSingleton<LLChannelManager> +class LLChannelManager : public LLSingleton<LLChannelManager> { public: - struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> + struct Params { LLUUID id; - LLChiclet* chiclet; - S32 channel_right_bound; - S32 channel_width; bool display_toasts_always; - EToastAlignment align; + EToastAlignment toast_align; + EChannelAlignment channel_align; - Params(): id(LLUUID("")), chiclet(NULL), - channel_right_bound(0), channel_width(0), - display_toasts_always(false), align(NA_BOTTOM) + Params(): id(LLUUID("")), display_toasts_always(false), toast_align(NA_BOTTOM), channel_align(CA_LEFT) {} }; struct ChannelElem { LLUUID id; - LLChiclet* chiclet; LLScreenChannel* channel; - ChannelElem() : id(LLUUID("")), chiclet(NULL), channel(NULL) { } + ChannelElem() : id(LLUUID("")), channel(NULL) { } ChannelElem(const ChannelElem &elem) { id = elem.id; - chiclet = elem.chiclet; channel = elem.channel; } @@ -85,12 +78,6 @@ public: { return (id == id_op); } - - bool operator == (const LLChiclet* chiclet_op) const - { - return (chiclet == chiclet_op); - } - }; LLChannelManager(); @@ -101,17 +88,17 @@ public: // removes a channel intended for the startup toast and allows other channels to show their toasts void onStartUpToastClose(); - //TODO: make protected? in order to be shure that channels are created only by notification handlers - LLScreenChannel* createChannel(LLChannelManager::Params& p); + // creates a new ScreenChannel according to the given parameters or returns existing if present + LLScreenChannel* getChannel(LLChannelManager::Params& p); + + // returns a channel by its ID + LLScreenChannel* findChannelByID(const LLUUID id); - LLScreenChannel* getChannelByID(const LLUUID id); - LLScreenChannel* getChannelByChiclet(const LLChiclet* chiclet); + // creator of the Notification channel, that is used in more than one handler + LLScreenChannel* createNotificationChannel(); // remove channel methods void removeChannelByID(const LLUUID id); - void removeChannelByChiclet(const LLChiclet* chiclet); - - void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); private: diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index a2dc97f7f5..20c44d5b11 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -38,6 +38,7 @@ #include "llgroupactions.h" #include "lliconctrl.h" #include "llimpanel.h" // LLFloaterIMPanel +#include "llimfloater.h" #include "llimview.h" #include "llfloaterreg.h" #include "lllocalcliprect.h" @@ -47,6 +48,7 @@ #include "llvoiceclient.h" #include "llvoicecontrolpanel.h" #include "llgroupmgr.h" +#include "llnotificationmanager.h" static LLDefaultChildRegistry::Register<LLChicletPanel> t1("chiclet_panel"); static LLDefaultChildRegistry::Register<LLTalkButton> t2("chiclet_talk"); @@ -84,7 +86,6 @@ LLNotificationChiclet::LLNotificationChiclet(const Params& p) : LLChiclet(p) , mButton(NULL) , mCounterCtrl(NULL) -, mNotificationChicletWindow(NULL) { LLButton::Params button_params = p.button; button_params.rect(p.rect()); @@ -94,6 +95,11 @@ LLNotificationChiclet::LLNotificationChiclet(const Params& p) LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications; mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params); addChild(mCounterCtrl); + + // connect counter handlers to the signals + connectCounterUpdatersToSignal("notify"); + connectCounterUpdatersToSignal("groupnotify"); + connectCounterUpdatersToSignal("notifytoast"); } LLNotificationChiclet::~LLNotificationChiclet() @@ -101,6 +107,25 @@ LLNotificationChiclet::~LLNotificationChiclet() } +void LLNotificationChiclet::connectCounterUpdatersToSignal(std::string notification_type) +{ + LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance(); + LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type); + if(n_handler) + { + if(notification_type == "notifytoast") + { + n_handler->setNewNotificationCallback(boost::bind(&LLNotificationChiclet::updateUreadIMNotifications, this)); + n_handler->setDelNotification(boost::bind(&LLNotificationChiclet::updateUreadIMNotifications, this)); + } + else + { + n_handler->setNewNotificationCallback(boost::bind(&LLNotificationChiclet::incUreadSystemNotifications, this)); + n_handler->setDelNotification(boost::bind(&LLNotificationChiclet::decUreadSystemNotifications, this)); + } + } +} + void LLNotificationChiclet::setCounter(S32 counter) { mCounterCtrl->setCounter(counter); @@ -259,7 +284,8 @@ LLIMP2PChiclet::Params::Params() rect(LLRect(0, 25, 45, 0)); avatar_icon.name("avatar_icon"); - avatar_icon.rect(LLRect(0, 25, 25, 0)); + avatar_icon.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + avatar_icon.rect(LLRect(0, 24, 25, 0)); avatar_icon.mouse_opaque(false); unread_notifications.name("unread"); @@ -432,7 +458,7 @@ LLIMGroupChiclet::Params::Params() rect(LLRect(0, 25, 45, 0)); group_icon.name("group_icon"); - group_icon.rect(LLRect(0, 25, 25, 0)); + group_icon.rect(LLRect(0, 24, 25, 0)); unread_notifications.name("unread"); unread_notifications.rect(LLRect(25, 25, 45, 0)); @@ -846,6 +872,27 @@ void LLChicletPanel::removeAll() showScrollButtonsIfNeeded(); } +void LLChicletPanel::scrollToChiclet(const LLChiclet* chiclet) +{ + const LLRect& rect = chiclet->getRect(); + + if (rect.mLeft < 0) + { + scroll(llabs(rect.mLeft)); + showScrollButtonsIfNeeded(); + } + else + { + S32 scrollWidth = mScrollArea->getRect().getWidth(); + + if (rect.mRight > scrollWidth) + { + scroll(-llabs(rect.mRight - scrollWidth)); + showScrollButtonsIfNeeded(); + } + } +} + void LLChicletPanel::reshape(S32 width, S32 height, BOOL called_from_parent ) { LLPanel::reshape(width,height,called_from_parent); @@ -861,7 +908,7 @@ void LLChicletPanel::reshape(S32 width, S32 height, BOOL called_from_parent ) width, height - scroll_button_rect.getHeight())); mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + SCROLL_BUTTON_PAD, - height + 7, width - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); + height, width - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); mShowControls = width > mMinWidth; mScrollArea->setVisible(mShowControls); diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 52bd7dbc31..316348cf1d 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -548,9 +548,6 @@ public: /*virtual*/ ~ LLNotificationChiclet(); - // Notification Chiclet Window - void setNotificationChicletWindow(LLFloater* wnd) { mNotificationChicletWindow = wnd; } - // methods for updating a number of unread System or IM notifications void incUreadSystemNotifications() { setCounter(++mUreadSystemNotifications + mUreadIMNotifications); } void decUreadSystemNotifications() { setCounter(--mUreadSystemNotifications + mUreadIMNotifications); } @@ -558,11 +555,12 @@ public: void setToggleState(BOOL toggled); protected: + // connect counter updaters to the corresponding signals + void connectCounterUpdatersToSignal(std::string notification_type); + LLNotificationChiclet(const Params& p); friend class LLUICtrlFactory; - LLFloater* mNotificationChicletWindow; - static S32 mUreadSystemNotifications; static S32 mUreadIMNotifications; @@ -644,6 +642,11 @@ public: */ void removeAll(); + /* + * Scrolls the panel to the specified chiclet + */ + void scrollToChiclet(const LLChiclet* chiclet); + boost::signals2::connection setChicletClickedCallback( const commit_callback_t& cb); @@ -814,6 +817,8 @@ T* LLChicletPanel::createChiclet(const LLUUID& session_id /*= LLUUID::null*/, S3 return NULL; } + scrollToChiclet(chiclet); + chiclet->setSessionId(session_id); return chiclet; diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp new file mode 100644 index 0000000000..131f9ceaf0 --- /dev/null +++ b/indra/newview/llexpandabletextbox.cpp @@ -0,0 +1,508 @@ +/** + * @file llexpandabletextbox.cpp + * @brief LLExpandableTextBox and related class implementations + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-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 "llexpandabletextbox.h" + +#include "llscrollcontainer.h" + +static LLDefaultChildRegistry::Register<LLExpandableTextBox> t1("expandable_text"); + +LLExpandableTextBox::LLTextBoxEx::Params::Params() +: expand_textbox("expand_textbox") +{ +} + +LLExpandableTextBox::LLTextBoxEx::LLTextBoxEx(const Params& p) +: LLTextBox(p) +{ + setIsChrome(TRUE); + + LLTextBox::Params params = p.expand_textbox; + mExpandTextBox = LLUICtrlFactory::create<LLTextBox>(params); + addChild(mExpandTextBox); + + LLRect rc = getLocalRect(); + rc.mRight -= getHPad(); + rc.mLeft = rc.mRight - mExpandTextBox->getTextPixelWidth(); + rc.mTop = mExpandTextBox->getTextPixelHeight(); + mExpandTextBox->setRect(rc); +} + +BOOL LLExpandableTextBox::LLTextBoxEx::handleMouseUp(S32 x, S32 y, MASK mask) +{ + BOOL ret = LLTextBox::handleMouseUp(x, y, mask); + + if(mExpandTextBox->getRect().pointInRect(x, y)) + { + onCommit(); + } + + return ret; +} + +void LLExpandableTextBox::LLTextBoxEx::draw() +{ + // draw text box + LLTextBox::draw(); + // force text box to draw children + LLUICtrl::draw(); +} + +void LLExpandableTextBox::LLTextBoxEx::drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color ) +{ + // *NOTE:dzaporozhan: + // Copy/paste from LLTextBox::drawText in order to modify last + // line width if needed and who "More" link + F32 alpha = getDrawContext().mAlpha; + if (mSegments.size() > 1) + { + // we have Urls (or other multi-styled segments) + drawTextSegments(x, y, text); + } + else if( mLineLengthList.empty() ) + { + // simple case of 1 line of text in one style + mDefaultFont->render(text, 0, (F32)x, (F32)y, color % alpha, + mHAlign, mVAlign, + 0, + mShadowType, + S32_MAX, getRect().getWidth(), NULL, mUseEllipses); + + mExpandTextBox->setVisible(FALSE); + } + else + { + // simple case of multiple lines of text, all in the same style + S32 cur_pos = 0; + for (std::vector<S32>::iterator iter = mLineLengthList.begin(); + iter != mLineLengthList.end(); ++iter) + { + S32 line_length = *iter; + S32 line_height = llfloor(mDefaultFont->getLineHeight()) + mLineSpacing; + S32 max_pixels = getRect().getWidth(); + + if(iter + 1 != mLineLengthList.end() + && y - line_height < line_height) + { + max_pixels = getCropTextWidth(); + } + + mDefaultFont->render(text, cur_pos, (F32)x, (F32)y, color % alpha, + mHAlign, mVAlign, + 0, + mShadowType, + line_length, max_pixels, NULL, mUseEllipses ); + + cur_pos += line_length + 1; + + y -= line_height; + if(y < line_height) + { + if( mLineLengthList.end() != iter + 1 ) + { + showExpandText(y); + } + else + { + hideExpandText(); + } + break; + } + } + } +} + +void LLExpandableTextBox::LLTextBoxEx::showExpandText(S32 y) +{ + LLRect rc = mExpandTextBox->getRect(); + rc.mTop = y + mExpandTextBox->getTextPixelHeight(); + rc.mBottom = y; + mExpandTextBox->setRect(rc); + mExpandTextBox->setVisible(TRUE); +} + +void LLExpandableTextBox::LLTextBoxEx::hideExpandText() +{ + mExpandTextBox->setVisible(FALSE); +} + +S32 LLExpandableTextBox::LLTextBoxEx::getCropTextWidth() +{ + return mExpandTextBox->getRect().mLeft - getHPad() * 2; +} + +void LLExpandableTextBox::LLTextBoxEx::drawTextSegments(S32 init_x, S32 init_y, const LLWString &text) +{ + // *NOTE:dzaporozhan: + // Copy/paste from LLTextBox::drawTextSegments in order to modify last + // line width if needed and who "More" link + F32 alpha = getDrawContext().mAlpha; + + const S32 text_len = text.length(); + if (text_len <= 0) + { + return; + } + + S32 cur_line = 0; + S32 num_lines = getLineCount(); + S32 line_start = getLineStart(cur_line); + S32 line_height = llround( mDefaultFont->getLineHeight() ) + mLineSpacing; + F32 text_y = (F32) init_y; + segment_set_t::iterator cur_seg = mSegments.begin(); + + // render a line of text at a time + const LLRect textRect = getLocalRect(); + while((textRect.mBottom <= text_y) && (cur_line < num_lines)) + { + S32 next_start = -1; + S32 line_end = text_len; + + if ((cur_line + 1) < num_lines) + { + next_start = getLineStart(cur_line + 1); + line_end = next_start; + } + if ( text[line_end-1] == '\n' ) + { + --line_end; + } + + // render all segments on this line + F32 text_x = init_x; + S32 seg_start = line_start; + while (seg_start < line_end && cur_seg != mSegments.end()) + { + // move to the next segment (or continue the previous one) + LLTextSegment *cur_segment = *cur_seg; + while (cur_segment->getEnd() <= seg_start) + { + if (++cur_seg == mSegments.end()) + { + return; + } + cur_segment = *cur_seg; + } + + // Draw a segment within the line + S32 clipped_end = llmin( line_end, cur_segment->getEnd() ); + S32 clipped_len = clipped_end - seg_start; + if( clipped_len > 0 ) + { + LLStyleSP style = cur_segment->getStyle(); + if (style && style->isVisible()) + { + // work out the color for the segment + LLColor4 color ; + if (getEnabled()) + { + color = style->isLink() ? mLinkColor.get() : mTextColor.get(); + } + else + { + color = mDisabledColor.get(); + } + color = color % alpha; + + S32 max_pixels = textRect.getWidth(); + + if(cur_line + 1 < num_lines + && text_y - line_height < line_height) + { + max_pixels = getCropTextWidth(); + } + + // render a single line worth for this segment + mDefaultFont->render(text, seg_start, text_x, text_y, color, + mHAlign, mVAlign, 0, mShadowType, clipped_len, + max_pixels, &text_x, mUseEllipses); + } + + seg_start += clipped_len; + } + } + + // move down one line + text_y -= (F32)line_height; + line_start = next_start; + cur_line++; + if(text_y < line_height) + { + if( cur_line < num_lines ) + { + showExpandText((S32)text_y); + } + else + { + hideExpandText(); + } + break; + } + } +} + +S32 LLExpandableTextBox::LLTextBoxEx::getVerticalTextDelta() +{ + S32 text_height = getTextPixelHeight(); + S32 textbox_height = getRect().getHeight(); + + return text_height - textbox_height; +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +LLExpandableTextBox::Params::Params() +: textbox("textbox") +, scroll("scroll") +, max_height("max_height", 0) +, bg_visible("bg_visible", false) +, expanded_bg_visible("expanded_bg_visible", true) +, bg_color("bg_color", LLColor4::black) +, expanded_bg_color("expanded_bg_color", LLColor4::black) +{ +} + +LLExpandableTextBox::LLExpandableTextBox(const Params& p) +: LLUICtrl(p) +, mMaxHeight(p.max_height) +, mBGVisible(p.bg_visible) +, mExpandedBGVisible(p.expanded_bg_visible) +, mBGColor(p.bg_color) +, mExpandedBGColor(p.expanded_bg_color) +, mExpanded(false) +{ + LLRect rc = getLocalRect(); + + LLScrollContainer::Params scroll_params = p.scroll; + scroll_params.rect(rc); + mScroll = LLUICtrlFactory::create<LLScrollContainer>(scroll_params); + addChild(mScroll); + + LLTextBoxEx::Params textbox_params = p.textbox; + textbox_params.rect(rc); + mTextBox = LLUICtrlFactory::create<LLTextBoxEx>(textbox_params); + mScroll->addChild(mTextBox); + + updateTextBoxRect(); + + mTextBox->setCommitCallback(boost::bind(&LLExpandableTextBox::onExpandClicked, this)); +} + +void LLExpandableTextBox::draw() +{ + if(mBGVisible && !mExpanded) + { + gl_rect_2d(getLocalRect(), mBGColor.get(), TRUE); + } + if(mExpandedBGVisible && mExpanded) + { + gl_rect_2d(getLocalRect(), mExpandedBGColor.get(), TRUE); + } + + collapseIfPosChanged(); + + LLUICtrl::draw(); +} + +void LLExpandableTextBox::collapseIfPosChanged() +{ + if(mExpanded) + { + LLView* parentp = getParent(); + LLRect parent_rect = parentp->getRect(); + parentp->localRectToOtherView(parent_rect, &parent_rect, getRootView()); + + if(parent_rect.mLeft != mParentRect.mLeft + || parent_rect.mTop != mParentRect.mTop) + { + collapseTextBox(); + } + } +} + +void LLExpandableTextBox::onExpandClicked() +{ + expandTextBox(); +} + +void LLExpandableTextBox::updateTextBoxRect() +{ + LLRect rc = getLocalRect(); + + rc.mLeft += mScroll->getBorderWidth(); + rc.mRight -= mScroll->getBorderWidth(); + rc.mTop -= mScroll->getBorderWidth(); + rc.mBottom += mScroll->getBorderWidth(); + + mTextBox->reshape(rc.getWidth(), rc.getHeight()); + mTextBox->setRect(rc); +} + +S32 LLExpandableTextBox::recalculateTextDelta(S32 text_delta) +{ + LLRect expanded_rect = getLocalRect(); + LLView* root_view = getRootView(); + LLRect window_rect = root_view->getRect(); + + LLRect expanded_screen_rect; + localRectToOtherView(expanded_rect, &expanded_screen_rect, root_view); + + // don't allow expanded text box bottom go off screen + if(expanded_screen_rect.mBottom - text_delta < window_rect.mBottom) + { + text_delta = expanded_screen_rect.mBottom - window_rect.mBottom; + } + // show scroll bar if max_height is valid + // and expanded size is greater that max_height + else if(mMaxHeight > 0 && expanded_rect.getHeight() + text_delta > mMaxHeight) + { + text_delta = mMaxHeight - expanded_rect.getHeight(); + } + + return text_delta; +} + +void LLExpandableTextBox::expandTextBox() +{ + S32 text_delta = mTextBox->getVerticalTextDelta(); + text_delta += mTextBox->getVPad() * 2 + mScroll->getBorderWidth() * 2; + // no need to expand + if(text_delta <= 0) + { + return; + } + + saveCollapsedState(); + + LLRect expanded_rect = getLocalRect(); + LLRect expanded_screen_rect; + + S32 updated_text_delta = recalculateTextDelta(text_delta); + // actual expand + expanded_rect.mBottom -= updated_text_delta; + + LLRect text_box_rect = mTextBox->getRect(); + + // check if we need to show scrollbar + if(text_delta != updated_text_delta) + { + static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); + + // disable horizontal scrollbar + text_box_rect.mRight -= scrollbar_size; + // text box size has changed - redo text wrap + mTextBox->setWrappedText(mText, text_box_rect.getWidth()); + // recalculate text delta since text wrap changed text height + text_delta = mTextBox->getVerticalTextDelta() + mTextBox->getVPad() * 2; + } + + // expand text + text_box_rect.mBottom -= text_delta; + mTextBox->reshape(text_box_rect.getWidth(), text_box_rect.getHeight()); + mTextBox->setRect(text_box_rect); + + // expand text box + localRectToOtherView(expanded_rect, &expanded_screen_rect, getParent()); + reshape(expanded_screen_rect.getWidth(), expanded_screen_rect.getHeight(), FALSE); + setRect(expanded_screen_rect); + + setFocus(TRUE); + // this lets us receive top_lost event(needed to collapse text box) + // it also draws text box above all other ui elements + gFocusMgr.setTopCtrl(this); + + mExpanded = true; +} + +void LLExpandableTextBox::collapseTextBox() +{ + if(!mExpanded) + { + return; + } + + mExpanded = false; + + reshape(mCollapsedRect.getWidth(), mCollapsedRect.getHeight(), FALSE); + setRect(mCollapsedRect); + + updateTextBoxRect(); + + mTextBox->setWrappedText(mText); + if(gFocusMgr.getTopCtrl() == this) + { + gFocusMgr.setTopCtrl(NULL); + } +} + +void LLExpandableTextBox::onFocusLost() +{ + collapseTextBox(); + + LLUICtrl::onFocusLost(); +} + +void LLExpandableTextBox::onTopLost() +{ + collapseTextBox(); + + LLUICtrl::onTopLost(); +} + +void LLExpandableTextBox::setValue(const LLSD& value) +{ + collapseTextBox(); + + mText = value.asString(); + mTextBox->setValue(value); +} + +void LLExpandableTextBox::setText(const std::string& str) +{ + collapseTextBox(); + + mText = str; + mTextBox->setText(str); +} + +void LLExpandableTextBox::saveCollapsedState() +{ + mCollapsedRect = getRect(); + + mParentRect = getParent()->getRect(); + // convert parent rect to screen coordinates, + // this will allow to track parent's position change + getParent()->localRectToOtherView(mParentRect, &mParentRect, getRootView()); +} diff --git a/indra/newview/llexpandabletextbox.h b/indra/newview/llexpandabletextbox.h new file mode 100644 index 0000000000..0a5a4c8b75 --- /dev/null +++ b/indra/newview/llexpandabletextbox.h @@ -0,0 +1,237 @@ +/** + * @file llexpandabletextbox.h + * @brief LLExpandableTextBox and related class definitions + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-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_LLEXPANDABLETEXTBOX_H +#define LL_LLEXPANDABLETEXTBOX_H + +#include "lltextbox.h" +#include "llscrollcontainer.h" + +/** + * LLExpandableTextBox is a text box control that will show "More" link at end of text + * if text doesn't fit into text box. After pressing "More" the text box will expand to show + * all text. If text is still too big, a scroll bar will appear inside expanded text box. + */ +class LLExpandableTextBox : public LLUICtrl +{ +protected: + + /** + * Extended text box. "More" link will appear at end of text if + * text is too long to fit into text box size. + */ + class LLTextBoxEx : public LLTextBox + { + public: + struct Params : public LLInitParam::Block<Params, LLTextBox::Params> + { + Optional<LLTextBox::Params> expand_textbox; + + Params(); + }; + + /** + * Draw text box and "More" link + */ + /*virtual*/ void draw(); + + /** + * Draws simple text(no urls) line by line, will show or hide "More" link + * if needed. + */ + /*virtual*/ void drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color ); + + /** + * Draws segmented text(with urls) line by line. Will show or hide "More" link + * if needed + */ + void drawTextSegments(S32 x, S32 y, const LLWString &text); + + /** + * Returns difference between text box height and text height. + * Value is positive if text height is greater than text box height. + */ + virtual S32 getVerticalTextDelta(); + + /** + * Returns text vertical padding + */ + virtual S32 getVPad() { return mVPad; } + + /** + * Returns text horizontal padding + */ + virtual S32 getHPad() { return mHPad; } + + /** + * Broadcasts "commit" signal if user clicked "More" link + */ + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + + protected: + + LLTextBoxEx(const Params& p); + friend class LLUICtrlFactory; + + /** + * Shows "More" link + */ + void showExpandText(S32 y); + + /** + * Hides "More" link + */ + void hideExpandText(); + + /** + * Returns cropped line width + */ + S32 getCropTextWidth(); + + private: + + LLTextBox* mExpandTextBox; + }; + +public: + + struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> + { + Optional<LLTextBoxEx::Params> textbox; + + Optional<LLScrollContainer::Params> scroll; + + Optional<S32> max_height; + + Optional<bool> bg_visible, + expanded_bg_visible; + + Optional<LLUIColor> bg_color, + expanded_bg_color; + + Params(); + }; + + /** + * Sets text + */ + virtual void setText(const std::string& str); + + /** + * Returns text + */ + virtual std::string getText() const { return mText; } + + /** + * Sets text + */ + /*virtual*/ void setValue(const LLSD& value); + + /** + * Returns text + */ + /*virtual*/ LLSD getValue() const { return mText; } + + /** + * Collapses text box on focus_lost event + */ + /*virtual*/ void onFocusLost(); + + /** + * Collapses text box on top_lost event + */ + /*virtual*/ void onTopLost(); + + /** + * Draws text box, collapses text box if its expanded and its parent's position changed + */ + /*virtual*/ void draw(); + +protected: + + LLExpandableTextBox(const Params& p); + friend class LLUICtrlFactory; + + /** + * Expands text box. + * A scroll bar will appear if expanded height is greater than max_height + */ + virtual void expandTextBox(); + + /** + * Collapses text box. + */ + virtual void collapseTextBox(); + + /** + * Collapses text box if it is expanded and its parent's position changed + */ + virtual void collapseIfPosChanged(); + + /** + * Updates text box rect to avoid horizontal scroll bar + */ + virtual void updateTextBoxRect(); + + /** + * User clicked on "More" link - expand text box + */ + virtual void onExpandClicked(); + + /** + * Saves collapsed text box's states(rect, parent rect...) + */ + virtual void saveCollapsedState(); + + /** + * Recalculate text delta considering min_height and window rect. + */ + virtual S32 recalculateTextDelta(S32 text_delta); + +protected: + + std::string mText; + LLTextBoxEx* mTextBox; + LLScrollContainer* mScroll; + + S32 mMaxHeight; + LLRect mCollapsedRect; + bool mExpanded; + LLRect mParentRect; + + bool mBGVisible; + bool mExpandedBGVisible; + LLUIColor mBGColor; + LLUIColor mExpandedBGColor; +}; + +#endif //LL_LLEXPANDABLETEXTBOX_H diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index ef71e35254..6b18984f88 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -144,6 +144,18 @@ public: void setLandmarkID(const LLUUID& id){ mUrlGetter.setLandmarkID(id); } const LLUUID& getLandmarkId() const { return mUrlGetter.getLandmarkId(); } + void onMouseEnter(S32 x, S32 y, MASK mask) + { + if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) + { + LLUICtrl::onMouseEnter(x, y, mask); + } + else + { + LLButton::onMouseEnter(x, y, mask); + } + } + protected: LLFavoriteLandmarkButton(const LLButton::Params& p) : LLButton(p) {} friend class LLUICtrlFactory; @@ -278,7 +290,8 @@ struct LLFavoritesSort }; LLFavoritesBarCtrl::Params::Params() -: chevron_button_tool_tip("chevron_button_tool_tip") +: chevron_button_tool_tip("chevron_button_tool_tip"), + image_drag_indication("image_drag_indication") { } @@ -287,7 +300,12 @@ LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), mPopupMenuHandle(), mInventoryItemsPopupMenuHandle(), - mChevronButtonToolTip(p.chevron_button_tool_tip) + mChevronButtonToolTip(p.chevron_button_tool_tip), + mImageDragIndication(p.image_drag_indication), + mShowDragMarker(FALSE), + mLandingTab(NULL), + mLastTab(NULL), + mTabsHighlightEnabled(TRUE) { // Register callback for menus with current registrar (will be parent panel's registrar) LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Favorites.DoToSelected", @@ -321,17 +339,49 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, case DAD_LANDMARK: { + /* + * add a callback to the end drag event. + * the callback will disconnet itself immediately after execution + * this is done because LLToolDragAndDrop is a common tool so it shouldn't + * be overloaded with redundant callbacks. + */ + if (!mEndDragConnection.connected()) + { + mEndDragConnection = LLToolDragAndDrop::getInstance()->setEndDragCallback(boost::bind(&LLFavoritesBarCtrl::onEndDrag, this)); + } + // Copy the item into the favorites folder (if it's not already there). LLInventoryItem *item = (LLInventoryItem *)cargo_data; + if (LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(findChildByLocalCoords(x, y))) + { + setLandingTab(dest); + } + /* + * the condition dest == NULL can be satisfied not only in the case + * of dragging to the right from the last tab of the favbar. there is a + * small gap between each tab. if the user drags something exactly there + * then mLandingTab will be set to NULL and the dragged item will be pushed + * to the end of the favorites bar. this is incorrect behavior. that's why + * we need an additional check which excludes the case described previously + * making sure that the mouse pointer is beyond the last tab. + */ + else if (mLastTab && x >= mLastTab->getRect().mRight) + { + setLandingTab(NULL); + } + // check if we are dragging an existing item from the favorites bar if (item && mDragItemId == item->getUUID()) { *accept = ACCEPT_YES_SINGLE; + showDragMarker(TRUE); + if (drop) { handleExistingFavoriteDragAndDrop(x, y); + showDragMarker(FALSE); } } else @@ -343,11 +393,14 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, break; } - *accept = ACCEPT_YES_COPY_SINGLE; + *accept = ACCEPT_YES_COPY_MULTI; + + showDragMarker(TRUE); if (drop) { handleNewFavoriteDragAndDrop(item, favorites_id, x, y); + showDragMarker(FALSE); } } } @@ -361,7 +414,13 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) { - LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(findChildByLocalCoords(x, y)); + LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(mLandingTab); + + // there is no need to handle if an item was dragged onto itself + if (dest && dest->getLandmarkId() == mDragItemId) + { + return; + } if (dest) { @@ -381,14 +440,17 @@ void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) menu->setVisible(FALSE); showDropDownMenu(); } - - mDragItemId = LLUUID::null; - getWindow()->setCursor(UI_CURSOR_ARROW); } void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, const LLUUID& favorites_id, S32 x, S32 y) { - LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(findChildByLocalCoords(x, y)); + LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(mLandingTab); + + // there is no need to handle if an item was dragged onto itself + if (dest && dest->getLandmarkId() == mDragItemId) + { + return; + } if (dest) { @@ -458,6 +520,30 @@ void LLFavoritesBarCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) LLUICtrl::reshape(width, height, called_from_parent); } +void LLFavoritesBarCtrl::draw() +{ + LLUICtrl::draw(); + + if (mShowDragMarker) + { + S32 w = mImageDragIndication->getWidth() / 2; + S32 h = mImageDragIndication->getHeight() / 2; + + if (mLandingTab) + { + // mouse pointer hovers over an existing tab + LLRect rect = mLandingTab->getRect(); + mImageDragIndication->draw(rect.mLeft - w/2, rect.getHeight(), w, h); + } + else if (mLastTab) + { + // mouse pointer hovers over the favbar empty space (right to the last tab) + LLRect rect = mLastTab->getRect(); + mImageDragIndication->draw(rect.mRight, rect.getHeight(), w, h); + } + } +} + LLXMLNodePtr LLFavoritesBarCtrl::getButtonXMLNode() { LLXMLNodePtr buttonXMLNode = NULL; @@ -628,11 +714,15 @@ void LLFavoritesBarCtrl::createButtons(const LLInventoryModel::item_array_t &ite { S32 curr_x = buttonHGap; // Adding buttons + + LLFavoriteLandmarkButton* fav_btn = NULL; + mLandingTab = mLastTab = NULL; + for(S32 i = mFirstDropDownItem -1, j = 0; i >= 0; i--) { LLViewerInventoryItem* item = items.get(j++); - LLFavoriteLandmarkButton* fav_btn = LLUICtrlFactory::defaultBuilder<LLFavoriteLandmarkButton>(buttonXMLNode, this, NULL); + fav_btn = LLUICtrlFactory::defaultBuilder<LLFavoriteLandmarkButton>(buttonXMLNode, this, NULL); if (NULL == fav_btn) { llwarns << "Unable to create button for landmark: " << item->getName() << llendl; @@ -657,6 +747,8 @@ void LLFavoritesBarCtrl::createButtons(const LLInventoryModel::item_array_t &ite curr_x += buttonWidth + buttonHGap; } + + mLastTab = fav_btn; } @@ -784,6 +876,7 @@ void LLFavoritesBarCtrl::showDropDownMenu() menu_item->setRightMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this,item->getUUID(),_1,_2,_3,_4)); menu_item->LLUICtrl::setMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseDown, this, item->getUUID(), _1, _2, _3, _4)); menu_item->LLUICtrl::setMouseUpCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseUp, this, item->getUUID(), _1, _2, _3, _4)); + menu_item->setLandmarkID(item->getUUID()); // Check whether item name wider than menu if (menu_item->getNominalWidth() > max_width) @@ -968,7 +1061,6 @@ void LLFavoritesBarCtrl::pastFromClipboard() const void LLFavoritesBarCtrl::onButtonMouseDown(LLUUID id, LLUICtrl* ctrl, S32 x, S32 y, MASK mask) { mDragItemId = id; - mStartDrag = TRUE; S32 screenX, screenY; localPointToScreen(x, y, &screenX, &screenY); @@ -981,9 +1073,18 @@ void LLFavoritesBarCtrl::onButtonMouseUp(LLUUID id, LLUICtrl* ctrl, S32 x, S32 y mDragItemId = LLUUID::null; } +void LLFavoritesBarCtrl::onEndDrag() +{ + mEndDragConnection.disconnect(); + + showDragMarker(FALSE); + mDragItemId = LLUUID::null; + LLView::getWindow()->setCursor(UI_CURSOR_ARROW); +} + BOOL LLFavoritesBarCtrl::handleHover(S32 x, S32 y, MASK mask) { - if (mDragItemId != LLUUID::null && mStartDrag) + if (mDragItemId != LLUUID::null) { S32 screenX, screenY; localPointToScreen(x, y, &screenX, &screenY); @@ -994,8 +1095,6 @@ BOOL LLFavoritesBarCtrl::handleHover(S32 x, S32 y, MASK mask) DAD_LANDMARK, mDragItemId, LLToolDragAndDrop::SOURCE_LIBRARY); - mStartDrag = FALSE; - return LLToolDragAndDrop::getInstance()->handleHover(x, y, mask); } } diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index 4cd92d1a58..0be8de29a9 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -43,6 +43,7 @@ public: struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { Optional<std::string> chevron_button_tool_tip; + Optional<LLUIImage*> image_drag_indication; Params(); }; @@ -65,6 +66,10 @@ public: // LLInventoryObserver observer trigger virtual void changed(U32 mask); virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void draw(); + + void showDragMarker(BOOL show) { mShowDragMarker = show; } + void setLandingTab(LLUICtrl* tab) { mLandingTab = tab; } protected: void updateButtons(U32 bar_width); @@ -78,6 +83,8 @@ protected: void onButtonMouseDown(LLUUID id, LLUICtrl* button, S32 x, S32 y, MASK mask); void onButtonMouseUp(LLUUID id, LLUICtrl* button, S32 x, S32 y, MASK mask); + void onEndDrag(); + void doToSelected(const LLSD& userdata); BOOL isClipboardPasteable() const; void pastFromClipboard() const; @@ -98,6 +105,7 @@ protected: LLRect mChevronRect; std::string mChevronButtonToolTip; + LLUIImage* mImageDragIndication; private: /* @@ -136,10 +144,16 @@ private: // finds an item by it's UUID in the items array LLInventoryModel::item_array_t::iterator findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id); - BOOL mSkipUpdate; - BOOL mStartDrag; + BOOL mShowDragMarker; + LLUICtrl* mLandingTab; + LLUICtrl* mLastTab; + LLUUID mDragItemId; LLInventoryModel::item_array_t mItems; + + BOOL mTabsHighlightEnabled; + + boost::signals2::connection mEndDragConnection; }; diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp index f4c4f38008..0511ec1063 100644 --- a/indra/newview/llfloatercamera.cpp +++ b/indra/newview/llfloatercamera.cpp @@ -88,7 +88,6 @@ void LLFloaterCamera::update() { ECameraControlMode mode = determineMode(); if (mode != mCurrMode) setMode(mode); - updatePosition(); show_tip(mMode2TipType[mode], this); } @@ -122,48 +121,20 @@ LLFloaterCamera* LLFloaterCamera::findInstance() return LLFloaterReg::findTypedInstance<LLFloaterCamera>("camera"); } -/*static*/ -void LLFloaterCamera::onClickCameraPresets(LLUICtrl* ctrl, const LLSD& param) -{ - std::string name = param.asString(); - - if ("rear_view" == name) - { - LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_REAR, ctrl); - gAgent.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); - } - else if ("group_view" == name) - { - LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_GROUP); - gAgent.switchCameraPreset(CAMERA_PRESET_GROUP_VIEW); - } - else if ("front_view" == name) - { - LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_FRONT); - gAgent.switchCameraPreset(CAMERA_PRESET_FRONT_VIEW); - } - -} - void LLFloaterCamera::onOpen(const LLSD& key) { - updatePosition(); -} - -void LLFloaterCamera::updatePosition() -{ - LLBottomTray* tray = LLBottomTray::getInstance(); - if (!tray) return; + LLButton *anchor_panel = LLBottomTray::getInstance()->getChild<LLButton>("camera_btn"); - LLButton* camera_button = tray->getChild<LLButton>("camera_btn"); + setDockControl(new LLDockControl( + anchor_panel, this, + getDockTongue(), LLDockControl::TOP)); - //align centers of a button and a floater - S32 x = camera_button->calcScreenRect().getCenterX() - getRect().getWidth()/2; - setOrigin(x, 0); + show_tip(mMode2TipType[mCurrMode], this); } + LLFloaterCamera::LLFloaterCamera(const LLSD& val) -: LLFloater(val), +: LLDockableFloater(NULL, false, val), mCurrMode(CAMERA_CTRL_MODE_ORBIT), mPrevMode(CAMERA_CTRL_MODE_ORBIT) { @@ -187,7 +158,7 @@ BOOL LLFloaterCamera::postBuild() update(); - return TRUE; + return LLDockableFloater::postBuild(); } ECameraControlMode LLFloaterCamera::determineMode() @@ -311,7 +282,8 @@ void LLFloaterCamera::updateState() LLRect controls_rect; if (childGetRect(CONTROLS, controls_rect)) { - static S32 height = controls_rect.getHeight(); + static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0); + static S32 height = controls_rect.getHeight() - floater_header_size; S32 newHeight = rect.getHeight(); if (showControls) @@ -330,3 +302,46 @@ void LLFloaterCamera::updateState() } } +//-------------LLFloaterCameraPresets------------------------ + +LLFloaterCameraPresets::LLFloaterCameraPresets(const LLSD& key): +LLDockableFloater(NULL, false, key) +{} + +BOOL LLFloaterCameraPresets::postBuild() +{ + setIsChrome(TRUE); + + //build dockTongue + LLDockableFloater::postBuild(); + + LLButton *anchor_btn = LLBottomTray::getInstance()->getChild<LLButton>("camera_presets_btn"); + + setDockControl(new LLDockControl( + anchor_btn, this, + getDockTongue(), LLDockControl::TOP)); + return TRUE; +} + +/*static*/ +void LLFloaterCameraPresets::onClickCameraPresets(LLUICtrl* ctrl, const LLSD& param) +{ + std::string name = param.asString(); + + if ("rear_view" == name) + { + LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_REAR, ctrl); + gAgent.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); + } + else if ("group_view" == name) + { + LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_GROUP); + gAgent.switchCameraPreset(CAMERA_PRESET_GROUP_VIEW); + } + else if ("front_view" == name) + { + LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_FRONT); + gAgent.switchCameraPreset(CAMERA_PRESET_FRONT_VIEW); + } + +} diff --git a/indra/newview/llfloatercamera.h b/indra/newview/llfloatercamera.h index 1181c443bf..020cae7e82 100644 --- a/indra/newview/llfloatercamera.h +++ b/indra/newview/llfloatercamera.h @@ -33,7 +33,7 @@ #ifndef LLFLOATERCAMERA_H #define LLFLOATERCAMERA_H -#include "llfloater.h" +#include "lldockablefloater.h" #include "llfirsttimetipmanager.h" @@ -51,7 +51,7 @@ enum ECameraControlMode }; class LLFloaterCamera - : public LLFloater + : public LLDockableFloater { friend class LLFloaterReg; @@ -70,14 +70,8 @@ public: static void updateIfNotInAvatarViewMode(); - static void onClickCameraPresets(LLUICtrl* ctrl, const LLSD& param); - virtual void onOpen(const LLSD& key); - // *HACK: due to hard enough to have this control aligned with "Camera" button while resizing - // let update its position in each frame - /*virtual*/ void draw(){updatePosition(); LLFloater::draw();} - LLJoystickCameraRotate* mRotate; LLJoystickCameraZoom* mZoom; LLJoystickCameraTrack* mTrack; @@ -113,9 +107,6 @@ private: void assignButton2Mode(ECameraControlMode mode, const std::string& button_name); void initMode2TipTypeMap(); - /*Updates position of the floater to be center aligned with "Camera" button.*/ - void updatePosition(); - ECameraControlMode mPrevMode; ECameraControlMode mCurrMode; @@ -124,4 +115,15 @@ private: }; +class LLFloaterCameraPresets : public LLDockableFloater +{ + friend class LLFloaterReg; +public: + static void onClickCameraPresets(LLUICtrl* ctrl, const LLSD& param); +private: + LLFloaterCameraPresets(const LLSD&); + ~LLFloaterCameraPresets(){} + /*virtual*/ BOOL postBuild(); + +}; #endif diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index c197c78a41..761b5c9219 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -102,6 +102,7 @@ #include "llboost.h" #include "llviewermedia.h" #include "llpluginclassmedia.h" +#include "llteleporthistorystorage.h" #include <boost/regex.hpp> @@ -221,6 +222,9 @@ bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response LLSearchHistory::getInstance()->save(); LLSearchComboBox* search_ctrl = LLNavigationBar::getInstance()->getChild<LLSearchComboBox>("search_combo_box"); search_ctrl->clearHistory(); + + LLTeleportHistoryStorage::getInstance()->purgeItems(); + LLTeleportHistoryStorage::getInstance()->save(); } return false; diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index cddc67cb0a..d3b013237b 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -35,24 +35,53 @@ #include "llgrouplist.h" // libs +#include "llbutton.h" +#include "lliconctrl.h" +#include "lltextbox.h" #include "lltrans.h" // newview #include "llagent.h" +#include "llgroupactions.h" +#include "llviewercontrol.h" // for gSavedSettings static LLDefaultChildRegistry::Register<LLGroupList> r("group_list"); +S32 LLGroupListItem::sIconWidth = 0; + +class LLGroupComparator : public LLFlatListView::ItemComparator +{ +public: + /** Returns true if item1 < item2, false otherwise */ + /*virtual*/ bool compare(const LLPanel* item1, const LLPanel* item2) const + { + std::string name1 = static_cast<const LLGroupListItem*>(item1)->getGroupName(); + std::string name2 = static_cast<const LLGroupListItem*>(item2)->getGroupName(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + return name1 < name2; + } +}; + +static const LLGroupComparator GROUP_COMPARATOR; LLGroupList::Params::Params() { - // Prevent the active group from being always first in the list. - online_go_first = false; + } LLGroupList::LLGroupList(const Params& p) -: LLAvatarList(p) +: LLFlatListView(p) { + mShowIcons = gSavedSettings.getBOOL("GroupListShowIcons"); + setCommitOnSelectionChange(true); + // TODO: implement context menu // display a context menu appropriate for a list of group names - setContextMenu(LLScrollListCtrl::MENU_GROUP); +// setContextMenu(LLScrollListCtrl::MENU_GROUP); + + // Set default sort order. + setComparator(&GROUP_COMPARATOR); } static bool findInsensitive(std::string haystack, const std::string& needle_upper) @@ -63,36 +92,185 @@ static bool findInsensitive(std::string haystack, const std::string& needle_uppe BOOL LLGroupList::update(const std::string& name_filter) { - LLCtrlListInterface *group_list = getListInterface(); const LLUUID& highlight_id = gAgent.getGroupID(); S32 count = gAgent.mGroups.count(); LLUUID id; - group_list->operateOnAll(LLCtrlListInterface::OP_DELETE); + clear(); for(S32 i = 0; i < count; ++i) { - // *TODO: check powers mask? id = gAgent.mGroups.get(i).mID; const LLGroupData& group_data = gAgent.mGroups.get(i); if (name_filter != LLStringUtil::null && !findInsensitive(group_data.mName, name_filter)) continue; - addItem(id, group_data.mName, highlight_id == id, ADD_BOTTOM); // ADD_SORTED can only sort by first column anyway + addNewItem(id, group_data.mName, group_data.mInsigniaID, highlight_id == id, ADD_BOTTOM); } - // Force sorting the list. - updateSort(); + // Sort the list. + sort(); // add "none" to list at top { std::string loc_none = LLTrans::getString("GroupsNone"); if (name_filter == LLStringUtil::null || findInsensitive(loc_none, name_filter)) - addItem(LLUUID::null, loc_none, highlight_id.isNull(), ADD_TOP); + addNewItem(LLUUID::null, loc_none, LLUUID::null, highlight_id.isNull(), ADD_TOP); + } - // Prevent the "none" item from being sorted. - setNeedsSort(false); + selectItemByUUID(highlight_id); + + return TRUE; +} + +void LLGroupList::toggleIcons() +{ + // Save the new value for new items to use. + mShowIcons = !mShowIcons; + gSavedSettings.setBOOL("GroupListShowIcons", mShowIcons); + + // Show/hide icons for all existing items. + std::vector<LLPanel*> items; + getItems(items); + for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++) + { + static_cast<LLGroupListItem*>(*it)->setGroupIconVisible(mShowIcons); } +} + +////////////////////////////////////////////////////////////////////////// +// PRIVATE Section +////////////////////////////////////////////////////////////////////////// + +void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, BOOL is_bold, EAddPosition pos) +{ + LLGroupListItem* item = new LLGroupListItem(); + + item->setName(name); + item->setGroupID(id); + item->setGroupIconID(icon_id); +// item->setContextMenu(mContextMenu); + + item->childSetVisible("info_btn", false); + item->setGroupIconVisible(mShowIcons); + + addItem(item, id, pos); + +// setCommentVisible(false); +} + + +/************************************************************************/ +/* LLGroupListItem implementation */ +/************************************************************************/ + +LLGroupListItem::LLGroupListItem() +: LLPanel(), +mGroupIcon(NULL), +mGroupNameBox(NULL), +mInfoBtn(NULL), +//mContextMenu(NULL), //TODO: +mGroupID(LLUUID::null) +{ + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_group_list_item.xml"); + + // Remember group icon width including its padding from the name text box, + // so that we can hide and show the icon again later. + if (!sIconWidth) + { + sIconWidth = mGroupNameBox->getRect().mLeft - mGroupIcon->getRect().mLeft; + } +} + +//virtual +BOOL LLGroupListItem::postBuild() +{ + mGroupIcon = getChild<LLIconCtrl>("group_icon"); + mGroupNameBox = getChild<LLTextBox>("group_name"); + + mInfoBtn = getChild<LLButton>("info_btn"); + mInfoBtn->setClickedCallback(boost::bind(&LLGroupListItem::onInfoBtnClick, this)); - group_list->selectByValue(highlight_id); return TRUE; } + +//virtual +void LLGroupListItem::setValue( const LLSD& value ) +{ + if (!value.isMap()) return; + if (!value.has("selected")) return; + childSetVisible("selected_icon", value["selected"]); +} + +void LLGroupListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + childSetVisible("hovered_icon", true); + if (mGroupID.notNull()) // don't show the info button for the "none" group + mInfoBtn->setVisible(true); + + LLPanel::onMouseEnter(x, y, mask); +} + +void LLGroupListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + childSetVisible("hovered_icon", false); + mInfoBtn->setVisible(false); + + LLPanel::onMouseLeave(x, y, mask); +} + +void LLGroupListItem::setName(const std::string& name) +{ + mGroupName = name; + mGroupNameBox->setValue(name); + mGroupNameBox->setToolTip(name); +} + +void LLGroupListItem::setGroupID(const LLUUID& group_id) +{ + mGroupID = group_id; + setActive(group_id == gAgent.getGroupID()); +} + +void LLGroupListItem::setGroupIconID(const LLUUID& group_icon_id) +{ + if (group_icon_id.notNull()) + { + mGroupIcon->setValue(group_icon_id); + } +} + +void LLGroupListItem::setGroupIconVisible(bool visible) +{ + // Already done? Then do nothing. + if (mGroupIcon->getVisible() == (BOOL)visible) + return; + + // Show/hide the group icon. + mGroupIcon->setVisible(visible); + + // Move the group name horizontally by icon size + its distance from the group name. + LLRect name_rect = mGroupNameBox->getRect(); + name_rect.mLeft += visible ? sIconWidth : -sIconWidth; + mGroupNameBox->setRect(name_rect); +} + +////////////////////////////////////////////////////////////////////////// +// Private Section +////////////////////////////////////////////////////////////////////////// +void LLGroupListItem::setActive(bool active) +{ + // Active group should be bold. + LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc()); + + // *NOTE dzaporozhan + // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font + // is predefined as bold (SansSerifSmallBold, for example) + new_desc.setStyle(active ? LLFontGL::BOLD : LLFontGL::NORMAL); + mGroupNameBox->setFont(LLFontGL::getFont(new_desc)); +} + +void LLGroupListItem::onInfoBtnClick() +{ + LLGroupActions::show(mGroupID); +} +//EOF diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h index e893313f4b..7708b58de6 100644 --- a/indra/newview/llgrouplist.h +++ b/indra/newview/llgrouplist.h @@ -33,22 +33,61 @@ #ifndef LL_LLGROUPLIST_H #define LL_LLGROUPLIST_H -#include <llscrolllistctrl.h> +#include "llflatlistview.h" +#include "llpanel.h" -#include "llavatarlist.h" - -// *TODO: derive from ListView when it's ready. -class LLGroupList: public LLAvatarList +class LLGroupList: public LLFlatListView { LOG_CLASS(LLGroupList); public: - struct Params : public LLInitParam::Block<Params, LLAvatarList::Params> + struct Params : public LLInitParam::Block<Params, LLFlatListView::Params> { Params(); }; - LLGroupList(const Params&); + LLGroupList(const Params& p); BOOL update(const std::string& name_filter = LLStringUtil::null); + void toggleIcons(); + bool getIconsVisible() const { return mShowIcons; } + +private: + void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, BOOL is_bold, EAddPosition pos = ADD_BOTTOM); + + bool mShowIcons; }; +class LLButton; +class LLIconCtrl; +class LLTextBox; + +class LLGroupListItem : public LLPanel +{ +public: + LLGroupListItem(); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void setValue(const LLSD& value); + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + + const LLUUID& getGroupID() const { return mGroupID; } + const std::string& getGroupName() const { return mGroupName; } + + void setName(const std::string& name); + void setGroupID(const LLUUID& group_id); + void setGroupIconID(const LLUUID& group_icon_id); + void setGroupIconVisible(bool visible); + +private: + void setActive(bool active); + void onInfoBtnClick(); + + LLTextBox* mGroupNameBox; + LLUUID mGroupID; + LLIconCtrl* mGroupIcon; + LLButton* mInfoBtn; + + std::string mGroupName; + + static S32 sIconWidth; // icon width + padding +}; #endif // LL_LLGROUPLIST_H diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp new file mode 100644 index 0000000000..29102feb64 --- /dev/null +++ b/indra/newview/llimfloater.cpp @@ -0,0 +1,416 @@ +/** + * @file llimfloater.cpp + * @brief LLIMFloater class definition + * + * $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 "llimfloater.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llbutton.h" +#include "llbottomtray.h" +#include "llchannelmanager.h" +#include "llchiclet.h" +#include "llfloaterreg.h" +#include "llimview.h" +#include "lllineeditor.h" +#include "llpanelimcontrolpanel.h" +#include "llscreenchannel.h" +#include "lltrans.h" +#include "llviewertexteditor.h" +#include "llviewerwindow.h" + + + +LLIMFloater::LLIMFloater(const LLUUID& session_id) + : LLDockableFloater(NULL, session_id), + mControlPanel(NULL), + mSessionID(session_id), + mLastMessageIndex(-1), + mLastFromName(), + mDialog(IM_NOTHING_SPECIAL), + mHistoryEditor(NULL), + mInputEditor(NULL), + mPositioned(false) +{ + LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); + if(session) + { + mDialog = session->mType; + } + + if (mDialog == IM_NOTHING_SPECIAL) + { + mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); + } + else + { + mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); + } +// LLUICtrlFactory::getInstance()->buildFloater(this, "floater_im_session.xml"); + + gFocusMgr.addFocusChangeCallback(boost::bind(&LLIMFloater::focusChangeCallback, this)); + + mCloseSignal.connect(boost::bind(&LLIMFloater::onClose, this)); +} + +void LLIMFloater::onClose() +{ + LLIMModel::instance().sendLeaveSession(mSessionID, mOtherParticipantUUID); + gIMMgr->removeSession(mSessionID); +} + +/* static */ +void LLIMFloater::newIMCallback(const LLSD& data){ + + if (data["num_unread"].asInteger() > 0) + { + LLUUID session_id = data["session_id"].asUUID(); + + LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + if (floater == NULL) + { + llwarns << "new_im_callback for non-existent session_id " << session_id << llendl; + return; + } + + // update if visible, otherwise will be updated when opened + if (floater->getVisible()) + { + floater->updateMessages(); + } + } +} + +void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) +{ + LLIMFloater* self = (LLIMFloater*) userdata; + self->sendMsg(); +} + +void LLIMFloater::sendMsg() +{ + if (!gAgent.isGodlike() + && (mDialog == IM_NOTHING_SPECIAL) + && mOtherParticipantUUID.isNull()) + { + llinfos << "Cannot send IM to everyone unless you're a god." << llendl; + return; + } + + if (mInputEditor) + { + LLWString text = mInputEditor->getConvertedText(); + if(!text.empty()) + { + // Truncate and convert to UTF8 for transport + 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); + + mInputEditor->setText(LLStringUtil::null); + + updateMessages(); + } + } +} + + + +LLIMFloater::~LLIMFloater() +{ +} + +//virtual +BOOL LLIMFloater::postBuild() +{ + LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); + if(session) + { + mOtherParticipantUUID = session->mOtherParticipantID; + mControlPanel->setID(session->mOtherParticipantID); + } + + LLButton* slide_left = getChild<LLButton>("slide_left_btn"); + slide_left->setVisible(mControlPanel->getVisible()); + slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); + + LLButton* slide_right = getChild<LLButton>("slide_right_btn"); + slide_right->setVisible(!mControlPanel->getVisible()); + slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); + + mInputEditor = getChild<LLLineEditor>("chat_editor"); + mInputEditor->setMaxTextLength(1023); + // enable line history support for instant message bar + mInputEditor->setEnableLineHistory(TRUE); + + mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived, this ); + mInputEditor->setFocusLostCallback( onInputEditorFocusLost, this ); + mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); + mInputEditor->setCommitOnFocusLost( FALSE ); + mInputEditor->setRevertOnEsc( FALSE ); + mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); + + childSetCommitCallback("chat_editor", onSendMsg, this); + + mHistoryEditor = getChild<LLViewerTextEditor>("im_text"); + mHistoryEditor->setParseHTML(TRUE); + + setTitle(LLIMModel::instance().getName(mSessionID)); + setDocked(true); + + return LLDockableFloater::postBuild(); +} + + + +// static +void* LLIMFloater::createPanelIMControl(void* userdata) +{ + LLIMFloater *self = (LLIMFloater*)userdata; + self->mControlPanel = new LLPanelIMControlPanel(); + self->mControlPanel->setXMLFilename("panel_im_control_panel.xml"); + return self->mControlPanel; +} + + +// static +void* LLIMFloater::createPanelGroupControl(void* userdata) +{ + LLIMFloater *self = (LLIMFloater*)userdata; + self->mControlPanel = new LLPanelGroupControlPanel(); + self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); + return self->mControlPanel; +} + + + +void LLIMFloater::focusChangeCallback() +{ + // hide docked floater if user clicked inside in-world area + if (isDocked() && gFocusMgr.getKeyboardFocus() == NULL) + { + setVisible(false); + } +} + +void LLIMFloater::onSlide() +{ + LLPanel* im_control_panel = getChild<LLPanel>("panel_im_control_panel"); + im_control_panel->setVisible(!im_control_panel->getVisible()); + + getChild<LLButton>("slide_left_btn")->setVisible(im_control_panel->getVisible()); + getChild<LLButton>("slide_right_btn")->setVisible(!im_control_panel->getVisible()); +} + +//static +LLIMFloater* LLIMFloater::show(const LLUUID& session_id) +{ + //hide all + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); + iter != inst_list.end(); ++iter) + { + LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); + if (floater && floater->isDocked()) + { + floater->setVisible(false); + } + } + + LLIMFloater* floater = LLFloaterReg::showTypedInstance<LLIMFloater>("impanel", session_id); + + floater->updateMessages(); + floater->mInputEditor->setFocus(TRUE); + + if (floater->getDockControl() == NULL) + { + LLChiclet* chiclet = + LLBottomTray::getInstance()->getChicletPanel()->findChiclet<LLChiclet>( + session_id); + if (chiclet == NULL) + { + llerror("Dock chiclet for LLIMFloater doesn't exists", 0); + } + else + { + LLBottomTray::getInstance()->getChicletPanel()->scrollToChiclet(chiclet); + } + + floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(), + LLDockControl::TOP, boost::bind(&LLIMFloater::getEnabledRect, floater, _1))); + } + + return floater; +} + +void LLIMFloater::getEnabledRect(LLRect& rect) +{ + rect = gViewerWindow->getWorldViewRect(); +} + +void LLIMFloater::setDocked(bool docked, bool pop_on_undock) +{ + // update notification channel state + LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getInstance()-> + findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))); + LLDockableFloater::setDocked(docked, pop_on_undock); + + // update notification channel state + if(channel) + { + channel->updateShowToastsState(); + } +} + +void LLIMFloater::setVisible(BOOL visible) +{ + LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getInstance()-> + findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))); + LLDockableFloater::setVisible(visible); + + // update notification channel state + if(channel) + { + channel->updateShowToastsState(); + } +} + +//static +bool LLIMFloater::toggle(const LLUUID& session_id) +{ + LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + if (floater && floater->getVisible()) + { + // clicking on chiclet to close floater just hides it to maintain existing + // scroll/text entry state + floater->setVisible(false); + return false; + } + else + { + // ensure the list of messages is updated when floater is made visible + show(session_id); + // update number of unread notifications in the SysWell + LLBottomTray::getInstance()->getSysWell()->updateUreadIMNotifications(); + return true; + } +} + +void LLIMFloater::updateMessages() +{ + std::list<LLSD> messages = LLIMModel::instance().getMessages(mSessionID, mLastMessageIndex+1); + std::string agent_name; + + gCacheName->getFullName(gAgentID, agent_name); + + if (messages.size()) + { + LLUIColor divider_color = LLUIColorTable::instance().getColor("LtGray_50"); + LLUIColor chat_color = LLUIColorTable::instance().getColor("IMChatColor"); + + std::ostringstream message; + std::list<LLSD>::const_reverse_iterator iter = messages.rbegin(); + std::list<LLSD>::const_reverse_iterator iter_end = messages.rend(); + for (; iter != iter_end; ++iter) + { + LLSD msg = *iter; + + const bool prepend_newline = true; + std::string from = msg["from"].asString(); + if (from == agent_name) + from = LLTrans::getString("You"); + if (mLastFromName != from) + { + message << from << " ----- " << msg["time"].asString(); + mHistoryEditor->appendColoredText(message.str(), false, + prepend_newline, divider_color); + message.str(""); + mLastFromName = from; + } + + message << msg["message"].asString(); + mHistoryEditor->appendColoredText(message.str(), false, + prepend_newline, chat_color); + message.str(""); + + mLastMessageIndex = msg["index"].asInteger(); + } + + mHistoryEditor->setCursorAndScrollToEnd(); + } +} + +// static +void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ) +{ + LLIMFloater* self= (LLIMFloater*) userdata; + + //in disconnected state IM input editor should be disabled + self->mInputEditor->setEnabled(!gDisconnected); + + self->mHistoryEditor->setCursorAndScrollToEnd(); +} + +// static +void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata) +{ + LLIMFloater* self = (LLIMFloater*) userdata; + self->setTyping(FALSE); +} + +// static +void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) +{ + LLIMFloater* self = (LLIMFloater*)userdata; + std::string text = self->mInputEditor->getText(); + if (!text.empty()) + { + self->setTyping(TRUE); + } + else + { + // Deleting all text counts as stopping typing. + self->setTyping(FALSE); + } +} + + +//just a stub for now +void LLIMFloater::setTyping(BOOL typing) +{ +} + diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h new file mode 100644 index 0000000000..276f38e829 --- /dev/null +++ b/indra/newview/llimfloater.h @@ -0,0 +1,112 @@ +/** + * @file llimfloater.h + * @brief LLIMFloater class definition + * + * $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_IMFLOATER_H +#define LL_IMFLOATER_H + +#include "lldockablefloater.h" + +class LLLineEditor; +class LLPanelChatControlPanel; +class LLViewerTextEditor; + + +/** + * Individual IM window that appears at the bottom of the screen, + * optionally "docked" to the bottom tray. + */ +class LLIMFloater : public LLDockableFloater +{ +public: + LLIMFloater(const LLUUID& session_id); + + virtual ~LLIMFloater(); + + // LLView overrides + /*virtual*/ BOOL postBuild(); + /*virtual*/ void setVisible(BOOL visible); + + // LLFloater overrides + /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); + + // Make IM conversion visible and update the message history + static LLIMFloater* show(const LLUUID& session_id); + + // Toggle panel specified by session_id + // Returns true iff panel became visible + static bool toggle(const LLUUID& session_id); + + // get new messages from LLIMModel + void updateMessages(); + static void onSendMsg( LLUICtrl*, void*); + void sendMsg(); + + // callback for LLIMModel on new messages + // route to specific floater if it is visible + static void newIMCallback(const LLSD& data); + + // called when docked floater's position has been set by chiclet + void setPositioned(bool b) { mPositioned = b; }; + + // handler for a CLOSE signal + void onClose(); + + +private: + + static void onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ); + static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); + static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); + void setTyping(BOOL typing); + void onSlide(); + static void* createPanelIMControl(void* userdata); + static void* createPanelGroupControl(void* userdata); + void focusChangeCallback(); + void getEnabledRect(LLRect& rect); + + LLPanelChatControlPanel* mControlPanel; + LLUUID mSessionID; + S32 mLastMessageIndex; + + // username of last user who added text to this conversation, used to + // suppress duplicate username divider bars + std::string mLastFromName; + + EInstantMessage mDialog; + LLUUID mOtherParticipantUUID; + LLViewerTextEditor* mHistoryEditor; + LLLineEditor* mInputEditor; + bool mPositioned; +}; + + +#endif // LL_IMFLOATER_H diff --git a/indra/newview/llimhandler.cpp b/indra/newview/llimhandler.cpp index a47477c446..46067c081f 100644 --- a/indra/newview/llimhandler.cpp +++ b/indra/newview/llimhandler.cpp @@ -36,29 +36,18 @@ #include "llnotificationhandler.h" #include "llagentdata.h" -#include "llbottomtray.h" -#include "llviewercontrol.h" #include "lltoastimpanel.h" +#include "llviewerwindow.h" using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLIMHandler::LLIMHandler() +LLIMHandler::LLIMHandler(e_notification_type type, const LLSD& id) { - - // getting a Chiclet and creating params for a channel - LLBottomTray* tray = LLBottomTray::getInstance(); - mChiclet = tray->getSysWell(); - - LLChannelManager::Params p; - // *TODO: createNotificationChannel method - p.id = LLUUID(gSavedSettings.getString("NotificationChannelUUID")); - p.channel_right_bound = tray->getRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); - p.channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mType = type; // Getting a Channel for our notifications - mChannel = LLChannelManager::getInstance()->createChannel(p); - + mChannel = LLChannelManager::getInstance()->createNotificationChannel(); } //-------------------------------------------------------------------------- @@ -67,12 +56,31 @@ LLIMHandler::~LLIMHandler() } //-------------------------------------------------------------------------- -void LLIMHandler::processNotification(const LLSD& notify) +void LLIMHandler::initChannel() +{ + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLIMHandler::processNotification(const LLSD& notify) { + if(!mChannel) + { + return false; + } + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); if(!notification) - return; + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") { @@ -95,40 +103,31 @@ void LLIMHandler::processNotification(const LLSD& notify) LLToastIMPanel* im_box = new LLToastIMPanel(im_p); LLToast::Params p; - p.id = notification->getID(); + p.notif_id = notification->getID(); + p.session_id = im_p.session_id; p.notification = notification; p.panel = im_box; p.can_be_stored = false; - p.on_toast_destroy = boost::bind(&LLIMHandler::onToastDestroy, this, _1); + p.on_delete_toast = boost::bind(&LLIMHandler::onDeleteToast, this, _1); mChannel->addToast(p); - - static_cast<LLNotificationChiclet*>(mChiclet)->updateUreadIMNotifications(); + // send a signal to the counter manager; + mNewNotificationSignal(); } else if (notify["sigtype"].asString() == "delete") { mChannel->killToastByNotificationID(notification->getID()); } + return true; } //-------------------------------------------------------------------------- -void LLIMHandler::onToastDestroy(LLToast* toast) -{ - toast->closeFloater(); - static_cast<LLNotificationChiclet*>(mChiclet)->updateUreadIMNotifications(); -} - -//-------------------------------------------------------------------------- -void LLIMHandler::onChicletClick(void) +void LLIMHandler::onDeleteToast(LLToast* toast) { + // send a signal to the counter manager + mDelNotificationSignal(); } //-------------------------------------------------------------------------- -void LLIMHandler::onChicletClose(void) -{ -} - -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 0efe9b9849..de4faf72f5 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -48,6 +48,7 @@ #include "llbutton.h" #include "llbottomtray.h" #include "llcallingcard.h" +#include "llchannelmanager.h" #include "llchat.h" #include "llchiclet.h" #include "llconsole.h" @@ -81,6 +82,7 @@ #include "llhttpclient.h" #include "llmutelist.h" #include "llstylemap.h" +#include "llappviewer.h" // // Constants @@ -1986,318 +1988,4 @@ bool LLFloaterIMPanel::onConfirmForceCloseError(const LLSD& notification, const } return false; } - - -LLIMFloater::LLIMFloater(const LLUUID& session_id) - : LLDockableFloater(NULL, session_id), - mControlPanel(NULL), - mSessionID(session_id), - mLastMessageIndex(-1), - mLastFromName(), - mDialog(IM_NOTHING_SPECIAL), - mHistoryEditor(NULL), - mInputEditor(NULL), - mPositioned(false) -{ - LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); - if(session) - { - mDialog = session->mType; - } - - if (mDialog == IM_NOTHING_SPECIAL) - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); - } - else - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - } -// LLUICtrlFactory::getInstance()->buildFloater(this, "floater_im_session.xml"); -} - -/* static */ -void LLIMFloater::newIMCallback(const LLSD& data){ - - if (data["num_unread"].asInteger() > 0) - { - LLUUID session_id = data["session_id"].asUUID(); - - LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (floater == NULL) - { - llwarns << "new_im_callback for non-existent session_id " << session_id << llendl; - return; - } - - // update if visible, otherwise will be updated when opened - if (floater->getVisible()) - { - floater->updateMessages(); - } - } -} - -void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) -{ - LLIMFloater* self = (LLIMFloater*) userdata; - self->sendMsg(); -} - -void LLIMFloater::sendMsg() -{ - if (!gAgent.isGodlike() - && (mDialog == IM_NOTHING_SPECIAL) - && mOtherParticipantUUID.isNull()) - { - llinfos << "Cannot send IM to everyone unless you're a god." << llendl; - return; - } - - if (mInputEditor) - { - LLWString text = mInputEditor->getConvertedText(); - if(!text.empty()) - { - // Truncate and convert to UTF8 for transport - 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); - - mInputEditor->setText(LLStringUtil::null); - - updateMessages(); - } - } -} - - - -LLIMFloater::~LLIMFloater() -{ -} - -//virtual -BOOL LLIMFloater::postBuild() -{ - LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); - if(session) - { - mOtherParticipantUUID = session->mOtherParticipantID; - mControlPanel->setID(session->mOtherParticipantID); - } - - LLButton* slide_left = getChild<LLButton>("slide_left_btn"); - slide_left->setVisible(mControlPanel->getVisible()); - slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); - - LLButton* slide_right = getChild<LLButton>("slide_right_btn"); - slide_right->setVisible(!mControlPanel->getVisible()); - slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); - - mInputEditor = getChild<LLLineEditor>("chat_editor"); - mInputEditor->setMaxTextLength(1023); - // enable line history support for instant message bar - mInputEditor->setEnableLineHistory(TRUE); - - mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived, this ); - mInputEditor->setFocusLostCallback( onInputEditorFocusLost, this ); - mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); - mInputEditor->setCommitOnFocusLost( FALSE ); - mInputEditor->setRevertOnEsc( FALSE ); - mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); - - childSetCommitCallback("chat_editor", onSendMsg, this); - - mHistoryEditor = getChild<LLViewerTextEditor>("im_text"); - mHistoryEditor->setParseHTML(TRUE); - - setTitle(LLIMModel::instance().getName(mSessionID)); - setDocked(true); - - return LLDockableFloater::postBuild(); -} - - - -// static -void* LLIMFloater::createPanelIMControl(void* userdata) -{ - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelIMControlPanel(); - self->mControlPanel->setXMLFilename("panel_im_control_panel.xml"); - return self->mControlPanel; -} - - -// static -void* LLIMFloater::createPanelGroupControl(void* userdata) -{ - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelGroupControlPanel(); - self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); - return self->mControlPanel; -} - -const U32 DOCK_ICON_HEIGHT = 6; - -//virtual -void LLIMFloater::onFocusLost() -{ - // spec says close if docked to bottom tray and user has clicked away - // (hence we are no longer focused) - if (isDocked()) - { - LLIMFloater* floater = LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", mSessionID); - if (floater) - { - floater->setVisible(false); - } - } -} - -void LLIMFloater::onSlide() -{ - LLPanel* im_control_panel = getChild<LLPanel>("panel_im_control_panel"); - im_control_panel->setVisible(!im_control_panel->getVisible()); - - getChild<LLButton>("slide_left_btn")->setVisible(im_control_panel->getVisible()); - getChild<LLButton>("slide_right_btn")->setVisible(!im_control_panel->getVisible()); -} - -//static -LLIMFloater* LLIMFloater::show(const LLUUID& session_id) -{ - //hide all - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); - iter != inst_list.end(); ++iter) - { - LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); - if (floater && floater->isDocked()) - { - floater->setVisible(false); - } - } - - LLIMFloater* floater = LLFloaterReg::showTypedInstance<LLIMFloater>("impanel", session_id); - - floater->updateMessages(); - floater->mInputEditor->setFocus(TRUE); - - if (floater->getDockControl() == NULL) - { - LLView* chiclet = - LLBottomTray::getInstance()->getChicletPanel()->findChiclet<LLView>( - session_id); - if (chiclet == NULL) - { - llerror("Dock chiclet for LLIMFloater doesn't exists", 0); - } - floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(), - LLDockControl::TOP, floater->isDocked())); - } - - return floater; -} - -//static -bool LLIMFloater::toggle(const LLUUID& session_id) -{ - LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (floater && floater->getVisible()) - { - // clicking on chiclet to close floater just hides it to maintain existing - // scroll/text entry state - floater->setVisible(false); - return false; - } - else - { - // ensure the list of messages is updated when floater is made visible - show(session_id); - // update number of unread notifications in the SysWell - LLBottomTray::getInstance()->getSysWell()->updateUreadIMNotifications(); - return true; - } -} - -void LLIMFloater::updateMessages() -{ - std::list<LLSD> messages = LLIMModel::instance().getMessages(mSessionID, mLastMessageIndex+1); - - if (messages.size()) - { - LLUIColor divider_color = LLUIColorTable::instance().getColor("LtGray_50"); - LLUIColor chat_color = LLUIColorTable::instance().getColor("IMChatColor"); - - std::ostringstream message; - std::list<LLSD>::const_reverse_iterator iter = messages.rbegin(); - std::list<LLSD>::const_reverse_iterator iter_end = messages.rend(); - for (; iter != iter_end; ++iter) - { - LLSD msg = *iter; - - const bool prepend_newline = true; - std::string from = msg["from"].asString(); - if (mLastFromName != from) - { - message << from << " ----- " << msg["time"].asString(); - mHistoryEditor->appendColoredText(message.str(), false, - prepend_newline, divider_color); - message.str(""); - mLastFromName = from; - } - - message << msg["message"].asString(); - mHistoryEditor->appendColoredText(message.str(), false, - prepend_newline, chat_color); - message.str(""); - - mLastMessageIndex = msg["index"].asInteger(); - } - - mHistoryEditor->setCursorAndScrollToEnd(); - } -} - -// static -void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ) -{ - LLIMFloater* self= (LLIMFloater*) userdata; - self->mHistoryEditor->setCursorAndScrollToEnd(); -} - -// static -void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata) -{ - LLIMFloater* self = (LLIMFloater*) userdata; - self->setTyping(FALSE); -} - -// static -void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) -{ - LLIMFloater* self = (LLIMFloater*)userdata; - std::string text = self->mInputEditor->getText(); - if (!text.empty()) - { - self->setTyping(TRUE); - } - else - { - // Deleting all text counts as stopping typing. - self->setTyping(FALSE); - } -} - - -//just a stub for now -void LLIMFloater::setTyping(BOOL typing) -{ -} diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index 1d69f1567c..dbf5e1cb6a 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -360,68 +360,4 @@ private: void disableWhileSessionStarting(); }; - -// Individual IM window that appears at the bottom of the screen, -// optionally "docked" to the bottom tray. -class LLIMFloater : public LLDockableFloater -{ -public: - LLIMFloater(const LLUUID& session_id); - - virtual ~LLIMFloater(); - - // LLView overrides - /*virtual*/ BOOL postBuild(); - - // Floater should close when user clicks away to other UI area, - // hence causing focus loss. - /*virtual*/ void onFocusLost(); - - // Make IM conversion visible and update the message history - static LLIMFloater* show(const LLUUID& session_id); - - // Toggle panel specified by session_id - // Returns true iff panel became visible - static bool toggle(const LLUUID& session_id); - - // get new messages from LLIMModel - void updateMessages(); - static void onSendMsg( LLUICtrl*, void*); - void sendMsg(); - - // callback for LLIMModel on new messages - // route to specific floater if it is visible - static void newIMCallback(const LLSD& data); - - // called when docked floater's position has been set by chiclet - void setPositioned(bool b) { mPositioned = b; }; - - - -private: - - static void onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ); - static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); - static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); - void setTyping(BOOL typing); - void onSlide(); - static void* createPanelIMControl(void* userdata); - static void* createPanelGroupControl(void* userdata); - - LLPanelChatControlPanel* mControlPanel; - LLUUID mSessionID; - S32 mLastMessageIndex; - // username of last user who added text to this conversation, used to - // suppress duplicate username divider bars - std::string mLastFromName; - EInstantMessage mDialog; - LLUUID mOtherParticipantUUID; - LLViewerTextEditor* mHistoryEditor; - LLLineEditor* mInputEditor; - bool mPositioned; -}; - - - - #endif // LL_IMPANEL_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 9ef98afe94..3cf78f957b 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -53,6 +53,7 @@ #include "llfloaterchatterbox.h" #include "llavataractions.h" #include "llhttpnode.h" +#include "llimfloater.h" #include "llimpanel.h" #include "llresizebar.h" #include "lltabcontainer.h" @@ -70,6 +71,7 @@ #include "llnotify.h" #include "llviewerregion.h" #include "lltrans.h" +#include "llrecentpeople.h" #include "llfirstuse.h" #include "llagentui.h" @@ -90,6 +92,11 @@ std::map<LLUUID, LLIMModel::LLIMSession*> LLIMModel::sSessionsMap; void toast_callback(const LLSD& msg){ + // do not show toast in busy mode + if (gAgent.getBusy()) + { + return; + } //we send notifications to reset counter also if (msg["num_unread"].asInteger()) @@ -101,8 +108,7 @@ void toast_callback(const LLSD& msg){ args["FROM_ID"] = msg["from_id"]; args["SESSION_ID"] = msg["session_id"]; - //LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLFloaterChatterBox::onOpen, LLFloaterChatterBox::getInstance(), msg["session_id"].asUUID())); - LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::toggle, msg["session_id"].asUUID())); + LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); } } @@ -1345,8 +1351,15 @@ LLUUID LLIMMgr::addSession( // This removes the panel referenced by the uuid, and then restores // internal consistency. The internal pointer is not deleted? Did you mean // a pointer to the corresponding LLIMSession? Session data is cleared now. -void LLIMMgr::removeSession(const LLUUID& session_id) +// Put a copy of UUID to avoid problem when passed reference becames invalid +// if it has been come from the object removed in observer. +void LLIMMgr::removeSession(LLUUID session_id) { + if (mBeingRemovedSessionID == session_id) + { + return; + } + LLFloaterIMPanel* floater = findFloaterBySession(session_id); if(floater) { diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 4eb743b1ac..219af0705d 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -38,7 +38,6 @@ #include "llinstantmessage.h" #include "lluuid.h" #include "llmultifloater.h" -#include "llrecentpeople.h" class LLFloaterChatterBox; class LLUUID; @@ -159,7 +158,7 @@ public: // This removes the panel referenced by the uuid, and then // restores internal consistency. The internal pointer is not // deleted. - void removeSession(const LLUUID& session_id); + void removeSession(LLUUID session_id); void inviteToSession( const LLUUID& session_id, diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 40c5a243cc..e5cf8ccf66 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -3084,8 +3084,15 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) items.push_back(std::string("Landmark Separator")); items.push_back(std::string("Teleport To Landmark")); - hideContextEntries(menu, items, disabled_items); + // 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")); + } + hideContextEntries(menu, items, disabled_items); } // Convenience function for the two functions below. diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp index df9aa32d1b..2ad83c76b5 100644 --- a/indra/newview/lllandmarkactions.cpp +++ b/indra/newview/lllandmarkactions.cpp @@ -52,10 +52,10 @@ #include "llinventorymodel.h" #include "llagentui.h" -// Returns true if the given inventory item is a landmark pointing to the current parcel. -// Used to filter inventory items. -class LLIsAgentParcelLandmark : public LLInventoryCollectFunctor + +class LLFetchlLandmarkByAgentPos : public LLInventoryCollectFunctor { + public: /*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) { @@ -69,8 +69,11 @@ public: LLVector3d landmark_global_pos; if (!landmark->getGlobalPos(landmark_global_pos)) return false; - - return LLViewerParcelMgr::getInstance()->inAgentParcel(landmark_global_pos); + LLVector3d a_pos = gAgent.getPositionGlobal(); + //we have to round off each coordinates to compare positions properly + return llround(a_pos.mdV[VX]) == llround(landmark_global_pos.mdV[VX]) + && llround(a_pos.mdV[VY]) == llround(landmark_global_pos.mdV[VY]) + && llround(a_pos.mdV[VZ]) == llround(landmark_global_pos.mdV[VZ]); } }; @@ -139,24 +142,22 @@ LLInventoryModel::item_array_t LLLandmarkActions::fetchLandmarksByName(std::stri bool LLLandmarkActions::landmarkAlreadyExists() { - // Determine whether there are landmarks pointing to the current parcel. - LLInventoryModel::item_array_t items; - collectParcelLandmark(items); - return !items.empty(); + // Determine whether there are landmarks pointing to the current global agent position. + return findLandmarkForAgentPos() != NULL; } -LLViewerInventoryItem* LLLandmarkActions::findLandmarkForAgentParcel() +LLViewerInventoryItem* LLLandmarkActions::findLandmarkForAgentPos() { // Determine whether there are landmarks pointing to the current parcel. LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - LLIsAgentParcelLandmark is_current_parcel_landmark; + LLFetchlLandmarkByAgentPos is_current_pos_landmark; gInventory.collectDescendentsIf(gInventory.getRootFolderID(), cats, items, LLInventoryModel::EXCLUDE_TRASH, - is_current_parcel_landmark); + is_current_pos_landmark); if(items.empty()) { @@ -287,13 +288,3 @@ bool LLLandmarkActions::getLandmarkGlobalPos(const LLUUID& landmarkInventoryItem return landmark->getGlobalPos(posGlobal); } - -void LLLandmarkActions::collectParcelLandmark(LLInventoryModel::item_array_t& items){ - LLInventoryModel::cat_array_t cats; - LLIsAgentParcelLandmark is_current_parcel_landmark; - gInventory.collectDescendentsIf(gInventory.getRootFolderID(), - cats, - items, - LLInventoryModel::EXCLUDE_TRASH, - is_current_parcel_landmark); -} diff --git a/indra/newview/lllandmarkactions.h b/indra/newview/lllandmarkactions.h index c74072c0f4..ce3ed76090 100644 --- a/indra/newview/lllandmarkactions.h +++ b/indra/newview/lllandmarkactions.h @@ -53,12 +53,12 @@ public: static bool landmarkAlreadyExists(); /** - * @brief Searches landmark for parcel agent is currently in. - * @return Returns landmark for agent parcel or NULL. + * @brief Searches landmark for agent global position. + * @return Returns landmark or NULL. * * *TODO: dzaporozhan: There can be many landmarks for single parcel. */ - static LLViewerInventoryItem* findLandmarkForAgentParcel(); + static LLViewerInventoryItem* findLandmarkForAgentPos(); /** * @brief Checks whether agent has rights to create landmark for current parcel. @@ -77,14 +77,6 @@ public: const std::string& name, const std::string& desc, const LLUUID& folder_id); - - /** - * @brief Trying to find in inventory a landmark of the current parcel. - * Normally items should contain only one item, - * because we can create the only landmark per parcel according to Navigation spec. - */ - static void collectParcelLandmark(LLInventoryModel::item_array_t& items); - /** * @brief Creates SLURL for given global position. */ diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 1d9220cf3d..9d14a3fbdc 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -419,8 +419,7 @@ void LLLocationInputCtrl::onInfoButtonClicked() void LLLocationInputCtrl::onAddLandmarkButtonClicked() { - LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentParcel(); - + LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos(); // Landmark exists, open it for preview and edit if(landmark && landmark->getUUID().notNull()) { @@ -677,15 +676,16 @@ void LLLocationInputCtrl::onLocationContextMenuItemClicked(const LLSD& userdata) } else if (item == std::string("landmark")) { - LLInventoryModel::item_array_t items; - LLLandmarkActions::collectParcelLandmark(items); + LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos(); - if(items.empty()) + if(!landmark) { LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); - }else{ + } + else + { LLSideTray::getInstance()->showPanel("panel_places", - LLSD().insert("type", "landmark").insert("id",items.get(0)->getUUID())); + LLSD().insert("type", "landmark").insert("id",landmark->getUUID())); } } else if (item == std::string("cut")) diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index 11c8b03f7f..d8f00ec370 100644 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -71,7 +71,7 @@ const std::string BOTTOM_TRAY_BUTTON_NAME = "movement_btn"; // protected LLFloaterMove::LLFloaterMove(const LLSD& key) -: LLFloater(key), +: LLDockableFloater(NULL, false, key), mForwardButton(NULL), mBackwardButton(NULL), mTurnLeftButton(NULL), @@ -79,7 +79,8 @@ LLFloaterMove::LLFloaterMove(const LLSD& key) mMoveUpButton(NULL), mMoveDownButton(NULL), mStopFlyingButton(NULL), - mModeActionsPanel(NULL) + mModeActionsPanel(NULL), + mCurrentMode(MM_WALK) { } @@ -88,6 +89,7 @@ BOOL LLFloaterMove::postBuild() { setIsChrome(TRUE); + LLDockableFloater::postBuild(); mForwardButton = getChild<LLJoystickAgentTurn>("forward btn"); mForwardButton->setHeldDownDelay(MOVE_BUTTON_DELAY); @@ -134,8 +136,6 @@ BOOL LLFloaterMove::postBuild() initModeTooltips(); - updatePosition(); - initModeButtonMap(); initMovementMode(); @@ -145,6 +145,18 @@ BOOL LLFloaterMove::postBuild() return TRUE; } +// virtual +void LLFloaterMove::setEnabled(BOOL enabled) +{ + //we need to enable/disable only buttons, EXT-1061. + + // is called before postBuild() - use findChild here. + LLPanel *panel_actions = findChild<LLPanel>("panel_actions"); + if (panel_actions) panel_actions->setEnabled(enabled); + + showModeButtons(enabled); +} + // static F32 LLFloaterMove::getYawRate( F32 time ) { @@ -266,6 +278,7 @@ void LLFloaterMove::onStopFlyingButtonClick() void LLFloaterMove::setMovementMode(const EMovementMode mode) { + mCurrentMode = mode; gAgent.setFlying(MM_FLY == mode); switch (mode) @@ -401,26 +414,49 @@ void LLFloaterMove::sUpdateFlyingStatus() void LLFloaterMove::showModeButtons(BOOL bShow) { - if (mModeActionsPanel->getVisible() == bShow) + // is called from setEnabled so can be called before postBuild(), check mModeActionsPanel agains to NULL + if (NULL == mModeActionsPanel || mModeActionsPanel->getVisible() == bShow) return; mModeActionsPanel->setVisible(bShow); + if (isDocked()) + { + return; + } + + updateHeight(bShow); +} + +void LLFloaterMove::updateHeight(bool show_mode_buttons) +{ + static bool size_changed = false; + static S32 origin_height = getRect().getHeight(); LLRect rect = getRect(); - static S32 height = mModeActionsPanel->getRect().getHeight(); + static S32 mode_panel_height = mModeActionsPanel->getRect().getHeight(); + S32 newHeight = getRect().getHeight(); - if (!bShow) + + if (!show_mode_buttons && origin_height == newHeight) { - newHeight -= height; + newHeight -= mode_panel_height; + size_changed = true; } - else + else if (show_mode_buttons && origin_height > newHeight) { - newHeight += height; + newHeight += mode_panel_height; + size_changed = true; } + + if (!size_changed) + return; + rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), newHeight); reshape(rect.getWidth(), rect.getHeight()); setRect(rect); + size_changed = false; } + //static void LLFloaterMove::enableInstance(BOOL bEnable) { @@ -428,17 +464,42 @@ void LLFloaterMove::enableInstance(BOOL bEnable) if (instance) { instance->setEnabled(bEnable); - instance->showModeButtons(bEnable); } } void LLFloaterMove::onOpen(const LLSD& key) { - updatePosition(); + LLButton *anchor_panel = LLBottomTray::getInstance()->getChild<LLButton>("movement_btn"); + + if (gAgent.getFlying()) + { + setFlyingMode(TRUE); + showModeButtons(FALSE); + } + + if (gAgent.getAvatarObject() && gAgent.getAvatarObject()->isSitting()) + { + setSittingMode(TRUE); + showModeButtons(FALSE); + } + + setDockControl(new LLDockControl( + anchor_panel, this, + getDockTongue(), LLDockControl::TOP)); + + showQuickTips(mCurrentMode); sUpdateFlyingStatus(); } +//virtual +void LLFloaterMove::setDocked(bool docked, bool pop_on_undock/* = true*/) +{ + LLDockableFloater::setDocked(docked, pop_on_undock); + bool show_mode_buttons = isDocked() || !gAgent.getFlying(); + updateHeight(show_mode_buttons); +} + void LLFloaterMove::showQuickTips(const EMovementMode mode) { LLFirstTimeTipsManager::EFirstTimeTipType tipType = LLFirstTimeTipsManager::FTT_MOVE_WALK; @@ -543,6 +604,7 @@ void LLPanelStandStopFlying::setVisible(BOOL visible) if (visible) { updatePosition(); + getParent()->sendChildToFront(this); } LLPanel::setVisible(visible); @@ -600,6 +662,15 @@ void LLPanelStandStopFlying::updatePosition() S32 y = tray->getRect().getHeight(); + LLFloater *move_floater = LLFloaterReg::findInstance("moveview"); + if (move_floater) + { + if (move_floater->isDocked()) + { + y = move_floater->getRect().mBottom + getRect().getHeight(); + } + } + setOrigin(x, y); } diff --git a/indra/newview/llmoveview.h b/indra/newview/llmoveview.h index 6e6af9b693..584c7c494c 100644 --- a/indra/newview/llmoveview.h +++ b/indra/newview/llmoveview.h @@ -34,7 +34,7 @@ #define LL_LLMOVEVIEW_H // Library includes -#include "llfloater.h" +#include "lldockablefloater.h" class LLButton; class LLJoystickAgentTurn; @@ -44,7 +44,7 @@ class LLJoystickAgentSlide; // Classes // class LLFloaterMove -: public LLFloater +: public LLDockableFloater { friend class LLFloaterReg; @@ -54,6 +54,7 @@ private: public: /*virtual*/ BOOL postBuild(); + /*virtual*/ void setEnabled(BOOL enabled); static F32 getYawRate(F32 time); static void setFlyingMode(BOOL fly); void setFlyingModeImpl(BOOL fly); @@ -62,10 +63,7 @@ public: static void setSittingMode(BOOL bSitting); static void enableInstance(BOOL bEnable); /*virtual*/ void onOpen(const LLSD& key); - - // *HACK: due to hard enough to have this control aligned with "Move" button while resizing - // let update its position in each frame - /*virtual*/ void draw(){updatePosition(); LLFloater::draw();} + /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); static void sUpdateFlyingStatus(); @@ -98,6 +96,7 @@ private: void updateButtonsWithMovementMode(const EMovementMode newMode); void updatePosition(); void showModeButtons(BOOL bShow); + void updateHeight(bool show_mode_buttons); public: @@ -117,6 +116,7 @@ private: typedef std::map<EMovementMode, LLButton*> mode_control_button_map_t; mode_control_button_map_t mModeControlButtonMap; + EMovementMode mCurrentMode; }; diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 6150d5da37..b53bb586f3 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -482,7 +482,7 @@ BOOL LLNearbyChat::handleRightMouseDown(S32 x, S32 y, MASK mask) void LLNearbyChat::onOpen(const LLSD& key ) { - LLNotificationsUI::LLScreenChannel* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->getChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + LLNotificationsUI::LLScreenChannel* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); if(chat_channel) { chat_channel->removeToastsFromChannel(); diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index e348189ea9..cec4b9f7c7 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -201,6 +201,35 @@ BOOL LLNearbyChatBar::postBuild() mChatBox->setMaxTextLength(1023); mChatBox->setEnableLineHistory(TRUE); + // TODO: Initialization of the output monitor's params should be done via xml + const S32 MONITOR_RIGHT_PAD = 2; + + LLRect monitor_rect = LLRect(0, 18, 18, 0); + LLRect chatbox_rect = mChatBox->getRect(); + + S32 monitor_height = monitor_rect.getHeight(); + monitor_rect.mLeft = chatbox_rect.getWidth() - monitor_rect.getWidth() - MONITOR_RIGHT_PAD; + monitor_rect.mRight = chatbox_rect.getWidth() - MONITOR_RIGHT_PAD; + monitor_rect.mBottom = (chatbox_rect.getHeight() / 2) - (monitor_height / 2); + monitor_rect.mTop = monitor_rect.mBottom + monitor_height; + + LLOutputMonitorCtrl::Params monitor_params = LLOutputMonitorCtrl::Params(); + monitor_params.name = "output_monitor"; + monitor_params.draw_border(false); + monitor_params.rect(monitor_rect); + monitor_params.auto_update(true); + monitor_params.speaker_id(gAgentID); + + LLView::Follows follows = LLView::Follows(); + follows.flags = FOLLOWS_RIGHT; + monitor_params.follows = follows; + mOutputMonitor = LLUICtrlFactory::create<LLOutputMonitorCtrl>(monitor_params); + mChatBox->addChild(mOutputMonitor); + + // never show "muted" because you can't mute yourself + mOutputMonitor->setIsMuted(false); + mOutputMonitor->setVisible(FALSE); + mTalkBtn = getChild<LLTalkButton>("talk"); // Speak button should be initially disabled because @@ -593,6 +622,7 @@ LLWString LLNearbyChatBar::stripChannelNumber(const LLWString &mesg, S32* channe void LLNearbyChatBar::setPTTState(bool state) { mTalkBtn->setSpeakBtnToggleState(state); + mOutputMonitor->setVisible(state); } void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index f310740f42..1b71ad69f2 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -39,6 +39,7 @@ #include "llchat.h" #include "llchiclet.h" #include "llvoiceclient.h" +#include "lloutputmonitorctrl.h" class LLGestureComboBox : public LLComboBox @@ -114,6 +115,7 @@ protected: LLLineEditor* mChatBox; LLTalkButton* mTalkBtn; + LLOutputMonitorCtrl* mOutputMonitor; }; #endif diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index a1912655a3..7eb5d91e53 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -49,23 +49,25 @@ namespace LLNotificationsUI{ LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& id) { mType = type; - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); - ///////////////////////////////////////////////////// - LLChannelManager::Params p; //TODO: check and correct + LLChannelManager::Params p; p.id = LLUUID(gSavedSettings.getString("NearByChatChannelUUID")); - p.channel_right_bound = nearby_chat->getRect().mRight; - p.channel_width = nearby_chat->getRect().mRight - 16; //HACK: 16 - ? - ///////////////////////////////////////////////////// - // Getting a Channel for our notifications - mChannel = LLChannelManager::getInstance()->createChannel(p); - mChannel->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_TOP); + mChannel = LLChannelManager::getInstance()->getChannel(p); mChannel->setOverflowFormatString("You have %d unread nearby chat messages"); } LLNearbyChatHandler::~LLNearbyChatHandler() { } + +void LLNearbyChatHandler::initChannel() +{ + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); + S32 channel_right_bound = nearby_chat->getRect().mRight; + S32 channel_width = nearby_chat->getRect().mRight - 16; //HACK: 16 - ? + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + void LLNearbyChatHandler::processChat(const LLChat& chat_msg) { if(chat_msg.mMuted == TRUE) @@ -80,6 +82,12 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg) nearby_chat->addMessage(chat_msg); if(nearby_chat->getVisible()) return;//no need in toast if chat is visible + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } LLUUID id; id.generate(); @@ -95,34 +103,14 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg) item->setVisible(true); LLToast::Params p; - p.id = id; + p.notif_id = id; p.panel = item; - p.on_toast_destroy = boost::bind(&LLNearbyChatHandler::onToastDestroy, this, _1); - p.on_mouse_enter = boost::bind(&LLNearbyChatHandler::removeNearbyToastsAndShowChat, this); + p.on_delete_toast = boost::bind(&LLNearbyChatHandler::onDeleteToast, this, _1); mChannel->addToast(p); } -void LLNearbyChatHandler::onToastDestroy(LLToast* toast) +void LLNearbyChatHandler::onDeleteToast(LLToast* toast) { - if(toast) - toast->closeFloater(); -} - -void LLNearbyChatHandler::onChicletClick(void) -{ -} -void LLNearbyChatHandler::onChicletClose(void) -{ -} - -void LLNearbyChatHandler::removeNearbyToastsAndShowChat() -{ - /* - if(mChannel) - mChannel->removeToastsFromChannel(); - - LLFloaterReg::showTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); - */ } } diff --git a/indra/newview/llnearbychathandler.h b/indra/newview/llnearbychathandler.h index 8fcd03689d..fb2abac6a4 100644 --- a/indra/newview/llnearbychathandler.h +++ b/indra/newview/llnearbychathandler.h @@ -46,12 +46,10 @@ public: virtual void processChat(const LLChat& chat_msg); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); protected: - void removeNearbyToastsAndShowChat(); + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); }; } diff --git a/indra/newview/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp index bd6c6b2308..3893eaa0d4 100644 --- a/indra/newview/llnotificationalerthandler.cpp +++ b/indra/newview/llnotificationalerthandler.cpp @@ -35,8 +35,8 @@ #include "llnotificationhandler.h" #include "lltoastnotifypanel.h" -#include "llbottomtray.h" #include "llviewercontrol.h" +#include "llviewerwindow.h" #include "lltoastalertpanel.h" @@ -47,17 +47,14 @@ LLAlertHandler::LLAlertHandler(e_notification_type type, const LLSD& id) : mIsMo { mType = type; - LLBottomTray* tray = LLBottomTray::getInstance(); LLChannelManager::Params p; p.id = LLUUID(gSavedSettings.getString("AlertChannelUUID")); - p.channel_right_bound = tray->getRect().getWidth() / 2; - p.channel_width = 0; p.display_toasts_always = true; - p.align = NA_CENTRE; + p.toast_align = NA_CENTRE; + p.channel_align = CA_CENTRE; // Getting a Channel for our notifications - mChannel = LLChannelManager::getInstance()->createChannel(p); - mChannel->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP); + mChannel = LLChannelManager::getInstance()->getChannel(p); mChannel->setShowToasts(true); } @@ -67,21 +64,42 @@ LLAlertHandler::~LLAlertHandler() } //-------------------------------------------------------------------------- -void LLAlertHandler::processNotification(const LLSD& notify) +void LLAlertHandler::initChannel() { + S32 channel_right_bound = gViewerWindow->getWorldViewRect().getWidth() / 2; + mChannel->init(channel_right_bound, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLAlertHandler::processNotification(const LLSD& notify) +{ + if(!mChannel) + { + return false; + } + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + if(!notification) + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } + if (notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "load") { LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); LLToast::Params p; - p.id = notification->getID(); + p.notif_id = notification->getID(); p.notification = notification; p.panel = dynamic_cast<LLToastPanel*>(alert_dialog); p.enable_hide_btn = false; p.can_fade = false; p.is_modal = mIsModal; - p.on_toast_destroy = boost::bind(&LLAlertHandler::onToastDestroy, this, _1); + p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); mChannel->addToast(p); } else if (notify["sigtype"].asString() == "change") @@ -93,25 +111,14 @@ void LLAlertHandler::processNotification(const LLSD& notify) { mChannel->killToastByNotificationID(notification->getID()); } + return true; } //-------------------------------------------------------------------------- -void LLAlertHandler::onToastDestroy(LLToast* toast) -{ - toast->closeFloater(); -} - -//-------------------------------------------------------------------------- -void LLAlertHandler::onChicletClick(void) -{ -} - -//-------------------------------------------------------------------------- -void LLAlertHandler::onChicletClose(void) +void LLAlertHandler::onDeleteToast(LLToast* toast) { } //-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationgrouphandler.cpp b/indra/newview/llnotificationgrouphandler.cpp index 31753efec9..c488d37ea5 100644 --- a/indra/newview/llnotificationgrouphandler.cpp +++ b/indra/newview/llnotificationgrouphandler.cpp @@ -34,11 +34,9 @@ #include "llnotificationhandler.h" #include "lltoastgroupnotifypanel.h" -#include "llbottomtray.h" #include "llgroupactions.h" #include "llviewercontrol.h" -#include "llfloaterreg.h" -#include "llsyswellwindow.h" +#include "llviewerwindow.h" using namespace LLNotificationsUI; @@ -47,16 +45,8 @@ LLGroupHandler::LLGroupHandler(e_notification_type type, const LLSD& id) { mType = type; - // getting a Chiclet and creating params for a channel - LLBottomTray* tray = LLBottomTray::getInstance(); - mChiclet = tray->getSysWell(); - LLChannelManager::Params p; - p.id = LLUUID(gSavedSettings.getString("NotificationChannelUUID")); - p.channel_right_bound = tray->getRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); - p.channel_width = gSavedSettings.getS32("NotifyBoxWidth"); - // Getting a Channel for our notifications - mChannel = LLChannelManager::getInstance()->createChannel(p); + mChannel = LLChannelManager::getInstance()->createNotificationChannel(); } //-------------------------------------------------------------------------- @@ -65,52 +55,63 @@ LLGroupHandler::~LLGroupHandler() } //-------------------------------------------------------------------------- -void LLGroupHandler::processNotification(const LLSD& notify) +void LLGroupHandler::initChannel() { + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLGroupHandler::processNotification(const LLSD& notify) +{ + if(!mChannel) + { + return false; + } + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + + if(!notification) + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } + if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") { LLPanel* notify_box = new LLToastGroupNotifyPanel(notification); LLToast::Params p; - p.id = notification->getID(); + p.notif_id = notification->getID(); p.notification = notification; p.panel = notify_box; - p.on_toast_destroy = boost::bind(&LLGroupHandler::onToastDestroy, this, _1); + p.on_delete_toast = boost::bind(&LLGroupHandler::onDeleteToast, this, _1); mChannel->addToast(p); - static_cast<LLNotificationChiclet*>(mChiclet)->incUreadSystemNotifications(); - - LLGroupActions::refresh_notices(); + // send a signal to the counter manager + mNewNotificationSignal(); + + LLGroupActions::refresh_notices(); } else if (notify["sigtype"].asString() == "delete") { mChannel->killToastByNotificationID(notification->getID()); } + return true; } //-------------------------------------------------------------------------- -void LLGroupHandler::onToastDestroy(LLToast* toast) -{ - static_cast<LLNotificationChiclet*>(mChiclet)->decUreadSystemNotifications(); - - LLToastPanel* panel = dynamic_cast<LLToastPanel*>(toast->getPanel()); - LLFloaterReg::getTypedInstance<LLSysWellWindow>("syswell_window")->removeItemByID(panel->getID()); - - // turning hovering off mannualy because onMouseLeave won't happen if a toast was closed using a keyboard - if(toast->hasFocus()) - mChannel->setHovering(false); - - toast->closeFloater(); -} - -//-------------------------------------------------------------------------- -void LLGroupHandler::onChicletClick(void) +void LLGroupHandler::onDeleteToast(LLToast* toast) { -} + // send a signal to the counter manager + mDelNotificationSignal(); -//-------------------------------------------------------------------------- -void LLGroupHandler::onChicletClose(void) -{ + // send a signal to a listener to let him perform some action + // in this case listener is a SysWellWindow and it will remove a corresponding item from its list + mNotificationIDSignal(toast->getNotificationID()); } //-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 6982ab7096..90ff5fbaac 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -65,33 +65,54 @@ typedef enum e_notification_type // LLEventHandler is a base class that specifies a common interface for all // notification handlers. It states, that every handler must react on the follofing // events: -// - destroying of a toast; -// - clicking on a correspondet chiclet; -// - closing of a correspondent chiclet. +// - deleting of a toast; +// - initialization of a corresponding channel; // Also every handler must have the following attributes: // - type of the notification that this handler is responsible to; -// - pointer to a correspondent chiclet; // - pointer to a correspondent screen channel, in which all toasts of the handled notification's // type should be displayed +// This class also provides the following signald: +// - increment counter signal +// - decrement counter signal +// - update counter signal +// - signal, that emits ID of the notification that is being processed // class LLEventHandler { public: virtual ~LLEventHandler() {}; - virtual void onToastDestroy(LLToast* toast)=0; - virtual void onChicletClick(void)=0; - virtual void onChicletClose(void)=0; + // callbacks for counters + typedef boost::function<void (void)> notification_callback_t; + typedef boost::signals2::signal<void (void)> notification_signal_t; + notification_signal_t mNewNotificationSignal; + notification_signal_t mDelNotificationSignal; + boost::signals2::connection setNewNotificationCallback(notification_callback_t cb) { return mNewNotificationSignal.connect(cb); } + boost::signals2::connection setDelNotification(notification_callback_t cb) { return mDelNotificationSignal.connect(cb); } + // callback for notification/toast + typedef boost::function<void (const LLUUID id)> notification_id_callback_t; + typedef boost::signals2::signal<void (const LLUUID id)> notification_id_signal_t; + notification_id_signal_t mNotificationIDSignal; + boost::signals2::connection setNotificationIDCallback(notification_id_callback_t cb) { return mNotificationIDSignal.connect(cb); } + +protected: + virtual void onDeleteToast(LLToast* toast)=0; + + // arrange handler's channel on a screen + // is necessary to unbind a moment of creation of a channel and a moment of positioning of it + // it is useful when positioning depends on positions of other controls, that could not be created + // at the moment, when a handlers creates a channel. + virtual void initChannel()=0; LLScreenChannel* mChannel; - LLChiclet* mChiclet; e_notification_type mType; + }; // LLSysHandler and LLChatHandler are more specific base classes // that divide all notification handlers on to groups: -// - handlers for different system notifications (script dialogs, tips, group notices and alerts); -// - handlers for different messaging notifications (nearby chat, IM chat, group chat etc.) +// - handlers for different system notifications (script dialogs, tips, group notices, alerts and IMs); +// - handlers for different messaging notifications (nearby chat) /** * Handler for system notifications. */ @@ -100,7 +121,7 @@ class LLSysHandler : public LLEventHandler public: virtual ~LLSysHandler() {}; - virtual void processNotification(const LLSD& notify)=0; + virtual bool processNotification(const LLSD& notify)=0; }; /** @@ -116,43 +137,59 @@ public: /** * Handler for IM notifications. - * It manages life time of tip and script notices. + * It manages life time of IMs, group messages. */ class LLIMHandler : public LLSysHandler { public: - LLIMHandler(); + LLIMHandler(e_notification_type type, const LLSD& id); virtual ~LLIMHandler(); // base interface functions - virtual void processNotification(const LLSD& notify); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); + virtual bool processNotification(const LLSD& notify); protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); }; /** * Handler for system informational notices. - * It manages life time of tip and script notices. + * It manages life time of tip notices. */ -class LLInfoHandler : public LLSysHandler +class LLTipHandler : public LLSysHandler { public: - LLInfoHandler(e_notification_type type, const LLSD& id); - virtual ~LLInfoHandler(); + LLTipHandler(e_notification_type type, const LLSD& id); + virtual ~LLTipHandler(); // base interface functions - virtual void processNotification(const LLSD& notify); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); + virtual bool processNotification(const LLSD& notify); - // own handlers - void onStoreToast(LLPanel* info_panel, LLUUID id); - void onRejectToast(LLToast::Params p); protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); +}; + +/** + * Handler for system informational notices. + * It manages life time of script notices. + */ +class LLScriptHandler : public LLSysHandler +{ +public: + LLScriptHandler(e_notification_type type, const LLSD& id); + virtual ~LLScriptHandler(); + + // base interface functions + virtual bool processNotification(const LLSD& notify); + +protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); + + // own handlers + void onRejectToast(LLUUID& id); }; @@ -164,14 +201,13 @@ class LLGroupHandler : public LLSysHandler public: LLGroupHandler(e_notification_type type, const LLSD& id); virtual ~LLGroupHandler(); - - - virtual void processNotification(const LLSD& notify); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); + + // base interface functions + virtual bool processNotification(const LLSD& notify); protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); }; /** @@ -185,12 +221,13 @@ public: void setAlertMode(bool is_modal) { mIsModal = is_modal; } - virtual void processNotification(const LLSD& notify); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); + // base interface functions + virtual bool processNotification(const LLSD& notify); protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); + bool mIsModal; }; diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index 31266fdecf..81a6b32917 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -71,12 +71,13 @@ void LLNotificationManager::init() LLNotifications::instance().getChannel("AlertModal")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); LLNotifications::instance().getChannel("IM Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mNotifyHandlers["notify"] = boost::shared_ptr<LLEventHandler>(new LLInfoHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["notifytip"] = mNotifyHandlers["notify"]; + mNotifyHandlers["notify"] = boost::shared_ptr<LLEventHandler>(new LLScriptHandler(NT_NOTIFY, LLSD())); + mNotifyHandlers["notifytip"] = boost::shared_ptr<LLEventHandler>(new LLTipHandler(NT_NOTIFY, LLSD())); mNotifyHandlers["groupnotify"] = boost::shared_ptr<LLEventHandler>(new LLGroupHandler(NT_GROUPNOTIFY, LLSD())); mNotifyHandlers["alert"] = boost::shared_ptr<LLEventHandler>(new LLAlertHandler(NT_ALERT, LLSD())); - mNotifyHandlers["alertmodal"] = mNotifyHandlers["alert"]; - mNotifyHandlers["notifytoast"] = boost::shared_ptr<LLEventHandler>(new LLIMHandler()); + mNotifyHandlers["alertmodal"] = boost::shared_ptr<LLEventHandler>(new LLAlertHandler(NT_ALERT, LLSD())); + static_cast<LLAlertHandler*>(mNotifyHandlers["alertmodal"].get())->setAlertMode(true); + mNotifyHandlers["notifytoast"] = boost::shared_ptr<LLEventHandler>(new LLIMHandler(NT_IMCHAT, LLSD())); mNotifyHandlers["nearbychat"] = boost::shared_ptr<LLEventHandler>(new LLNearbyChatHandler(NT_NEARBYCHAT, LLSD())); } @@ -92,17 +93,12 @@ bool LLNotificationManager::onNotification(const LLSD& notify) return false; std::string notification_type = notification->getType(); - handle = dynamic_cast<LLSysHandler*>(mNotifyHandlers[notification_type].get()); + handle = static_cast<LLSysHandler*>(mNotifyHandlers[notification_type].get()); if(!handle) return false; - if( notification_type == "alertmodal" ) - dynamic_cast<LLAlertHandler*>(handle)->setAlertMode(true); - - handle->processNotification(notify); - - return true; + return handle->processNotification(notify); } //-------------------------------------------------------------------------- @@ -124,7 +120,15 @@ void LLNotificationManager::onChat(const LLChat& msg,ENotificationType type) } //-------------------------------------------------------------------------- +LLEventHandler* LLNotificationManager::getHandlerForNotification(std::string notification_type) +{ + std::map<std::string, boost::shared_ptr<LLEventHandler> >::iterator it = mNotifyHandlers.find(notification_type); + if(it != mNotifyHandlers.end()) + return (*it).second.get(); + return NULL; +} +//-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationmanager.h b/indra/newview/llnotificationmanager.h index 838a00ee11..072fc6f24c 100644 --- a/indra/newview/llnotificationmanager.h +++ b/indra/newview/llnotificationmanager.h @@ -50,7 +50,7 @@ class LLToast; /** * Responsible for registering notification handlers. */ -class LLNotificationManager : public LLUICtrl, public LLSingleton<LLNotificationManager> +class LLNotificationManager : public LLSingleton<LLNotificationManager> { typedef std::pair<std::string, LLEventHandler*> eventhandlers; public: @@ -68,6 +68,10 @@ public: // this method reacts on chat notifications and calls an appropriate handler void onChat(const LLChat& msg,ENotificationType type); + // get a handler for a certain type of notification + LLEventHandler* getHandlerForNotification(std::string notification_type); + + private: //TODO (*) std::map<std::string, boost::shared_ptr<LLEventHandler> > mNotifyHandlers; diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp new file mode 100644 index 0000000000..72855ac0fd --- /dev/null +++ b/indra/newview/llnotificationscripthandler.cpp @@ -0,0 +1,135 @@ +/** + * @file llnotificationscripthandler.cpp + * @brief Notification Handler Class for Simple Notifications and Notification Tips + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-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" // must be first include + +#include "llnotificationhandler.h" +#include "lltoastnotifypanel.h" +#include "llviewercontrol.h" +#include "llviewerwindow.h" + +using namespace LLNotificationsUI; + +//-------------------------------------------------------------------------- +LLScriptHandler::LLScriptHandler(e_notification_type type, const LLSD& id) +{ + mType = type; + + // Getting a Channel for our notifications + mChannel = LLChannelManager::getInstance()->createNotificationChannel(); + mChannel->setControlHovering(true); + mChannel->setOnRejectToastCallback(boost::bind(&LLScriptHandler::onRejectToast, this, _1)); +} + +//-------------------------------------------------------------------------- +LLScriptHandler::~LLScriptHandler() +{ +} + +//-------------------------------------------------------------------------- +void LLScriptHandler::initChannel() +{ + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLScriptHandler::processNotification(const LLSD& notify) +{ + if(!mChannel) + { + return false; + } + + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + + if(!notification) + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } + + if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") + { + LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + p.on_delete_toast = boost::bind(&LLScriptHandler::onDeleteToast, this, _1); + mChannel->addToast(p); + + // send a signal to the counter manager + mNewNotificationSignal(); + + } + else if (notify["sigtype"].asString() == "delete") + { + mChannel->killToastByNotificationID(notification->getID()); + } + return true; +} + +//-------------------------------------------------------------------------- + +void LLScriptHandler::onDeleteToast(LLToast* toast) +{ + // send a signal to the counter manager + mDelNotificationSignal(); + + // send a signal to a listener to let him perform some action + // in this case listener is a SysWellWindow and it will remove a corresponding item from its list + mNotificationIDSignal(toast->getNotificationID()); +} + +//-------------------------------------------------------------------------- +void LLScriptHandler::onRejectToast(LLUUID& id) +{ + LLNotificationPtr notification = LLNotifications::instance().find(id); + + if(notification) + { + LLNotifications::instance().cancel(notification); + } +} + +//-------------------------------------------------------------------------- + + + + diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp new file mode 100644 index 0000000000..740acb6365 --- /dev/null +++ b/indra/newview/llnotificationtiphandler.cpp @@ -0,0 +1,113 @@ +/** + * @file llnotificationtiphandler.cpp + * @brief Notification Handler Class for Notification Tips + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-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" // must be first include + +#include "llnotificationhandler.h" +#include "lltoastnotifypanel.h" +#include "llviewercontrol.h" +#include "llviewerwindow.h" + +using namespace LLNotificationsUI; + + +//-------------------------------------------------------------------------- +LLTipHandler::LLTipHandler(e_notification_type type, const LLSD& id) +{ + mType = type; + + // Getting a Channel for our notifications + mChannel = LLChannelManager::getInstance()->createNotificationChannel(); +} + +//-------------------------------------------------------------------------- +LLTipHandler::~LLTipHandler() +{ +} + +//-------------------------------------------------------------------------- +void LLTipHandler::initChannel() +{ + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLTipHandler::processNotification(const LLSD& notify) +{ + if(!mChannel) + { + return false; + } + + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + + if(!notification) + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } + + if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") + { + LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + p.is_tip = true; + p.can_be_stored = false; + + mChannel->addToast(p); + + } + else if (notify["sigtype"].asString() == "delete") + { + mChannel->killToastByNotificationID(notification->getID()); + } + return true; +} + +//-------------------------------------------------------------------------- +void LLTipHandler::onDeleteToast(LLToast* toast) +{ +} + +//-------------------------------------------------------------------------- + + diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index 49b48f5a8e..6e1dc6940e 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -129,10 +129,10 @@ void LLOutputMonitorCtrl::draw() const F32 LEVEL_1 = LLVoiceClient::OVERDRIVEN_POWER_LEVEL * 2.f / 3.f; const F32 LEVEL_2 = LLVoiceClient::OVERDRIVEN_POWER_LEVEL; - if (getVisible() && mAutoUpdate && !mIsMuted && mSpeakerId.notNull()) + if (mIsParentVisible && getVisible() && mAutoUpdate && !mIsMuted && mSpeakerId.notNull()) { setPower(gVoiceClient->getCurrentPower(mSpeakerId)); - setIsTalking(gVoiceClient->getUserPTTState()); + setIsTalking(gVoiceClient->getIsSpeaking(mSpeakerId)); } LLPointer<LLUIImage> icon; @@ -220,6 +220,12 @@ void LLOutputMonitorCtrl::draw() gl_rect_2d(0, monh, monw, 0, sColorBound, FALSE); } +void LLOutputMonitorCtrl::handleVisibilityChange(BOOL new_visibility) +{ + mIsParentVisible = new_visibility; + LLView::handleVisibilityChange(new_visibility); +} + void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id) { if (speaker_id.isNull()) return; diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index 7a7b8bc3a1..0e213c4326 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -72,6 +72,8 @@ public: // llview overrides virtual void draw(); + void handleVisibilityChange(BOOL new_visibility); + void setPower(F32 val); F32 getPower(F32 val) const { return mPower; } @@ -102,6 +104,8 @@ private: F32 mPower; bool mIsMuted; bool mIsTalking; + /** Stores flag whether parent is visible. If not it will not update indicator*/ + bool mIsParentVisible; LLPointer<LLUIImage> mImageMute; LLPointer<LLUIImage> mImageOff; LLPointer<LLUIImage> mImageOn; diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index b7f2f67a9a..649697e091 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -311,7 +311,8 @@ void LLPanelProfileTab::onOpen(const LLSD& key) void LLPanelProfileTab::scrollToTop() { - LLScrollContainer* scrollContainer = getChild<LLScrollContainer>("profile_scroll"); + LLScrollContainer* scrollContainer = findChild<LLScrollContainer>("profile_scroll"); + if (scrollContainer) scrollContainer->goToTop(); } diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index d1ce6b14ed..490c845c94 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -44,6 +44,9 @@ #include "llfloaterreg.h" #include "llfloater.h" +#include "llagent.h" +#include "llstatusbar.h" // can_afford_transaction() + #include "llsidetraypanelcontainer.h" #include "llpanelgroupnotices.h" @@ -162,15 +165,25 @@ BOOL LLPanelGroup::postBuild() button->setEnabled(false); + button = getChild<LLButton>("btn_join"); + button->setVisible(false); + button->setEnabled(true); + + button = getChild<LLButton>("btn_cancel"); + button->setVisible(false); button->setEnabled(true); + button = getChild<LLButton>("btn_refresh"); button->setClickedCallback(onBtnRefresh, this); button->setVisible(mAllowEdit); getChild<LLButton>("btn_create")->setVisible(false); - childSetCommitCallback("btn_create",boost::bind(&LLPanelGroup::onBtnCreate,this),NULL); childSetCommitCallback("back",boost::bind(&LLPanelGroup::onBackBtnClick,this),NULL); + childSetCommitCallback("btn_create",boost::bind(&LLPanelGroup::onBtnCreate,this),NULL); + childSetCommitCallback("btn_join",boost::bind(&LLPanelGroup::onBtnJoin,this),NULL); + childSetCommitCallback("btn_cancel",boost::bind(&LLPanelGroup::onBtnCancel,this),NULL); + LLPanelGroupTab* panel_general = findChild<LLPanelGroupTab>("group_general_tab_panel"); LLPanelGroupTab* panel_roles = findChild<LLPanelGroupTab>("group_roles_tab_panel"); LLPanelGroupTab* panel_notices = findChild<LLPanelGroupTab>("group_notices_tab_panel"); @@ -181,41 +194,30 @@ BOOL LLPanelGroup::postBuild() if(panel_notices) mTabs.push_back(panel_notices); if(panel_land) mTabs.push_back(panel_land); + if(panel_general) panel_general->setupCtrls(this); return TRUE; } -void LLPanelGroup::reshape(S32 width, S32 height, BOOL called_from_parent ) +void LLPanelGroup::reposButton(const std::string& name) { - LLPanel::reshape(width, height, called_from_parent ); - - LLRect btn_rect; - - LLButton* button = findChild<LLButton>("btn_apply"); - if(button) - { - btn_rect = button->getRect(); - btn_rect.setLeftTopAndSize( btn_rect.mLeft, btn_rect.getHeight() + 2, btn_rect.getWidth(), btn_rect.getHeight()); - button->setRect(btn_rect); - } - - button = findChild<LLButton>("btn_create"); - if(button) - { - btn_rect = button->getRect(); + LLButton* button = findChild<LLButton>(name); + if(!button) + return; + LLRect btn_rect = button->getRect(); btn_rect.setLeftTopAndSize( btn_rect.mLeft, btn_rect.getHeight() + 2, btn_rect.getWidth(), btn_rect.getHeight()); button->setRect(btn_rect); - } +} +void LLPanelGroup::reshape(S32 width, S32 height, BOOL called_from_parent ) +{ + LLPanel::reshape(width, height, called_from_parent ); - button = findChild<LLButton>("btn_refresh"); - if(button) - { - btn_rect = button->getRect(); - btn_rect.setLeftTopAndSize( btn_rect.mLeft, btn_rect.getHeight() + 2, btn_rect.getWidth(), btn_rect.getHeight()); - button->setRect(btn_rect); - } + reposButton("btn_apply"); + reposButton("btn_create"); + reposButton("btn_refresh"); + reposButton("btn_cancel"); } void LLPanelGroup::onBackBtnClick() @@ -247,33 +249,94 @@ void LLPanelGroup::onBtnApply(void* user_data) LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data); self->apply(); } +void LLPanelGroup::onBtnJoin() +{ + lldebugs << "joining group: " << mID << llendl; + + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); + + if (gdatap) + { + S32 cost = gdatap->mMembershipFee; + LLSD args; + args["COST"] = llformat("%d", cost); + LLSD payload; + payload["group_id"] = mID; + + if (can_afford_transaction(cost)) + { + LLNotifications::instance().add("JoinGroupCanAfford", args, payload, LLPanelGroup::joinDlgCB); + } + else + { + LLNotifications::instance().add("JoinGroupCannotAfford", args, payload); + } + } + else + { + llwarns << "LLGroupMgr::getInstance()->getGroupData(" << mID << ") was NULL" << llendl; + } +} +bool LLPanelGroup::joinDlgCB(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + + if (option == 1) + { + // user clicked cancel + return false; + } + + LLGroupMgr::getInstance()->sendGroupMemberJoin(notification["payload"]["group_id"].asUUID()); + return false; +} + +void LLPanelGroup::onBtnCancel() +{ + onBackBtnClick(); +} void LLPanelGroup::changed(LLGroupChange gc) { for(std::vector<LLPanelGroupTab* >::iterator it = mTabs.begin();it!=mTabs.end();++it) (*it)->update(gc); - - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); - if(gdatap) - childSetValue("group_name", gdatap->mName); + update(gc); } void LLPanelGroup::notifyObservers() { - for(std::vector<LLPanelGroupTab* >::iterator it = mTabs.begin();it!=mTabs.end();++it) - (*it)->update(GC_ALL); + changed(GC_ALL); +} +void LLPanelGroup::update(LLGroupChange gc) +{ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); if(gdatap) + { childSetValue("group_name", gdatap->mName); - -} - + LLGroupData agent_gdatap; + bool is_member = gAgent.getGroupData(mID,agent_gdatap); + LLButton* btn_join = getChild<LLButton>("btn_join"); + bool join_btn_visible = !is_member && gdatap->mOpenEnrollment; + btn_join->setVisible(join_btn_visible); + if(join_btn_visible) + { + LLStringUtil::format_map_t string_args; + string_args["[AMOUNT]"] = llformat("%d", gdatap->mMembershipFee); + std::string fee_buff = getString("group_join_btn", string_args); + btn_join->setLabelSelected(fee_buff); + btn_join->setLabelUnselected(fee_buff); + } + } +} void LLPanelGroup::setGroupID(const LLUUID& group_id) { + std::string str_group_id; + group_id.toString(str_group_id); + LLGroupMgr::getInstance()->removeObserver(this); mID = group_id; LLGroupMgr::getInstance()->addObserver(this); @@ -288,6 +351,8 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) LLButton* button_apply = findChild<LLButton>("btn_apply"); LLButton* button_refresh = findChild<LLButton>("btn_refresh"); LLButton* button_create = findChild<LLButton>("btn_create"); + LLButton* button_join = findChild<LLButton>("btn_join"); + LLButton* button_cancel = findChild<LLButton>("btn_cancel"); bool is_null_group_id = group_id == LLUUID::null; @@ -295,8 +360,11 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) button_apply->setVisible(!is_null_group_id); if(button_refresh) button_refresh->setVisible(!is_null_group_id); + if(button_create) button_create->setVisible(is_null_group_id); + if(button_cancel) + button_cancel->setVisible(!is_null_group_id); getChild<LLUICtrl>("prepend_founded_by")->setVisible(!is_null_group_id); @@ -307,6 +375,9 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) if(!tab_general || !tab_roles || !tab_notices || !tab_land) return; + + if(button_join) + button_join->setVisible(false); if(is_null_group_id)//creating new group { @@ -323,6 +394,10 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) tab_roles->canOpenClose(false); tab_notices->canOpenClose(false); tab_land->canOpenClose(false); + + getChild<LLUICtrl>("group_name")->setVisible(false); + getChild<LLUICtrl>("group_name_editor")->setVisible(true); + } else { @@ -338,6 +413,9 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) tab_roles->canOpenClose(true); tab_notices->canOpenClose(true); tab_land->canOpenClose(true); + + getChild<LLUICtrl>("group_name")->setVisible(true); + getChild<LLUICtrl>("group_name_editor")->setVisible(false); } } @@ -395,11 +473,9 @@ void LLPanelGroup::draw() void LLPanelGroup::refreshData() { LLGroupMgr::getInstance()->clearGroupData(getID()); - - for(std::vector<LLPanelGroupTab* >::iterator it = mTabs.begin();it!=mTabs.end();++it) - (*it)->activate(); - + setGroupID(getID()); + // 5 second timeout childDisable("btn_refresh"); mRefreshTimer.start(); diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index 6db6738d18..f2118a7244 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -91,12 +91,20 @@ public: protected: + virtual void update(LLGroupChange gc); + void onBtnCreate(); void onBackBtnClick(); + void onBtnJoin(); + void onBtnCancel(); static void onBtnApply(void*); static void onBtnRefresh(void*); + static bool joinDlgCB(const LLSD& notification, const LLSD& response); + + void reposButton(const std::string& name); + protected: bool apply(LLPanelGroupTab* tab); diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index f3893a104c..d63fd141b0 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -50,7 +50,6 @@ #include "llnamelistctrl.h" #include "llscrolllistitem.h" #include "llspinctrl.h" -#include "llstatusbar.h" // can_afford_transaction() #include "lltextbox.h" #include "lltexteditor.h" #include "lltexturectrl.h" @@ -96,9 +95,6 @@ BOOL LLPanelGroupGeneral::postBuild() { bool recurse = true; - // General info - mGroupNameEditor = getChild<LLLineEditor>("group_name_editor", recurse); - mEditCharter = getChild<LLTextEditor>("charter", recurse); if(mEditCharter) { @@ -194,7 +190,6 @@ BOOL LLPanelGroupGeneral::postBuild() // If the group_id is null, then we are creating a new group if (mGroupID.isNull()) { - mGroupNameEditor->setEnabled(TRUE); mEditCharter->setEnabled(TRUE); mCtrlShowInGroupList->setEnabled(TRUE); @@ -217,6 +212,7 @@ void LLPanelGroupGeneral::setupCtrls(LLPanel* panel_group) mDefaultIconID = mInsignia->getImageAssetID(); } mFounderName = panel_group->getChild<LLNameBox>("founder_name"); + mGroupNameEditor = panel_group->getChild<LLLineEditor>("group_name_editor"); } // static @@ -298,56 +294,6 @@ void LLPanelGroupGeneral::onClickInfo(void *userdata) } // static -void LLPanelGroupGeneral::onClickJoin(void *userdata) -{ - LLPanelGroupGeneral *self = (LLPanelGroupGeneral *)userdata; - - if ( !self ) return; - - lldebugs << "joining group: " << self->mGroupID << llendl; - - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(self->mGroupID); - - if (gdatap) - { - S32 cost = gdatap->mMembershipFee; - LLSD args; - args["COST"] = llformat("%d", cost); - LLSD payload; - payload["group_id"] = self->mGroupID; - - if (can_afford_transaction(cost)) - { - LLNotifications::instance().add("JoinGroupCanAfford", args, payload, LLPanelGroupGeneral::joinDlgCB); - } - else - { - LLNotifications::instance().add("JoinGroupCannotAfford", args, payload); - } - } - else - { - llwarns << "LLGroupMgr::getInstance()->getGroupData(" << self->mGroupID - << ") was NULL" << llendl; - } -} - -// static -bool LLPanelGroupGeneral::joinDlgCB(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - - if (option == 1) - { - // user clicked cancel - return false; - } - - LLGroupMgr::getInstance()->sendGroupMemberJoin(notification["payload"]["group_id"].asUUID()); - return false; -} - -// static void LLPanelGroupGeneral::openProfile(void* data) { LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data; @@ -883,6 +829,8 @@ void LLPanelGroupGeneral::reset() mComboActiveTitle->setVisible(false); mInsignia->setImageAssetID(LLUUID::null); + + mInsignia->setEnabled(true); { std::string empty_str = ""; diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index b828480a12..7e90e43cf9 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -79,7 +79,6 @@ private: static void onCommitUserOnly(LLUICtrl* ctrl, void* data); static void onCommitTitle(LLUICtrl* ctrl, void* data); static void onCommitEnrollment(LLUICtrl* ctrl, void* data); - static void onClickJoin(void* userdata); static void onClickInfo(void* userdata); static void onReceiveNotices(LLUICtrl* ctrl, void* data); static void openProfile(void* data); diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 51cdc5af93..a7590ac1dd 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -82,6 +82,9 @@ void LLPanelIMControlPanel::onShareButtonClicked() void LLPanelIMControlPanel::setID(const LLUUID& avatar_id) { + // Disable "Add friend" button for friends. + childSetEnabled("add_friend_btn", !LLAvatarActions::isFriend(avatar_id)); + getChild<LLAvatarIconCtrl>("avatar_icon")->setValue(avatar_id); } diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 309a97a9f2..6a41b6feb9 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -42,15 +42,18 @@ #include "llpanelpeople.h" // newview +#include "llaccordionctrltab.h" #include "llagent.h" #include "llavataractions.h" #include "llavatarlist.h" +#include "llavatarlistitem.h" #include "llcallingcard.h" // for LLAvatarTracker #include "llfloateravatarpicker.h" //#include "llfloaterminiinspector.h" #include "llfriendcard.h" #include "llgroupactions.h" #include "llgrouplist.h" +#include "llpanelpeoplemenus.h" #include "llrecentpeople.h" #include "llviewercontrol.h" // for gSavedSettings #include "llviewermenu.h" // for gMenuHolder @@ -68,6 +71,27 @@ static const std::string FRIENDS_TAB_NAME = "friends_panel"; static const std::string GROUP_TAB_NAME = "groups_panel"; static const std::string RECENT_TAB_NAME = "recent_panel"; +/** Comparator for comparing avatar items by last interaction date */ +class LLAvatarItemRecentComparator : public LLAvatarItemComparator +{ +public: + LLAvatarItemRecentComparator() {}; + virtual ~LLAvatarItemRecentComparator() {}; + +protected: + virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const + { + LLRecentPeople& people = LLRecentPeople::instance(); + const LLDate& date1 = people.getDate(avatar_item1->getAvatarId()); + const LLDate& date2 = people.getDate(avatar_item2->getAvatarId()); + + //older comes first + return date1 > date2; + } +}; + +static const LLAvatarItemRecentComparator RECENT_COMPARATOR; + static LLRegisterPanelClassWrapper<LLPanelPeople> t_people("panel_people"); //============================================================================= @@ -299,7 +323,7 @@ private: /** * Updates the recent people list (those the agent has recently interacted with). */ -class LLRecentListUpdater : public LLAvatarListUpdater +class LLRecentListUpdater : public LLAvatarListUpdater, public boost::signals2::trackable { LOG_CLASS(LLRecentListUpdater); @@ -398,21 +422,25 @@ LLPanelPeople::~LLPanelPeople() LLView::deleteViewByHandle(mGroupPlusMenuHandle); LLView::deleteViewByHandle(mNearbyViewSortMenuHandle); LLView::deleteViewByHandle(mFriendsViewSortMenuHandle); + LLView::deleteViewByHandle(mGroupsViewSortMenuHandle); LLView::deleteViewByHandle(mRecentViewSortMenuHandle); } -void onAvatarListTmpDoubleClicked(LLAvatarListTmp* list) -{ - LLUUID clicked_id = list->getCurrentID(); - if (clicked_id.isNull()) +void LLPanelPeople::onFriendsAccordionExpandedCollapsed(const LLSD& param, LLAvatarList* avatar_list) +{ + if(!avatar_list) + { + llerrs << "Bad parameter" << llendl; return; + } -#if 0 // SJB: Useful for testing, but not currently functional or to spec - LLAvatarActions::showProfile(clicked_id); -#else // spec says open IM window - LLAvatarActions::startIM(clicked_id); -#endif + bool expanded = param.asBoolean(); + + if(!expanded) + { + avatar_list->resetSelection(); + } } BOOL LLPanelPeople::postBuild() @@ -427,11 +455,22 @@ BOOL LLPanelPeople::postBuild() mOnlineFriendList = getChild<LLPanel>(FRIENDS_TAB_NAME)->getChild<LLAvatarList>("avatars_online"); mAllFriendList = getChild<LLPanel>(FRIENDS_TAB_NAME)->getChild<LLAvatarList>("avatars_all"); + mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online")); + mAllFriendList->setNoItemsCommentText(getString("no_friends")); mNearbyList = getChild<LLPanel>(NEARBY_TAB_NAME)->getChild<LLAvatarList>("avatar_list"); + mNearbyList->setNoItemsCommentText(getString("no_one_near")); + + mRecentList = getChild<LLPanel>(RECENT_TAB_NAME)->getChild<LLAvatarList>("avatar_list"); + mRecentList->setNoItemsCommentText(getString("no_people")); - mRecentList = getChild<LLPanel>(RECENT_TAB_NAME)->getChild<LLAvatarListTmp>("avatar_list"); mGroupList = getChild<LLGroupList>("group_list"); + mGroupList->setNoItemsCommentText(getString("no_groups")); + + mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu); + mRecentList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu); + + mRecentList->setComparator(&RECENT_COMPARATOR); LLPanel* groups_panel = getChild<LLPanel>(GROUP_TAB_NAME); groups_panel->childSetAction("activate_btn", boost::bind(&LLPanelPeople::onActivateButtonClicked, this)); @@ -445,15 +484,24 @@ BOOL LLPanelPeople::postBuild() mOnlineFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mOnlineFriendList)); mAllFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mAllFriendList)); mNearbyList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mNearbyList)); - mRecentList->setDoubleClickCallback(boost::bind(onAvatarListTmpDoubleClicked, mRecentList)); + mRecentList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mRecentList)); + mOnlineFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mOnlineFriendList)); mAllFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mAllFriendList)); mNearbyList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mNearbyList)); - mRecentList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this)); + mRecentList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mRecentList)); mGroupList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onGroupInfoButtonClicked, this)); mGroupList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this)); + LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>("tab_all"); + accordion_tab->setDropDownStateChangedCallback( + boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _2, mAllFriendList)); + + accordion_tab = getChild<LLAccordionCtrlTab>("tab_online"); + accordion_tab->setDropDownStateChangedCallback( + boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _2, mOnlineFriendList)); + buttonSetAction("view_profile_btn", boost::bind(&LLPanelPeople::onViewProfileButtonClicked, this)); buttonSetAction("add_friend_btn", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); buttonSetAction("group_info_btn", boost::bind(&LLPanelPeople::onGroupInfoButtonClicked, this)); @@ -466,6 +514,7 @@ BOOL LLPanelPeople::postBuild() getChild<LLPanel>(NEARBY_TAB_NAME)->childSetAction("nearby_view_sort_btn",boost::bind(&LLPanelPeople::onNearbyViewSortButtonClicked, this)); getChild<LLPanel>(RECENT_TAB_NAME)->childSetAction("recent_viewsort_btn",boost::bind(&LLPanelPeople::onRecentViewSortButtonClicked, this)); getChild<LLPanel>(FRIENDS_TAB_NAME)->childSetAction("friends_viewsort_btn",boost::bind(&LLPanelPeople::onFriendsViewSortButtonClicked, this)); + getChild<LLPanel>(GROUP_TAB_NAME)->childSetAction("groups_viewsort_btn",boost::bind(&LLPanelPeople::onGroupsViewSortButtonClicked, this)); // Must go after setting commit callback and initializing all pointers to children. mTabContainer->selectTabByName(FRIENDS_TAB_NAME); @@ -476,6 +525,7 @@ BOOL LLPanelPeople::postBuild() registrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); registrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2)); registrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2)); + registrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2)); registrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2)); LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); @@ -489,6 +539,10 @@ BOOL LLPanelPeople::postBuild() if(friend_view_sort) mFriendsViewSortMenuHandle = friend_view_sort->getHandle(); + LLMenuGL* group_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_groups_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(group_view_sort) + mGroupsViewSortMenuHandle = group_view_sort->getHandle(); + LLMenuGL* recent_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_recent_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if(recent_view_sort) mRecentViewSortMenuHandle = recent_view_sort->getHandle(); @@ -497,13 +551,28 @@ BOOL LLPanelPeople::postBuild() // Perform initial update. mFriendListUpdater->forceUpdate(); - mRecentListUpdater->forceUpdate(); + mNearbyListUpdater->forceUpdate(); mGroupListUpdater->forceUpdate(); mRecentListUpdater->forceUpdate(); + // call this method in case some list is empty and buttons can be in inconsistent state + updateButtons(); + return TRUE; } +void LLPanelPeople::applyFilterToTab(const std::string& tab_name) +{ + if (tab_name == FRIENDS_TAB_NAME) // this tab has two lists + filterFriendList(); + else if (tab_name == NEARBY_TAB_NAME) + filterNearbyList(); + else if (tab_name == RECENT_TAB_NAME) + filterRecentList(); + else if (tab_name == GROUP_TAB_NAME) + updateGroupList(); +} + bool LLPanelPeople::updateFriendList(U32 changed_mask) { // Refresh names. @@ -570,10 +639,7 @@ bool LLPanelPeople::updateGroupList() return true; // there's no point in further updates bool have_names = mGroupList->update(mFilterSubString); - - if (mGroupList->isEmpty()) - mGroupList->setCommentText(getString("no_groups")); - + updateButtons(); return have_names; } @@ -587,22 +653,15 @@ bool LLPanelPeople::filterFriendList() mOnlineFriendList->update(mOnlineFriendVec, mFilterSubString) & mAllFriendList->update(mAllFriendVec, mFilterSubString); - if (mOnlineFriendVec.size() == 0) - mOnlineFriendList->setCommentText(getString("no_friends_online")); - - if (mAllFriendVec.size() == 0) - mAllFriendList->setCommentText(getString("no_friends")); + updateButtons(); return have_names; } bool LLPanelPeople::filterNearbyList() { bool have_names = mNearbyList->update(mNearbyVec, mFilterSubString); - - if (mNearbyVec.size() == 0) - mNearbyList->setCommentText(getString("no_one_near")); - + updateButtons(); return have_names; } @@ -612,9 +671,11 @@ bool LLPanelPeople::filterRecentList() return true; if (mRecentVec.size() > 0) - return mRecentList->update(mRecentVec, mFilterSubString); - - mRecentList->setCommentText(getString("no_people")); + { + bool updated = mRecentList->update(mRecentVec, mFilterSubString); + updateButtons(); + return updated; + } return true; } @@ -664,12 +725,12 @@ void LLPanelPeople::updateButtons() if (group_tab_active) { - bool item_selected = mGroupList->getFirstSelected() != NULL; + bool item_selected = mGroupList->getSelectedItem() != NULL; bool cur_group_active = true; if (item_selected) { - selected_id = mGroupList->getCurrentID(); + selected_id = mGroupList->getSelectedUUID(); cur_group_active = (gAgent.getGroupID() == selected_id); } @@ -714,20 +775,20 @@ LLUUID LLPanelPeople::getCurrentItemID() const { LLUUID cur_online_friend; - if ((cur_online_friend = mOnlineFriendList->getCurrentID()).notNull()) + if ((cur_online_friend = mOnlineFriendList->getSelectedUUID()).notNull()) return cur_online_friend; - return mAllFriendList->getCurrentID(); + return mAllFriendList->getSelectedUUID(); } if (cur_tab == NEARBY_TAB_NAME) - return mNearbyList->getCurrentID(); + return mNearbyList->getSelectedUUID(); if (cur_tab == RECENT_TAB_NAME) - return mRecentList->getCurrentID(); + return mRecentList->getSelectedUUID(); if (cur_tab == GROUP_TAB_NAME) - return mGroupList->getCurrentID(); + return mGroupList->getSelectedUUID(); llassert(0 && "unknown tab selected"); return LLUUID::null; @@ -785,20 +846,16 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string) LLStringUtil::toUpper(mFilterSubString); LLStringUtil::trimHead(mFilterSubString); - // Apply new filter to all tabs. - filterNearbyList(); - filterFriendList(); - filterRecentList(); - updateGroupList(); - - updateButtons(); + // Apply new filter to current tab. + applyFilterToTab(getActiveTabName()); } void LLPanelPeople::onTabSelected(const LLSD& param) { std::string tab_name = getChild<LLPanel>(param.asString())->getName(); mNearbyListUpdater->setActive(tab_name == NEARBY_TAB_NAME); - updateButtons(); + applyFilterToTab(tab_name); + // No need to call updateButtons() because applyFilterToTab() does that. if (GROUP_TAB_NAME == tab_name) mFilterEditor->setLabel(getString("groups_filter_label")); @@ -808,7 +865,7 @@ void LLPanelPeople::onTabSelected(const LLSD& param) void LLPanelPeople::onAvatarListDoubleClicked(LLAvatarList* list) { - LLUUID clicked_id = list->getCurrentID(); + LLUUID clicked_id = list->getSelectedUUID(); if (clicked_id.isNull()) return; @@ -826,9 +883,9 @@ void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list) if (getActiveTabName() == FRIENDS_TAB_NAME) { if (list == mOnlineFriendList) - mAllFriendList->deselectAllItems(TRUE); + mAllFriendList->resetSelection(true); else if (list == mAllFriendList) - mOnlineFriendList->deselectAllItems(TRUE); + mOnlineFriendList->resetSelection(true); else llassert(0 && "commit on unknown friends list"); } @@ -890,7 +947,7 @@ void LLPanelPeople::onImButtonClicked() void LLPanelPeople::onActivateButtonClicked() { - LLGroupActions::activate(mGroupList->getCurrentID()); + LLGroupActions::activate(mGroupList->getSelectedUUID()); } // static @@ -909,7 +966,7 @@ bool LLPanelPeople::onFriendListUpdate(U32 changed_mask) // Update online status in the Recent tab. // *TODO: isn't it too much to update the whole list? - updateRecentList(); +// updateRecentList(); // mantipov: seems online status should be supported by LLAvatarListItem itself. return have_names; } @@ -957,6 +1014,17 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) { } } + +void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata) +{ + std::string chosen_item = userdata.asString(); + + if (chosen_item == "show_icons") + { + mGroupList->toggleIcons(); + } +} + void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) { std::string chosen_item = userdata.asString(); @@ -966,6 +1034,7 @@ void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) } else if (chosen_item == "sort_name") { + mNearbyList->sortByName(); } else if (chosen_item == "view_icons") { @@ -978,11 +1047,14 @@ void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata) { std::string chosen_item = userdata.asString(); - if (chosen_item == "sort_most") + if (chosen_item == "sort_recent") { - } + mRecentList->setComparator(&RECENT_COMPARATOR); + mRecentList->sort(); + } else if (chosen_item == "sort_name") { + mRecentList->sortByName(); } else if (chosen_item == "view_icons") { @@ -1008,6 +1080,7 @@ void LLPanelPeople::onMoreButtonClicked() { // *TODO: not implemented yet } + void LLPanelPeople::onFriendsViewSortButtonClicked() { LLMenuGL* menu = (LLMenuGL*)mFriendsViewSortMenuHandle.get(); @@ -1015,6 +1088,15 @@ void LLPanelPeople::onFriendsViewSortButtonClicked() return; showGroupMenu(menu); } + +void LLPanelPeople::onGroupsViewSortButtonClicked() +{ + LLMenuGL* menu = (LLMenuGL*)mGroupsViewSortMenuHandle.get(); + if (!menu) + return; + showGroupMenu(menu); +} + void LLPanelPeople::onRecentViewSortButtonClicked() { LLMenuGL* menu = (LLMenuGL*)mRecentViewSortMenuHandle.get(); @@ -1022,6 +1104,7 @@ void LLPanelPeople::onRecentViewSortButtonClicked() return; showGroupMenu(menu); } + void LLPanelPeople::onNearbyViewSortButtonClicked() { LLMenuGL* menu = (LLMenuGL*)mNearbyViewSortMenuHandle.get(); @@ -1041,4 +1124,3 @@ void LLPanelPeople::onOpen(const LLSD& key) else reSelectedCurrentTab(); } - diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index c0c2f70614..8cd3cc7feb 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -40,7 +40,6 @@ class LLFilterEditor; class LLTabContainer; class LLAvatarList; -class LLAvatarListTmp; class LLGroupList; class LLPanelPeople : public LLPanel @@ -67,6 +66,7 @@ private: bool filterFriendList(); bool filterNearbyList(); bool filterRecentList(); + void applyFilterToTab(const std::string& tab_name); void updateButtons(); const std::string& getActiveTabName() const; LLUUID getCurrentItemID() const; @@ -97,6 +97,7 @@ private: void onRecentViewSortButtonClicked(); void onNearbyViewSortButtonClicked(); void onFriendsViewSortButtonClicked(); + void onGroupsViewSortButtonClicked(); void onAvatarListDoubleClicked(LLAvatarList* list); void onAvatarListCommitted(LLAvatarList* list); void onGroupPlusButtonClicked(); @@ -105,6 +106,7 @@ private: void onFriendsViewSortMenuItemClicked(const LLSD& userdata); void onNearbyViewSortMenuItemClicked(const LLSD& userdata); + void onGroupsViewSortMenuItemClicked(const LLSD& userdata); void onRecentViewSortMenuItemClicked(const LLSD& userdata); // misc callbacks @@ -114,17 +116,20 @@ private: const std::vector<LLUUID>& ids, void*); + void onFriendsAccordionExpandedCollapsed(const LLSD& param, LLAvatarList* avatar_list); + LLFilterEditor* mFilterEditor; LLTabContainer* mTabContainer; LLAvatarList* mOnlineFriendList; LLAvatarList* mAllFriendList; LLAvatarList* mNearbyList; - LLAvatarListTmp* mRecentList; + LLAvatarList* mRecentList; LLGroupList* mGroupList; LLHandle<LLView> mGroupPlusMenuHandle; LLHandle<LLView> mNearbyViewSortMenuHandle; LLHandle<LLView> mFriendsViewSortMenuHandle; + LLHandle<LLView> mGroupsViewSortMenuHandle; LLHandle<LLView> mRecentViewSortMenuHandle; Updater* mFriendListUpdater; diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp new file mode 100644 index 0000000000..0e88058bb1 --- /dev/null +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -0,0 +1,148 @@ +/** + * @file llpanelpeoplemenus.h + * @brief Menus used by the side tray "People" 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" + +// libs +#include "llmenugl.h" +#include "lluictrlfactory.h" + +#include "llpanelpeoplemenus.h" + +// newview +#include "llagentdata.h" // for gAgentID +#include "llavataractions.h" +#include "llviewermenu.h" // for gMenuHolder + +namespace LLPanelPeopleMenus +{ + +NearbyMenu gNearbyMenu; + +//== ContextMenu ============================================================== + +ContextMenu::ContextMenu() +: mMenu(NULL) +{ +} + +void ContextMenu::show(LLView* spawning_view, const LLUUID& id, S32 x, S32 y) +{ + if (mMenu) + { + //preventing parent (menu holder) from deleting already "dead" context menus on exit + LLView* parent = mMenu->getParent(); + if (parent) + { + parent->removeChild(mMenu); + mMenu->setParent(NULL); + } + delete mMenu; + } + + mID = id; + mMenu = createMenu(); + mMenu->show(x, y); + LLMenuGL::showPopup(spawning_view, mMenu, x, y); +} + +//== NearbyMenu =============================================================== + +LLContextMenu* NearbyMenu::createMenu() +{ + // set up the callbacks for all of the avatar menu items + // (N.B. callbacks don't take const refs as mID is local scope) + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("Avatar.Profile", boost::bind(&LLAvatarActions::showProfile, mID)); + registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, mID)); + registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startIM, mID)); + registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startIM, mID)); // *TODO: unimplemented + registrar.add("Avatar.OfferTeleport", boost::bind(&NearbyMenu::offerTeleport, this)); + registrar.add("Avatar.ShowOnMap", boost::bind(&LLAvatarActions::startIM, mID)); // *TODO: unimplemented + registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::startIM, mID)); // *TODO: unimplemented + registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, mID)); + registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, mID)); + + enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2)); + enable_registrar.add("Avatar.CheckItem", boost::bind(&NearbyMenu::checkContextMenuItem, this, _2)); + + // create the context menu from the XUI + return LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>( + "menu_people_nearby.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); +} + +bool NearbyMenu::enableContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == std::string("can_block")) + { + std::string firstname, lastname; + gCacheName->getName(mID, firstname, lastname); + bool is_linden = !LLStringUtil::compareStrings(lastname, "Linden"); + bool is_self = mID == gAgentID; + return !is_self && !is_linden; + } + else if (item == std::string("can_add")) + { + return !LLAvatarActions::isFriend(mID); + } + else if (item == std::string("can_delete")) + { + return LLAvatarActions::isFriend(mID); + } + + return false; +} + +bool NearbyMenu::checkContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == std::string("is_blocked")) + { + return LLAvatarActions::isBlocked(mID); + } + + return false; +} + +void NearbyMenu::offerTeleport() +{ + // boost::bind cannot recognize overloaded method LLAvatarActions::offerTeleport(), + // so we have to use a wrapper. + LLAvatarActions::offerTeleport(mID); +} + +} // namespace LLPanelPeopleMenus diff --git a/indra/newview/llpanelpeoplemenus.h b/indra/newview/llpanelpeoplemenus.h new file mode 100644 index 0000000000..0012ac38f8 --- /dev/null +++ b/indra/newview/llpanelpeoplemenus.h @@ -0,0 +1,82 @@ +/** + * @file llpanelpeoplemenus.h + * @brief Menus used by the side tray "People" 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_LLPANELPEOPLEMENUS_H +#define LL_LLPANELPEOPLEMENUS_H + +#include "llavatarlistitem.h" + +namespace LLPanelPeopleMenus +{ + +/** + * Base context menu. + */ +class ContextMenu : public LLAvatarListItem::ContextMenu +{ +public: + ContextMenu(); + virtual ~ContextMenu() {} + + /** + * Show the menu at specified coordinates. + * + * @param id either avatar or group id + */ + /*virtual*/ void show(LLView* spawning_view, const LLUUID& id, S32 x, S32 y); + +protected: + + virtual LLContextMenu* createMenu() = 0; + + LLUUID mID; + LLContextMenu* mMenu; +}; + +/** + * Menu used in the nearby people list. + */ +class NearbyMenu : public ContextMenu +{ +public: + /*virtual*/ LLContextMenu* createMenu(); +private: + bool enableContextMenuItem(const LLSD& userdata); + bool checkContextMenuItem(const LLSD& userdata); + void offerTeleport(); +}; + +extern NearbyMenu gNearbyMenu; + +} // namespace LLPanelPeopleMenus + +#endif // LL_LLPANELPEOPLEMENUS_H diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index 9ae58d1cb6..42185d28e5 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -64,6 +64,7 @@ #define LABEL_PICK = "Pick" #define LABEL_CHANGES = "Changes" +std::string SET_LOCATION_NOTICE("(will update after save)"); LLPanelPick::LLPanelPick(BOOL edit_mode/* = FALSE */) : LLPanel(), LLAvatarPropertiesObserver(), LLRemoteParcelInfoObserver(), @@ -71,7 +72,8 @@ LLPanelPick::LLPanelPick(BOOL edit_mode/* = FALSE */) mSnapshotCtrl(NULL), mPickId(LLUUID::null), mCreatorId(LLUUID::null), - mDataReceived(FALSE) + mDataReceived(FALSE), + mIsPickNew(false) { if (edit_mode) { @@ -171,7 +173,7 @@ void LLPanelPick::init(LLPickData *pick_data) setPickName(pick_data->name); setPickDesc(pick_data->desc); - setPickLocation(pick_data->location_text); + mSnapshotCtrl->setImageAssetID(pick_data->snapshot_id); //*HACK see reset() where the texture control was set to FALSE @@ -180,27 +182,45 @@ void LLPanelPick::init(LLPickData *pick_data) mPosGlobal = pick_data->pos_global; mSimName = pick_data->sim_name; mParcelId = pick_data->parcel_id; + + setPickLocation(createLocationText(pick_data->user_name, pick_data->original_name, + pick_data->sim_name, pick_data->pos_global)); } -// Fill in some reasonable defaults for a new pick. -void LLPanelPick::createNewPick() +void LLPanelPick::prepareNewPick(const LLVector3d pos_global, + const std::string& name, + const std::string& desc, + const LLUUID& snapshot_id, + const LLUUID& parcel_id) { mPickId.generate(); mCreatorId = gAgent.getID(); - mPosGlobal = gAgent.getPositionGlobal(); + mPosGlobal = pos_global; + setPickName(name); + setPickDesc(desc); + mSnapshotCtrl->setImageAssetID(snapshot_id); + mParcelId = parcel_id; + + setPickLocation(createLocationText(std::string(""), SET_LOCATION_NOTICE, name, pos_global)); + childSetLabelArg(XML_BTN_SAVE, SAVE_BTN_LABEL, std::string("Pick")); + + mIsPickNew = true; +} + +// Fill in some reasonable defaults for a new pick. +void LLPanelPick::prepareNewPick() +{ // Try to fill in the current parcel LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); if (parcel) { - setPickName(parcel->getName()); - setPickDesc(parcel->getDesc()); - mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID()); + prepareNewPick(gAgent.getPositionGlobal(), + parcel->getName(), + parcel->getDesc(), + parcel->getSnapshotID(), + parcel->getID()); } - - sendUpdate(); - - childSetLabelArg(XML_BTN_SAVE, SAVE_BTN_LABEL, std::string("Pick")); } /*virtual*/ void LLPanelPick::processProperties(void* data, EAvatarProcessorType type) @@ -235,6 +255,15 @@ void LLPanelPick::setEditMode( BOOL edit_mode ) deleteAllChildren(); + // *WORKAROUND: for EXT-931. Children are created for both XML_PANEL_EDIT_PICK & XML_PANEL_PICK_INFO files + // The reason is in LLPanel::initPanelXML called from the LLUICtrlFactory::buildPanel(). + // It creates children from the xml file stored while previous initializing in the "mXMLFilename" member + // and then in creates children from the parameters passed from the LLUICtrlFactory::buildPanel(). + // Xml filename is stored after LLPanel::initPanelXML is called (added with export-from-ll/viewer-2-0, r1594 into LLUICtrlFactory::buildPanel & LLUICtrlFactory::buildFloater) + // In case panel creates children from the different xml files they appear from both files. + // So, let clear xml filename related to this instance. + setXMLFilename(""); + if (edit_mode) { LLUICtrlFactory::getInstance()->buildPanel(this, XML_PANEL_EDIT_PICK); @@ -258,6 +287,40 @@ void LLPanelPick::setEditMode( BOOL edit_mode ) updateButtons(); } +////////////////////////////////////////////////////////////////////////// +// PROTECTED AREA +////////////////////////////////////////////////////////////////////////// + +//static +std::string LLPanelPick::createLocationText(const std::string& owner_name, const std::string& original_name, + const std::string& sim_name, const LLVector3d& pos_global) +{ + std::string location_text; + location_text.append(owner_name); + if (!original_name.empty()) + { + if (!location_text.empty()) location_text.append(", "); + location_text.append(original_name); + + } + if (!sim_name.empty()) + { + if (!location_text.empty()) location_text.append(", "); + location_text.append(sim_name); + } + + if (!location_text.empty()) location_text.append(" "); + + if (!pos_global.isNull()) + { + S32 region_x = llround((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = llround((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = llround((F32)pos_global.mdV[VZ]); + location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); + } + return location_text; +} + void LLPanelPick::setPickName(std::string name) { if (mEditMode) @@ -288,7 +351,7 @@ void LLPanelPick::setPickDesc(std::string desc) mDesc = desc; } -void LLPanelPick::setPickLocation(std::string location) +void LLPanelPick::setPickLocation(const std::string& location) { childSetWrappedText(XML_LOCATION, location); @@ -375,6 +438,12 @@ void LLPanelPick::onClickCancel() { if (!mEditMode) return; + if (mIsPickNew) + { + mBackCb(this, LLSD()); + return; + } + LLUUID pick_id = mPickId; LLUUID creator_id = mCreatorId; reset(); @@ -385,29 +454,34 @@ void LLPanelPick::onClickCancel() void LLPanelPick::onClickSet() { if (!mEditMode) return; - if (!mDataReceived) return; + if (!mIsPickNew && !mDataReceived) return; // Save location for later. mPosGlobal = gAgent.getPositionGlobal(); - S32 region_x = llround((F32)mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)mPosGlobal.mdV[VZ]); - - std::string location_text = "(will update after save), "; - location_text.append(mSimName); - location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); - - setPickLocation(location_text); + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel) + { + mParcelId = parcel->getID(); + mSimName = parcel->getName(); + } + setPickLocation(createLocationText(std::string(""), SET_LOCATION_NOTICE, mSimName, mPosGlobal)); } // static void LLPanelPick::onClickSave() { if (!mEditMode) return; - if (!mDataReceived) return; + if (!mIsPickNew && !mDataReceived) return; sendUpdate(); + + if (mIsPickNew) + { + mBackCb(this, LLSD()); + return; + } + setEditMode(FALSE); } diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h index 2cd4706dfe..7ce58b59af 100644 --- a/indra/newview/llpanelpick.h +++ b/indra/newview/llpanelpick.h @@ -56,9 +56,14 @@ public: /*virtual*/ BOOL postBuild(); - // Create a new pick, including creating an id, giving a sane - // initial position, etc. - void createNewPick(); + // Prepares a new pick, including creating an id, giving a sane + // initial position, etc (saved on clicking Save Pick button - onClickSave callback). + void prepareNewPick(); + void prepareNewPick(const LLVector3d pos_global, + const std::string& name, + const std::string& desc, + const LLUUID& snapshot_id, + const LLUUID& parcel_id); //initializes the panel with data of the pick with id = pick_id //owned by the avatar with id = creator_id @@ -87,9 +92,16 @@ public: protected: + /** + * "Location text" is actually the owner name, the original + * name that owner gave the parcel, and the location. + */ + static std::string createLocationText(const std::string& owner_name, const std::string& original_name, + const std::string& sim_name, const LLVector3d& pos_global); + void setPickName(std::string name); void setPickDesc(std::string desc); - void setPickLocation(std::string location); + void setPickLocation(const std::string& location); std::string getPickName(); std::string getPickDesc(); @@ -120,6 +132,7 @@ protected: BOOL mEditMode; LLTextureCtrl* mSnapshotCtrl; BOOL mDataReceived; + bool mIsPickNew; LLUUID mPickId; LLUUID mCreatorId; diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index d374d24316..93317e613f 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -34,6 +34,7 @@ #include "llagent.h" #include "llavatarconstants.h" +#include "llflatlistview.h" #include "lltexturectrl.h" #include "llviewergenericmessage.h" // send_generic_message #include "llmenugl.h" @@ -45,8 +46,6 @@ #include "llpanelavatar.h" #include "llpanelprofile.h" #include "llpanelpick.h" -#include "llscrollcontainer.h" -#include "lllistctrl.h" static const std::string XML_BTN_NEW = "new_btn"; static const std::string XML_BTN_DELETE = "trash_btn"; @@ -148,7 +147,7 @@ LLPickItem* LLPanelPicks::getSelectedPickItem() BOOL LLPanelPicks::postBuild() { - mPicksList = getChild<LLListCtrl>("picks_list"); + mPicksList = getChild<LLFlatListView>("picks_list"); childSetAction(XML_BTN_DELETE, boost::bind(&LLPanelPicks::onClickDelete, this)); @@ -199,6 +198,11 @@ void LLPanelPicks::onOpen(const LLSD& key) mPopupMenu->setItemVisible("pick_separator", TRUE); } + if(getAvatarId() != key.asUUID()) + { + mPicksList->goToTop(); + } + LLPanelProfileTab::onOpen(key); } @@ -314,7 +318,7 @@ void LLPanelPicks::onClickNew() { buildPickPanel(); mPickPanel->setEditMode(TRUE); - mPickPanel->createNewPick(); + mPickPanel->prepareNewPick(); getProfilePanel()->togglePanel(mPickPanel); } @@ -386,7 +390,6 @@ void LLPickItem::init(LLPickData* pick_data) setPickDesc(pick_data->desc); setSnapshotId(pick_data->snapshot_id); mPosGlobal = pick_data->pos_global; - mLocation = pick_data->location_text; LLTextureCtrl* picture = getChild<LLTextureCtrl>("picture"); picture->setImageAssetID(pick_data->snapshot_id); @@ -434,11 +437,6 @@ const LLVector3d& LLPickItem::getPosGlobal() return mPosGlobal; } -const std::string& LLPickItem::getLocation() -{ - return mLocation; -} - const std::string LLPickItem::getDescription() { return childGetValue("picture_descr").asString(); diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h index 97e8e607c8..27a21305b3 100644 --- a/indra/newview/llpanelpicks.h +++ b/indra/newview/llpanelpicks.h @@ -48,7 +48,7 @@ class LLPanelPick; class LLAgent; class LLMenuGL; class LLPickItem; -class LLListCtrl; +class LLFlatListView; class LLPanelPicks : public LLPanelProfileTab @@ -107,7 +107,7 @@ private: LLMenuGL* mPopupMenu; LLPanelProfile* mProfilePanel; LLPanelPick* mPickPanel; - LLListCtrl* mPicksList; + LLFlatListView* mPicksList; }; class LLPickItem : public LLPanel, public LLAvatarPropertiesObserver @@ -142,8 +142,6 @@ public: const LLVector3d& getPosGlobal(); - const std::string& getLocation(); - const std::string getDescription(); /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); @@ -167,7 +165,6 @@ protected: bool mNeedData; std::string mPickName; - std::string mLocation; }; #endif // LL_LLPANELPICKS_H diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index 7a19b8877e..eb269fabe3 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -44,6 +44,7 @@ #include "llqueryflags.h" #include "llbutton.h" +#include "lliconctrl.h" #include "lllineeditor.h" #include "llscrollcontainer.h" #include "lltextbox.h" @@ -55,6 +56,7 @@ #include "llfloaterworldmap.h" #include "llinventorymodel.h" #include "lllandmarkactions.h" +#include "llpanelpick.h" #include "lltexturectrl.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" @@ -68,6 +70,7 @@ LLPanelPlaceInfo::LLPanelPlaceInfo() : LLPanel(), mParcelID(), mRequestedID(), + mPosRegion(), mLandmarkID(), mMinHeight(0), mScrollingPanel(NULL), @@ -88,6 +91,8 @@ BOOL LLPanelPlaceInfo::postBuild() mTitle = getChild<LLTextBox>("panel_title"); mCurrentTitle = mTitle->getText(); + mForSaleIcon = getChild<LLIconCtrl>("icon_for_sale"); + // Since this is only used in the directory browser, always // disable the snapshot control. Otherwise clicking on it will // open a texture picker. @@ -251,6 +256,8 @@ void LLPanelPlaceInfo::resetLocation() mParcelID.setNull(); mRequestedID.setNull(); mLandmarkID.setNull(); + mPosRegion.clearVec(); + mForSaleIcon->setVisible(FALSE); std::string not_available = getString("not_available"); mMaturityRatingText->setValue(not_available); mParcelOwner->setValue(not_available); @@ -449,12 +456,27 @@ void LLPanelPlaceInfo::processParcelInfo(const LLParcelData& parcel_data) //update for_sale banner, here we should use DFQ_FOR_SALE instead of PF_FOR_SALE //because we deal with remote parcel response format - bool isForSale = (parcel_data.flags & DFQ_FOR_SALE)? TRUE : FALSE; - getChild<LLIconCtrl>("icon_for_sale")->setVisible(isForSale); + bool isForSale = (parcel_data.flags & DFQ_FOR_SALE) && + mInfoType == AGENT ? TRUE : FALSE; + mForSaleIcon->setVisible(isForSale); + + S32 region_x; + S32 region_y; + S32 region_z; - S32 region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS; - S32 region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS; - S32 region_z = llround(parcel_data.global_z); + // If the region position is zero, grab position from the global + if(mPosRegion.isExactlyZero()) + { + region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS; + region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS; + region_z = llround(parcel_data.global_z); + } + else + { + region_x = llround(mPosRegion.mV[VX]); + region_y = llround(mPosRegion.mV[VY]); + region_z = llround(mPosRegion.mV[VZ]); + } std::string name = getString("not_available"); if (!parcel_data.sim_name.empty()) @@ -487,15 +509,15 @@ void LLPanelPlaceInfo::displayParcelInfo(const LLUUID& region_id, if (!region) return; + mPosRegion.setVec((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()) { - F32 region_x = (F32)fmod(pos_global.mdV[VX], (F64)REGION_WIDTH_METERS); - F32 region_y = (F32)fmod(pos_global.mdV[VY], (F64)REGION_WIDTH_METERS); - LLVector3 pos_region(region_x, region_y, (F32)pos_global.mdV[VZ]); - - body["location"] = ll_sd_from_vector3(pos_region); + body["location"] = ll_sd_from_vector3(mPosRegion); if (!region_id.isNull()) { body["region_id"] = region_id; @@ -536,20 +558,22 @@ void LLPanelPlaceInfo::displaySelectedParcelInfo(LLParcel* parcel, { case SIM_ACCESS_MATURE: parcel_data.flags = 0x1; + break; case SIM_ACCESS_ADULT: parcel_data.flags = 0x2; + break; default: parcel_data.flags = 0; } parcel_data.desc = parcel->getDesc(); parcel_data.name = parcel->getName(); - parcel_data.sim_name = gAgent.getRegion()->getName(); + parcel_data.sim_name = region->getName(); parcel_data.snapshot_id = parcel->getSnapshotID(); - parcel_data.global_x = pos_global.mdV[0]; - parcel_data.global_y = pos_global.mdV[1]; - parcel_data.global_z = pos_global.mdV[2]; + parcel_data.global_x = pos_global.mdV[VX]; + parcel_data.global_y = pos_global.mdV[VY]; + parcel_data.global_z = pos_global.mdV[VZ]; std::string on = getString("on"); std::string off = getString("off"); @@ -853,30 +877,19 @@ void LLPanelPlaceInfo::createLandmark(const LLUUID& folder_id) folder_id.notNull() ? folder_id : gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK)); } -void LLPanelPlaceInfo::createPick(const LLVector3d& global_pos) +void LLPanelPlaceInfo::createPick(const LLVector3d& pos_global, LLPanelPick* pick_panel) { - LLPickData pick_data; - - pick_data.agent_id = gAgent.getID(); - pick_data.session_id = gAgent.getSessionID(); - pick_data.pick_id = LLUUID::generateNewID(); - pick_data.creator_id = gAgentID; - - //legacy var needs to be deleted - pick_data.top_pick = FALSE; - pick_data.parcel_id = mParcelID; - pick_data.name = mParcelName->getText(); - if (pick_data.name.empty()) + std::string name = mParcelName->getText(); + if (name.empty()) { - pick_data.name = mRegionName->getText(); + name = mRegionName->getText(); } - pick_data.desc = mDescEditor->getText(); - pick_data.snapshot_id = mSnapshotCtrl->getImageAssetID(); - pick_data.pos_global = global_pos; - pick_data.sort_order = 0; - pick_data.enabled = TRUE; - LLAvatarPropertiesProcessor::instance().sendPickInfoUpdate(&pick_data); + pick_panel->prepareNewPick(pos_global, + name, + mDescEditor->getText(), + mSnapshotCtrl->getImageAssetID(), + mParcelID); } // virtual diff --git a/indra/newview/llpanelplaceinfo.h b/indra/newview/llpanelplaceinfo.h index 32ae4334aa..23a845bc20 100644 --- a/indra/newview/llpanelplaceinfo.h +++ b/indra/newview/llpanelplaceinfo.h @@ -38,15 +38,15 @@ #include "v3dmath.h" #include "lluuid.h" -#include "lliconctrl.h" - #include "llpanelmedia.h" #include "llremoteparcelrequest.h" class LLButton; class LLInventoryItem; class LLLineEditor; +class LLPanelPick; class LLParcel; +class LLIconCtrl; class LLTextBox; class LLTextEditor; class LLTextureCtrl; @@ -88,7 +88,7 @@ public: // Create a pick for the location specified // by global_pos. - void createPick(const LLVector3d& global_pos); + void createPick(const LLVector3d& pos_global, LLPanelPick* pick_panel); BOOL isMediaPanelVisible(); void toggleMediaPanel(BOOL visible); @@ -133,11 +133,13 @@ private: LLUUID mParcelID; LLUUID mRequestedID; LLUUID mLandmarkID; + LLVector3 mPosRegion; std::string mCurrentTitle; S32 mMinHeight; INFO_TYPE mInfoType; LLTextBox* mTitle; + LLIconCtrl* mForSaleIcon; LLTextureCtrl* mSnapshotCtrl; LLTextBox* mRegionName; LLTextBox* mParcelName; diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 11ddc3dd9a..4e070df7eb 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -48,14 +48,15 @@ #include "lluictrlfactory.h" #include "llagent.h" +#include "llavatarpropertiesprocessor.h" #include "llfloaterworldmap.h" #include "llinventorymodel.h" #include "lllandmarkactions.h" #include "lllandmarklist.h" #include "llpanelplaceinfo.h" #include "llpanellandmarks.h" +#include "llpanelpick.h" #include "llpanelteleporthistory.h" -#include "llsidetray.h" #include "llteleporthistorystorage.h" #include "lltoggleablemenu.h" #include "llviewerinventory.h" @@ -72,10 +73,12 @@ static const std::string REMOTE_PLACE_INFO_TYPE = "remote_place"; static const std::string TELEPORT_HISTORY_INFO_TYPE = "teleport_history"; // Helper functions +static bool is_agent_in_selected_parcel(LLParcel* parcel); static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right); static std::string getFullFolderName(const LLViewerInventoryCategory* cat); static void collectLandmarkFolders(LLInventoryModel::cat_array_t& cats); static void onSLURLBuilt(std::string& slurl); +static void setAllChildrenVisible(LLView* view, BOOL visible); //Observer classes class LLPlacesParcelObserver : public LLParcelObserver @@ -118,6 +121,7 @@ LLPanelPlaces::LLPanelPlaces() mActivePanel(NULL), mFilterEditor(NULL), mPlaceInfo(NULL), + mPickPanel(NULL), mItem(NULL), mPlaceMenu(NULL), mLandmarkMenu(NULL), @@ -332,6 +336,7 @@ void LLPanelPlaces::onFilterEdit(const std::string& search_string) LLStringUtil::toUpper(mFilterSubString); LLStringUtil::trimHead(mFilterSubString); + if (mActivePanel) mActivePanel->onSearchEdit(mFilterSubString); } } @@ -380,6 +385,7 @@ void LLPanelPlaces::onTeleportButtonClicked() } else { + if (mActivePanel) mActivePanel->onTeleport(); } } @@ -425,6 +431,7 @@ void LLPanelPlaces::onShowOnMapButtonClicked() } else { + if (mActivePanel) mActivePanel->onShowOnMap(); } } @@ -496,10 +503,23 @@ void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param) { if (!mPlaceInfo) return; - - mPlaceInfo->createPick(mPosGlobal); - onBackButtonClicked(); + if (mPickPanel == NULL) + { + mPickPanel = new LLPanelPick(); + addChild(mPickPanel); + + mPickPanel->setExitCallback(boost::bind(&LLPanelPlaces::togglePickPanel, this, FALSE)); + } + + togglePickPanel(TRUE); + + LLRect rect = getRect(); + mPickPanel->reshape(rect.getWidth(), rect.getHeight()); + mPickPanel->setRect(rect); + mPickPanel->setEditMode(TRUE); + + mPlaceInfo->createPick(mPosGlobal, mPickPanel); } } @@ -508,6 +528,12 @@ void LLPanelPlaces::onCreateLandmarkButtonClicked(const LLUUID& folder_id) if (!mPlaceInfo) return; + // To prevent creating duplicate landmarks + // disable landmark creating buttons until + // the information on existing landmarks is reloaded. + mCreateLandmarkBtn->setEnabled(FALSE); + mFolderMenuBtn->setEnabled(FALSE); + mPlaceInfo->createLandmark(folder_id); } @@ -544,6 +570,14 @@ void LLPanelPlaces::toggleMediaPanel() onOpen(LLSD().insert("type", AGENT_INFO_TYPE)); } +void LLPanelPlaces::togglePickPanel(BOOL visible) +{ + setAllChildrenVisible(this, !visible); + + if (mPickPanel) + mPickPanel->setVisible(visible); +} + void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible) { if (!mPlaceInfo) @@ -568,16 +602,16 @@ void LLPanelPlaces::changedParcelSelection() if (!mPlaceInfo) return; - mParcel = LLViewerParcelMgr::getInstance()->getFloatingParcelSelection(); + LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); + mParcel = parcel_mgr->getFloatingParcelSelection(); LLParcel* parcel = mParcel->getParcel(); - LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); + LLViewerRegion* region = parcel_mgr->getSelectionRegion(); if (!region || !parcel) return; // If agent is inside the selected parcel show agent's region<X, Y, Z>, // otherwise show region<X, Y, Z> of agent's selection point. - if (region == gAgent.getRegion() && - parcel->getLocalID() == LLViewerParcelMgr::getInstance()->getAgentParcel()->getLocalID()) + if (is_agent_in_selected_parcel(parcel)) { mPosGlobal = gAgent.getPositionGlobal(); } @@ -629,6 +663,10 @@ void LLPanelPlaces::changedInventory(U32 mask) mActivePanel = dynamic_cast<LLPanelPlacesTab*>(mTabContainer->getCurrentPanel()); + // Filter applied to show all items. + if (mActivePanel) + mActivePanel->onSearchEdit(mFilterSubString); + // we don't need to monitor inventory changes anymore, // so remove the observer gInventory.removeObserver(mInventoryObserver); @@ -658,7 +696,7 @@ void LLPanelPlaces::updateVerbs() bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE; bool is_create_landmark_visible = mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE; bool is_media_panel_visible = mPlaceInfo->isMediaPanelVisible(); - + mTeleportBtn->setVisible(!is_create_landmark_visible); mShareBtn->setVisible(!is_create_landmark_visible); mCreateLandmarkBtn->setVisible(is_create_landmark_visible); @@ -679,10 +717,11 @@ void LLPanelPlaces::updateVerbs() else if (is_create_landmark_visible) { // Enable "Create Landmark" only if there is no landmark - // for the current parcel. - bool no_landmark = !LLLandmarkActions::landmarkAlreadyExists(); - mCreateLandmarkBtn->setEnabled(no_landmark); - mFolderMenuBtn->setEnabled(no_landmark); + // for the current parcel and agent is inside it. + bool enable = !LLLandmarkActions::landmarkAlreadyExists() && + is_agent_in_selected_parcel(mParcel->getParcel()); + mCreateLandmarkBtn->setEnabled(enable); + mFolderMenuBtn->setEnabled(enable); } else if (mPlaceInfoType == LANDMARK_INFO_TYPE || mPlaceInfoType == REMOTE_PLACE_INFO_TYPE) { @@ -693,6 +732,7 @@ void LLPanelPlaces::updateVerbs() } else { + if (mActivePanel) mActivePanel->updateVerbs(); } } @@ -822,6 +862,18 @@ void LLPanelPlaces::showLandmarkFoldersMenu() LLMenuGL::showPopup(this, menu, btn_rect.mRight, btn_rect.mTop); } +static bool is_agent_in_selected_parcel(LLParcel* parcel) +{ + LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); + + LLViewerRegion* region = parcel_mgr->getSelectionRegion(); + if (!region || !parcel) + return false; + + return region == gAgent.getRegion() && + parcel->getLocalID() == parcel_mgr->getAgentParcel()->getLocalID(); +} + static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right) { return left.second < right.second; @@ -895,3 +947,16 @@ static void onSLURLBuilt(std::string& slurl) LLNotifications::instance().add("CopySLURL", args); } + +static void setAllChildrenVisible(LLView* view, BOOL visible) +{ + const LLView::child_list_t* children = view->getChildList(); + for (LLView::child_list_const_iter_t child_it = children->begin(); child_it != children->end(); ++child_it) + { + LLView* child = *child_it; + if (child->getParent() == view) + { + child->setVisible(visible); + } + } +} diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index 54bc2b9003..f208e91237 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -37,6 +37,7 @@ class LLInventoryItem; class LLFilterEditor; class LLLandmark; +class LLPanelPick; class LLPanelPlaceInfo; class LLPanelPlacesTab; class LLParcelSelection; @@ -77,6 +78,7 @@ private: void onBackButtonClicked(); void toggleMediaPanel(); + void togglePickPanel(BOOL visible); void togglePlaceInfoPanel(BOOL visible); void onAgentParcelChange(); @@ -88,6 +90,7 @@ private: LLPanelPlacesTab* mActivePanel; LLTabContainer* mTabContainer; LLPanelPlaceInfo* mPlaceInfo; + LLPanelPick* mPickPanel; LLToggleableMenu* mPlaceMenu; LLToggleableMenu* mLandmarkMenu; diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index ce01568e99..be28129451 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -104,10 +104,6 @@ void LLPanelProfile::onOpen(const LLSD& key) { getTabCtrl()->getCurrentPanel()->onOpen(getAvatarId()); } - - // Update the avatar name. - gCacheName->get(getAvatarId(), FALSE, - boost::bind(&LLPanelProfile::onAvatarNameCached, this, _1, _2, _3, _4)); } //*TODO redo panel toggling @@ -171,8 +167,3 @@ void LLPanelProfile::setAllChildrenVisible(BOOL visible) } } -void LLPanelProfile::onAvatarNameCached(const LLUUID& id, const std::string& first_name, const std::string& last_name, BOOL is_group) -{ - llassert(getAvatarId() == id); - getChild<LLTextBox>("user_name", FALSE)->setValue(first_name + " " + last_name); -} diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index 0864ec1bc3..bb893f257a 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -72,13 +72,6 @@ protected: profile_tabs_t& getTabContainer() { return mTabContainer; } private: - // LLCacheName will call this function when avatar name is loaded from server. - // This is required to display names that have not been cached yet. - void onAvatarNameCached( - const LLUUID& id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group); LLTabContainer* mTabCtrl; profile_tabs_t mTabContainer; diff --git a/indra/newview/llpanelprofileview.cpp b/indra/newview/llpanelprofileview.cpp index 0762bbeb87..f1db2416e4 100644 --- a/indra/newview/llpanelprofileview.cpp +++ b/indra/newview/llpanelprofileview.cpp @@ -70,6 +70,10 @@ void LLPanelProfileView::onOpen(const LLSD& key) setAvatarId(id); } + // Update the avatar name. + gCacheName->get(getAvatarId(), FALSE, + boost::bind(&LLPanelProfileView::onAvatarNameCached, this, _1, _2, _3, _4)); + // status should only show if viewer has permission to view online/offline. EXT-453 mStatusText->setVisible(isGrantedToSeeOnlineStatus()); updateOnlineStatus(); @@ -133,4 +137,21 @@ void LLPanelProfileView::updateOnlineStatus() mStatusText->setValue(status); } +void LLPanelProfileView::onAvatarNameCached(const LLUUID& id, const std::string& first_name, const std::string& last_name, BOOL is_group) +{ + llassert(getAvatarId() == id); + getChild<LLTextBox>("user_name", FALSE)->setValue(first_name + " " + last_name); +} + +void LLPanelProfileView::togglePanel(LLPanel* panel) +{ + LLPanelProfile::togglePanel(panel); + if(FALSE == panel->getVisible()) + { + // LLPanelProfile::togglePanel shows/hides all children, + // we don't want to display online status for non friends, so re-hide it here + mStatusText->setVisible(isGrantedToSeeOnlineStatus()); + } +} + // EOF diff --git a/indra/newview/llpanelprofileview.h b/indra/newview/llpanelprofileview.h index 533ab94cc3..07a6c3a9a0 100644 --- a/indra/newview/llpanelprofileview.h +++ b/indra/newview/llpanelprofileview.h @@ -60,6 +60,8 @@ public: /*virtual*/ BOOL postBuild(); + /*virtual*/ void togglePanel(LLPanel* panel); + protected: void onBackBtnClick(); @@ -67,6 +69,14 @@ protected: void updateOnlineStatus(); private: + // LLCacheName will call this function when avatar name is loaded from server. + // This is required to display names that have not been cached yet. + void onAvatarNameCached( + const LLUUID& id, + const std::string& first_name, + const std::string& last_name, + BOOL is_group); + LLTextBox* mStatusText; }; diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 1fd7928bfc..f6672d9c8b 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -40,6 +40,83 @@ #include "llteleporthistorystorage.h" #include "llaccordionctrl.h" #include "llaccordionctrltab.h" +#include "llflatlistview.h" +#include "lltextbox.h" + +class LLTeleportHistoryFlatItem : public LLPanel +{ +public: + LLTeleportHistoryFlatItem(S32 index, const std::string ®ion_name); + virtual ~LLTeleportHistoryFlatItem() {}; + + virtual BOOL postBuild(); + + S32 getIndex() { return mIndex; } + + /*virtual*/ void setValue(const LLSD& value); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); +private: + void onInfoBtnClick(); + + LLButton* mInfoBtn; + + S32 mIndex; + std::string mRegionName; +}; + +LLTeleportHistoryFlatItem::LLTeleportHistoryFlatItem(S32 index, const std::string ®ion_name) +: LLPanel(), + mIndex(index), + mRegionName(region_name) +{ + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_teleport_history_item.xml"); +} + +//virtual +BOOL LLTeleportHistoryFlatItem::postBuild() +{ + LLTextBox *region = getChild<LLTextBox>("region"); + region->setValue(mRegionName); + + mInfoBtn = getChild<LLButton>("info_btn"); + mInfoBtn->setClickedCallback(boost::bind(&LLTeleportHistoryFlatItem::onInfoBtnClick, this)); + + return true; +} + +void LLTeleportHistoryFlatItem::setValue(const LLSD& value) +{ + if (!value.isMap()) return;; + if (!value.has("selected")) return; + childSetVisible("selected_icon", value["selected"]); +} + +void LLTeleportHistoryFlatItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + childSetVisible("hovered_icon", true); + mInfoBtn->setVisible(true); + + LLPanel::onMouseEnter(x, y, mask); +} + +void LLTeleportHistoryFlatItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + childSetVisible("hovered_icon", false); + mInfoBtn->setVisible(false); + + LLPanel::onMouseLeave(x, y, mask); +} + +void LLTeleportHistoryFlatItem::onInfoBtnClick() +{ + LLSD params; + params["id"] = mIndex; + params["type"] = "teleport_history"; + + LLSideTray::getInstance()->showPanel("panel_places", params); +} // Not yet implemented; need to remove buildPanel() from constructor when we switch //static LLRegisterPanelClassWrapper<LLTeleportHistoryPanel> t_teleport_history("panel_teleport_history"); @@ -48,7 +125,7 @@ LLTeleportHistoryPanel::LLTeleportHistoryPanel() : LLPanelPlacesTab(), mFilterSubString(LLStringUtil::null), mTeleportHistory(NULL), - mHistoryAccordeon(NULL), + mHistoryAccordion(NULL), mLastSelectedScrollList(NULL) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_teleport_history.xml"); @@ -66,26 +143,25 @@ BOOL LLTeleportHistoryPanel::postBuild() mTeleportHistory->setHistoryChangedCallback(boost::bind(&LLTeleportHistoryPanel::showTeleportHistory, this)); } - mHistoryAccordeon = getChild<LLAccordionCtrl>("history_accordion"); + mHistoryAccordion = getChild<LLAccordionCtrl>("history_accordion"); - if (mHistoryAccordeon) + if (mHistoryAccordion) { - for (child_list_const_iter_t iter = mHistoryAccordeon->beginChild(); iter != mHistoryAccordeon->endChild(); iter++) + for (child_list_const_iter_t iter = mHistoryAccordion->beginChild(); iter != mHistoryAccordion->endChild(); iter++) { if (dynamic_cast<LLAccordionCtrlTab*>(*iter)) { LLAccordionCtrlTab* tab = (LLAccordionCtrlTab*)*iter; mItemContainers.put(tab); - LLScrollListCtrl* sl = getScrollListFromTab(tab); - if (sl) + LLFlatListView* fl = getFlatListViewFromTab(tab); + if (fl) { - sl->setDoubleClickCallback(onDoubleClickItem, this); - sl->setCommitOnSelectionChange(FALSE); - sl->setCommitCallback(boost::bind(&LLTeleportHistoryPanel::handleItemSelect, this, sl)); + fl->setCommitOnSelectionChange(true); + //fl->setDoubleClickCallback(onDoubleClickItem, this); + fl->setCommitCallback(boost::bind(&LLTeleportHistoryPanel::handleItemSelect, this, fl)); } - } } } @@ -109,13 +185,12 @@ void LLTeleportHistoryPanel::onShowOnMap() if (!mLastSelectedScrollList) return; - LLScrollListItem* itemp = mLastSelectedScrollList->getFirstSelected(); + LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedScrollList->getSelectedItem()); + if(!itemp) return; - S32 index = itemp->getColumn(LIST_INDEX)->getValue().asInteger(); - - LLVector3d global_pos = mTeleportHistory->getItems()[mTeleportHistory->getItems().size() - 1 - index].mGlobalPos; + LLVector3d global_pos = mTeleportHistory->getItems()[itemp->getIndex()].mGlobalPos; if (!global_pos.isExactlyZero()) { @@ -130,14 +205,12 @@ void LLTeleportHistoryPanel::onTeleport() if (!mLastSelectedScrollList) return; - LLScrollListItem* itemp = mLastSelectedScrollList->getFirstSelected(); + LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedScrollList->getSelectedItem()); if(!itemp) return; - S32 index = itemp->getColumn(LIST_INDEX)->getValue().asInteger(); - // teleport to existing item in history, so we don't add it again - mTeleportHistory->goToItem(mTeleportHistory->getItems().size() - 1 - index); + mTeleportHistory->goToItem(itemp->getIndex()); } /* @@ -177,15 +250,15 @@ void LLTeleportHistoryPanel::updateVerbs() return; } - LLScrollListItem* itemp = mLastSelectedScrollList->getFirstSelected(); + LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedScrollList->getSelectedItem()); - mTeleportBtn->setEnabled(NULL != itemp && 0 < itemp->getColumn(LIST_INDEX)->getValue().asInteger()); + mTeleportBtn->setEnabled(NULL != itemp && 0 < itemp->getIndex()); mShowOnMapBtn->setEnabled(NULL != itemp); } void LLTeleportHistoryPanel::showTeleportHistory() { - if (!mHistoryAccordeon) + if (!mHistoryAccordion) return; const LLTeleportHistoryStorage::slurl_list_t& hist_items = mTeleportHistory->getItems(); @@ -193,15 +266,17 @@ void LLTeleportHistoryPanel::showTeleportHistory() const U32 seconds_in_day = 24 * 60 * 60; LLDate curr_date = LLDate::now(); - curr_date.secondsSinceEpoch(curr_date.secondsSinceEpoch() + seconds_in_day); - 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); - LLScrollListCtrl *curr_scroll_list = NULL; + LLFlatListView* curr_flat_view = NULL; - S32 index = 0; + S32 index = hist_items.size() - 1; for (LLTeleportHistoryStorage::slurl_list_t::const_reverse_iterator iter = hist_items.rbegin(); iter != hist_items.rend(); ++iter) @@ -219,71 +294,104 @@ void LLTeleportHistoryPanel::showTeleportHistory() { const LLDate &date = (*iter).mDate; - S32 year, month, day; - if (!date.split(&year, &month, &day)) - { - llwarns << "Failed to split date: " << date << llendl; - continue; - } - - if (day != curr_day || month != curr_month || year != curr_year) + if (date < curr_date) { LLAccordionCtrlTab* tab = NULL; - while (curr_tab < tabs_cnt - 1 && (day != curr_day || month != curr_month || year != curr_year)) + 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); + } + } tab->setVisible(true); - curr_scroll_list = getScrollListFromTab(tab); - if (curr_scroll_list) + curr_flat_view = getFlatListViewFromTab(tab); + if (curr_flat_view) { - curr_scroll_list->deleteAllItems(); + curr_flat_view->clear(); } } } - LLSD row; - row["id"] = index; - - if (curr_scroll_list) - { - LLSD& icon_column = row["columns"][LIST_ICON]; - icon_column["column"] = "landmark_icon"; - icon_column["type"] = "icon"; - icon_column["value"] = "inv_item_landmark.tga"; - - LLSD& region_column = row["columns"][LIST_ITEM_TITLE]; - region_column["column"] = "region"; - region_column["type"] = "text"; - region_column["value"] = (*iter).mTitle; - - LLSD& index_column = row["columns"][LIST_INDEX]; - index_column["column"] = "index"; - index_column["type"] = "text"; - index_column["value"] = index; - - index++; - - curr_scroll_list->addElement(row); + if (curr_flat_view) + { + curr_flat_view->addItem(new LLTeleportHistoryFlatItem(index, (*iter).mTitle)); } + + index--; } - mHistoryAccordeon->arrange(); + // 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); + + mHistoryAccordion->arrange(); updateVerbs(); } -void LLTeleportHistoryPanel::handleItemSelect(LLScrollListCtrl* sl) +void LLTeleportHistoryPanel::handleItemSelect(LLFlatListView* selected) { - mLastSelectedScrollList = sl; + mLastSelectedScrollList = selected; + + S32 tabs_cnt = mItemContainers.size(); + + for (S32 n = 0; n < tabs_cnt; n++) + { + LLAccordionCtrlTab* tab = mItemContainers.get(n); + + if (!tab->getVisible()) + continue; + + LLFlatListView *flv = getFlatListViewFromTab(tab); + if (!flv) + continue; + + if (flv == selected) + continue; + + flv->resetSelection(true); + } + updateVerbs(); } @@ -303,15 +411,16 @@ void LLTeleportHistoryPanel::onDoubleClickItem(void* user_data) LLSideTray::getInstance()->showPanel("panel_places", key);*/ } -LLScrollListCtrl* LLTeleportHistoryPanel::getScrollListFromTab(LLAccordionCtrlTab *tab) +LLFlatListView* LLTeleportHistoryPanel::getFlatListViewFromTab(LLAccordionCtrlTab *tab) { for (child_list_const_iter_t iter = tab->beginChild(); iter != tab->endChild(); iter++) { - if (dynamic_cast<LLScrollListCtrl*>(*iter)) + if (dynamic_cast<LLFlatListView*>(*iter)) { - return (LLScrollListCtrl*)*iter; // There should be one scroll list per tab. + return (LLFlatListView*)*iter; // There should be one scroll list per tab. } } return NULL; } + diff --git a/indra/newview/llpanelteleporthistory.h b/indra/newview/llpanelteleporthistory.h index a1c15d087b..66187e69c6 100644 --- a/indra/newview/llpanelteleporthistory.h +++ b/indra/newview/llpanelteleporthistory.h @@ -34,7 +34,6 @@ #define LL_LLPANELTELEPORTHISTORY_H #include "lluictrlfactory.h" -#include "llscrolllistctrl.h" #include "llpanelplacestab.h" #include "llteleporthistory.h" @@ -42,6 +41,7 @@ class LLTeleportHistoryStorage; class LLAccordionCtrl; class LLAccordionCtrlTab; +class LLFlatListView; class LLTeleportHistoryPanel : public LLPanelPlacesTab { @@ -60,19 +60,12 @@ private: static void onDoubleClickItem(void* user_data); void showTeleportHistory(); - void handleItemSelect(LLScrollListCtrl* ); - LLScrollListCtrl* getScrollListFromTab(LLAccordionCtrlTab *); - - enum TELEPORT_HISTORY_COLUMN_ORDER - { - LIST_ICON, - LIST_ITEM_TITLE, - LIST_INDEX - }; + void handleItemSelect(LLFlatListView* ); + LLFlatListView* getFlatListViewFromTab(LLAccordionCtrlTab *); LLTeleportHistoryStorage* mTeleportHistory; - LLAccordionCtrl* mHistoryAccordeon; - LLScrollListCtrl* mLastSelectedScrollList; + LLAccordionCtrl* mHistoryAccordion; + LLFlatListView* mLastSelectedScrollList; std::string mFilterSubString; typedef LLDynamicArray<LLAccordionCtrlTab*> item_containers_t; diff --git a/indra/newview/llrecentpeople.cpp b/indra/newview/llrecentpeople.cpp index 04abe36878..b491c7e109 100644 --- a/indra/newview/llrecentpeople.cpp +++ b/indra/newview/llrecentpeople.cpp @@ -40,27 +40,38 @@ using namespace LLOldEvents; bool LLRecentPeople::add(const LLUUID& id) { - if (contains(id) || id == gAgent.getID()) + if (id == gAgent.getID()) return false; LLDate date_added = LLDate::now(); - mList.insert(std::make_pair(id, date_added)); + + //[] instead of insert to replace existing id->date with new date value + mPeople[id] = date_added; mChangedSignal(); return true; } bool LLRecentPeople::contains(const LLUUID& id) const { - return mList.find(id) != mList.end(); + return mPeople.find(id) != mPeople.end(); } void LLRecentPeople::get(std::vector<LLUUID>& result) const { result.clear(); - for (recent_people_t::const_iterator pos = mList.begin(); pos != mList.end(); ++pos) + for (recent_people_t::const_iterator pos = mPeople.begin(); pos != mPeople.end(); ++pos) result.push_back((*pos).first); } +const LLDate& LLRecentPeople::getDate(const LLUUID& id) const +{ + recent_people_t::const_iterator it = mPeople.find(id); + if (it!= mPeople.end()) return (*it).second; + + static LLDate no_date = LLDate(); + return no_date; +} + // virtual bool LLRecentPeople::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) { diff --git a/indra/newview/llrecentpeople.h b/indra/newview/llrecentpeople.h index c18116b4e5..e0f2faaec5 100644 --- a/indra/newview/llrecentpeople.h +++ b/indra/newview/llrecentpeople.h @@ -81,6 +81,8 @@ public: */ void get(std::vector<LLUUID>& result) const; + const LLDate& getDate(const LLUUID& id) const; + /** * Set callback to be called when the list changed. * @@ -97,7 +99,7 @@ public: private: typedef std::map<LLUUID, LLDate> recent_people_t; - recent_people_t mList; + recent_people_t mPeople; signal_t mChangedSignal; }; diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp index 1ac0175b83..95e3dd6d78 100644 --- a/indra/newview/llremoteparcelrequest.cpp +++ b/indra/newview/llremoteparcelrequest.cpp @@ -84,7 +84,7 @@ void LLRemoteParcelRequestResponder::error(U32 status, const std::string& reason void LLRemoteParcelInfoProcessor::addObserver(const LLUUID& parcel_id, LLRemoteParcelInfoObserver* observer) { - // Check if the observer is alredy in observsrs list for this UUID + // Check if the observer is already in observers list for this UUID observer_multimap_t::iterator it; it = mObservers.find(parcel_id); @@ -155,7 +155,6 @@ void LLRemoteParcelInfoProcessor::processParcelInfoReply(LLMessageSystem* msg, v for (; oi != end; ++oi) { oi->second->processParcelInfo(parcel_data); - LLRemoteParcelInfoProcessor::getInstance()->removeObserver(parcel_data.parcel_id, oi->second); } } diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index 2157f1af74..082bba027f 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -39,9 +39,15 @@ #include "lltoastpanel.h" #include "llviewercontrol.h" +#include "llviewerwindow.h" #include "llfloaterreg.h" #include "lltrans.h" +#include "lldockablefloater.h" +#include "llimpanel.h" +#include "llsyswellwindow.h" +#include "llimfloater.h" + #include <algorithm> using namespace LLNotificationsUI; @@ -53,37 +59,52 @@ LLScreenChannel::LLScreenChannel(LLUUID& id): mOverflowToastPanel(NULL), mStartU mToastAlignment(NA_BOTTOM), mCanStoreToasts(true), mHiddenToastsNum(0), mOverflowToastHidden(false), mIsHovering(false), mControlHovering(false), - mShowToasts(false) + mShowToasts(true) { mID = id; - - setFollows(FOLLOWS_RIGHT | FOLLOWS_BOTTOM | FOLLOWS_TOP); - mOverflowFormatString = LLTrans::getString("OverflowInfoChannelString"); - + mWorldViewRectConnection = gViewerWindow->setOnWorldViewRectUpdated(boost::bind(&LLScreenChannel::updatePositionAndSize, this, _1, _2)); setMouseOpaque( false ); + setVisible(FALSE); } //-------------------------------------------------------------------------- void LLScreenChannel::init(S32 channel_left, S32 channel_right) { - S32 channel_top = getRootView()->getRect().getHeight() - gSavedSettings.getS32("NavBarMargin"); - S32 channel_bottom = getRootView()->getRect().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin"); + S32 channel_top = gViewerWindow->getWorldViewRect().getHeight(); + S32 channel_bottom = gViewerWindow->getWorldViewRect().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin"); setRect(LLRect(channel_left, channel_top, channel_right, channel_bottom)); - + setVisible(TRUE); } //-------------------------------------------------------------------------- LLScreenChannel::~LLScreenChannel() { + mWorldViewRectConnection.disconnect(); } //-------------------------------------------------------------------------- -void LLScreenChannel::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLScreenChannel::updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect) { - LLUICtrl::reshape(width, height, called_from_parent); - if(mToastAlignment != NA_CENTRE) - showToasts(); + S32 top_delta = old_world_rect.mTop - new_world_rect.mTop; + S32 right_delta = old_world_rect.mRight - new_world_rect.mRight; + + LLRect this_rect = getRect(); + + this_rect.mTop -= top_delta; + switch(mChannelAlignment) + { + case CA_LEFT : + break; + case CA_CENTRE : + this_rect.setCenterAndSize(new_world_rect.getWidth() / 2, new_world_rect.getHeight() / 2, this_rect.getWidth(), this_rect.getHeight()); + break; + case CA_RIGHT : + this_rect.mLeft -= right_delta; + this_rect.mRight -= right_delta; + } + setRect(this_rect); + redrawToasts(); } //-------------------------------------------------------------------------- @@ -91,12 +112,12 @@ void LLScreenChannel::addToast(LLToast::Params p) { bool store_toast = false, show_toast = false; - show_toast = mShowToasts || p.force_show; + show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show); store_toast = !show_toast && p.can_be_stored && mCanStoreToasts; if(!show_toast && !store_toast) { - mOnRejectToast(p); + mRejectToastSignal(p.notif_id); return; } @@ -104,7 +125,8 @@ void LLScreenChannel::addToast(LLToast::Params p) mOverflowToastHidden = false; - new_toast_elem.toast->setOnFadeCallback(boost::bind(&LLScreenChannel::onToastFade, this, new_toast_elem.toast)); + new_toast_elem.toast->setOnFadeCallback(boost::bind(&LLScreenChannel::onToastFade, this, _1)); + new_toast_elem.toast->setOnToastDestroyedCallback(boost::bind(&LLScreenChannel::onToastDestroyed, this, _1)); if(mControlHovering) { new_toast_elem.toast->setOnToastHoverCallback(boost::bind(&LLScreenChannel::onToastHover, this, _1, _2)); @@ -113,7 +135,7 @@ void LLScreenChannel::addToast(LLToast::Params p) if(show_toast) { mToastList.push_back(new_toast_elem); - showToasts(); + redrawToasts(); } else // store_toast { @@ -123,15 +145,27 @@ void LLScreenChannel::addToast(LLToast::Params p) } //-------------------------------------------------------------------------- +void LLScreenChannel::onToastDestroyed(LLToast* toast) +{ + std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), static_cast<LLPanel*>(toast)); + + if(it != mToastList.end()) + { + mToastList.erase(it); + } +} + + +//-------------------------------------------------------------------------- void LLScreenChannel::onToastFade(LLToast* toast) { std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), static_cast<LLPanel*>(toast)); - bool destroy_toast = !mCanStoreToasts || !toast->getCanBeStored(); - if(destroy_toast) + bool delete_toast = !mCanStoreToasts || !toast->getCanBeStored(); + if(delete_toast) { mToastList.erase(it); - toast->mOnToastDestroy(toast); + deleteToast(toast); } else { @@ -139,7 +173,22 @@ void LLScreenChannel::onToastFade(LLToast* toast) mToastList.erase(it); } - showToasts(); + redrawToasts(); +} + +//-------------------------------------------------------------------------- +void LLScreenChannel::deleteToast(LLToast* toast) +{ + // send signal to observers about destroying of a toast + toast->mOnDeleteToastSignal(toast); + + // update channel's Hovering state + // turning hovering off mannualy because onMouseLeave won't happen if a toast was closed using a keyboard + if(toast->hasFocus()) + setHovering(false); + + // close the toast + toast->closeFloater(); } //-------------------------------------------------------------------------- @@ -173,11 +222,11 @@ void LLScreenChannel::loadStoredToastsToChannel() } mStoredToastList.clear(); - showToasts(); + redrawToasts(); } //-------------------------------------------------------------------------- -void LLScreenChannel::loadStoredToastByIDToChannel(LLUUID id) +void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) { std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), id); @@ -191,11 +240,11 @@ void LLScreenChannel::loadStoredToastByIDToChannel(LLUUID id) mToastList.push_back((*it)); mStoredToastList.erase(it); - showToasts(); + redrawToasts(); } //-------------------------------------------------------------------------- -void LLScreenChannel::removeStoredToastByID(LLUUID id) +void LLScreenChannel::removeStoredToastByNotificationID(LLUUID id) { // *TODO: may be remove this function std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), id); @@ -205,7 +254,7 @@ void LLScreenChannel::removeStoredToastByID(LLUUID id) LLToast* toast = (*it).toast; mStoredToastList.erase(it); - toast->discardNotification(); + mRejectToastSignal(toast->getNotificationID()); } //-------------------------------------------------------------------------- @@ -223,15 +272,15 @@ void LLScreenChannel::killToastByNotificationID(LLUUID id) // NOTE: if a notification is unresponded this function will be called twice for the same toast. // At first, the notification will be discarded, at second (it will be caused by discarding), // the toast will be destroyed. - if(toast->getIsNotificationUnResponded()) + if(toast->isNotificationValid()) { - toast->discardNotification(); + mRejectToastSignal(toast->getNotificationID()); } else { mToastList.erase(it); - toast->mOnToastDestroy(toast); - showToasts(); + deleteToast(toast); + redrawToasts(); } return; } @@ -243,8 +292,9 @@ void LLScreenChannel::killToastByNotificationID(LLUUID id) { LLToast* toast = (*it).toast; mStoredToastList.erase(it); - toast->discardNotification(); - toast->mOnToastDestroy(toast); + // send signal to a listener to let him perform some action on toast rejecting + mRejectToastSignal(toast->getNotificationID()); + deleteToast(toast); } } @@ -261,12 +311,12 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel) delete old_panel; toast->insertPanel(panel); toast->resetTimer(); - showToasts(); + redrawToasts(); } } //-------------------------------------------------------------------------- -void LLScreenChannel::showToasts() +void LLScreenChannel::redrawToasts() { if(mToastList.size() == 0 || mIsHovering) return; @@ -487,31 +537,55 @@ void LLScreenChannel::removeToastsFromChannel() hideToastsFromScreen(); for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++) { - // *TODO: ivestigate mOnToastDestroy callback - change name or/and place - (*it).toast->mOnToastDestroy((*it).toast); + deleteToast((*it).toast); } mToastList.clear(); } //-------------------------------------------------------------------------- -void LLScreenChannel::removeAndStoreAllVisibleToasts() +void LLScreenChannel::removeAndStoreAllStorableToasts() { if(mToastList.size() == 0) return; hideToastsFromScreen(); - for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++) + for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end();) { if((*it).toast->getCanBeStored()) { mStoredToastList.push_back(*it); mOnStoreToast((*it).toast->getPanel(), (*it).id); (*it).toast->stopTimer(); + it = mToastList.erase(it); } - (*it).toast->setVisible(FALSE); + else + { + ++it; } + } + redrawToasts(); +} - mToastList.clear(); +//-------------------------------------------------------------------------- +void LLScreenChannel::removeToastsBySessionID(LLUUID id) +{ + if(mToastList.size() == 0) + return; + + hideToastsFromScreen(); + for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end();) + { + if((*it).toast->getSessionID() == id) + { + deleteToast((*it).toast); + it = mToastList.erase(it); + } + else + { + ++it; + } + } + redrawToasts(); } //-------------------------------------------------------------------------- @@ -546,11 +620,30 @@ void LLScreenChannel::onToastHover(LLToast* toast, bool mouse_enter) } if(!mIsHovering) - showToasts(); + redrawToasts(); } //-------------------------------------------------------------------------- +void LLScreenChannel::updateShowToastsState() +{ + LLFloater* floater = LLDockableFloater::getInstanceHandle().get(); + if(!floater) + { + setShowToasts(true); + return; + } + if(dynamic_cast<LLIMFloater*>(floater) || dynamic_cast<LLSysWellWindow*>(floater)) + { + setShowToasts(!(floater->getVisible() && floater->isDocked())); + if (!getShowToasts()) + { + removeAndStoreAllStorableToasts(); + } + + } +} +//-------------------------------------------------------------------------- diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h index 746580b574..f1ef6bd64d 100644 --- a/indra/newview/llscreenchannel.h +++ b/indra/newview/llscreenchannel.h @@ -48,6 +48,12 @@ typedef enum e_notification_toast_alignment NA_BOTTOM, } EToastAlignment; +typedef enum e_channel_alignment +{ + CA_LEFT, + CA_CENTRE, + CA_RIGHT, +} EChannelAlignment; /** * Screen channel manages toasts visibility and positioning on the screen. @@ -60,12 +66,14 @@ public: virtual ~LLScreenChannel(); // Channel's outfit-functions - // classic reshape - void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + // update channel's size and position in the World View + void updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect); // initialization of channel's shape and position void init(S32 channel_left, S32 channel_right); // set allignment of toasts inside a channel - void setToastAlignment(e_notification_toast_alignment align) {mToastAlignment = align;} + void setToastAlignment(EToastAlignment align) {mToastAlignment = align;} + // set allignment of channel inside a world view + void setChannelAlignment(EChannelAlignment align) {mChannelAlignment = align;} // set a template for a string in the OverflowToast void setOverflowFormatString ( std::string str) { mOverflowFormatString = str; } @@ -80,15 +88,17 @@ public: // removes all toasts from a channel void removeToastsFromChannel(); // show all toasts in a channel - void showToasts(); + void redrawToasts(); // void loadStoredToastsToChannel(); - // finds a toast among stored by its ID and throws it on a screen to a channel - void loadStoredToastByIDToChannel(LLUUID id); - // removes a toast from stored finding it by its ID - void removeStoredToastByID(LLUUID id); - // remove all toasts from screen and store them - void removeAndStoreAllVisibleToasts(); + // finds a toast among stored by its Notification ID and throws it on a screen to a channel + void loadStoredToastByNotificationIDToChannel(LLUUID id); + // removes a toast from stored finding it by its Notification ID + void removeStoredToastByNotificationID(LLUUID id); + // removes from channel all toasts that belongs to the certain IM session + void removeToastsBySessionID(LLUUID id); + // remove all storable toasts from screen and store them + void removeAndStoreAllStorableToasts(); // close the Overflow Toast void closeOverflowToastPanel(); // close the StartUp Toast @@ -113,6 +123,8 @@ public: void setShowToasts(bool show) { mShowToasts = show; } // determine whether channel shows toasts or not bool getShowToasts() { return mShowToasts; } + // let a channel update its ShowToast flag + void updateShowToastsState(); // Channel's other interface functions functions // get number of hidden notifications from a channel @@ -124,17 +136,17 @@ public: // get ID of a channel LLUUID getChannelID() { return mID; } - // Channel's callbacks - // callback for storing of faded toasts + // Channel's signals + // signal on storing of faded toasts event typedef boost::function<void (LLPanel* info_panel, const LLUUID id)> store_tost_callback_t; typedef boost::signals2::signal<void (LLPanel* info_panel, const LLUUID id)> store_tost_signal_t; store_tost_signal_t mOnStoreToast; boost::signals2::connection setOnStoreToastCallback(store_tost_callback_t cb) { return mOnStoreToast.connect(cb); } - // callback for discarding of a rejected toast - typedef boost::function<void (LLToast::Params p)> reject_tost_callback_t; - typedef boost::signals2::signal<void (LLToast::Params p)> reject_tost_signal_t; - reject_tost_signal_t mOnRejectToast; - boost::signals2::connection setOnRejectToastCallback(reject_tost_callback_t cb) { return mOnRejectToast.connect(cb); } + // signal on rejecting of a toast event + typedef boost::function<void (LLUUID id)> reject_tost_callback_t; + typedef boost::signals2::signal<void (LLUUID id)> reject_tost_signal_t; + reject_tost_signal_t mRejectToastSignal; + boost::signals2::connection setOnRejectToastCallback(reject_tost_callback_t cb) { return mRejectToastSignal.connect(cb); } private: struct ToastElem @@ -142,7 +154,7 @@ private: LLUUID id; LLToast* toast; - ToastElem(LLToast::Params p) : id(p.id) + ToastElem(LLToast::Params p) : id(p.notif_id) { toast = new LLToast(p); } @@ -167,11 +179,14 @@ private: // Channel's handlers void onToastHover(LLToast* toast, bool mouse_enter); void onToastFade(LLToast* toast); + void onToastDestroyed(LLToast* toast); void onOverflowToastHide(); void onStartUpToastHide(); // void storeToast(ToastElem& toast_elem); + // send signal to observers about destroying of a toast, update channel's Hovering state, close the toast + void deleteToast(LLToast* toast); // show-functions depending on allignment of toasts void showToastsBottom(); @@ -194,7 +209,8 @@ private: // controls whether a channel shows toasts or not bool mShowToasts; // - e_notification_toast_alignment mToastAlignment; + EToastAlignment mToastAlignment; + EChannelAlignment mChannelAlignment; // attributes for the Overflow Toast S32 mHiddenToastsNum; @@ -207,6 +223,9 @@ private: // channel's ID LLUUID mID; + // store a connection to prevent futher crash that is caused by sending a signal to a destroyed channel + boost::signals2::connection mWorldViewRectConnection; + std::vector<ToastElem> mToastList; std::vector<ToastElem> mStoredToastList; std::map<LLToast*, bool> mToastEventStack; diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 40b8445dcf..d7df258750 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -237,9 +237,6 @@ BOOL LLStatusBar::handleRightMouseDown(S32 x, S32 y, MASK mask) BOOL LLStatusBar::postBuild() { - mCommitCallbackRegistrar.add("HideNavbarMenu.Action", boost::bind(&LLStatusBar::onHideNavbarContextMenuItemClicked, this, _2)); - mEnableCallbackRegistrar.add("HideNavbarMenu.EnableMenuItem", boost::bind(&LLStatusBar::onHideNavbarContextMenuItemEnabled, this, _2)); - mHideNavbarContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_hide_navbar.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); gMenuHolder->addChild(mHideNavbarContextMenu); @@ -563,43 +560,6 @@ void LLStatusBar::setupDate() } } -bool LLStatusBar::onHideNavbarContextMenuItemEnabled(const LLSD& userdata) -{ - std::string item = userdata.asString(); - - if (item == "show_navbar_navigation_panel") - { - return gSavedSettings.getBOOL("ShowNavbarNavigationPanel"); - } - else if (item == "show_navbar_favorites_panel") - { - return gSavedSettings.getBOOL("ShowNavbarFavoritesPanel"); - } - - return FALSE; -} - -void LLStatusBar::onHideNavbarContextMenuItemClicked(const LLSD& userdata) -{ - std::string item = userdata.asString(); - - if (item == "show_navbar_navigation_panel") - { - BOOL state = !gSavedSettings.getBOOL("ShowNavbarNavigationPanel"); - - LLNavigationBar::getInstance()->showNavigationPanel(state); - gSavedSettings.setBOOL("ShowNavbarNavigationPanel", state); - } - else if (item == "show_navbar_favorites_panel") - { - BOOL state = !gSavedSettings.getBOOL("ShowNavbarFavoritesPanel"); - - LLNavigationBar::getInstance()->showFavoritesPanel(state); - gSavedSettings.setBOOL("ShowNavbarFavoritesPanel", state); - } -} - - void LLStatusBar::onMainMenuRightClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) { handleRightMouseDown(x, y, mask); diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h index b77db2c525..81a69e9590 100644 --- a/indra/newview/llstatusbar.h +++ b/indra/newview/llstatusbar.h @@ -91,9 +91,6 @@ private: // simple method to setup the part that holds the date void setupDate(); - bool onHideNavbarContextMenuItemEnabled(const LLSD& userdata); - void onHideNavbarContextMenuItemClicked(const LLSD& userdata); - void onMainMenuRightClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask); static void onCommitSearch(LLUICtrl*, void* data); static void onClickSearch(void* data); diff --git a/indra/newview/llsyswellitem.cpp b/indra/newview/llsyswellitem.cpp index 7d9ebc7208..eef8435006 100644 --- a/indra/newview/llsyswellitem.cpp +++ b/indra/newview/llsyswellitem.cpp @@ -40,7 +40,7 @@ #include "lluicolortable.h" //--------------------------------------------------------------------------------- -LLSysWellItem::LLSysWellItem(const Params& p) : LLScrollingPanel(p), +LLSysWellItem::LLSysWellItem(const Params& p) : LLPanel(p), mTitle(NULL), mCloseBtn(NULL), mIcon(NULL) @@ -75,15 +75,6 @@ void LLSysWellItem::onClickCloseBtn() } //--------------------------------------------------------------------------------- -void LLSysWellItem::updatePanel(BOOL allow_modify) -{ - S32 parent_width = getParent()->getRect().getWidth(); - S32 panel_height = getRect().getHeight(); - - reshape(parent_width, panel_height, TRUE); -} - -//--------------------------------------------------------------------------------- BOOL LLSysWellItem::handleMouseDown(S32 x, S32 y, MASK mask) { if(!mCloseBtn->getRect().pointInRect(x, y)) diff --git a/indra/newview/llsyswellitem.h b/indra/newview/llsyswellitem.h index b0761f2790..b9b00e972a 100644 --- a/indra/newview/llsyswellitem.h +++ b/indra/newview/llsyswellitem.h @@ -33,14 +33,14 @@ #ifndef LL_LLSYSWELLITEM_H #define LL_LLSYSWELLITEM_H -#include "llscrollingpanellist.h" +#include "llpanel.h" #include "lltextbox.h" #include "llbutton.h" #include "lliconctrl.h" #include <string> -class LLSysWellItem : public LLScrollingPanel +class LLSysWellItem : public LLPanel { public: struct Params : public LLInitParam::Block<Params, LLPanel::Params> @@ -54,8 +54,6 @@ public: LLSysWellItem(const Params& p); virtual ~LLSysWellItem(); - void updatePanel(BOOL allow_modify); - // title void setTitle( std::string title ); diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 98428bf0f7..c1eecf4c12 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -32,6 +32,8 @@ #include "llviewerprecompiledheaders.h" // must be first include +#include "llflatlistview.h" + #include "llsyswellwindow.h" #include "llbottomtray.h" @@ -39,30 +41,102 @@ #include "llviewerwindow.h" #include "llchiclet.h" +#include "lltoastpanel.h" +#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), - mScrollContainer(NULL), - mNotificationList(NULL) + mMessageList(NULL), + mSeparator(NULL) { LLIMMgr::getInstance()->addSessionObserver(this); LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLSysWellWindow::findIMChiclet, this, _1)); + + mTypedItemsCount[IT_NOTIFICATION] = 0; + mTypedItemsCount[IT_INSTANT_MESSAGE] = 0; } //--------------------------------------------------------------------------------- BOOL LLSysWellWindow::postBuild() { - mScrollContainer = getChild<LLScrollContainer>("notification_list_container"); - mTwinListPanel = getChild<LLPanel>("twin_list_panel"); - mNotificationList = getChild<LLScrollingPanelList>("notification_list"); - mIMRowList = getChild<LLScrollingPanelList>("im_row_panel_list"); + mMessageList = getChild<LLFlatListView>("notification_list"); + + // init connections to the list's update events + connectListUpdaterToSignal("notify"); + connectListUpdaterToSignal("groupnotify"); + + // get a corresponding channel + initChannel(); - mScrollContainer->setBorderVisible(FALSE); + LLPanel::Params params; + mSeparator = LLUICtrlFactory::create<LLPanel>(params); + LLUICtrlFactory::instance().buildPanel(mSeparator, "panel_separator.xml"); + + LLRect rc = mSeparator->getRect(); + rc.setOriginAndSize(0, 0, mMessageList->getItemsRect().getWidth(), rc.getHeight()); + mSeparator->setRect(rc); + mSeparator->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP); + mSeparator->setVisible(FALSE); + + mMessageList->addItem(mSeparator); return LLDockableFloater::postBuild(); } //--------------------------------------------------------------------------------- +void LLSysWellWindow::connectListUpdaterToSignal(std::string notification_type) +{ + LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance(); + LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type); + if(n_handler) + { + n_handler->setNotificationIDCallback(boost::bind(&LLSysWellWindow::removeItemByID, this, _1)); + } + else + { + llwarns << "LLSysWellWindow::connectListUpdaterToSignal() - could not get a handler for '" << notification_type <<"' type of notifications" << llendl; + } +} + +//--------------------------------------------------------------------------------- +void LLSysWellWindow::onChicletClick() +{ + // 1 - remove StartUp toast and channel if present + if(!LLNotificationsUI::LLScreenChannel::getStartUpToastShown()) + { + LLNotificationsUI::LLChannelManager::getInstance()->onStartUpToastClose(); + } + + // 2 - toggle instance of SysWell's chiclet-window + toggleWindow(); +} + +//--------------------------------------------------------------------------------- LLSysWellWindow::~LLSysWellWindow() { LLIMMgr::getInstance()->removeSessionObserver(this); @@ -71,57 +145,56 @@ LLSysWellWindow::~LLSysWellWindow() //--------------------------------------------------------------------------------- void LLSysWellWindow::addItem(LLSysWellItem::Params p) { + LLSD value = get_notification_value(p.notification_id); // do not add clones - if( findItemByID(p.notification_id) >= 0 ) + if( mMessageList->getItemByValue(value)) return; LLSysWellItem* new_item = new LLSysWellItem(p); - mNotificationList->addPanel(dynamic_cast<LLScrollingPanel*>(new_item)); + if (mMessageList->addItem(new_item, value, ADD_TOP)) + { + handleItemAdded(IT_NOTIFICATION); + reshapeWindow(); new_item->setOnItemCloseCallback(boost::bind(&LLSysWellWindow::onItemClose, this, _1)); new_item->setOnItemClickCallback(boost::bind(&LLSysWellWindow::onItemClick, this, _1)); + } + else + { + llwarns << "Unable to add Notification into the list, notification ID: " << p.notification_id + << ", title: " << p.title + << llendl; + + new_item->die(); + } } //--------------------------------------------------------------------------------- void LLSysWellWindow::clear() { - // *TODO: fill later + mMessageList->clear(); } //--------------------------------------------------------------------------------- -S32 LLSysWellWindow::findItemByID(const LLUUID& id) +void LLSysWellWindow::removeItemByID(const LLUUID& id) { - const LLScrollingPanelList::panel_list_t list = mNotificationList->getPanelList(); - if(list.size() == 0) - return -1; - - LLScrollingPanelList::panel_list_t::const_iterator it; - S32 index = 0; - for(it = list.begin(); it != list.end(); ++it, ++index) + if(mMessageList->removeItemByValue(get_notification_value(id))) { - if( dynamic_cast<LLSysWellItem*>(*it)->getID() == id ) - break; + handleItemRemoved(IT_NOTIFICATION); + reshapeWindow(); } - - if(it == list.end()) - return -1; else - return index; - -} - -//--------------------------------------------------------------------------------- -void LLSysWellWindow::removeItemByID(const LLUUID& id) -{ - S32 index = findItemByID(id); - - if(index >= 0) - mNotificationList->removePanel(index); - else - return; + { + llwarns << "Unable to remove notification from the list, ID: " << id + << llendl; + } - reshapeWindow(); + // hide chiclet window if there are no items left + if(isWindowEmpty()) + { + setVisible(FALSE); + } } //--------------------------------------------------------------------------------- @@ -129,7 +202,7 @@ void LLSysWellWindow::onItemClick(LLSysWellItem* item) { LLUUID id = item->getID(); if(mChannel) - mChannel->loadStoredToastByIDToChannel(id); + mChannel->loadStoredToastByNotificationIDToChannel(id); } //--------------------------------------------------------------------------------- @@ -139,9 +212,37 @@ void LLSysWellWindow::onItemClose(LLSysWellItem* item) removeItemByID(id); if(mChannel) mChannel->killToastByNotificationID(id); +} - // hide chiclet window if there are no items left - setVisible(!isWindowEmpty()); +//-------------------------------------------------------------------------- +void LLSysWellWindow::onStoreToast(LLPanel* info_panel, LLUUID id) +{ + LLSysWellItem::Params p; + p.notification_id = id; + p.title = static_cast<LLToastPanel*>(info_panel)->getTitle(); + addItem(p); +} + +//--------------------------------------------------------------------------------- +void LLSysWellWindow::initChannel() +{ + LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID( + LLUUID(gSavedSettings.getString("NotificationChannelUUID"))); + if(channel) + { + mChannel = channel; + mChannel->setOnStoreToastCallback(boost::bind(&LLSysWellWindow::onStoreToast, this, _1, _2)); + } + else + { + llwarns << "LLSysWellWindow::initChannel() - could not get a requested screen channel" << llendl; + } +} + +//--------------------------------------------------------------------------------- +void LLSysWellWindow::getEnabledRect(LLRect& rect) +{ + rect = gViewerWindow->getWorldViewRect(); } //--------------------------------------------------------------------------------- @@ -151,9 +252,26 @@ void LLSysWellWindow::toggleWindow() { setDockControl(new LLDockControl( LLBottomTray::getInstance()->getSysWell(), this, - getDockTongue(), LLDockControl::TOP, isDocked())); + getDockTongue(), LLDockControl::TOP, boost::bind(&LLSysWellWindow::getEnabledRect, this, _1))); + } + + if(!getVisible()) + { + if(mChannel) + { + mChannel->removeAndStoreAllStorableToasts(); + } + if(isWindowEmpty()) + { + return; + } + + setVisible(TRUE); + } + else + { + setVisible(FALSE); } - setVisible(!getVisible()); //set window in foreground setFocus(getVisible()); } @@ -161,15 +279,12 @@ void LLSysWellWindow::toggleWindow() //--------------------------------------------------------------------------------- void LLSysWellWindow::setVisible(BOOL visible) { - // on Show adjust position of SysWell chiclet's window if(visible) { if (LLBottomTray::instanceExists()) { LLBottomTray::getInstance()->getSysWell()->setToggleState(TRUE); } - if(mChannel) - mChannel->removeAndStoreAllVisibleToasts(); } else { @@ -178,86 +293,56 @@ void LLSysWellWindow::setVisible(BOOL visible) LLBottomTray::getInstance()->getSysWell()->setToggleState(FALSE); } } - if(mChannel) - mChannel->setShowToasts(!visible); LLDockableFloater::setVisible(visible); + + // update notification channel state + if(mChannel) + { + mChannel->updateShowToastsState(); + } } //--------------------------------------------------------------------------------- -void LLSysWellWindow::reshapeWindow() +void LLSysWellWindow::setDocked(bool docked, bool pop_on_undock) { - // Get size for scrollbar and floater's header - const LLUICachedControl<S32> SCROLLBAR_SIZE("UIScrollbarSize", 0); - const LLUICachedControl<S32> HEADER_SIZE("UIFloaterHeaderSize", 0); - - LLRect notif_list_rect = mNotificationList->getRect(); - LLRect im_list_rect = mIMRowList->getRect(); - LLRect panel_rect = mTwinListPanel->getRect(); - - S32 notif_list_height = notif_list_rect.getHeight(); - S32 im_list_height = im_list_rect.getHeight(); + LLDockableFloater::setDocked(docked, pop_on_undock); - S32 new_panel_height = notif_list_height + LLScrollingPanelList::GAP_BETWEEN_PANELS + im_list_height; - S32 new_window_height = new_panel_height + LLScrollingPanelList::GAP_BETWEEN_PANELS + HEADER_SIZE; - - U32 twinListWidth = 0; - - if (new_window_height > MAX_WINDOW_HEIGHT) - { - twinListWidth = MIN_PANELLIST_WIDTH - SCROLLBAR_SIZE; - new_window_height = MAX_WINDOW_HEIGHT; - } - else + // update notification channel state + if(mChannel) { - twinListWidth = MIN_PANELLIST_WIDTH; + mChannel->updateShowToastsState(); } - - reshape(MIN_WINDOW_WIDTH, new_window_height, FALSE); - mTwinListPanel->reshape(twinListWidth, new_panel_height, TRUE); - mNotificationList->reshape(twinListWidth, notif_list_height, TRUE); - mIMRowList->reshape(twinListWidth, im_list_height, TRUE); - - // arrange panel and lists - // move panel - panel_rect.setLeftTopAndSize(1, new_panel_height, twinListWidth, new_panel_height); - mTwinListPanel->setRect(panel_rect); - // move notif list panel - notif_list_rect.setLeftTopAndSize(notif_list_rect.mLeft, new_panel_height, twinListWidth, notif_list_height); - mNotificationList->setRect(notif_list_rect); - // move IM list panel - im_list_rect.setLeftTopAndSize(im_list_rect.mLeft, notif_list_rect.mBottom - LLScrollingPanelList::GAP_BETWEEN_PANELS, twinListWidth, im_list_height); - mIMRowList->setRect(im_list_rect); - - mNotificationList->updatePanels(TRUE); - mIMRowList->updatePanels(TRUE); } //--------------------------------------------------------------------------------- -LLSysWellWindow::RowPanel * LLSysWellWindow::findIMRow(const LLUUID& sessionId) +void LLSysWellWindow::reshapeWindow() { - RowPanel * res = NULL; - const LLScrollingPanelList::panel_list_t &list = mIMRowList->getPanelList(); - if (!list.empty()) + // save difference between floater height and the list height to take it into account while calculating new window height + // it includes height from floater top to list top and from floater bottom and list bottom + static S32 parent_list_delta_height = getRect().getHeight() - mMessageList->getRect().getHeight(); + + S32 notif_list_height = mMessageList->getItemsRect().getHeight() + 2 * mMessageList->getBorderWidth(); + + LLRect curRect = getRect(); + + S32 new_window_height = notif_list_height + parent_list_delta_height; + + if (new_window_height > MAX_WINDOW_HEIGHT) { - for (LLScrollingPanelList::panel_list_t::const_iterator iter = list.begin(); iter != list.end(); ++iter) - { - RowPanel *panel = static_cast<RowPanel*> (*iter); - if (panel->mChiclet->getSessionId() == sessionId) - { - res = panel; - break; - } - } + new_window_height = MAX_WINDOW_HEIGHT; } - return res; + S32 newY = curRect.mTop + new_window_height - curRect.getHeight(); + curRect.setLeftTopAndSize(curRect.mLeft, newY, MIN_WINDOW_WIDTH, new_window_height); + reshape(curRect.getWidth(), curRect.getHeight(), TRUE); + setRect(curRect); } //--------------------------------------------------------------------------------- LLChiclet* LLSysWellWindow::findIMChiclet(const LLUUID& sessionId) { LLChiclet* res = NULL; - RowPanel* panel = findIMRow(sessionId); + RowPanel* panel = mMessageList->getTypedItemByValue<RowPanel>(get_session_value(sessionId)); if (panel != NULL) { res = panel->mChiclet; @@ -270,34 +355,51 @@ LLChiclet* LLSysWellWindow::findIMChiclet(const LLUUID& sessionId) 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))) + { + handleItemAdded(IT_INSTANT_MESSAGE); + } + else + { + llwarns << "Unable to add IM Row into the list, sessionID: " << sessionId + << ", name: " << name + << ", other participant ID: " << otherParticipantId + << llendl; - mIMRowList->addPanel(new RowPanel(this, sessionId, chicletCounter, name, otherParticipantId)); + item->die(); + } } //--------------------------------------------------------------------------------- void LLSysWellWindow::delIMRow(const LLUUID& sessionId) { - RowPanel *panel = findIMRow(sessionId); - if (panel != NULL) + if (mMessageList->removeItemByValue(get_session_value(sessionId))) + { + handleItemRemoved(IT_INSTANT_MESSAGE); + } + else { - mIMRowList->removePanel(panel); + llwarns << "Unable to remove IM Row from the list, sessionID: " << sessionId + << llendl; } + // remove all toasts that belong to this session from a screen + if(mChannel) + mChannel->removeToastsBySessionID(sessionId); + // hide chiclet window if there are no items left - setVisible(!isWindowEmpty()); + if(isWindowEmpty()) + { + setVisible(FALSE); + } } //--------------------------------------------------------------------------------- bool LLSysWellWindow::isWindowEmpty() { - if(mIMRowList->getPanelList().size() == 0 && LLBottomTray::getInstance()->getSysWell()->getCounter() == 0) - { - return true; - } - else - { - return false; - } + // keep in mind, mSeparator is always in the list + return mMessageList->size() == 1; } //--------------------------------------------------------------------------------- @@ -305,7 +407,7 @@ bool LLSysWellWindow::isWindowEmpty() void LLSysWellWindow::sessionAdded(const LLUUID& sessionId, const std::string& name, const LLUUID& otherParticipantId) { - if (findIMRow(sessionId) == NULL) + if (mMessageList->getItemByValue(get_session_value(sessionId)) == NULL) { S32 chicletCounter = 0; LLIMModel::LLIMSession* session = get_if_there(LLIMModel::sSessionsMap, @@ -328,10 +430,57 @@ void LLSysWellWindow::sessionRemoved(const LLUUID& sessionId) LLBottomTray::getInstance()->getSysWell()->updateUreadIMNotifications(); } +void LLSysWellWindow::handleItemAdded(EItemType added_item_type) +{ + bool should_be_shown = ++mTypedItemsCount[added_item_type] == 1 && anotherTypeExists(added_item_type); + + if (should_be_shown && !mSeparator->getVisible()) + { + mSeparator->setVisible(TRUE); + + // refresh list to recalculate mSeparator position + mMessageList->reshape(mMessageList->getRect().getWidth(), mMessageList->getRect().getHeight()); + } +} + +void LLSysWellWindow::handleItemRemoved(EItemType removed_item_type) +{ + bool should_be_hidden = --mTypedItemsCount[removed_item_type] == 0; + + if (should_be_hidden && mSeparator->getVisible()) + { + mSeparator->setVisible(FALSE); + + // refresh list to recalculate mSeparator position + mMessageList->reshape(mMessageList->getRect().getWidth(), mMessageList->getRect().getHeight()); + } +} + +bool LLSysWellWindow::anotherTypeExists(EItemType item_type) +{ + bool exists = false; + switch(item_type) + { + case IT_INSTANT_MESSAGE: + if (mTypedItemsCount[IT_NOTIFICATION] > 0) + { + exists = true; + } + break; + case IT_NOTIFICATION: + if (mTypedItemsCount[IT_INSTANT_MESSAGE] > 0) + { + exists = true; + } + break; + } + return exists; +} + //--------------------------------------------------------------------------------- LLSysWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) : - LLScrollingPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent) + LLPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_activeim_row.xml", NULL); @@ -373,8 +522,8 @@ LLSysWellWindow::RowPanel::~RowPanel() //--------------------------------------------------------------------------------- void LLSysWellWindow::RowPanel::onClose() { - mParent->mIMRowList->removePanel(this); gIMMgr->removeSession(mChiclet->getSessionId()); + // This row panel will be removed from the list in LLSysWellWindow::sessionRemoved(). } //--------------------------------------------------------------------------------- @@ -400,13 +549,4 @@ BOOL LLSysWellWindow::RowPanel::handleMouseDown(S32 x, S32 y, MASK mask) return LLPanel::handleMouseDown(x, y, mask); } -//--------------------------------------------------------------------------------- -void LLSysWellWindow::RowPanel::updatePanel(BOOL allow_modify) -{ - S32 parent_width = getParent()->getRect().getWidth(); - S32 panel_height = getRect().getHeight(); - - reshape(parent_width, panel_height, TRUE); -} - -//--------------------------------------------------------------------------------- +// EOF diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index d76147b489..37a2690a82 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -44,7 +44,7 @@ #include "boost/shared_ptr.hpp" - +class LLFlatListView; class LLSysWellWindow : public LLDockableFloater, LLIMSessionObserver { @@ -54,36 +54,52 @@ public: BOOL postBuild(); // other interface functions + // check is window empty bool isWindowEmpty(); - // change attributes - void setChannel(LLNotificationsUI::LLScreenChannel* channel) {mChannel = channel;} - // Operating with items void addItem(LLSysWellItem::Params p); void clear( void ); void removeItemByID(const LLUUID& id); - S32 findItemByID(const LLUUID& id); // Operating with outfit virtual void setVisible(BOOL visible); void adjustWindowPosition(); void toggleWindow(); - /*virtua*/BOOL canClose() { return FALSE; } + /*virtual*/ BOOL canClose() { return FALSE; } + /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); // Handlers void onItemClick(LLSysWellItem* item); void onItemClose(LLSysWellItem* item); + void onStoreToast(LLPanel* info_panel, LLUUID id); + void onChicletClick(); // size constants for the window and for its elements static const S32 MAX_WINDOW_HEIGHT = 200; - static const S32 MIN_WINDOW_WIDTH = 320; - static const S32 MIN_PANELLIST_WIDTH = 318; + static const S32 MIN_WINDOW_WIDTH = 318; private: + + typedef enum{ + IT_NOTIFICATION, + IT_INSTANT_MESSAGE + }EItemType; + + // gets a rect valid for SysWellWindow's position on a screen (EXT-1111) + void getEnabledRect(LLRect& rect); + // connect counter and list updaters to the corresponding signals + void connectListUpdaterToSignal(std::string notification_type); + // init Window's channel + void initChannel(); + void handleItemAdded(EItemType added_item_type); + void handleItemRemoved(EItemType removed_item_type); + bool anotherTypeExists(EItemType item_type) ; + + + class RowPanel; void reshapeWindow(); - RowPanel * findIMRow(const LLUUID& sessionId); LLChiclet * findIMChiclet(const LLUUID& sessionId); void addIMRow(const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId); void delIMRow(const LLUUID& sessionId); @@ -93,23 +109,28 @@ private: // pointer to a corresponding channel's instance LLNotificationsUI::LLScreenChannel* mChannel; - LLPanel* mTwinListPanel; - LLScrollContainer* mScrollContainer; - LLScrollingPanelList* mIMRowList; - LLScrollingPanelList* mNotificationList; + LLFlatListView* mMessageList; + + /** + * Special panel which is used as separator of Notifications & IM Rows. + * It is always presents in the list and shown when it is necessary. + * It should be taken into account when reshaping and checking list size + */ + LLPanel* mSeparator; + + typedef std::map<EItemType, S32> typed_items_count_t; + typed_items_count_t mTypedItemsCount; private: /** * Scrolling row panel. */ - class RowPanel: public LLScrollingPanel + class RowPanel: public LLPanel { public: RowPanel(const LLSysWellWindow* parent, const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId); virtual ~RowPanel(); - /*virtual*/ - void updatePanel(BOOL allow_modify); void onMouseEnter(S32 x, S32 y, MASK mask); void onMouseLeave(S32 x, S32 y, MASK mask); BOOL handleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index fb7574d68b..97a15759bf 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -44,7 +44,8 @@ using namespace LLNotificationsUI; LLToast::LLToast(LLToast::Params p) : LLFloater(LLSD()), mPanel(p.panel), mTimerValue(p.timer_period), - mID(p.id), + mNotificationID(p.notif_id), + mSessionID(p.session_id), mCanFade(p.can_fade), mCanBeStored(p.can_be_stored), mHideBtnEnabled(p.enable_hide_btn), @@ -73,12 +74,12 @@ LLToast::LLToast(LLToast::Params p) : LLFloater(LLSD()), setFocus(TRUE); } - - if(!p.on_toast_destroy.empty()) - mOnToastDestroy.connect(p.on_toast_destroy); + // init callbacks if present + if(!p.on_delete_toast.empty()) + mOnDeleteToastSignal.connect(p.on_delete_toast); if(!p.on_mouse_enter.empty()) - mOnMousEnter.connect(p.on_mouse_enter); + mOnMouseEnterSignal.connect(p.on_mouse_enter); } //-------------------------------------------------------------------------- @@ -102,6 +103,7 @@ void LLToast::setHideButtonEnabled(bool enabled) //-------------------------------------------------------------------------- LLToast::~LLToast() { + mOnToastDestroyedSignal(this); if(mIsModal) { gFocusMgr.unlockFocus(); @@ -142,7 +144,7 @@ void LLToast::hide() { setVisible(FALSE); mTimer.stop(); - mOnFade(this); + mOnFadeSignal(this); } //-------------------------------------------------------------------------- @@ -160,7 +162,7 @@ void LLToast::tick() { setVisible(FALSE); mTimer.stop(); - mOnFade(this); + mOnFadeSignal(this); } } @@ -223,7 +225,7 @@ void LLToast::setVisible(BOOL show) //-------------------------------------------------------------------------- void LLToast::onMouseEnter(S32 x, S32 y, MASK mask) { - mOnToastHover(this, MOUSE_ENTER); + mOnToastHoverSignal(this, MOUSE_ENTER); setBackgroundOpaque(TRUE); if(mCanFade) @@ -234,13 +236,13 @@ void LLToast::onMouseEnter(S32 x, S32 y, MASK mask) sendChildToFront(mHideBtn); if(mHideBtn && mHideBtn->getEnabled()) mHideBtn->setVisible(TRUE); - mOnMousEnter(this); + mOnMouseEnterSignal(this); } //-------------------------------------------------------------------------- void LLToast::onMouseLeave(S32 x, S32 y, MASK mask) { - mOnToastHover(this, MOUSE_LEAVE); + mOnToastHoverSignal(this, MOUSE_LEAVE); if(mCanFade) { @@ -270,21 +272,11 @@ BOOL LLToast::handleMouseDown(S32 x, S32 y, MASK mask) } //-------------------------------------------------------------------------- -void LLToast::discardNotification() -{ - if(mNotification) - { - mNotification->setIgnored(TRUE); - mNotification->respond(mNotification->getResponseTemplate()); - } -} - -//-------------------------------------------------------------------------- -bool LLToast::getIsNotificationUnResponded() +bool LLToast::isNotificationValid() { if(mNotification) { - return !mNotification->isRespondedTo(); + return !mNotification->isCancelled(); } return false; } diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h index 05e63a60c5..9248747c43 100644 --- a/indra/newview/lltoast.h +++ b/indra/newview/lltoast.h @@ -57,13 +57,14 @@ public: typedef boost::function<void (LLToast* toast)> toast_callback_t; typedef boost::signals2::signal<void (LLToast* toast)> toast_signal_t; - struct Params : public LLInitParam::Block<Params, LLFloater::Params> + struct Params { LLPanel* panel; - LLUUID id; //notification or message ID + LLUUID notif_id; //notification ID + LLUUID session_id; //im session ID LLNotificationPtr notification; F32 timer_period; - toast_callback_t on_toast_destroy; + toast_callback_t on_delete_toast; toast_callback_t on_mouse_enter; bool can_fade; bool can_be_stored; @@ -100,8 +101,6 @@ public: void insertPanel(LLPanel* panel); // get toast's panel LLPanel* getPanel() { return mPanel; } - // discard notification - void discardNotification(); // enable/disable Toast's Hide button void setHideButtonEnabled(bool enabled); // initialize and start Toast's timer @@ -120,8 +119,12 @@ public: // get/set Toast's flags or states - // get information whether the notification corresponding to the toast is responded or not - bool getIsNotificationUnResponded(); + // get information whether the notification corresponding to the toast is valid or not + bool isNotificationValid(); + // get toast's Notification ID + const LLUUID getNotificationID() { return mNotificationID;} + // get toast's Session ID + const LLUUID getSessionID() { return mSessionID;} // void setCanFade(bool can_fade); // @@ -132,18 +135,18 @@ public: void setModal(bool modal); - // Registers callbacks for events - toast_signal_t mOnFade; - toast_signal_t mOnMousEnter; - toast_signal_t mOnToastDestroy; - boost::signals2::connection setOnFadeCallback(toast_callback_t cb) { return mOnFade.connect(cb); } - boost::signals2::connection setOnMouseEnterCallback(toast_callback_t cb) { return mOnMousEnter.connect(cb); } - boost::signals2::connection setOnToastDestroyCallback(toast_callback_t cb) { return mOnToastDestroy.connect(cb); } + // Registers signals/callbacks for events + toast_signal_t mOnFadeSignal; + toast_signal_t mOnMouseEnterSignal; + toast_signal_t mOnDeleteToastSignal; + toast_signal_t mOnToastDestroyedSignal; + boost::signals2::connection setOnFadeCallback(toast_callback_t cb) { return mOnFadeSignal.connect(cb); } + boost::signals2::connection setOnToastDestroyedCallback(toast_callback_t cb) { return mOnToastDestroyedSignal.connect(cb); } typedef boost::function<void (LLToast* toast, bool mouse_enter)> toast_hover_check_callback_t; typedef boost::signals2::signal<void (LLToast* toast, bool mouse_enter)> toast_hover_check_signal_t; - toast_hover_check_signal_t mOnToastHover; - boost::signals2::connection setOnToastHoverCallback(toast_hover_check_callback_t cb) { return mOnToastHover.connect(cb); } + toast_hover_check_signal_t mOnToastHoverSignal; + boost::signals2::connection setOnToastHoverCallback(toast_hover_check_callback_t cb) { return mOnToastHoverSignal.connect(cb); } private: @@ -153,7 +156,8 @@ private: // on timer finished function void tick(); - LLUUID mID; + LLUUID mNotificationID; + LLUUID mSessionID; LLNotificationPtr mNotification; LLTimer mTimer; diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index d401943020..fe1492d937 100644 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -44,14 +44,14 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_instant_message.xml"); - mAvatar = getChild<LLAvatarIconCtrl>("avatar"); + LLIconCtrl* sys_msg_icon = getChild<LLIconCtrl>("sys_msg_icon"); + mAvatar = getChild<LLAvatarIconCtrl>("avatar_icon"); mUserName = getChild<LLTextBox>("user_name"); mTime = getChild<LLTextBox>("time_box"); mMessage = getChild<LLTextBox>("message"); mReplyBtn = getChild<LLButton>("reply"); mMessage->setValue(p.message); - mAvatar->setValue(p.avatar_id); mUserName->setValue(p.from); mTime->setValue(p.time); mSessionID = p.session_id; @@ -60,6 +60,9 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif // if message comes from the system - there shouldn't be a reply btn if(p.from == "Second Life") { + mAvatar->setVisible(FALSE); + sys_msg_icon->setVisible(TRUE); + mReplyBtn->setVisible(FALSE); S32 btn_height = mReplyBtn->getRect().getHeight(); LLRect msg_rect = mMessage->getRect(); @@ -68,6 +71,10 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif } else { + mAvatar->setVisible(TRUE); + sys_msg_icon->setVisible(FALSE); + + mAvatar->setValue(p.avatar_id); mReplyBtn->setClickedCallback(boost::bind(&LLToastIMPanel::onClickReplyBtn, this)); } @@ -88,9 +95,7 @@ LLToastIMPanel::~LLToastIMPanel() //-------------------------------------------------------------------------- void LLToastIMPanel::onClickReplyBtn() { - LLSD response = mNotification->getResponseTemplate(); - response["respondbutton"] = true; - mNotification->respond(response); + mNotification->respond(mNotification->getResponseTemplate()); } //-------------------------------------------------------------------------- diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h index b51ce23364..af21b07a3d 100644 --- a/indra/newview/lltoastimpanel.h +++ b/indra/newview/lltoastimpanel.h @@ -43,7 +43,7 @@ class LLToastIMPanel: public LLToastPanel { public: - struct Params : public LLInitParam::Block<Params> + struct Params { LLNotificationPtr notification; LLUUID avatar_id; diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index 844c54da6a..9761a45d83 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -171,6 +171,7 @@ LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : LLToas 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); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 08040cfaa5..c3064ffa43 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -54,6 +54,7 @@ #include "llmutelist.h" #include "llnotify.h" #include "llpreviewnotecard.h" +#include "llrecentpeople.h" #include "llselectmgr.h" #include "lltoolmgr.h" #include "lltooltip.h" @@ -646,6 +647,7 @@ void LLToolDragAndDrop::beginMultiDrag( void LLToolDragAndDrop::endDrag() { + mEndDragSignal(); LLSelectMgr::getInstance()->unhighlightAll(); setMouseCapture(FALSE); } diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index c9fef26b58..acf01869e7 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -51,6 +51,8 @@ class LLPickInfo; class LLToolDragAndDrop : public LLTool, public LLSingleton<LLToolDragAndDrop> { public: + typedef boost::signals2::signal<void ()> enddrag_signal_t; + LLToolDragAndDrop(); // overridden from LLTool @@ -87,6 +89,8 @@ public: const LLUUID& getObjectID() const { return mObjectID; } EAcceptance getLastAccept() { return mLastAccept; } + boost::signals2::connection setEndDragCallback( const enddrag_signal_t::slot_type& cb ) { return mEndDragSignal.connect(cb); } + protected: enum EDropTarget { @@ -131,6 +135,8 @@ protected: S32 mCurItemIndex; std::string mToolTipMsg; + enddrag_signal_t mEndDragSignal; + // array of pointers to functions that implement the logic to // dragging and dropping into the simulator. static dragOrDrop3dImpl sDragAndDrop3d[DAD_COUNT][DT_COUNT]; diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index b888560fc7..24f4745c18 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -72,6 +72,8 @@ #include "llvosurfacepatch.h" #include "llvowlsky.h" #include "llrender.h" +#include "llbottomtray.h" +#include "llnavigationbar.h" #ifdef TOGGLE_HACKED_GODLIKE_VIEWER BOOL gHackGodmode = FALSE; @@ -483,6 +485,36 @@ bool toggle_agent_pause(const LLSD& newvalue) return true; } +bool toggle_show_gesture_button(const LLSD& newvalue) +{ + LLBottomTray::getInstance()->showGestureButton(newvalue.asBoolean()); + return true; +} + +bool toggle_show_move_button(const LLSD& newvalue) +{ + LLBottomTray::getInstance()->showMoveButton(newvalue.asBoolean()); + return true; +} + +bool toggle_show_camera_button(const LLSD& newvalue) +{ + LLBottomTray::getInstance()->showCameraButton(newvalue.asBoolean()); + return true; +} + +bool toggle_show_navigation_panel(const LLSD& newvalue) +{ + LLNavigationBar::getInstance()->showNavigationPanel(newvalue.asBoolean()); + return true; +} + +bool toggle_show_favorites_panel(const LLSD& newvalue) +{ + LLNavigationBar::getInstance()->showFavoritesPanel(newvalue.asBoolean()); + return true; +} + //////////////////////////////////////////////////////////////////////////// void settings_setup_listeners() @@ -619,6 +651,11 @@ void settings_setup_listeners() gSavedSettings.getControl("QAMode")->getSignal()->connect(boost::bind(&show_debug_menus)); gSavedSettings.getControl("UseDebugMenus")->getSignal()->connect(boost::bind(&show_debug_menus)); gSavedSettings.getControl("AgentPause")->getSignal()->connect(boost::bind(&toggle_agent_pause, _2)); + gSavedSettings.getControl("ShowGestureButton")->getSignal()->connect(boost::bind(&toggle_show_gesture_button, _2)); + gSavedSettings.getControl("ShowMoveButton")->getSignal()->connect(boost::bind(&toggle_show_move_button, _2)); + gSavedSettings.getControl("ShowCameraButton")->getSignal()->connect(boost::bind(&toggle_show_camera_button, _2)); + gSavedSettings.getControl("ShowNavbarNavigationPanel")->getSignal()->connect(boost::bind(&toggle_show_navigation_panel, _2)); + gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2)); } #if TEST_CACHED_CONTROL diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 441d0ebeaa..51d699c0f7 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -69,6 +69,7 @@ #include "llfloatermediabrowser.h" #include "llfloaterhud.h" #include "llfloaterimagepreview.h" +#include "llimfloater.h" #include "llimpanel.h" #include "llfloaterinspect.h" #include "llfloaterinventory.h" @@ -143,6 +144,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("bumps", "floater_bumps.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBump>); LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>); + LLFloaterReg::add("camera_presets", "floater_camera_presets.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCameraPresets>); LLFloaterReg::add("chat", "floater_chat_history.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChat>); LLFloaterReg::add("nearby_chat", "floater_nearby_chat.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNearbyChat>); LLFloaterReg::add("communicate", "floater_chatterbox.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatterBox>); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 9d278e6a6c..a0bd5f301b 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3830,6 +3830,7 @@ void handle_reset_view() } else { + gAgent.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); reset_view_final( TRUE ); LLFloaterCamera::resetCameraMode(); } diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index e37bad6b0e..390e1fe032 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -98,6 +98,7 @@ #include "llnotifications.h" #include "llnotify.h" #include "llpanelgrouplandmoney.h" +#include "llrecentpeople.h" #include "llselectmgr.h" #include "llsidetray.h" #include "llstartup.h" diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 5fd5397970..d23f10f880 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1559,9 +1559,19 @@ void LLViewerWindow::initWorldUI() navbar->showFavoritesPanel(FALSE); } - if (!gSavedSettings.getBOOL("ShowCameraAndMoveControls")) + if (!gSavedSettings.getBOOL("ShowCameraButton")) { - LLBottomTray::getInstance()->showCameraAndMoveControls(FALSE); + LLBottomTray::getInstance()->showCameraButton(FALSE); + } + + if (!gSavedSettings.getBOOL("ShowMoveButton")) + { + LLBottomTray::getInstance()->showMoveButton(FALSE); + } + + if (!gSavedSettings.getBOOL("ShowGestureButton")) + { + LLBottomTray::getInstance()->showGestureButton(FALSE); } getRootView()->addChild(gStatusBar); @@ -1577,13 +1587,6 @@ void LLViewerWindow::initWorldUI() // menu holder appears on top to get first pass at all mouse events getRootView()->sendChildToFront(gMenuHolder); - //Channel Manager - LLNotificationsUI::LLChannelManager* channel_manager = LLNotificationsUI::LLChannelManager::getInstance(); - getRootView()->addChild(channel_manager); - //Notification Manager - LLNotificationsUI::LLNotificationManager* notify_manager = LLNotificationsUI::LLNotificationManager::getInstance(); - getRootView()->addChild(notify_manager); - if ( gHUDView == NULL ) { LLRect hud_rect = full_window; @@ -2859,6 +2862,9 @@ void LLViewerWindow::updateWorldViewRect(bool use_full_window) if (mWorldViewRect != new_world_rect) { + // sending a signal with a new WorldView rect + mOnWorldViewRectUpdated(mWorldViewRect, new_world_rect); + mWorldViewRect = new_world_rect; gResizeScreenTexture = TRUE; LLViewerCamera::getInstance()->setViewHeightInPixels( mWorldViewRect.getHeight() ); @@ -3215,9 +3221,18 @@ LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_trans pickAsync(x, y_from_bot, gKeyboard->currentMask(TRUE), NULL, pick_transparent); // assume that pickAsync put the results in the back of the mPicks list + if(mPicks.size() != 0) + { mLastPick = mPicks.back(); mLastPick.fetchResults(); mPicks.pop_back(); + } + else + { + llwarns << "List of last picks is empty" << llendl; + llwarns << "Using stub pick" << llendl; + mLastPick = LLPickInfo(); + } return mLastPick; } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index e4f6240fc7..231b857d1f 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -196,6 +196,11 @@ public: typedef boost::signals2::signal<void (void)> bottom_tray_signal_t; bottom_tray_signal_t mOnBottomTrayWidthChanged; boost::signals2::connection setOnBottomTrayWidthChanged(bottom_tray_callback_t cb) { return mOnBottomTrayWidthChanged.connect(cb); } + // signal on update of WorldView rect + typedef boost::function<void (LLRect old_world_rect, LLRect new_world_rect)> world_rect_callback_t; + typedef boost::signals2::signal<void (LLRect old_world_rect, LLRect new_world_rect)> world_rect_signal_t; + world_rect_signal_t mOnWorldViewRectUpdated; + boost::signals2::connection setOnWorldViewRectUpdated(world_rect_callback_t cb) { return mOnWorldViewRectUpdated.connect(cb); } // // ACCESSORS diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 2304571cf1..6401389c8f 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -46,6 +46,7 @@ # include "expat/expat.h" #endif #include "llcallbacklist.h" +#include "llcallingcard.h" // for LLFriendObserver #include "llviewerregion.h" #include "llviewernetwork.h" // for gGridChoice #include "llbase64.h" diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index fe99e787da..bddd18dee8 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -42,9 +42,9 @@ class LLVivoxProtocolParser; #include "v3math.h" #include "llframetimer.h" #include "llviewerregion.h" -#include "llcallingcard.h" // for LLFriendObserver #include "m3math.h" // LLMatrix3 +class LLFriendObserver; class LLVoiceClientParticipantObserver { public: diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 8440022844..46c294768d 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -514,6 +514,9 @@ <texture name="toolbar_btn_disabled.tga" scale.left="7" scale.top="32" scale.right="121" scale.bottom="0" /> <texture name="toolbar_btn_selected.tga" scale.left="7" scale.top="32" scale.right="121" scale.bottom="0" /> + <texture name="toggle_button_off" file_name="toggle_button_off.png" preload="true" /> + <texture name="toggle_button_selected" file_name="toggle_button_selected.png" preload="true" /> + <!--TODO: REPLACE CODE REFERENCE TO THIS ART WITH Icon_Minimize_Background above --> <texture name="minimize_inactive.tga" preload="true" /> <texture name="minimize.tga" preload="true" /> diff --git a/indra/newview/skins/default/xui/en/favorites_bar_button.xml b/indra/newview/skins/default/xui/en/favorites_bar_button.xml index e2f81168fa..dcd85e1f58 100644 --- a/indra/newview/skins/default/xui/en/favorites_bar_button.xml +++ b/indra/newview/skins/default/xui/en/favorites_bar_button.xml @@ -6,9 +6,13 @@ halign="center" height="23" image_disabled="transparent.j2c" - image_disabled_selected="PushButton_Selected" - image_selected="PushButton_Selected" - image_unselected="PushButton_Off" + image_disabled_selected="transparent.j2c" + image_selected="transparent.j2c" + image_unselected="transparent.j2c" + image_hover_selected="PushButton_Selected" + image_hover_unselected="PushButton_Off" + image_pressed="PushButton_Press" + image_pressed_selected="PushButton_Selected_Press" hover_glow_amount="0.15" layout="topleft" left="2" diff --git a/indra/newview/skins/default/xui/en/floater_camera.xml b/indra/newview/skins/default/xui/en/floater_camera.xml index 1592ed4aa4..0784b88944 100644 --- a/indra/newview/skins/default/xui/en/floater_camera.xml +++ b/indra/newview/skins/default/xui/en/floater_camera.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater + can_dock="true" can_minimize="false" can_close="false" center_horiz="true" @@ -29,6 +30,7 @@ layout="topleft" left="0" top="0" + mouse_opaque="false" name="controls" width="105"> <joystick_rotate @@ -67,7 +69,7 @@ height="64" image_unselected="cam_zoom_out.tga" layout="topleft" - left_delta="74" + left_delta="70" minus_image="cam_zoom_minus_in.tga" name="zoom" picture_style="true" diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index a5d76821a8..be1278e8cc 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -9,7 +9,7 @@ help_topic="panel_im" top="0" can_dock="true" - can_minimize="false" + can_minimize="true" visible="true" width="315"> <layout_stack follows="left|top|right|bottom" diff --git a/indra/newview/skins/default/xui/en/floater_moveview.xml b/indra/newview/skins/default/xui/en/floater_moveview.xml index dc0ff7ac4c..82acea47be 100644 --- a/indra/newview/skins/default/xui/en/floater_moveview.xml +++ b/indra/newview/skins/default/xui/en/floater_moveview.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater + can_dock="true" can_close="false" can_minimize="false" center_horiz="true" @@ -41,6 +42,7 @@ follows="left|top" layout="topleft" left="0" + mouse_opaque="false" name="panel_actions" top="0" width="115"> @@ -95,7 +97,7 @@ scale_image="false" tool_tip="Fly Down, Press "C"" top_delta="0" - width="25" /> + width="20" /> <joystick_turn follows="left|bottom" height="25" diff --git a/indra/newview/skins/default/xui/en/floater_sys_well.xml b/indra/newview/skins/default/xui/en/floater_sys_well.xml index 3f31641cac..30406cad63 100644 --- a/indra/newview/skins/default/xui/en/floater_sys_well.xml +++ b/indra/newview/skins/default/xui/en/floater_sys_well.xml @@ -11,7 +11,7 @@ title="NOTIFICATIONS" width="320" min_width="320" - height="60" + height="23" can_minimize="false" can_tear_off="false" can_resize="false" @@ -19,36 +19,13 @@ can_close="false" can_dock="true" > - <scroll_container - follows="top|bottom" + <flat_list_view + color="FloaterDefaultBackgroundColor" + follows="all" layout="topleft" - name="notification_list_container" - left="0" - top="18" - width="320" - height="42"> - <panel - layout="topleft" - name="twin_list_panel" - left="1" - top="0" - width="318" - height="32" - bg_alpha_color="1.0 1.0 1.0 1.0"> - <scrolling_panel_list - layout="topleft" name="notification_list" left="1" - top="0" + top="20" height="0" width="318"/> - <scrolling_panel_list - layout="topleft" - name="im_row_panel_list" - left="1" - top="2" - height="0" - width="318"/> - </panel> -</scroll_container> </floater> diff --git a/indra/newview/skins/default/xui/en/menu_bottomtray.xml b/indra/newview/skins/default/xui/en/menu_bottomtray.xml new file mode 100644 index 0000000000..e98920f8c2 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_bottomtray.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu + height="201" + layout="topleft" + left="100" + mouse_opaque="false" + name="hide_camera_move_controls_menu" + top="624" + visible="false" + width="128"> + <menu_item_check + label="Gesture button" + layout="topleft" + name="ShowGestureButton"> + <menu_item_check.on_click + function="ToggleControl" + parameter="ShowGestureButton" /> + <menu_item_check.on_check + function="CheckControl" + parameter="ShowGestureButton" /> + </menu_item_check> + <menu_item_check + label="Move button" + layout="topleft" + name="ShowMoveButton"> + <menu_item_check.on_click + function="ToggleControl" + parameter="ShowMoveButton" /> + <menu_item_check.on_check + function="CheckControl" + parameter="ShowMoveButton" /> + </menu_item_check> + <menu_item_check + label="Camera button" + layout="topleft" + name="ShowCameraButton"> + <menu_item_check.on_click + function="ToggleControl" + parameter="ShowCameraButton" /> + <menu_item_check.on_check + function="CheckControl" + parameter="ShowCameraButton" /> + </menu_item_check> +</menu> diff --git a/indra/newview/skins/default/xui/en/menu_hide_navbar.xml b/indra/newview/skins/default/xui/en/menu_hide_navbar.xml index 1ad10abbeb..a175b3103f 100644 --- a/indra/newview/skins/default/xui/en/menu_hide_navbar.xml +++ b/indra/newview/skins/default/xui/en/menu_hide_navbar.xml @@ -11,23 +11,23 @@ <menu_item_check label="Show Navigation Bar" layout="topleft" - name="Show Navigation Bar"> + name="ShowNavbarNavigationPanel"> <menu_item_check.on_click - function="HideNavbarMenu.Action" - parameter="show_navbar_navigation_panel" /> + function="ToggleControl" + parameter="ShowNavbarNavigationPanel" /> <menu_item_check.on_check - function="HideNavbarMenu.EnableMenuItem" - parameter="show_navbar_navigation_panel" /> + function="CheckControl" + parameter="ShowNavbarNavigationPanel" /> </menu_item_check> <menu_item_check label="Show Favorites Bar" layout="topleft" - name="Show Favorites Bar"> + name="ShowNavbarFavoritesPanel"> <menu_item_check.on_click - function="HideNavbarMenu.Action" - parameter="show_navbar_favorites_panel" /> + function="ToggleControl" + parameter="ShowNavbarFavoritesPanel" /> <menu_item_check.on_check - function="HideNavbarMenu.EnableMenuItem" - parameter="show_navbar_favorites_panel" /> + function="CheckControl" + parameter="ShowNavbarFavoritesPanel" /> </menu_item_check> </menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml new file mode 100644 index 0000000000..ec8582f5b5 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false" opaque="true" color="MenuDefaultBgColor" drop_shadow="true"> + <menu_item_check + label="Display Group Icons" + layout="topleft" + name="Display Group Icons"> + <menu_item_check.on_click + function="People.Groups.ViewSort.Action" + parameter="show_icons" /> + <menu_item_check.on_check + function="CheckControl" + parameter="GroupListShowIcons" /> + </menu_item_check> +</menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml new file mode 100644 index 0000000000..643336cf6c --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<context_menu + layout="topleft" + name="Avatar Context Menu"> + <menu_item_call + label="View Profile" + layout="topleft" + name="View Profile"> + <menu_item_call.on_click + function="Avatar.Profile" /> + </menu_item_call> + <menu_item_call + label="Add Friend" + layout="topleft" + name="Add Friend"> + <menu_item_call.on_click + function="Avatar.AddFriend" /> + <menu_item_call.on_enable + function="Avatar.EnableItem" + parameter="can_add" /> + </menu_item_call> + <menu_item_call + label="IM" + layout="topleft" + name="IM"> + <menu_item_call.on_click + function="Avatar.IM" /> + </menu_item_call> + <menu_item_call + enabled="false" + label="Call" + layout="topleft" + name="Call"> + <menu_item_call.on_click + function="Avatar.Call" /> + </menu_item_call> + <menu_item_call + enabled="false" + label="Share" + layout="topleft" + name="Share"> + <menu_item_call.on_click + function="Avatar.Share" /> + </menu_item_call> + <menu_item_call + label="Pay" + layout="topleft" + name="Pay"> + <menu_item_call.on_click + function="Avatar.Pay" /> + </menu_item_call> + <menu_item_check + label="Block/Unblock" + layout="topleft" + name="Block/Unblock"> + <menu_item_check.on_click + function="Avatar.BlockUnblock" /> + <menu_item_check.on_check + function="Avatar.CheckItem" + parameter="is_blocked" /> + <menu_item_check.on_enable + function="Avatar.EnableItem" + parameter="can_block" /> + </menu_item_check> +</context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml index 88b0528e53..d9606de90d 100644 --- a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml @@ -2,8 +2,8 @@ <menu name="menu_group_plus" left="0" bottom="0" visible="false" mouse_opaque="false" opaque="true" color="MenuDefaultBgColor" drop_shadow="true"> - <menu_item_call name="sort_most" label="Sort by Most Speakers"> - <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="sort_most" /> + <menu_item_call name="sort_most" label="Sort by Most Recent"> + <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="sort_recent" /> </menu_item_call> <menu_item_call name="sort_name" label="Sort by Name"> <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="sort_name" /> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index b430de2b7b..bc51ba5e6a 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -127,12 +127,14 @@ <menu_item_call.on_click function="World.SetAway" /> </menu_item_call> + <menu_item_separator + layout="topleft"/> <menu_item_call label="Set Busy" layout="topleft" name="Set Busy"> <menu_item_call.on_click - function="World.SetBusy" /> + function="World.SetBusy"/> </menu_item_call> </menu> <menu_item_separator @@ -381,7 +383,7 @@ <menu_item_check.on_click function="Floater.Toggle" parameter="world_map" /> - </menu_item_check> + </menu_item_check> <menu_item_check label="Mini-Map" layout="topleft" @@ -395,7 +397,31 @@ parameter="mini_map" /> </menu_item_check> <menu_item_separator - layout="topleft" /> + layout="topleft" /> + <menu_item_check + label="Show Navigation Bar" + layout="topleft" + name="ShowNavbarNavigationPanel"> + <menu_item_check.on_click + function="ToggleControl" + parameter="ShowNavbarNavigationPanel" /> + <menu_item_check.on_check + function="CheckControl" + parameter="ShowNavbarNavigationPanel" /> + </menu_item_check> + <menu_item_check + label="Show Favorites Bar" + layout="topleft" + name="ShowNavbarFavoritesPanel"> + <menu_item_check.on_click + function="ToggleControl" + parameter="ShowNavbarFavoritesPanel" /> + <menu_item_check.on_check + function="CheckControl" + parameter="ShowNavbarFavoritesPanel" /> + </menu_item_check> + <menu_item_separator + layout="topleft" /> <menu_item_call label="Take Snapshot" layout="topleft" @@ -466,8 +492,6 @@ parameter="editor" /> </menu_item_call> </menu> - <menu_item_separator - layout="topleft" /> </menu> <menu create_jump_keys="true" 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 f211ae0ad6..d5ed0c986d 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,7 +38,7 @@ width="20" /> <text follows="left|right" - font="SansSerifBigBold" + font="SansSerifSmallBold" height="20" layout="topleft" left_pad="5" diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index 6449059ef0..90b331a39f 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel - mouse_opaque="false" + mouse_opaque="true" background_visible="true" bg_alpha_color="0.25 0.25 0.25 1" bg_opaque_color="0.25 0.25 0.25 1" @@ -36,13 +36,13 @@ width="5"/> <layout_panel mouse_opaque="false" - auto_resize="false" + auto_resize="true" follows="left|right" height="28" layout="topleft" left="5" min_height="28" - width="600" + width="450" top="0" min_width="305" name="chat_bar" @@ -110,46 +110,13 @@ name="cam_panel" top_delta="-10" width="150"> - <split_button - follows="right" - height="18" - name="presets" - top="6" - width="35"> - <split_button.arrow_button - image_selected="camera_presets/camera_presets_arrow.png" - image_unselected="camera_presets/camera_presets_arrow.png" - image_disabled_selected="camera_presets/camera_presets_arrow.png" - image_disabled="camera_presets/camera_presets_arrow.png" - name="camera_presets" - tool_tip="Camera Presets" - /> - <split_button.item - image_selected="camera_presets/camera_presets_rear_view.png" - image_unselected="camera_presets/camera_presets_rear_view.png" - name="rear_view" - tool_tip="Rear View" - /> - <split_button.item - image_selected="camera_presets/camera_presets_34_view.png" - image_unselected="camera_presets/camera_presets_34_view.png" - name="group_view" - tool_tip="Group View" - /> - <split_button.item - image_selected="camera_presets/camera_presets_fron_view.png" - image_unselected="camera_presets/camera_presets_fron_view.png" - name="front_view" - tool_tip="Front View" - /> - </split_button> <button follows="right" height="20" is_toggle="true" - label="Camera" + label="View" layout="topleft" - left_pad="0" + left="0" tab_stop="false" tool_tip="Shows/Hide Camera controls" top="6" @@ -159,11 +126,28 @@ function="Button.SetFloaterToggle" parameter="camera" /> </button> + <button + follows="right" + name="camera_presets_btn" + top="6" + height="20" + width="20" + left_pad="0" + tab_stop="false" + is_toggle="true" + picture_style="true" + image_selected="toggle_button_selected" + image_unselected="toggle_button_off"> + <button.init_callback + function="Button.SetFloaterToggle" + parameter="camera_presets" + /> + </button> <split_button arrow_position="right" follows="right" height="18" - left_pad="0" + left_pad="4" name="snapshots" top="6" width="35"> diff --git a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml index 311caa4866..c4c8aa24c4 100644 --- a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml +++ b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml @@ -16,7 +16,10 @@ name="want_apply_text"> Do you want to apply these changes? </panel.string> - + <panel.string + name="group_join_btn"> + Join (L$[AMOUNT]) + </panel.string> <button layout="topleft" name="back" @@ -39,6 +42,18 @@ follows="top|left|right" mouse_opaque="true" name="group_name">(Loading...)</text> + <line_editor + follows="left|top" + font="SansSerif" + label="Type your new group name here" + layout="topleft" + left_delta="0" + max_length="35" + name="group_name_editor" + top_delta="5" + width="250" + height="20" + visible="true" /> <texture_picker follows="left|top" height="113" @@ -47,7 +62,7 @@ left="10" name="insignia" tool_tip="Click to choose a picture" - top_pad="15" + top_pad="5" width="100" /> <text type="string" @@ -72,11 +87,21 @@ use_ellipses="true" width="140" /> <button + follows="left|top" + left_delta="0" + top_pad="10" + height="20" + label="Join" + label_selected="Join" + name="btn_join" + visible="true" + width="65" /> + <button top="632" height="20" font="SansSerifSmall" - label="Apply" - label_selected="Apply" + label="Save" + label_selected="Save" name="btn_apply" left="5" width="65" /> @@ -93,13 +118,21 @@ <button top="632" height="20" - font="SansSerifSmall" label="Create" label_selected="Create" name="btn_create" left="5" visible="false" width="65" /> + <button + top="632" + left="75" + height="20" + label="Cancel" + label_selected="Cancel" + name="btn_cancel" + visible="false" + width="65" /> <accordion layout="topleft" left="2" width="296" top="135" height="500" follows="all" name="group_accordion"> <accordion_tab min_height="445" title="Group General" name="group_general_tab"> <panel class="panel_group_general" filename="panel_group_general.xml" name="group_general_tab_panel"/> diff --git a/indra/newview/skins/default/xui/en/panel_group_list_item.xml b/indra/newview/skins/default/xui/en/panel_group_list_item.xml new file mode 100644 index 0000000000..3f49bfad36 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_group_list_item.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="top|right|left" + height="24" + layout="topleft" + left="0" + name="group_list_item" + top="0" + width="320"> + <icon + follows="top|right|left" + height="24" + image_name="ListItem_Over" + layout="topleft" + left="0" + name="hovered_icon" + top="0" + visible="false" + width="320" /> + <icon + height="24" + follows="top|right|left" + image_name="ListItem_Select" + layout="topleft" + left="0" + name="selected_icon" + top="0" + visible="false" + width="320" /> + <icon + follows="top|left" + height="18" + image_name="icon_group.tga" + layout="topleft" + left="5" + mouse_opaque="true" + name="group_icon" + top="4" + width="18" /> + <text + follows="left|right" + font="SansSerifSmall" + height="17" + layout="topleft" + left_pad="5" + name="group_name" + text_color="white" + top="7" + use_ellipses="true" + value="Unknown" + width="263" /> + <button + follows="right" + height="18" + image_disabled="Info" + image_disabled_selected="Info" + image_hover_selected="Info" + image_selected="Info" + image_unselected="Info" + layout="topleft" + name="info_btn" + picture_style="true" + right="-5" + top="4" + width="18" /> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_instant_message.xml b/indra/newview/skins/default/xui/en/panel_instant_message.xml index ace456918c..7db1b9e916 100644 --- a/indra/newview/skins/default/xui/en/panel_instant_message.xml +++ b/indra/newview/skins/default/xui/en/panel_instant_message.xml @@ -33,7 +33,17 @@ layout="topleft" left="5" mouse_opaque="true" - name="avatar" + name="avatar_icon" + top="5" + width="20" /> + <icon + follows="right" + height="20" + image_name="icon_top_pick.tga" + layout="topleft" + left="5" + mouse_opaque="true" + name="sys_msg_icon" top="5" width="20" /> <text diff --git a/indra/newview/skins/default/xui/en/panel_navigation_bar.xml b/indra/newview/skins/default/xui/en/panel_navigation_bar.xml index 13ceab7604..1178d75b2c 100644 --- a/indra/newview/skins/default/xui/en/panel_navigation_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_navigation_bar.xml @@ -135,6 +135,7 @@ layout="topleft" left="5" name="favorite" + image_drag_indication="Arrow_Down" chevron_button_tool_tip="Show more of My Favorites" top="32" width="590" /> diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 132a2d1dc5..786c39c5e9 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -127,7 +127,6 @@ color="DkGray" name="tab_online" title="Online"> <avatar_list - draw_heading="false" follows="all" height="150" layout="topleft" @@ -143,7 +142,6 @@ color="DkGray" name="tab_all" title="All"> <avatar_list - draw_heading="false" follows="all" height="230" layout="topleft" @@ -219,7 +217,6 @@ color="DkGray" name="groups_panel" width="313"> <group_list - draw_heading="false" follows="left|top|right|bottom" height="357" layout="topleft" @@ -240,7 +237,6 @@ color="DkGray" name="bottom_panel" width="313"> <button - enabled="false" follows="bottom|left" font="SansSerifBigBold" tool_tip="Change sort and view of Groups list" @@ -250,7 +246,7 @@ color="DkGray" image_unselected="OptionsMenu_Off" layout="topleft" left="10" - name="gear_btn" + name="groups_viewsort_btn" picture_style="true" top="5" width="18" /> @@ -307,7 +303,7 @@ color="DkGray" layout="topleft" name="recent_panel" width="313"> - <avatar_list_tmp + <avatar_list color="DkGray2" follows="left|top|right|bottom" height="357" diff --git a/indra/newview/skins/default/xui/en/panel_pick_list_item.xml b/indra/newview/skins/default/xui/en/panel_pick_list_item.xml index 37e873b6a8..686faab8eb 100644 --- a/indra/newview/skins/default/xui/en/panel_pick_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_pick_list_item.xml @@ -9,6 +9,7 @@ top="0" width="305"> <icon + follows="top|bottom|left|right" height="120" image_name="ListItem_Over" left="0" @@ -19,6 +20,7 @@ visible="false" width="305"/> <icon + follows="top|bottom|left|right" height="120" image_name="ListItem_Select" left="0" diff --git a/indra/newview/skins/default/xui/en/panel_picks.xml b/indra/newview/skins/default/xui/en/panel_picks.xml index bceaedcdb4..d0bde77cf2 100644 --- a/indra/newview/skins/default/xui/en/panel_picks.xml +++ b/indra/newview/skins/default/xui/en/panel_picks.xml @@ -8,24 +8,16 @@ name="panel_picks" top="0" width="313"> - <scroll_container + <flat_list_view color="DkGray2" follows="left|top|right|bottom" height="495" layout="topleft" left="0" - name="profile_scroll" + name="picks_list" opaque="true" top="0" - width="313"> - <list - height="120" - layout="topleft" - left="0" - name="picks_list" - top="0" - width="305" /> - </scroll_container> + width="313" /> <panel background_visible="true" bevel_style="none" diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml index 7f72c7b0e9..56ba5970f2 100644 --- a/indra/newview/skins/default/xui/en/panel_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_profile.xml @@ -72,15 +72,16 @@ top_delta="0" value="[SECOND_LIFE]:" width="170" /> - <text + <expandable_text follows="left|top|right" height="90" layout="topleft" name="sl_description_edit" width="170" - word_wrap="true"> + expanded_bg_visible="true" + expanded_bg_color="black"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. - </text> + </expandable_text> </panel> <panel follows="left|top" @@ -112,15 +113,16 @@ top_delta="0" value="Real World:" width="175" /> - <text + <expandable_text follows="left|top|right" height="90" layout="topleft" name="fl_description_edit" width="180" - word_wrap="true"> + expanded_bg_visible="true" + expanded_bg_color="black"> Lorem ipsum dolor sit amet, consectetur adlkjpiscing elit moose moose. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet. adipiscing elit. Aenean rigviverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet sorbet ipsum. adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. - </text> + </expandable_text> </panel> <text type="string" diff --git a/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml b/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml index a5c3be3349..f25c4bfbba 100644 --- a/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml +++ b/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml @@ -4,6 +4,7 @@ bottom="0" follows="left|top|right" height="30" + width="305" layout="topleft" left="0" name="sidetray_tab_panel"> diff --git a/indra/newview/skins/default/xui/en/panel_sys_well_item.xml b/indra/newview/skins/default/xui/en/panel_sys_well_item.xml index 3ac15fe550..53ee0d159d 100644 --- a/indra/newview/skins/default/xui/en/panel_sys_well_item.xml +++ b/indra/newview/skins/default/xui/en/panel_sys_well_item.xml @@ -4,7 +4,7 @@ <panel name="sys_well_item" title="sys_well_item" - visible="false" + visible="true" top="0" left="0" width="318" 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 70198dc626..a0fc986423 100644 --- a/indra/newview/skins/default/xui/en/panel_teleport_history.xml +++ b/indra/newview/skins/default/xui/en/panel_teleport_history.xml @@ -13,10 +13,9 @@ <accordion_tab can_resize="false" layout="topleft" - min_height="100" name="today" title="Today"> - <scroll_list + <flat_list_view draw_heading="false" follows="all" height="150" @@ -24,20 +23,16 @@ left="0" name="today_items" top="0" - width="285"> - <column name="landmark_icon" width="20" /> - <column dynamic_width="true" label="Region" name="region" /> - <column name="index" width="0" /> - </scroll_list> + width="380"> + </flat_list_view> </accordion_tab> <accordion_tab can_resize="false" layout="topleft" - min_height="100" name="yesterday" title="Yesterday"> - <scroll_list + <flat_list_view draw_heading="false" follows="all" height="150" @@ -45,20 +40,33 @@ left="0" name="yesterday_items" top="0" - width="285"> - <column name="landmark_icon" width="20" /> - <column dynamic_width="true" label="Region" name="region" /> - <column name="index" width="0" /> - </scroll_list> + width="380"> + </flat_list_view> + </accordion_tab> + + <accordion_tab + can_resize="false" + layout="topleft" + name="2_days_ago" + title="2 days ago"> + <flat_list_view + draw_heading="false" + follows="all" + height="150" + layout="topleft" + left="0" + name="2_days_ago" + top="0" + width="380"> + </flat_list_view> </accordion_tab> <accordion_tab can_resize="false" layout="topleft" - min_height="100" name="3_days_ago" title="3 days ago"> - <scroll_list + <flat_list_view draw_heading="false" follows="all" height="150" @@ -66,20 +74,16 @@ left="0" name="3_days_ago" top="0" - width="285"> - <column name="landmark_icon" width="20" /> - <column dynamic_width="true" label="Region" name="region" /> - <column name="index" width="0" /> - </scroll_list> + width="380"> + </flat_list_view> </accordion_tab> <accordion_tab can_resize="false" layout="topleft" - min_height="100" name="4_days_ago" title="4 days ago"> - <scroll_list + <flat_list_view draw_heading="false" follows="all" height="150" @@ -87,20 +91,16 @@ left="0" name="4_days_ago" top="0" - width="285"> - <column name="landmark_icon" width="20" /> - <column dynamic_width="true" label="Region" name="region" /> - <column name="index" width="0" /> - </scroll_list> + width="380"> + </flat_list_view> </accordion_tab> <accordion_tab can_resize="false" layout="topleft" - min_height="100" name="5_days_ago" title="5 days ago"> - <scroll_list + <flat_list_view draw_heading="false" follows="all" height="150" @@ -108,55 +108,59 @@ left="0" name="5_days_ago_items" top="0" - width="285"> - <column name="landmark_icon" width="20" /> - <column dynamic_width="true" label="Region" name="region" /> - <column name="index" width="0" /> - </scroll_list> + width="380"> + </flat_list_view> </accordion_tab> <accordion_tab can_resize="false" layout="topleft" - min_height="100" - name="6_days_ago" - title="6 days ago"> - <scroll_list + name="6_days_and_older" + title="6 days and older"> + <flat_list_view draw_heading="false" follows="all" height="150" layout="topleft" left="0" - name="6_days_ago" + name="6_days_and_older_items" top="0" - width="285"> - <column name="landmark_icon" width="20" /> - <column dynamic_width="true" label="Region" name="region" /> - <column name="index" width="0" /> - </scroll_list> + width="380"> + </flat_list_view> </accordion_tab> - + <accordion_tab can_resize="false" layout="topleft" - min_height="100" - name="older_than_6_days" - title="Older than 6 days"> - <scroll_list + name="1_month_and_older" + title="1 month and older"> + <flat_list_view draw_heading="false" follows="all" height="150" layout="topleft" left="0" - name="older_than_6_days_items" + name="1_month_and_older_items" top="0" - width="285"> - <column name="landmark_icon" width="20" /> - <column dynamic_width="true" label="Region" name="region" /> - <column name="index" width="0" /> - </scroll_list> + width="380"> + </flat_list_view> </accordion_tab> - + + <accordion_tab + can_resize="false" + layout="topleft" + name="6_months_and_older" + title="6 months and older"> + <flat_list_view + draw_heading="false" + follows="all" + height="150" + layout="topleft" + left="0" + name="6_months_and_older_items" + top="0" + width="380"> + </flat_list_view> + </accordion_tab> </accordion> - </panel> diff --git a/indra/newview/skins/default/xui/en/panel_teleport_history_item.xml b/indra/newview/skins/default/xui/en/panel_teleport_history_item.xml new file mode 100644 index 0000000000..bb37f43220 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_teleport_history_item.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="top|right|left" + height="20" + layout="topleft" + left="0" + name="teleport_history_item" + top="0" + width="380"> + <icon + follows="top|right|left" + height="20" + image_name="ListItem_Over" + layout="topleft" + left="0" + name="hovered_icon" + top="0" + visible="false" + width="380" /> + <icon + height="20" + follows="top|right|left" + image_name="ListItem_Select" + layout="topleft" + left="0" + name="selected_icon" + top="0" + visible="false" + width="380" /> + <icon + height="20" + follows="top|right|left" + image_name="ListItem_Select" + layout="topleft" + left="0" + name="landmark_icon" + top="0" + visible="false" + width="20" /> + <text + follows="left|right" + height="20" + layout="topleft" + left_pad="5" + name="region" + text_color="white" + top="4" + value="Unknown" + width="380" /> + <button + follows="right" + height="18" + image_disabled="Info" + image_disabled_selected="Info" + image_hover_selected="Info" + image_selected="Info" + image_unselected="Info" + layout="topleft" + name="info_btn" + picture_style="true" + visible="false" + right="-5" + top="2" + width="18" /> +</panel> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 2eb9ba6d07..13a53a4ce3 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -1819,7 +1819,7 @@ this texture in your inventory <string name="AnimFlagStart" value=" Start Animation : " /> <string name="Wave" value=" Wave " /> <string name="HelloAvatar" value=" Hello, avatar! " /> - <string name="ViewAllGestures" value=" View All >>" /> + <string name="ViewAllGestures" value=" View All >>" /> <!-- inventory filter --> <!-- use value="" because they have preceding spaces --> diff --git a/indra/newview/skins/default/xui/en/widgets/expandable_text.xml b/indra/newview/skins/default/xui/en/widgets/expandable_text.xml new file mode 100644 index 0000000000..bcfc2f6472 --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/expandable_text.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<expandable_text + max_height="300" > + <textbox + follows="left|top" + name="text" + use_ellipses="true" + word_wrap="true" + tab_stop="true" + v_pad="2" + h_pad="3" > + <expand_textbox + name="expand" + follows="bottom|right" + visible="false" + bg_visible="false" + tab_stop="false" + v_pad="0" + h_pad="0" + text="[http://www.DUMMY.com More]" + tool_tip="Click to expand text"/> + </textbox> + <scroll + name="scroll" + follows="all" + /> +</expandable_text>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/widgets/flat_list_view.xml b/indra/newview/skins/default/xui/en/widgets/flat_list_view.xml index 24d072a573..8078579806 100644 --- a/indra/newview/skins/default/xui/en/widgets/flat_list_view.xml +++ b/indra/newview/skins/default/xui/en/widgets/flat_list_view.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <flat_list_view allow_select="true" - item_pad="5" + color="DkGray2" + item_pad="0" keep_one_selected="true" multi_select="false" opaque="true" />
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/widgets/list.xml b/indra/newview/skins/default/xui/en/widgets/list.xml deleted file mode 100644 index 4a59716464..0000000000 --- a/indra/newview/skins/default/xui/en/widgets/list.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<list - allow_select="true" - bg_opaque_color="PanelFocusBackgroundColor" - bg_alpha_color="PanelDefaultBackgroundColor" - background_visible="false" - background_opaque="false" - item_pad="5" - keep_one_selected="true" - multi_select="false" />
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/widgets/location_input.xml b/indra/newview/skins/default/xui/en/widgets/location_input.xml index a37ed76c51..df8a9431d7 100644 --- a/indra/newview/skins/default/xui/en/widgets/location_input.xml +++ b/indra/newview/skins/default/xui/en/widgets/location_input.xml @@ -44,7 +44,8 @@ label="" pad_right="0" tool_tip="My Location History"/> - <combo_list bg_writeable_color="MenuDefaultBgColor" page_lines="10"/> + <combo_list bg_writeable_color="MenuDefaultBgColor" page_lines="10" + scroll_bar_bg_visible="true" /> <combo_editor name="Combo Text Entry" text_pad_left="20" select_on_focus="false" diff --git a/indra/newview/skins/default/xui/en/widgets/scroll_list.xml b/indra/newview/skins/default/xui/en/widgets/scroll_list.xml index a037d4904b..cb876da8d8 100644 --- a/indra/newview/skins/default/xui/en/widgets/scroll_list.xml +++ b/indra/newview/skins/default/xui/en/widgets/scroll_list.xml @@ -13,4 +13,6 @@ background_visible="false" heading_height="23" draw_border="false" - draw_heading="false" /> + draw_heading="false" + scroll_bar_bg_visible="false" + scroll_bar_bg_color="black" /> |