diff options
author | William Todd Stinson <stinson@lindenlab.com> | 2012-10-03 17:48:01 -0700 |
---|---|---|
committer | William Todd Stinson <stinson@lindenlab.com> | 2012-10-03 17:48:01 -0700 |
commit | 36df0f8a06629c5a14358c07d0f12ce1019f066e (patch) | |
tree | 7cf7bbbaeb351b84888032ff98627b04fd871b60 /indra | |
parent | 3d5e24d135bd5d2636f075f9ef12ffba2129c61f (diff) | |
parent | 046e90a8f74f3c13107a7edc72e912799a5e62e9 (diff) |
Pull and merge from ssh://stinson@hg.lindenlab.com/richard/viewer-chui/.
Diffstat (limited to 'indra')
64 files changed, 1671 insertions, 442 deletions
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 029c47c726..58b17f74a8 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -240,6 +240,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) mTitle(p.title), mShortTitle(p.short_title), mSingleInstance(p.single_instance), + mIsReuseInitialized(p.reuse_instance.isProvided()), mReuseInstance(p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance), // reuse single-instance floaters by default mKey(key), mCanTearOff(p.can_tear_off), @@ -631,6 +632,10 @@ void LLFloater::setVisible( BOOL visible ) void LLFloater::setIsSingleInstance(BOOL is_single_instance) { mSingleInstance = is_single_instance; + if (!mIsReuseInitialized) + { + mReuseInstance = is_single_instance; // reuse single-instance floaters by default + } } diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 4b738f88ea..07b79d5523 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -447,9 +447,10 @@ private: LLUIString mTitle; LLUIString mShortTitle; - BOOL mSingleInstance; // TRUE if there is only ever one instance of the floater - bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it - std::string mInstanceName; // Store the instance name so we can remove ourselves from the list + BOOL mSingleInstance; // TRUE if there is only ever one instance of the floater + bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it + bool mIsReuseInitialized; // true if mReuseInstance already set from parameters + std::string mInstanceName; // Store the instance name so we can remove ourselves from the list BOOL mCanTearOff; BOOL mCanMinimize; diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index 11004fe390..9a4a90206b 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -56,11 +56,7 @@ const S32 RENAME_WIDTH_PAD = 4; const S32 RENAME_HEIGHT_PAD = 1; const S32 AUTO_OPEN_STACK_DEPTH = 16; -const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH - + LLFolderViewItem::ICON_PAD - + LLFolderViewItem::ARROW_SIZE - + LLFolderViewItem::TEXT_PAD - + /*first few characters*/ 40; + const S32 MINIMUM_RENAMER_WIDTH = 80; // *TODO: move in params in xml if necessary. Requires modification of LLFolderView & LLInventoryPanel Params. @@ -142,7 +138,8 @@ LLFolderView::Params::Params() use_label_suffix("use_label_suffix"), allow_multiselect("allow_multiselect", true), show_empty_message("show_empty_message", true), - use_ellipses("use_ellipses", false) + use_ellipses("use_ellipses", false), + options_menu("options_menu", "") { folder_indentation = -4; } @@ -211,10 +208,11 @@ LLFolderView::LLFolderView(const Params& p) // Textbox LLTextBox::Params text_p; LLFontGL* font = getLabelFontForStyle(mLabelStyle); - LLRect new_r = LLRect(rect.mLeft + ICON_PAD, - rect.mTop - TEXT_PAD, + //mIconPad, mTextPad are set in folder_view_item.xml + LLRect new_r = LLRect(rect.mLeft + mIconPad, + rect.mTop - mTextPad, rect.mRight, - rect.mTop - TEXT_PAD - font->getLineHeight()); + rect.mTop - mTextPad - font->getLineHeight()); text_p.rect(new_r); text_p.name(std::string(p.name)); text_p.font(font); @@ -231,7 +229,7 @@ LLFolderView::LLFolderView(const Params& p) // make the popup menu available - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(p.options_menu, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); if (!menu) { menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); @@ -1533,14 +1531,18 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) && menu ) { if (mCallbackRegistrar) + { mCallbackRegistrar->pushScope(); + } updateMenuOptions(menu); menu->updateParent(LLMenuGL::sMenuContainer); LLMenuGL::showPopup(this, menu, x, y); if (mCallbackRegistrar) + { mCallbackRegistrar->popScope(); + } } else { @@ -1652,12 +1654,13 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); S32 label_height = getLabelFontForStyle(mLabelStyle)->getLineHeight(); // when navigating with keyboard, only move top of opened folder on screen, otherwise show whole folder - S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); + S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + item->getIconPad()) : local_rect.getHeight(); // get portion of item that we want to see... LLRect item_local_rect = LLRect(item->getIndentation(), local_rect.getHeight(), - llmin(MIN_ITEM_WIDTH_VISIBLE, local_rect.getWidth()), + //+40 is supposed to include few first characters + llmin(item->getLabelXPos() - item->getIndentation() + 40, local_rect.getWidth()), llmax(0, local_rect.getHeight() - max_height_to_show)); LLRect item_doc_rect; @@ -1874,7 +1877,7 @@ void LLFolderView::updateRenamerPosition() if(mRenameItem) { // See also LLFolderViewItem::draw() - S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mRenameItem->getIndentation(); + S32 x = mRenameItem->getLabelXPos(); S32 y = mRenameItem->getRect().getHeight() - mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD; mRenameItem->localPointToScreen( x, y, &x, &y ); screenPointToLocal( x, y, &x, &y ); diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h index 81b0f087e8..487391a477 100644 --- a/indra/llui/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -94,6 +94,8 @@ public: use_ellipses, show_item_link_overlays; Mandatory<LLFolderViewModelInterface*> view_model; + Mandatory<std::string> options_menu; + Params(); }; diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index a20ce23b18..0b04288950 100755 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -44,6 +44,18 @@ static LLDefaultChildRegistry::Register<LLFolderViewItem> r("folder_view_item"); // statics std::map<U8, LLFontGL*> LLFolderViewItem::sFonts; // map of styles to fonts +LLUIColor LLFolderViewItem::sFgColor; +LLUIColor LLFolderViewItem::sHighlightBgColor; +LLUIColor LLFolderViewItem::sHighlightFgColor; +LLUIColor LLFolderViewItem::sFocusOutlineColor; +LLUIColor LLFolderViewItem::sMouseOverColor; +LLUIColor LLFolderViewItem::sFilterBGColor; +LLUIColor LLFolderViewItem::sFilterTextColor; +LLUIColor LLFolderViewItem::sSuffixColor; +LLUIColor LLFolderViewItem::sLibraryColor; +LLUIColor LLFolderViewItem::sLinkColor; +LLUIColor LLFolderViewItem::sSearchStatusColor; + // only integers can be initialized in header const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f; const F32 LLFolderViewItem::FOLDER_OPEN_TIME_CONSTANT = 0.03f; @@ -90,7 +102,14 @@ LLFolderViewItem::Params::Params() item_height("item_height"), item_top_pad("item_top_pad"), creation_date(), - allow_open("allow_open", true) + allow_open("allow_open", true), + left_pad("left_pad", 0), + icon_pad("icon_pad", 0), + icon_width("icon_width", 0), + text_pad("text_pad", 0), + text_pad_right("text_pad_right", 0), + arrow_size("arrow_size", 0), + max_folder_item_overlap("max_folder_item_overlap", 0) {} // Default constructor @@ -98,7 +117,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) : LLView(p), mLabelWidth(0), mLabelWidthDirty(false), - mLabelPaddingRight(DEFAULT_TEXT_PADDING_RIGHT), + mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT), mParentFolder( NULL ), mIsSelected( FALSE ), mIsCurSelection( FALSE ), @@ -114,12 +133,31 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mRoot(p.root), mViewModelItem(p.listener), mIsMouseOverTitle(false), - mAllowOpen(p.allow_open) -{ + mAllowOpen(p.allow_open), + mLeftPad(p.left_pad), + mIconPad(p.icon_pad), + mIconWidth(p.icon_width), + mTextPad(p.text_pad), + mTextPadRight(p.text_pad_right), + mArrowSize(p.arrow_size), + mMaxFolderItemOverlap(p.max_folder_item_overlap) +{ + sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); + sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); + sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); + sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); + sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); + sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); + sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE); + sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE); + sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); + sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); + if (mViewModelItem) { mViewModelItem->setFolderViewItem(this); -} + } } BOOL LLFolderViewItem::postBuild() @@ -216,7 +254,7 @@ void LLFolderViewItem::refresh() mLabel = vmi.getDisplayName(); - setToolTip(mLabel); + setToolTip(vmi.getName()); mIcon = vmi.getIcon(); mIconOpen = vmi.getIconOpen(); mIconOverlay = vmi.getIconOverlay(); @@ -291,11 +329,11 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height ) : 0; if (mLabelWidthDirty) { - mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + mLabelPaddingRight; + mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + mLabelPaddingRight; mLabelWidthDirty = false; } - *width = llmax(*width, mLabelWidth + mIndentation); + *width = llmax(*width, mLabelWidth); // determine if we need to use ellipses to avoid horizontal scroll. EXT-719 bool use_ellipses = getRoot()->getUseEllipses(); @@ -313,6 +351,21 @@ S32 LLFolderViewItem::getItemHeight() return mItemHeight; } +S32 LLFolderViewItem::getLabelXPos() +{ + return getIndentation() + mArrowSize + mTextPad + mIconWidth + mIconPad; +} + +S32 LLFolderViewItem::getIconPad() +{ + return mIconPad; +} + +S32 LLFolderViewItem::getTextPad() +{ + return mTextPad; +} + // *TODO: This can be optimized a lot by simply recording that it is // selected in the appropriate places, and assuming that set selection // means 'deselect' for a leaf item. Do this optimization after @@ -484,7 +537,7 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > drag_and_drop_threshold() * drag_and_drop_threshold() && root->getCurSelectedItem() && root->startDrag()) - { + { // RN: when starting drag and drop, clear out last auto-open root->autoOpenTest(NULL); root->setShowSelectionContext(TRUE); @@ -495,13 +548,13 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) gFocusMgr.setKeyboardFocus(NULL); getWindow()->setCursor(UI_CURSOR_ARROW); - return TRUE; - } - else + } + else if (x != mDragStartX || y != mDragStartY) { getWindow()->setCursor(UI_CURSOR_NOLOCKED); - return TRUE; } + + return TRUE; } else { @@ -595,6 +648,22 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, return handled; } +void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color) +{ + //--------------------------------------------------------------------------------// + // Draw open folder arrow + // + const S32 TOP_PAD = default_params.item_top_pad; + + if (hasVisibleChildren() || getViewModelItem()->hasChildren()) + { + LLUIImage* arrow_image = default_params.folder_arrow_image; + gl_draw_scaled_rotated_image( + mIndentation, getRect().getHeight() - mArrowSize - mTextPad - TOP_PAD, + mArrowSize, mArrowSize, mControlLabelRotation, arrow_image->getImage(), fg_color); + } +} + void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &bgColor, const LLUIColor &focusOutlineColor, const LLUIColor &mouseOverColor) { @@ -705,18 +774,6 @@ void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y void LLFolderViewItem::draw() { - static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); - static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); - static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); - static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); - static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); - static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); - static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); - static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE); - static LLUIColor sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE); - static LLUIColor sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); - static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); - const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled @@ -727,24 +784,14 @@ void LLFolderViewItem::draw() getViewModelItem()->update(); - //--------------------------------------------------------------------------------// - // Draw open folder arrow - // - if (hasVisibleChildren() || getViewModelItem()->hasChildren()) - { - LLUIImage* arrow_image = default_params.folder_arrow_image; - gl_draw_scaled_rotated_image( - mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD - TOP_PAD, - ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor); - } - + drawOpenFolderArrow(default_params, sFgColor); drawHighlight(show_context, filled, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); //--------------------------------------------------------------------------------// // Draw open icon // - const S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD; + const S32 icon_x = mIndentation + mArrowSize + mTextPad; if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) // For open folders { mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1); @@ -769,8 +816,8 @@ void LLFolderViewItem::draw() std::string::size_type filter_string_length = mViewModelItem->hasFilterStringMatch() ? mViewModelItem->getFilterStringSize() : 0; F32 right_x = 0; - F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; - F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); + F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + F32 text_left = (F32)getLabelXPos(); std::string combined_string = mLabel + mLabelSuffix; if (filter_string_length > 0) @@ -804,14 +851,15 @@ void LLFolderViewItem::draw() if (filter_string_length > 0) { F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mViewModelItem->getFilterStringOffset()); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; + F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; font->renderUTF8( combined_string, mViewModelItem->getFilterStringOffset(), match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, filter_string_length, S32_MAX, &right_x, FALSE ); } - - LLView::draw(); + //Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to + //be distorted...oddly. I initially added this in but didn't need it after all. So removing to prevent unnecessary bug. + //LLView::draw(); } const LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) const @@ -847,6 +895,22 @@ LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): { } +void LLFolderViewFolder::updateLabelRotation() +{ + if (mAutoOpenCountdown != 0.f) + { + mControlLabelRotation = mAutoOpenCountdown * -90.f; + } + else if (isOpen()) + { + mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); + } + else + { + mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); + } +} + // Destroys the object LLFolderViewFolder::~LLFolderViewFolder( void ) { @@ -987,7 +1051,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height ) folders_t::iterator fit = iter++; // number of pixels that bottom of folder label is from top of parent folder if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) + > llround(mCurHeight) + mMaxFolderItemOverlap) { // hide if beyond current folder height (*fit)->setVisible(FALSE); @@ -1000,7 +1064,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height ) items_t::iterator iit = iter++; // number of pixels that bottom of item label is from top of parent folder if (getRect().getHeight() - (*iit)->getRect().mBottom - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) + > llround(mCurHeight) + mMaxFolderItemOverlap) { (*iit)->setVisible(FALSE); } @@ -1760,7 +1824,7 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) } if( !handled ) { - if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD) + if(mIndentation < x && x < mIndentation + mArrowSize + mTextPad) { toggleOpen(); handled = TRUE; @@ -1784,7 +1848,7 @@ BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) } if( !handled ) { - if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD) + if(mIndentation < x && x < mIndentation + mArrowSize + mTextPad) { // don't select when user double-clicks plus sign // so as not to contradict single-click behavior @@ -1802,18 +1866,7 @@ BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) void LLFolderViewFolder::draw() { - if (mAutoOpenCountdown != 0.f) - { - mControlLabelRotation = mAutoOpenCountdown * -90.f; - } - else if (isOpen()) - { - mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); - } - else - { - mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); - } + updateLabelRotation(); LLFolderViewItem::draw(); diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 5c97407bc1..d4002c3184 100755 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -60,18 +60,18 @@ public: Optional<time_t> creation_date; Optional<bool> allow_open; + Optional<S32> left_pad, + icon_pad, + icon_width, + text_pad, + text_pad_right, + arrow_size, + max_folder_item_overlap; Params(); }; - // layout constants - static const S32 LEFT_PAD = 5, - ICON_PAD = 2, - ICON_WIDTH = 16, - TEXT_PAD = 1, - DEFAULT_TEXT_PADDING_RIGHT = 4, - ARROW_SIZE = 12, - MAX_FOLDER_ITEM_OVERLAP = 2; - + + static const S32 DEFAULT_LABEL_PADDING_RIGHT = 4; // animation parameters static const F32 FOLDER_CLOSE_TIME_CONSTANT, FOLDER_OPEN_TIME_CONSTANT; @@ -99,6 +99,14 @@ protected: S32 mDragStartX, mDragStartY; + S32 mLeftPad, + mIconPad, + mIconWidth, + mTextPad, + mTextPadRight, + mArrowSize, + mMaxFolderItemOverlap; + F32 mControlLabelRotation; LLFolderView* mRoot; bool mHasVisibleChildren, @@ -108,6 +116,19 @@ protected: mAllowOpen, mSelectPending; + // For now assuming all colors are the same in derived classes. + static LLUIColor sFgColor; + static LLUIColor sHighlightBgColor; + static LLUIColor sHighlightFgColor; + static LLUIColor sFocusOutlineColor; + static LLUIColor sMouseOverColor; + static LLUIColor sFilterBGColor; + static LLUIColor sFilterTextColor; + static LLUIColor sSuffixColor; + static LLUIColor sLibraryColor; + static LLUIColor sLinkColor; + static LLUIColor sSearchStatusColor; + // this is an internal method used for adding items to folders. A // no-op at this level, but reimplemented in derived classes. virtual void addItem(LLFolderViewItem*) { } @@ -136,6 +157,9 @@ public: // makes sure that this view and it's children are the right size. virtual S32 arrange( S32* width, S32* height ); virtual S32 getItemHeight(); + virtual S32 getLabelXPos(); + S32 getIconPad(); + S32 getTextPad(); // If 'selection' is 'this' then note that otherwise ignore. // Returns TRUE if this item ends up being selected. @@ -236,6 +260,7 @@ public: // virtual void handleDropped(); virtual void draw(); + void drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color); void drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &bgColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor); void drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x); virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, @@ -263,6 +288,8 @@ protected: LLFolderViewFolder( const LLFolderViewItem::Params& ); friend class LLUICtrlFactory; + void updateLabelRotation(); + public: typedef std::list<LLFolderViewItem*> items_t; typedef std::list<LLFolderViewFolder*> folders_t; diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index 22bfc4dfb4..c6030c9b71 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -226,7 +226,7 @@ public: mParent(NULL), mRootViewModel(root_view_model) { - std::for_each(mChildren.begin(), mChildren.end(), DeletePointer()); + mChildren.clear(); } void requestSort() { mSortVersion = -1; } @@ -254,6 +254,16 @@ public: virtual void addChild(LLFolderViewModelItem* child) { + // Avoid duplicates: bail out if that child is already present in the list + // Note: this happens when models are created before views + child_list_t::const_iterator iter; + for (iter = mChildren.begin(); iter != mChildren.end(); iter++) + { + if (child == *iter) + { + return; + } + } mChildren.push_back(child); child->setParent(this); dirtyFilter(); diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 4c730286da..be6d359c9a 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -767,7 +767,7 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& { // freeze new size as fraction F32 new_fractional_size = (updated_auto_resize_headroom == 0.f) ? MAX_FRACTIONAL_SIZE - : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim()) / updated_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); + : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim() - 1) / updated_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); fraction_given_up -= new_fractional_size - panelp->mFractionalSize; fraction_remaining -= panelp->mFractionalSize; panelp->mFractionalSize = new_fractional_size; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 5c2b3236f6..8323bfc12f 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -873,13 +873,12 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) // allow "scrubbing" over ui by showing next tooltip immediately // if previous one was still visible F32 timeout = LLToolTipMgr::instance().toolTipVisible() - ? LLUI::sSettingGroups["config"]->getF32( "ToolTipFastDelay" ) - : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" ); + ? LLUI::sSettingGroups["config"]->getF32( "ToolTipFastDelay" ) + : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" ); LLToolTipMgr::instance().show(LLToolTip::Params() - .message(tooltip) - .sticky_rect(calcScreenRect()) - .delay_time(timeout)); - + .message(tooltip) + .sticky_rect(calcScreenRect()) + .delay_time(timeout)); handled = TRUE; } diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 1c35349510..15b85a6418 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -67,7 +67,6 @@ const BOOL NOT_MOUSE_OPAQUE = FALSE; const U32 GL_NAME_UI_RESERVED = 2; - // maintains render state during traversal of UI tree class LLViewDrawContext { diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 6436a85105..345c81b838 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -197,6 +197,7 @@ set(viewer_SOURCE_FILES llfloaterbuycurrencyhtml.cpp llfloaterbuyland.cpp llfloatercamera.cpp + llfloaterchatvoicevolume.cpp llfloatercolorpicker.cpp llfloaterconversationlog.cpp llfloaterconversationpreview.cpp @@ -781,6 +782,7 @@ set(viewer_HEADER_FILES llfloaterbuycurrencyhtml.h llfloaterbuyland.h llfloatercamera.h + llfloaterchatvoicevolume.h llfloatercolorpicker.h llfloaterconversationlog.h llfloaterconversationpreview.h diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 51211a8ce5..d4bbd84d0f 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -239,14 +239,4 @@ is_running_function="Floater.IsOpen" is_running_parameters="camera" /> - <command name="voice" - available_in_toybox="true" - icon="Command_Voice_Icon" - label_ref="Command_Voice_Label" - tooltip_ref="Command_Voice_Tooltip" - execute_function="Floater.ToggleOrBringToFront" - execute_parameters="voice_controls" - is_running_function="Floater.IsOpen" - is_running_parameters="voice_controls" - /> </commands> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 08a1a237f5..6b15e4b21a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -42,6 +42,7 @@ #include "llagentcamera.h" #include "llagentlanguage.h" #include "llagentwearables.h" +#include "llimfloatercontainer.h" #include "llwindow.h" #include "llviewerstats.h" #include "llviewerstatsrecorder.h" @@ -1204,7 +1205,7 @@ bool LLAppViewer::mainLoop() LLVoiceChannel::initClass(); LLVoiceClient::getInstance()->init(gServicePump); - LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::sOnCurrentChannelChanged, _1), true); + LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLIMFloaterContainer::onCurrentChannelChanged, _1), true); LLTimer frameTimer,idleTimer; LLTimer debugTime; LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 42a0376774..248685b964 100755 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -42,6 +42,7 @@ #include "llappviewer.h" // for gLastVersionChannel #include "llcachename.h" #include "llcallingcard.h" // for LLAvatarTracker +#include "llconversationlog.h" #include "llfloateravatarpicker.h" // for LLFloaterAvatarPicker #include "llfloatergroupinvite.h" #include "llfloatergroups.h" @@ -897,6 +898,22 @@ void LLAvatarActions::inviteToGroup(const LLUUID& id) } } +// static +void LLAvatarActions::viewChatHistory(const LLUUID& id) +{ + const std::vector<LLConversation>& conversations = LLConversationLog::instance().getConversations(); + std::vector<LLConversation>::const_iterator iter = conversations.begin(); + + for (; iter != conversations.end(); ++iter) + { + if (iter->getParticipantID() == id) + { + LLFloaterReg::showInstance("preview_conversation", iter->getSessionID(), true); + break; + } + } +} + //== private methods ======================================================================================== // static diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 473b9cecc3..6e60f624ad 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -218,6 +218,11 @@ public: */ static void buildResidentsString(const std::vector<LLAvatarName> avatar_names, std::string& residents_string); + /** + * Opens the chat history for avatar + */ + static void viewChatHistory(const LLUUID& id); + static std::set<LLUUID> getInventorySelectedUUIDs(); private: diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index 38b755004c..e767609d74 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -364,7 +364,8 @@ void LLCallFloater::onAvatarListRefreshed() } // static -void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/) +// This entry point now disable, but left for later use. +void LLCallFloater::onCurrentChannelChanged(const LLUUID& /*session_id*/) { LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel(); diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 181c92276d..e1c7b3f43a 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -74,7 +74,7 @@ public: */ /*virtual*/ void onParticipantsChanged(); - static void sOnCurrentChannelChanged(const LLUUID& session_id); + static void onCurrentChannelChanged(const LLUUID& session_id); private: typedef enum e_voice_controls_type @@ -260,7 +260,7 @@ private: * * Is used to ignore voice channel changed callback for the same channel. * - * @see sOnCurrentChannelChanged() + * @see onCurrentChannelChanged() */ static LLVoiceChannel* sCurrentVoiceChannel; diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index 612744c3e9..5fc305da81 100644 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -28,6 +28,7 @@ #include "llviewerprecompiledheaders.h" #include "llconversationmodel.h" +#include "llimview.h" //For LLIMModel // // Conversation items : common behaviors @@ -38,7 +39,8 @@ LLConversationItem::LLConversationItem(std::string display_name, const LLUUID& u mName(display_name), mUUID(uuid), mNeedsRefresh(true), - mConvType(CONV_UNKNOWN) + mConvType(CONV_UNKNOWN), + mLastActiveTime(0.0) { } @@ -47,7 +49,8 @@ LLConversationItem::LLConversationItem(const LLUUID& uuid, LLFolderViewModelInte mName(""), mUUID(uuid), mNeedsRefresh(true), - mConvType(CONV_UNKNOWN) + mConvType(CONV_UNKNOWN), + mLastActiveTime(0.0) { } @@ -56,7 +59,8 @@ LLConversationItem::LLConversationItem(LLFolderViewModelInterface& root_view_mod mName(""), mUUID(), mNeedsRefresh(true), - mConvType(CONV_UNKNOWN) + mConvType(CONV_UNKNOWN), + mLastActiveTime(0.0) { } @@ -81,6 +85,24 @@ void LLConversationItem::showProperties(void) { } +void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items) +{ + items.push_back(std::string("view_profile")); + items.push_back(std::string("im")); + items.push_back(std::string("offer_teleport")); + items.push_back(std::string("voice_call")); + items.push_back(std::string("chat_history")); + items.push_back(std::string("separator_chat_history")); + items.push_back(std::string("add_friend")); + items.push_back(std::string("remove_friend")); + items.push_back(std::string("invite_to_group")); + items.push_back(std::string("separator_invite_to_group")); + items.push_back(std::string("map")); + items.push_back(std::string("share")); + items.push_back(std::string("pay")); + items.push_back(std::string("block_unblock")); +} + // // LLConversationItemSession // @@ -167,9 +189,100 @@ void LLConversationItemSession::setParticipantIsModerator(const LLUUID& particip } } +void LLConversationItemSession::setTimeNow(const LLUUID& participant_id) +{ + mLastActiveTime = LLFrameTimer::getElapsedSeconds(); + mNeedsRefresh = true; + LLConversationItemParticipant* participant = findParticipant(participant_id); + if (participant) + { + participant->setTimeNow(); + } +} + +void LLConversationItemSession::setDistance(const LLUUID& participant_id, F64 dist) +{ + LLConversationItemParticipant* participant = findParticipant(participant_id); + if (participant) + { + participant->setDistance(dist); + mNeedsRefresh = true; + } +} + +void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLConversationItemParticipant::buildContextMenu()" << llendl; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + if(this->getType() == CONV_SESSION_1_ON_1) + { + items.push_back(std::string("close_conversation")); + items.push_back(std::string("separator_disconnect_from_voice")); + buildParticipantMenuOptions(items); + } + else if(this->getType() == CONV_SESSION_GROUP) + { + items.push_back(std::string("close_conversation")); + addVoiceOptions(items); + items.push_back(std::string("chat_history")); + items.push_back(std::string("separator_chat_history")); + items.push_back(std::string("group_profile")); + items.push_back(std::string("activate_group")); + items.push_back(std::string("leave_group")); + } + else if(this->getType() == CONV_SESSION_AD_HOC) + { + items.push_back(std::string("close_conversation")); + addVoiceOptions(items); + items.push_back(std::string("chat_history")); + } + + hide_context_entries(menu, items, disabled_items); +} + +void LLConversationItemSession::addVoiceOptions(menuentry_vec_t& items) +{ + LLVoiceChannel* voice_channel = LLIMModel::getInstance() ? LLIMModel::getInstance()->getVoiceChannel(this->getUUID()) : NULL; + + if(voice_channel != LLVoiceChannel::getCurrentVoiceChannel()) + { + items.push_back(std::string("open_voice_conversation")); + } + else + { + items.push_back(std::string("disconnect_from_voice")); + } +} + +// The time of activity of a session is the time of the most recent activity, session and participants included +const bool LLConversationItemSession::getTime(F64& time) const +{ + F64 most_recent_time = mLastActiveTime; + bool has_time = (most_recent_time > 0.1); + LLConversationItemParticipant* participant = NULL; + child_list_t::const_iterator iter; + for (iter = mChildren.begin(); iter != mChildren.end(); iter++) + { + participant = dynamic_cast<LLConversationItemParticipant*>(*iter); + F64 participant_time; + if (participant->getTime(participant_time)) + { + has_time = true; + most_recent_time = llmax(most_recent_time,participant_time); + } + } + if (has_time) + { + time = most_recent_time; + } + return has_time; +} + void LLConversationItemSession::dumpDebugData() { - llinfos << "Merov debug : session, uuid = " << mUUID << ", name = " << mName << ", is loaded = " << mIsLoaded << llendl; + llinfos << "Merov debug : session " << this << ", uuid = " << mUUID << ", name = " << mName << ", is loaded = " << mIsLoaded << llendl; LLConversationItemParticipant* participant = NULL; child_list_t::iterator iter; for (iter = mChildren.begin(); iter != mChildren.end(); iter++) @@ -186,21 +299,34 @@ void LLConversationItemSession::dumpDebugData() LLConversationItemParticipant::LLConversationItemParticipant(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : LLConversationItem(display_name,uuid,root_view_model), mIsMuted(false), - mIsModerator(false) + mIsModerator(false), + mDistToAgent(-1.0) { mConvType = CONV_PARTICIPANT; } LLConversationItemParticipant::LLConversationItemParticipant(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : - LLConversationItem(uuid,root_view_model) + LLConversationItem(uuid,root_view_model), + mIsMuted(false), + mIsModerator(false), + mDistToAgent(-1.0) { mConvType = CONV_PARTICIPANT; } +void LLConversationItemParticipant::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + buildParticipantMenuOptions(items); + hide_context_entries(menu, items, disabled_items); +} + void LLConversationItemParticipant::onAvatarNameCache(const LLAvatarName& av_name) { - mName = av_name.mDisplayName; - // *TODO : we should also store that one, to be used in the tooltip : av_name.mUsername + mName = (av_name.mUsername.empty() ? av_name.mDisplayName : av_name.mUsername); + mDisplayName = (av_name.mDisplayName.empty() ? av_name.mUsername : av_name.mDisplayName); mNeedsRefresh = true; if (mParent) { @@ -210,13 +336,14 @@ void LLConversationItemParticipant::onAvatarNameCache(const LLAvatarName& av_nam void LLConversationItemParticipant::dumpDebugData() { - llinfos << "Merov debug : participant, uuid = " << mUUID << ", name = " << mName << ", muted = " << mIsMuted << ", moderator = " << mIsModerator << llendl; + llinfos << "Merov debug : participant, uuid = " << mUUID << ", name = " << mName << ", display name = " << mDisplayName << ", muted = " << mIsMuted << ", moderator = " << mIsModerator << llendl; } // // LLConversationSort // +// Comparison operator: returns "true" is a comes before b, "false" otherwise bool LLConversationSort::operator()(const LLConversationItem* const& a, const LLConversationItem* const& b) const { LLConversationItem::EConversationType type_a = a->getType(); @@ -224,54 +351,93 @@ bool LLConversationSort::operator()(const LLConversationItem* const& a, const LL if ((type_a == LLConversationItem::CONV_PARTICIPANT) && (type_b == LLConversationItem::CONV_PARTICIPANT)) { - // If both are participants + // If both items are participants U32 sort_order = getSortOrderParticipants(); if (sort_order == LLConversationFilter::SO_DATE) { - F32 time_a = 0.0; - F32 time_b = 0.0; - if (a->getTime(time_a) && b->getTime(time_b)) + F64 time_a = 0.0; + F64 time_b = 0.0; + bool has_time_a = a->getTime(time_a); + bool has_time_b = b->getTime(time_b); + if (has_time_a && has_time_b) { + // Most recent comes first return (time_a > time_b); } + else if (has_time_a || has_time_b) + { + // If we have only one time available, the element with time must come first + return has_time_a; + } + // If no time available, we'll default to sort by name at the end of this method } else if (sort_order == LLConversationFilter::SO_DISTANCE) { - F32 dist_a = 0.0; - F32 dist_b = 0.0; - if (a->getDistanceToAgent(dist_a) && b->getDistanceToAgent(dist_b)) + F64 dist_a = 0.0; + F64 dist_b = 0.0; + bool has_dist_a = a->getDistanceToAgent(dist_a); + bool has_dist_b = b->getDistanceToAgent(dist_b); + if (has_dist_a && has_dist_b) { - return (dist_a > dist_b); + // Closest comes first + return (dist_a < dist_b); } + else if (has_dist_a || has_dist_b) + { + // If we have only one distance available, the element with it must come first + return has_dist_a; + } + // If no distance available, we'll default to sort by name at the end of this method } } else if ((type_a > LLConversationItem::CONV_PARTICIPANT) && (type_b > LLConversationItem::CONV_PARTICIPANT)) { // If both are sessions U32 sort_order = getSortOrderSessions(); - if (sort_order == LLConversationFilter::SO_DATE) + if ((type_a == LLConversationItem::CONV_SESSION_NEARBY) || (type_b == LLConversationItem::CONV_SESSION_NEARBY)) { - F32 time_a = 0.0; - F32 time_b = 0.0; - if (a->getTime(time_a) && b->getTime(time_b)) + // If one is the nearby session, put nearby session *always* first + return (type_a == LLConversationItem::CONV_SESSION_NEARBY); + } + else if (sort_order == LLConversationFilter::SO_DATE) + { + // Sort by time + F64 time_a = 0.0; + F64 time_b = 0.0; + bool has_time_a = a->getTime(time_a); + bool has_time_b = b->getTime(time_b); + if (has_time_a && has_time_b) { + // Most recent comes first return (time_a > time_b); } + else if (has_time_a || has_time_b) + { + // If we have only one time available, the element with time must come first + return has_time_a; + } + // If no time available, we'll default to sort by name at the end of this method } else if (sort_order == LLConversationFilter::SO_SESSION_TYPE) { - return (type_a < type_b); + if (type_a != type_b) + { + // Lowest types come first. See LLConversationItem definition of types + return (type_a < type_b); + } + // If types are identical, we'll default to sort by name at the end of this method } } else { - // If one is a participant and the other a session, the session is always "less" than the participant + // If one item is a participant and the other a session, the session comes before the participant // so we simply compare the type // Notes: as a consequence, CONV_UNKNOWN (which should never get created...) always come first - return (type_a < type_b); + return (type_a > type_b); } - // By default, in all other possible cases (including sort order of type LLConversationFilter::SO_NAME of course), sort by name - S32 compare = LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()); + // By default, in all other possible cases (including sort order type LLConversationFilter::SO_NAME of course), + // we sort by name + S32 compare = LLStringUtil::compareDict(a->getName(), b->getName()); return (compare < 0); } diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index dbc04223af..bc72cd96ea 100755 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -41,6 +41,8 @@ class LLConversationItemParticipant; typedef std::map<LLUUID, LLConversationItem*> conversations_items_map; typedef std::map<LLUUID, LLFolderViewItem*> conversations_widgets_map; +typedef std::vector<std::string> menuentry_vec_t; + // Conversation items: we hold a list of those and create an LLFolderViewItem widget for each // that we tuck into the mConversationsListPanel. class LLConversationItem : public LLFolderViewModelItemCommon @@ -104,10 +106,10 @@ public: virtual void selectItem(void) { } virtual void showProperties(void); - // Methods used in sorting (see LLConversationSort::operator() + // Methods used in sorting (see LLConversationSort::operator()) EConversationType const getType() const { return mConvType; } - virtual const bool getTime(F32& time) const { return false; } - virtual const bool getDistanceToAgent(F32& distance) const { return false; } + virtual const bool getTime(F64& time) const { time = mLastActiveTime; return (time > 0.1); } + virtual const bool getDistanceToAgent(F64& distance) const { return false; } // This method will be called to determine if a drop can be // performed, and will set drop to TRUE if a drop is @@ -124,11 +126,14 @@ public: void resetRefresh() { mNeedsRefresh = false; } bool needsRefresh() { return mNeedsRefresh; } + void buildParticipantMenuOptions(menuentry_vec_t& items); + protected: std::string mName; // Name of the session or the participant LLUUID mUUID; // UUID of the session or the participant EConversationType mConvType; // Type of conversation item bool mNeedsRefresh; // Flag signaling to the view that something changed for this item + F64 mLastActiveTime; }; class LLConversationItemSession : public LLConversationItem @@ -149,9 +154,15 @@ public: void setParticipantIsMuted(const LLUUID& participant_id, bool is_muted); void setParticipantIsModerator(const LLUUID& participant_id, bool is_moderator); + void setTimeNow(const LLUUID& participant_id); + void setDistance(const LLUUID& participant_id, F64 dist); bool isLoaded() { return mIsLoaded; } + void buildContextMenu(LLMenuGL& menu, U32 flags); + void addVoiceOptions(menuentry_vec_t& items); + virtual const bool getTime(F64& time) const; + void dumpDebugData(); private: @@ -165,18 +176,27 @@ public: LLConversationItemParticipant(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model); virtual ~LLConversationItemParticipant() {} + virtual const std::string& getDisplayName() const { return mDisplayName; } + bool isMuted() { return mIsMuted; } bool isModerator() {return mIsModerator; } void setIsMuted(bool is_muted) { mIsMuted = is_muted; mNeedsRefresh = true; } void setIsModerator(bool is_moderator) { mIsModerator = is_moderator; mNeedsRefresh = true; } - + void setTimeNow() { mLastActiveTime = LLFrameTimer::getElapsedSeconds(); mNeedsRefresh = true; } + void setDistance(F64 dist) { mDistToAgent = dist; mNeedsRefresh = true; } + + void buildContextMenu(LLMenuGL& menu, U32 flags); void onAvatarNameCache(const LLAvatarName& av_name); + virtual const bool getDistanceToAgent(F64& dist) const { dist = mDistToAgent; return (dist >= 0.0); } + void dumpDebugData(); private: bool mIsMuted; // default is false bool mIsModerator; // default is false + std::string mDisplayName; + F64 mDistToAgent; // Distance to the agent. A negative (meaningless) value means the distance has not been set. }; // We don't want to ever filter conversations but we need to declare that class to create a conversation view model. @@ -260,4 +280,15 @@ public: private: }; +// Utility function to hide all entries except those in the list +// Can be called multiple times on the same menu (e.g. if multiple items +// are selected). If "append" is false, then only common enabled items +// are set as enabled. + +//(defined in inventorybridge.cpp) +//TODO: Gilbert Linden - Refactor to make this function non-global +void hide_context_entries(LLMenuGL& menu, + const menuentry_vec_t &entries_to_show, + const menuentry_vec_t &disabled_entries); + #endif // LL_LLCONVERSATIONMODEL_H diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index 721abd5892..d4eb551f7a 100755 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -30,10 +30,12 @@ #include "llconversationview.h" #include <boost/bind.hpp> +#include "llagentdata.h" #include "llconversationmodel.h" #include "llimconversation.h" #include "llimfloatercontainer.h" #include "llfloaterreg.h" +#include "llgroupiconctrl.h" #include "lluictrlfactory.h" // @@ -43,6 +45,30 @@ static LLDefaultChildRegistry::Register<LLConversationViewSession> r_conversatio const LLColor4U DEFAULT_WHITE(255, 255, 255); +class LLNearbyVoiceClientStatusObserver : public LLVoiceClientStatusObserver +{ +public: + + LLNearbyVoiceClientStatusObserver(LLConversationViewSession* conv) + : conversation(conv) + {} + + virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal) + { + if (conversation + && status != STATUS_JOINING + && status != STATUS_LEFT_CHANNEL + && LLVoiceClient::getInstance()->voiceEnabled() + && LLVoiceClient::getInstance()->isVoiceWorking()) + { + conversation->showVoiceIndicator(); + } + } + +private: + LLConversationViewSession* conversation; +}; + LLConversationViewSession::Params::Params() : container() {} @@ -51,157 +77,109 @@ LLConversationViewSession::LLConversationViewSession(const LLConversationViewSes LLFolderViewFolder(p), mContainer(p.container), mItemPanel(NULL), - mSessionTitle(NULL) + mCallIconLayoutPanel(NULL), + mSessionTitle(NULL), + mSpeakingIndicator(NULL), + mVoiceClientObserver(NULL), + mMinimizedMode(false) { } +LLConversationViewSession::~LLConversationViewSession() +{ + mActiveVoiceChannelConnection.disconnect(); + + if(LLVoiceClient::instanceExists() && mVoiceClientObserver) + { + LLVoiceClient::getInstance()->removeObserver(mVoiceClientObserver); + } +} + BOOL LLConversationViewSession::postBuild() { LLFolderViewItem::postBuild(); mItemPanel = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>("panel_conversation_list_item.xml", NULL, LLPanel::child_registry_t::instance()); - addChild(mItemPanel); + mCallIconLayoutPanel = mItemPanel->getChild<LLPanel>("call_icon_panel"); mSessionTitle = mItemPanel->getChild<LLTextBox>("conversation_title"); - refresh(); + mActiveVoiceChannelConnection = LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLConversationViewSession::onCurrentVoiceSessionChanged, this, _1)); + mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicatorn"); - return TRUE; -} - -void LLConversationViewSession::draw() -{ -// *TODO Seth PE: remove the code duplicated from LLFolderViewFolder::draw() -// ***** LLFolderViewFolder::draw() code begin ***** - if (mAutoOpenCountdown != 0.f) - { - mControlLabelRotation = mAutoOpenCountdown * -90.f; - } - else if (isOpen()) - { - mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); - } - else - { - mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); - } -// ***** LLFolderViewFolder::draw() code end ***** - -// *TODO Seth PE: remove the code duplicated from LLFolderViewItem::draw() -// ***** LLFolderViewItem::draw() code begin ***** - - static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); - static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); - static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); - static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); - - const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - const S32 TOP_PAD = default_params.item_top_pad; - const S32 FOCUS_LEFT = 1; - - getViewModelItem()->update(); - - //--------------------------------------------------------------------------------// - // Draw open folder arrow - // - if (hasVisibleChildren() || getViewModelItem()->hasChildren()) - { - LLUIImage* arrow_image = default_params.folder_arrow_image; - gl_draw_scaled_rotated_image( - mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD - TOP_PAD, - ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor); - } - - - //--------------------------------------------------------------------------------// - // Draw highlight for selected items - // - const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); - const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled - const S32 focus_top = getRect().getHeight(); - const S32 focus_bottom = getRect().getHeight() - mItemHeight; - const bool folder_open = (getRect().getHeight() > mItemHeight + 4); - if (mIsSelected) // always render "current" item. Only render other selected items if mShowSingleSelection is FALSE + LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); + if (vmi) { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLColor4 bg_color = sHighlightBgColor; - if (!mIsCurSelection) + switch(vmi->getType()) { - // do time-based fade of extra objects - F32 fade_time = (getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.0f); - if (getRoot() && getRoot()->getShowSingleSelection()) - { - // fading out - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f); - } - else + case LLConversationItem::CONV_PARTICIPANT: + case LLConversationItem::CONV_SESSION_1_ON_1: + { + LLIMModel::LLIMSession* session= LLIMModel::instance().findIMSession(vmi->getUUID()); + if (session) { - // fading in - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]); + LLAvatarIconCtrl* icon = mItemPanel->getChild<LLAvatarIconCtrl>("avatar_icon"); + icon->setVisible(true); + icon->setValue(session->mOtherParticipantID); + mSpeakingIndicator->setSpeakerId(gAgentID, session->mSessionID, true); } + break; } - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - bg_color, filled); - if (mIsCurSelection) + case LLConversationItem::CONV_SESSION_AD_HOC: { - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - sFocusOutlineColor, FALSE); + LLGroupIconCtrl* icon = mItemPanel->getChild<LLGroupIconCtrl>("group_icon"); + icon->setVisible(true); + mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true); } - if (folder_open) + case LLConversationItem::CONV_SESSION_GROUP: { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, // overlap with bottom edge of above rect - getRect().getWidth() - 2, - 0, - sFocusOutlineColor, FALSE); - if (show_context) + LLGroupIconCtrl* icon = mItemPanel->getChild<LLGroupIconCtrl>("group_icon"); + icon->setVisible(true); + icon->setValue(vmi->getUUID()); + mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true); + break; + } + case LLConversationItem::CONV_SESSION_NEARBY: + { + LLIconCtrl* icon = mItemPanel->getChild<LLIconCtrl>("nearby_chat_icon"); + icon->setVisible(true); + mSpeakingIndicator->setSpeakerId(gAgentID, LLUUID::null, true); + if(LLVoiceClient::instanceExists()) { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, - getRect().getWidth() - 2, - 0, - sHighlightBgColor, TRUE); + LLNearbyVoiceClientStatusObserver* mVoiceClientObserver = new LLNearbyVoiceClientStatusObserver(this); + LLVoiceClient::getInstance()->addObserver(mVoiceClientObserver); } + break; + } + default: + break; } - } - else if (mIsMouseOverTitle) - { - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - sMouseOverColor, FALSE); } - //--------------------------------------------------------------------------------// - // Draw DragNDrop highlight - // - if (mDragAndDropTarget) + refresh(); + + return TRUE; +} + +void LLConversationViewSession::draw() +{ + getViewModelItem()->update(); + + const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); + const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); + + // we don't draw the open folder arrow in minimized mode + if (!mMinimizedMode) { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - sHighlightBgColor, FALSE); - if (folder_open) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, // overlap with bottom edge of above rect - getRect().getWidth() - 2, - 0, - sHighlightBgColor, FALSE); - } - mDragAndDropTarget = FALSE; + // update the rotation angle of open folder arrow + updateLabelRotation(); + + drawOpenFolderArrow(default_params, sFgColor); } -// ***** LLFolderViewItem::draw() code end ***** + + // draw highlight for selected items + drawHighlight(show_context, true, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); // draw children if root folder, or any other folder that is open or animating to closed state bool draw_children = getRoot() == static_cast<LLFolderViewFolder*>(this) @@ -227,7 +205,8 @@ void LLConversationViewSession::draw() // virtual S32 LLConversationViewSession::arrange(S32* width, S32* height) { - LLRect rect(getIndentation() + ARROW_SIZE, + S32 h_pad = getIndentation() + mArrowSize; + LLRect rect(mMinimizedMode ? getLocalRect().mLeft : h_pad, getLocalRect().mTop, getLocalRect().mRight, getLocalRect().mTop - getItemHeight()); @@ -236,6 +215,16 @@ S32 LLConversationViewSession::arrange(S32* width, S32* height) return LLFolderViewFolder::arrange(width, height); } +// virtual +void LLConversationViewSession::toggleOpen() +{ + // conversations should not be opened while in minimized mode + if (!mMinimizedMode) + { + LLFolderViewFolder::toggleOpen(); + } +} + void LLConversationViewSession::selectItem() { @@ -257,6 +246,18 @@ void LLConversationViewSession::selectItem() LLFolderViewItem::selectItem(); } +void LLConversationViewSession::toggleMinimizedMode(bool is_minimized) +{ + mMinimizedMode = is_minimized; + + // hide the layout stack which contains all item's child widgets + // except for the icon which we display in minimized mode + getChild<LLView>("conversation_item_stack")->setVisible(!mMinimizedMode); + + S32 h_pad = getIndentation() + mArrowSize; + mItemPanel->translate(mMinimizedMode ? -h_pad : h_pad, 0); +} + void LLConversationViewSession::setVisibleIfDetached(BOOL visible) { // Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized @@ -288,6 +289,14 @@ LLConversationViewParticipant* LLConversationViewSession::findParticipant(const return (iter == getItemsEnd() ? NULL : participant); } +void LLConversationViewSession::showVoiceIndicator() +{ + if (LLVoiceChannel::getCurrentVoiceChannel()->getSessionID().isNull()) + { + mCallIconLayoutPanel->setVisible(true); + } +} + void LLConversationViewSession::refresh() { // Refresh the session view from its model data @@ -305,6 +314,25 @@ void LLConversationViewSession::refresh() LLFolderViewFolder::refresh(); } +void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& session_id) +{ + LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); + + if (vmi) + { + bool is_active = vmi->getUUID() == session_id; + bool is_nearby = vmi->getType() == LLConversationItem::CONV_SESSION_NEARBY; + + if (is_nearby) + { + mSpeakingIndicator->setSpeakerId(is_active ? gAgentID : LLUUID::null); + } + + mSpeakingIndicator->switchIndicator(is_active); + mCallIconLayoutPanel->setVisible(is_active); + } +} + // // Implementation of conversations list participant (avatar) widgets // @@ -336,7 +364,7 @@ void LLConversationViewParticipant::initFromParams(const LLConversationViewParti applyXUILayout(avatar_icon_params, this); LLAvatarIconCtrl * avatarIcon = LLUICtrlFactory::create<LLAvatarIconCtrl>(avatar_icon_params); addChild(avatarIcon); - + LLButton::Params info_button_params(params.info_button()); applyXUILayout(info_button_params, this); LLButton * button = LLUICtrlFactory::create<LLButton>(info_button_params); @@ -366,7 +394,7 @@ BOOL LLConversationViewParticipant::postBuild() sStaticInitialized = true; } - computeLabelRightPadding(); + updateChildren(); return LLFolderViewItem::postBuild(); } @@ -381,16 +409,11 @@ void LLConversationViewParticipant::draw() const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled - const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - const S32 TOP_PAD = default_params.item_top_pad; - const LLFontGL* font = getLabelFontForStyle(mLabelStyle); F32 right_x = 0; - //TEXT_PAD, TOP_PAD, ICON_PAD and mIndentation are temporary values and will non-const eventually since they don't - //apply to every single layout - F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; - F32 text_left = (F32)(mAvatarIcon->getRect().mRight + ICON_PAD + mIndentation); + F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; + F32 text_left = (F32)getLabelXPos(); LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor; drawHighlight(show_context, filled, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); @@ -437,15 +460,20 @@ void LLConversationViewParticipant::onInfoBtnClick() void LLConversationViewParticipant::onMouseEnter(S32 x, S32 y, MASK mask) { mInfoBtn->setVisible(true); - computeLabelRightPadding(); + updateChildren(); LLFolderViewItem::onMouseEnter(x, y, mask); } void LLConversationViewParticipant::onMouseLeave(S32 x, S32 y, MASK mask) { mInfoBtn->setVisible(false); - computeLabelRightPadding(); - LLFolderViewItem::onMouseEnter(x, y, mask); + updateChildren(); + LLFolderViewItem::onMouseLeave(x, y, mask); +} + +S32 LLConversationViewParticipant::getLabelXPos() +{ + return mAvatarIcon->getRect().mRight + mIconPad; } // static @@ -463,12 +491,14 @@ void LLConversationViewParticipant::initChildrenWidths(LLConversationViewPartici llassert(index == 0); } -void LLConversationViewParticipant::computeLabelRightPadding() +void LLConversationViewParticipant::updateChildren() { - mLabelPaddingRight = DEFAULT_TEXT_PADDING_RIGHT; + mLabelPaddingRight = DEFAULT_LABEL_PADDING_RIGHT; LLView* control; S32 ctrl_width; + LLRect controlRect; + //Cycles through controls starting from right to left for (S32 i = 0; i < ALIC_COUNT; ++i) { control = getItemChildView((EAvatarListItemChildIndex)i); @@ -476,9 +506,22 @@ void LLConversationViewParticipant::computeLabelRightPadding() // skip invisible views if (!control->getVisible()) continue; + //Get current pos/dimensions + controlRect = control->getRect(); + ctrl_width = sChildrenWidths[i]; // including space between current & left controls // accumulate the amount of space taken by the controls mLabelPaddingRight += ctrl_width; + + //Reposition visible controls in case adjacent controls to the right are hidden. + controlRect.setLeftTopAndSize( + getLocalRect().getWidth() - mLabelPaddingRight, + controlRect.mTop, + controlRect.getWidth(), + controlRect.getHeight()); + + //Sets the new position + control->setShape(controlRect); } } diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h index 075ad09d5b..c81c70b456 100755 --- a/indra/newview/llconversationview.h +++ b/indra/newview/llconversationview.h @@ -38,6 +38,8 @@ class LLIMFloaterContainer; class LLConversationViewSession; class LLConversationViewParticipant; +class LLVoiceClientStatusObserver; + // Implementation of conversations list session widgets class LLConversationViewSession : public LLFolderViewFolder @@ -57,7 +59,7 @@ protected: LLIMFloaterContainer* mContainer; public: - virtual ~LLConversationViewSession( void ) { } + virtual ~LLConversationViewSession(); virtual void selectItem(); /*virtual*/ BOOL postBuild(); @@ -65,14 +67,31 @@ public: /*virtual*/ S32 arrange(S32* width, S32* height); + /*virtual*/ void toggleOpen(); + + void toggleMinimizedMode(bool is_minimized); + void setVisibleIfDetached(BOOL visible); LLConversationViewParticipant* findParticipant(const LLUUID& participant_id); + void showVoiceIndicator(); + virtual void refresh(); private: - LLPanel* mItemPanel; - LLTextBox* mSessionTitle; + + void onCurrentVoiceSessionChanged(const LLUUID& session_id); + + LLPanel* mItemPanel; + LLPanel* mCallIconLayoutPanel; + LLTextBox* mSessionTitle; + LLOutputMonitorCtrl* mSpeakingIndicator; + + bool mMinimizedMode; + + LLVoiceClientStatusObserver* mVoiceClientObserver; + + boost::signals2::connection mActiveVoiceChannelConnection; }; // Implementation of conversations list participant (avatar) widgets @@ -101,6 +120,8 @@ public: void onMouseEnter(S32 x, S32 y, MASK mask); void onMouseLeave(S32 x, S32 y, MASK mask); + /*virtual*/ S32 getLabelXPos(); + protected: friend class LLUICtrlFactory; LLConversationViewParticipant( const Params& p ); @@ -125,7 +146,7 @@ private: static bool sStaticInitialized; // this variable is introduced to improve code readability static S32 sChildrenWidths[ALIC_COUNT]; static void initChildrenWidths(LLConversationViewParticipant* self); - void computeLabelRightPadding(); + void updateChildren(); LLView* getItemChildView(EAvatarListItemChildIndex child_view_index); }; diff --git a/indra/newview/llfloaterchatvoicevolume.cpp b/indra/newview/llfloaterchatvoicevolume.cpp new file mode 100644 index 0000000000..3c76a3a43c --- /dev/null +++ b/indra/newview/llfloaterchatvoicevolume.cpp @@ -0,0 +1,44 @@ +/** + * @file llfloaterchatvoicevolume.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterchatvoicevolume.h" + +LLFloaterChatVoiceVolume::LLFloaterChatVoiceVolume(const LLSD& key) +: LLInspect(key) +{ +} + +void LLFloaterChatVoiceVolume::onOpen(const LLSD& key) +{ + LLInspect::onOpen(key); + LLUI::positionViewNearMouse(this); +} + +LLFloaterChatVoiceVolume::~LLFloaterChatVoiceVolume() +{ + LLTransientFloaterMgr::getInstance()->removeControlView(this); +}; diff --git a/indra/newview/llfloaterchatvoicevolume.h b/indra/newview/llfloaterchatvoicevolume.h new file mode 100644 index 0000000000..61ad92b6da --- /dev/null +++ b/indra/newview/llfloaterchatvoicevolume.h @@ -0,0 +1,44 @@ +/** + * @file llfloaterchatvoicevolume.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLFLOATERCHATVOICEVOLUME_H_ +#define LLFLOATERCHATVOICEVOLUME_H_ + +#include "llinspect.h" +#include "lltransientfloatermgr.h" + +class LLFloaterChatVoiceVolume : public LLInspect, LLTransientFloater +{ +public: + + LLFloaterChatVoiceVolume(const LLSD& key); + virtual ~LLFloaterChatVoiceVolume(); + + virtual void onOpen(const LLSD& key); + + /*virtual*/ LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; } +}; + +#endif /* LLFLOATERCHATVOICEVOLUME_H_ */ diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index a2efe63546..2ad7f9b193 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -42,18 +42,19 @@ const F32 REFRESH_INTERVAL = 0.2f; -LLIMConversation::LLIMConversation(const LLUUID& session_id) +LLIMConversation::LLIMConversation(const LLSD& session_id) : LLTransientDockableFloater(NULL, true, session_id) , mIsP2PChat(false) , mExpandCollapseBtn(NULL) , mTearOffBtn(NULL) , mCloseBtn(NULL) - , mSessionID(session_id) + , mSessionID(session_id.asUUID()) , mParticipantList(NULL) , mChatHistory(NULL) , mInputEditor(NULL) , mInputEditorTopPad(0) , mRefreshTimer(new LLTimer()) + , mHasFocus(false) { mSession = LLIMModel::getInstance()->findIMSession(mSessionID); @@ -216,10 +217,10 @@ void LLIMConversation::onFocusReceived() LLTransientDockableFloater::onFocusReceived(); - mHasFocus = mHaveFocus; - mHaveFocus = true; + mHadFocus = mHasFocus; + mHasFocus = true; - if (! mHasFocus) + if (! mHadFocus) { LLIMFloaterContainer* container = LLIMFloaterContainer::getInstance(); container->setConvItemSelect(mSessionID); @@ -229,7 +230,7 @@ void LLIMConversation::onFocusReceived() void LLIMConversation::onFocusLost() { setBackgroundOpaque(false); - mHaveFocus = false; + mHasFocus = false; LLTransientDockableFloater::onFocusLost(); } @@ -250,6 +251,14 @@ std::string LLIMConversation::appendTime() void LLIMConversation::appendMessage(const LLChat& chat, const LLSD &args) { + // Update the participant activity time + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->setTimeNow(mSessionID,chat.mFromID); + } + + LLChat& tmp_chat = const_cast<LLChat&>(chat); if(tmp_chat.mTimeStr.empty()) @@ -415,6 +424,7 @@ void LLIMConversation::updateHeaderAndToolbar() mExpandCollapseBtn->setEnabled(!is_torn_off || !mIsP2PChat); mTearOffBtn->setImageOverlay(getString(is_torn_off? "return_icon" : "tear_off_icon")); + mTearOffBtn->setToolTip(getString(!is_torn_off? "tooltip_to_separate_window" : "tooltip_to_main_window")); mCloseBtn->setVisible(!is_torn_off && !mIsNearbyChat); diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h index e09ba79a6a..c54081d316 100644 --- a/indra/newview/llimconversation.h +++ b/indra/newview/llimconversation.h @@ -47,7 +47,7 @@ class LLIMConversation public: LOG_CLASS(LLIMConversation); - LLIMConversation(const LLUUID& session_id); + LLIMConversation(const LLSD& session_id); ~LLIMConversation(); // reload all message with new settings of visual modes @@ -140,8 +140,8 @@ private: LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. + bool mHadFocus; bool mHasFocus; - bool mHaveFocus; }; diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 2474fe0891..99337bd5f3 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -111,23 +111,25 @@ void LLIMFloater::onClickCloseBtn() { LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); - if (session == NULL) + if (session != NULL) { - llwarns << "Empty session with id: " << (mSessionID.asString()) << llendl; - return; - } + bool is_call_with_chat = session->isGroupSessionType() + || session->isAdHocSessionType() || session->isP2PSessionType(); - bool is_call_with_chat = session->isGroupSessionType() - || session->isAdHocSessionType() || session->isP2PSessionType(); - - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - if (is_call_with_chat && voice_channel != NULL - && voice_channel->isActive()) + if (is_call_with_chat && voice_channel != NULL + && voice_channel->isActive()) + { + LLSD payload; + payload["session_id"] = mSessionID; + LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); + return; + } + } + else { - LLSD payload; - payload["session_id"] = mSessionID; - LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); + llwarns << "Empty session with id: " << (mSessionID.asString()) << llendl; return; } @@ -582,13 +584,14 @@ void LLIMFloater::onParticipantsListChanged(LLUICtrl* ctrl) build_names_string(temp_uuids, ui_title); updateSessionName(ui_title, ui_title); } - } +} -//static -LLIMFloater* LLIMFloater::addToIMContainer(const LLUUID& session_id) +void LLIMFloater::addToHost(const LLUUID& session_id, const bool force) { - if (!gIMMgr->hasSession(session_id)) - return NULL; + if (!LLIMConversation::isChatMultiTab() || !gIMMgr->hasSession(session_id)) + { + return; + } // Test the existence of the floater before we try to create it bool exist = findInstance(session_id); @@ -612,19 +615,22 @@ LLIMFloater* LLIMFloater::addToIMContainer(const LLUUID& session_id) } } - if (floater_container && floater_container->getVisible()) - { - floater->openFloater(floater->getKey()); - floater->setVisible(TRUE); - } - else + if (force) { - floater->setVisible(FALSE); + if (floater_container && floater_container->getVisible()) + { + floater->openFloater(floater->getKey()); + floater->setVisible(TRUE); + } + else + { + floater->setVisible(FALSE); + } } } - return floater; } + //static LLIMFloater* LLIMFloater::show(const LLUUID& session_id) { @@ -907,6 +913,7 @@ void LLIMFloater::updateMessages() chat.mText = message; } + // Add the message to the chat log appendMessage(chat); mLastMessageIndex = msg["index"].asInteger(); @@ -1322,23 +1329,6 @@ void LLIMFloater::onIMChicletCreated( const LLUUID& session_id ) { LLIMFloater::addToHost(session_id); } -void LLIMFloater::addToHost(const LLUUID& session_id) - { - if (LLIMConversation::isChatMultiTab()) -{ - LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); - if (!im_box) - { - im_box = LLIMFloaterContainer::getInstance(); - } - - if (im_box && !LLIMFloater::findInstance(session_id)) - { - LLIMFloater* new_tab = LLIMFloater::getInstance(session_id); - im_box->addFloater(new_tab, FALSE, LLTabContainer::END); - } - } -} boost::signals2::connection LLIMFloater::setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb) { diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index e4a67a3d56..5ed1d1ab35 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -71,14 +71,13 @@ public: static LLIMFloater* findInstance(const LLUUID& session_id); static LLIMFloater* getInstance(const LLUUID& session_id); - static void addToHost(const LLUUID& session_id); + static void addToHost(const LLUUID& session_id, const bool force = false); // LLFloater overrides /*virtual*/ void onClose(bool app_quitting); /*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); - static LLIMFloater* addToIMContainer(const LLUUID& session_id); // Toggle panel specified by session_id // Returns true iff panel became visible @@ -128,6 +127,7 @@ public: static void onIMChicletCreated(const LLUUID& session_id); bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; } + const LLUUID& getOtherParticipantUUID() {return mOtherParticipantUUID;} static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb); static floater_showed_signal_t sIMFloaterShowedSignal; diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 4e0fba9502..4af170b3db 100755..100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -39,6 +39,7 @@ #include "llavatariconctrl.h" #include "llavatarnamecache.h" #include "llcallbacklist.h" +#include "llgroupactions.h" #include "llgroupiconctrl.h" #include "llfloateravatarpicker.h" #include "llfloaterpreference.h" @@ -47,6 +48,7 @@ #include "llviewercontrol.h" #include "llconversationview.h" #include "llcallbacklist.h" +#include "llworld.h" // // LLIMFloaterContainer @@ -57,8 +59,14 @@ LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed) mConversationsRoot(NULL), mInitialized(false) { + mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLIMFloaterContainer::isActionChecked, this, _2)); mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLIMFloaterContainer::onCustomAction, this, _2)); - mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLIMFloaterContainer::isActionChecked, this, _2)); + + mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLIMFloaterContainer::checkContextMenuItem, this, _2)); + mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLIMFloaterContainer::enableContextMenuItem, this, _2)); + mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLIMFloaterContainer::doToSelected, this, _2)); + + mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&LLIMFloaterContainer::doToSelectedGroup, this, _2)); // Firstly add our self to IMSession observers, so we catch session events LLIMMgr::getInstance()->addSessionObserver(this); @@ -85,13 +93,13 @@ LLIMFloaterContainer::~LLIMFloaterContainer() void LLIMFloaterContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) { - LLIMFloater::addToIMContainer(session_id); + LLIMFloater::addToHost(session_id, true); addConversationListItem(session_id); } void LLIMFloaterContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) { - LLIMFloater::addToIMContainer(session_id); + LLIMFloater::addToHost(session_id, true); addConversationListItem(session_id); } @@ -106,6 +114,16 @@ void LLIMFloaterContainer::sessionRemoved(const LLUUID& session_id) removeConversationListItem(session_id); } +// static +void LLIMFloaterContainer::onCurrentChannelChanged(const LLUUID& session_id) +{ + if (session_id != LLUUID::null) + { + LLIMFloater::show(session_id); + } +} + + BOOL LLIMFloaterContainer::postBuild() { mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&LLIMFloaterContainer::onNewMessageReceived, this, _1)); @@ -132,7 +150,10 @@ BOOL LLIMFloaterContainer::postBuild() p.listener = base_item; p.view_model = &mConversationViewModel; p.root = NULL; + p.use_ellipses = true; + p.options_menu = "menu_conversation.xml"; mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p); + mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); // a scroller for folder view LLRect scroller_view_rect = mConversationsListPanel->getRect(); @@ -157,8 +178,7 @@ BOOL LLIMFloaterContainer::postBuild() collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed")); - LLAvatarNameCache::addUseDisplayNamesCallback( - boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); + LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); if (! mMessagesPane->isCollapsed()) { @@ -175,8 +195,11 @@ BOOL LLIMFloaterContainer::postBuild() mInitialized = true; - // Add callback: we'll take care of view updates on idle + // Add callbacks: + // We'll take care of view updates on idle gIdleCallbacks.addFunction(idle, this); + // When display name option change, we need to reload all participant names + LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLIMFloaterContainer::processParticipantsStyleUpdate, this)); return TRUE; } @@ -220,8 +243,8 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, session_id)); } else - { - LLUUID avatar_id = LLIMModel::getInstance()->getOtherParticipantID(session_id); + { LLUUID avatar_id = session_id.notNull()? + LLIMModel::getInstance()->getOtherParticipantID(session_id) : LLUUID(); LLAvatarIconCtrl::Params icon_params; icon_params.avatar_id = avatar_id; @@ -329,10 +352,48 @@ void LLIMFloaterContainer::setMinimized(BOOL b) } } +// Update all participants in the conversation lists +void LLIMFloaterContainer::processParticipantsStyleUpdate() +{ + // On each session in mConversationsItems + for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) + { + // Get the current session descriptors + LLConversationItem* session_model = it_session->second; + // Iterate through each model participant child + LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = session_model->getChildrenBegin(); + LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = session_model->getChildrenEnd(); + while (current_participant_model != end_participant_model) + { + LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model); + // Get the avatar name for this participant id from the cache and update the model + LLUUID participant_id = participant_model->getUUID(); + LLAvatarName av_name; + LLAvatarNameCache::get(participant_id,&av_name); + // Avoid updating the model though if the cache is still waiting for its first update + if (!av_name.mDisplayName.empty()) + { + participant_model->onAvatarNameCache(av_name); + } + // Bind update to the next cache name signal + LLAvatarNameCache::get(participant_id, boost::bind(&LLConversationItemParticipant::onAvatarNameCache, participant_model, _2)); + // Next participant + current_participant_model++; + } + } +} + // static void LLIMFloaterContainer::idle(void* user_data) { LLIMFloaterContainer* self = static_cast<LLIMFloaterContainer*>(user_data); + + // Update the distance to agent in the nearby chat session if required + // Note: it makes no sense of course to update the distance in other session + if (self->mConversationViewModel.getSorter().getSortOrderParticipants() == LLConversationFilter::SO_DISTANCE) + { + self->setNearbyDistances(); + } self->mConversationsRoot->update(); } @@ -385,6 +446,8 @@ void LLIMFloaterContainer::draw() } // Reset the need for refresh session_model->resetRefresh(); + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); // Next participant current_participant_model++; } @@ -410,11 +473,11 @@ void LLIMFloaterContainer::tabClose() } void LLIMFloaterContainer::setVisible(BOOL visible) -{ +{ LLNearbyChat* nearby_chat; if (visible) { // Make sure we have the Nearby Chat present when showing the conversation container - LLIMConversation* nearby_chat = LLFloaterReg::findTypedInstance<LLIMConversation>("nearby_chat"); + nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat"); if (nearby_chat == NULL) { // If not found, force the creation of the nearby chat conversation panel @@ -424,6 +487,12 @@ void LLIMFloaterContainer::setVisible(BOOL visible) } } + nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat && !nearby_chat->isHostSet()) + { + nearby_chat->addToHost(); + } + // We need to show/hide all the associated conversations that have been torn off // (and therefore, are not longer managed by the multifloater), // so that they show/hide with the conversations manager. @@ -488,6 +557,22 @@ void LLIMFloaterContainer::collapseConversationsPane(bool collapse) S32 collapsed_width = mConversationsPane->getMinDim(); updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") - collapsed_width); + + for (conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + widget_it != mConversationsWidgets.end(); ++widget_it) + { + LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second); + if (widget) + { + widget->toggleMinimizedMode(collapse); + + // force closing all open conversations when collapsing to minimized state + if (collapse) + { + widget->setOpen(false); + } +} + } } void LLIMFloaterContainer::updateState(bool collapse, S32 delta_width) @@ -650,63 +735,338 @@ void LLIMFloaterContainer::setSortOrder(const LLConversationSort& order) gSavedSettings.setU32("ConversationSortOrder", (U32)order); } -void LLIMFloaterContainer::repositioningWidgets() +void LLIMFloaterContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids) +{ + const std::set<LLFolderViewItem*> selectedItems = mConversationsRoot->getSelectionList(); + + std::set<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); + const std::set<LLFolderViewItem*>::const_iterator it_end = selectedItems.end(); + LLConversationItem * conversationItem; + + for (; it != it_end; ++it) + { + conversationItem = static_cast<LLConversationItem *>((*it)->getViewModelItem()); + selected_uuids.push_back(conversationItem->getUUID()); + } +} + +const LLConversationItem * LLIMFloaterContainer::getCurSelectedViewModelItem() +{ + LLConversationItem * conversationItem = NULL; + + if(mConversationsRoot && + mConversationsRoot->getCurSelectedItem() && + mConversationsRoot->getCurSelectedItem()->getViewModelItem()) + { + conversationItem = static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem()); + } + + return conversationItem; +} + +void LLIMFloaterContainer::getParticipantUUIDs(uuid_vec_t& selected_uuids) +{ + //Find the conversation floater associated with the selected id + const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); + + if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT) + { + getSelectedUUIDs(selected_uuids); + } + //When a one-on-one conversation exists, retrieve the participant id from the conversation floater + else if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1) + { + LLIMFloater *conversationFloater = LLIMFloater::findInstance(conversationItem->getUUID()); + LLUUID participantID = conversationFloater->getOtherParticipantUUID(); + selected_uuids.push_back(participantID); + } +} + +void LLIMFloaterContainer::doToParticipants(const std::string& command, uuid_vec_t& selectedIDS) +{ + if(selectedIDS.size() > 0) { - if (!mInitialized) + const LLUUID userID = selectedIDS.front(); + + if ("view_profile" == command) + { + LLAvatarActions::showProfile(userID); + } + else if("im" == command) + { + LLAvatarActions::startIM(userID); + } + else if("offer_teleport" == command) + { + LLAvatarActions::offerTeleport(selectedIDS); + } + else if("voice_call" == command) + { + LLAvatarActions::startCall(userID); + } + else if("chat_history" == command) + { + LLAvatarActions::viewChatHistory(userID); + } + else if("add_friend" == command) + { + LLAvatarActions::requestFriendshipDialog(userID); + } + else if("remove_friend" == command) + { + LLAvatarActions::removeFriendDialog(userID); + } + else if("invite_to_group" == command) + { + LLAvatarActions::inviteToGroup(userID); + } + else if("map" == command) + { + LLAvatarActions::showOnMap(userID); + } + else if("share" == command) + { + LLAvatarActions::share(userID); + } + else if("pay" == command) + { + LLAvatarActions::pay(userID); + } + else if("block_unblock" == command) + { + LLAvatarActions::toggleBlock(userID); + } +} +} + +void LLIMFloaterContainer::doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS) +{ + //Find the conversation floater associated with the selected id + const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); + LLIMFloater *conversationFloater = LLIMFloater::findInstance(conversationItem->getUUID()); + + if(conversationFloater) + { + //Close the selected conversation + if("close_conversation" == command) + { + LLFloater::onClickClose(conversationFloater); + } + else if("open_voice_conversation" == command) + { + gIMMgr->startCall(conversationItem->getUUID()); + } + else if("disconnect_from_voice" == command) + { + gIMMgr->endCall(conversationItem->getUUID()); + } + else if("chat_history" == command) + { + LLAvatarActions::viewChatHistory(conversationItem->getUUID()); + } + else + { + doToParticipants(command, selectedIDS); + } + } +} + +void LLIMFloaterContainer::doToSelected(const LLSD& userdata) +{ + std::string command = userdata.asString(); + const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); + uuid_vec_t selected_uuids; + + getParticipantUUIDs(selected_uuids); + + if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT) + { + doToParticipants(command, selected_uuids); + } + else + { + doToSelectedConversation(command, selected_uuids); + } +} + +void LLIMFloaterContainer::doToSelectedGroup(const LLSD& userdata) +{ + std::string action = userdata.asString(); + LLUUID selected_group = getCurSelectedViewModelItem()->getUUID(); + + if (action == "group_profile") + { + LLGroupActions::show(selected_group); + } + else if (action == "activate_group") + { + LLGroupActions::activate(selected_group); + } + else if (action == "leave_group") + { + LLGroupActions::leave(selected_group); + } +} + +bool LLIMFloaterContainer::enableContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + uuid_vec_t mUUIDs; + getParticipantUUIDs(mUUIDs); + + if(mUUIDs.size() <= 0) + { + return false; + } + + // Note: can_block and can_delete is used only for one person selected menu + // so we don't need to go over all uuids. + + if (item == std::string("can_block")) + { + const LLUUID& id = mUUIDs.front(); + return LLAvatarActions::canBlock(id); + } + else if (item == std::string("can_add")) + { + // We can add friends if: + // - there are selected people + // - and there are no friends among selection yet. + + //EXT-7389 - disable for more than 1 + if(mUUIDs.size() > 1) + { + return false; + } + + bool result = true; + + uuid_vec_t::const_iterator + id = mUUIDs.begin(), + uuids_end = mUUIDs.end(); + + for (;id != uuids_end; ++id) + { + if ( LLAvatarActions::isFriend(*id) ) + { + result = false; + break; + } + } + + return result; + } + else if (item == std::string("can_delete")) + { + // We can remove friends if: + // - there are selected people + // - and there are only friends among selection. + + bool result = (mUUIDs.size() > 0); + + uuid_vec_t::const_iterator + id = mUUIDs.begin(), + uuids_end = mUUIDs.end(); + + for (;id != uuids_end; ++id) + { + if ( !LLAvatarActions::isFriend(*id) ) + { + result = false; + break; + } + } + + return result; + } + else if (item == std::string("can_call")) + { + return LLAvatarActions::canCall(); + } + else if (item == std::string("can_show_on_map")) + { + const LLUUID& id = mUUIDs.front(); + + return (LLAvatarTracker::instance().isBuddyOnline(id) && is_agent_mappable(id)) + || gAgent.isGodlike(); + } + else if(item == std::string("can_offer_teleport")) + { + return LLAvatarActions::canOfferTeleport(mUUIDs); + } + return false; +} + +bool LLIMFloaterContainer::checkContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + uuid_vec_t mUUIDs; + getParticipantUUIDs(mUUIDs); + + if(mUUIDs.size() > 0 ) + { + if (item == std::string("is_blocked")) + { + return LLAvatarActions::isBlocked(mUUIDs.front()); + } + } + + return false; +} + +void LLIMFloaterContainer::setConvItemSelect(const LLUUID& session_id) +{ + LLFolderViewItem* widget = mConversationsWidgets[session_id]; + if (widget && mSelectedSession != session_id) { - return; + mSelectedSession = session_id; + (widget->getRoot())->setSelection(widget, FALSE, FALSE); } +} - if (!mConversationsPane->isCollapsed()) +void LLIMFloaterContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) +{ + conversations_items_map::iterator item_it = mConversationsItems.find(session_id); + if (item_it != mConversationsItems.end()) { - S32 list_width = (mConversationsPane->getRect()).getWidth(); - gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", list_width); - } - LLRect panel_rect = mConversationsListPanel->getRect(); - S32 item_height = 16; - int index = 0; - for (conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); - widget_it != mConversationsWidgets.end(); - widget_it++) - { - LLFolderViewFolder* widget = dynamic_cast<LLFolderViewFolder*>(widget_it->second); - widget->setVisible(TRUE); - widget->setRect(LLRect(0, - panel_rect.getHeight() - item_height*index, - panel_rect.getWidth(), - panel_rect.getHeight() - item_height*(index+1))); - index++; - // Reposition the children as well - // Merov : This is highly suspiscious but gets the debug hack to work. This needs to be revised though. - if (widget->getItemsCount() != 0) + LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(item_it->second); + if (item) { - BOOL is_open = widget->isOpen(); - widget->setOpen(TRUE); - LLFolderViewFolder::items_t::const_iterator current = widget->getItemsBegin(); - LLFolderViewFolder::items_t::const_iterator end = widget->getItemsEnd(); - while (current != end) - { - LLFolderViewItem* item = (*current); - item->setVisible(TRUE); - item->setRect(LLRect(0, - panel_rect.getHeight() - item_height*index, - panel_rect.getWidth(), - panel_rect.getHeight() - item_height*(index+1))); - index++; - current++; - } - widget->setOpen(is_open); + item->setTimeNow(participant_id); + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); } } } -void LLIMFloaterContainer::setConvItemSelect(LLUUID& session_id) +void LLIMFloaterContainer::setNearbyDistances() { - LLFolderViewItem* widget = mConversationsWidgets[session_id]; - if (widget && mSelectedSession != session_id) + // Get the nearby chat session: that's the one with uuid nul in mConversationsItems + conversations_items_map::iterator item_it = mConversationsItems.find(LLUUID()); + if (item_it != mConversationsItems.end()) { - mSelectedSession = session_id; - (widget->getRoot())->setSelection(widget, FALSE, FALSE); + LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(item_it->second); + if (item) + { + // Get the positions of the nearby avatars and their ids + std::vector<LLVector3d> positions; + uuid_vec_t avatar_ids; + LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); + // Get the position of the agent + const LLVector3d& me_pos = gAgent.getPositionGlobal(); + // For each nearby avatar, compute and update the distance + int avatar_count = positions.size(); + for (int i = 0; i < avatar_count; i++) + { + F64 dist = dist_vec_squared(positions[i], me_pos); + item->setDistance(avatar_ids[i],dist); + } + // Also does it for the agent itself + item->setDistance(gAgent.getID(),0.0f); + // Request resort + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + } } } @@ -763,7 +1123,12 @@ void LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid) participant_view->addToFolder(widget); current_participant_model++; } + + setConvItemSelect(uuid); + // set the widget to minimized mode if conversations pane is collapsed + widget->toggleMinimizedMode(mConversationsPane->isCollapsed()); + return; } diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index 1f526091bb..76e468f979 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -62,15 +62,15 @@ public: /*virtual*/ void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); - void setConvItemSelect(LLUUID& session_id); + void setConvItemSelect(const LLUUID& session_id); /*virtual*/ void tabClose(); static LLFloater* getCurrentVoiceFloater(); - static LLIMFloaterContainer* findInstance(); - static LLIMFloaterContainer* getInstance(); + static void onCurrentChannelChanged(const LLUUID& session_id); + virtual void setMinimized(BOOL b); void collapseMessagesPane(bool collapse); @@ -96,11 +96,11 @@ private: void onNewMessageReceived(const LLSD& data); void onExpandCollapseButtonClicked(); + void processParticipantsStyleUpdate(); void collapseConversationsPane(bool collapse); void updateState(bool collapse, S32 delta_width); - void repositioningWidgets(); void onAddButtonClicked(); void onAvatarPicked(const uuid_vec_t& ids); @@ -111,6 +111,16 @@ private: void setSortOrderParticipants(const LLConversationFilter::ESortOrderType order); void setSortOrder(const LLConversationSort& order); + void getSelectedUUIDs(uuid_vec_t& selected_uuids); + const LLConversationItem * getCurSelectedViewModelItem(); + void getParticipantUUIDs(uuid_vec_t& selected_uuids); + void doToSelected(const LLSD& userdata); + void doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS); + void doToParticipants(const std::string& item, uuid_vec_t& selectedIDS); + void doToSelectedGroup(const LLSD& userdata); + bool checkContextMenuItem(const LLSD& userdata); + bool enableContextMenuItem(const LLSD& userdata); + LLButton* mExpandCollapseBtn; LLLayoutPanel* mMessagesPane; LLLayoutPanel* mConversationsPane; @@ -124,6 +134,8 @@ private: public: void removeConversationListItem(const LLUUID& uuid, bool change_focus = true); void addConversationListItem(const LLUUID& uuid); + void setTimeNow(const LLUUID& session_id, const LLUUID& participant_id); + void setNearbyDistances(); private: LLConversationViewSession* createConversationItemWidget(LLConversationItem* item); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 72c54e5ce2..139713b96e 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -1531,6 +1531,15 @@ LLUIImagePtr LLItemBridge::getIcon() const return LLInventoryIcon::getIcon(LLInventoryIcon::ICONNAME_OBJECT); } +LLUIImagePtr LLItemBridge::getIconOverlay() const +{ + if (getItem() && getItem()->getIsLinkType()) + { + return LLUI::getUIImage("Inv_Link"); + } + return NULL; +} + PermissionMask LLItemBridge::getPermissionMask() const { LLViewerInventoryItem* item = getItem(); diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 6beccf19ae..b33972167c 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -228,6 +228,7 @@ public: virtual BOOL isItemCopyable() const; virtual bool hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } + virtual LLUIImagePtr getIconOverlay() const; LLViewerInventoryItem* getItem() const; diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 2a84616ddf..a8d99ad7de 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -172,6 +172,7 @@ LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id ) p.show_empty_message = mShowEmptyMessage; p.show_item_link_overlays = mShowItemLinkOverlays; p.root = NULL; + p.options_menu = "menu_inventory.xml"; return LLUICtrlFactory::create<LLFolderView>(p); } @@ -441,8 +442,9 @@ void LLInventoryPanel::modelChanged(U32 mask) handled = true; if (model_item && view_item && viewmodel_item) { + const LLUUID& idp = viewmodel_item->getUUID(); view_item->destroyView(); - removeItemID(viewmodel_item->getUUID()); + removeItemID(idp); } view_item = buildNewViews(item_id); viewmodel_item = diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 76626bd5a6..b96b486868 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -88,29 +88,36 @@ static LLChatTypeTrigger sChatTypeTriggers[] = { LLNearbyChat::LLNearbyChat(const LLSD& llsd) -: LLIMConversation(llsd.asUUID()), +: LLIMConversation(llsd), //mOutputMonitor(NULL), mSpeakerMgr(NULL), - mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) + mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT), + mIsHostSet(false) { mIsP2PChat = false; mIsNearbyChat = true; setIsChrome(TRUE); - mKey = LLSD(LLUUID()); mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); mSessionID = LLUUID(); - setName("nearby_chat"); - setIsSingleInstance(TRUE); +} + +//static +LLNearbyChat* LLNearbyChat::buildFloater(const LLSD& key) +{ + LLFloaterReg::getInstance("im_container"); + return new LLNearbyChat(key); } //virtual BOOL LLNearbyChat::postBuild() { + setIsSingleInstance(TRUE); BOOL result = LLIMConversation::postBuild(); mInputEditor->setCommitCallback(boost::bind(&LLNearbyChat::onChatBoxCommit, this)); mInputEditor->setKeystrokeCallback(boost::bind(&onChatBoxKeystroke, _1, this)); mInputEditor->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); mInputEditor->setFocusReceivedCallback(boost::bind(&LLNearbyChat::onChatBoxFocusReceived, this)); + mInputEditor->setLabel(LLTrans::getString("NearbyChatTitle")); // mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator"); // mOutputMonitor->setVisible(FALSE); @@ -122,8 +129,6 @@ BOOL LLNearbyChat::postBuild() // it is used for show the item's name in the conversations list setTitle(LLTrans::getString("NearbyChatTitle")); - addToHost(); - //for menu LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; @@ -306,9 +311,16 @@ void LLNearbyChat::addToHost() setHost(NULL); } } + + mIsHostSet = true; } } +bool LLNearbyChat::isHostSet() +{ + return mIsHostSet; +} + // virtual void LLNearbyChat::onOpen(const LLSD& key) { diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 648098113a..93168ba96a 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -45,9 +45,11 @@ class LLNearbyChat { public: // constructor for inline chat-bars (e.g. hosted in chat history window) - LLNearbyChat(const LLSD& key = LLSD()); + LLNearbyChat(const LLSD& key = LLSD(LLUUID())); ~LLNearbyChat() {} + static LLNearbyChat* buildFloater(const LLSD& key); + /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); @@ -76,6 +78,8 @@ public: static void startChat(const char* line); static void stopChat(); + bool isHostSet(); + static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); @@ -117,6 +121,7 @@ private: LLHandle<LLView> mPopupMenuHandle; std::vector<LLChat> mMessageArchive; + bool mIsHostSet; }; #endif diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index f3e17ea61b..7834f6d320 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -487,6 +487,7 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, if(chat_msg.mText.empty()) return;//don't process empty messages + LLFloaterReg::getInstance("im_container"); LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"); // Build notification data diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp index 5dcd84b400..290a81f91c 100644 --- a/indra/newview/llnotificationscripthandler.cpp +++ b/indra/newview/llnotificationscripthandler.cpp @@ -109,7 +109,7 @@ bool LLScriptHandler::processNotification(const LLNotificationPtr& notification) void LLScriptHandler::onDelete( LLNotificationPtr notification ) { - if(notification->hasFormElements()) + if(notification->hasFormElements() && !notification->canShowToast()) { LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID()); } @@ -128,10 +128,11 @@ void LLScriptHandler::onDeleteToast(LLToast* toast) // in this case listener is a SysWellWindow and it will remove a corresponding item from its list LLNotificationPtr notification = LLNotifications::getInstance()->find(toast->getNotificationID()); - if( notification && notification->hasFormElements()) + if( notification && notification->hasFormElements() && !notification->canShowToast()) { LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID()); } + } diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index 096e714981..4a9a50d96a 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -74,7 +74,8 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p) mSpeakerId(p.speaker_id), mIsAgentControl(false), mIsSwitchDirty(false), - mShouldSwitchOn(false) + mShouldSwitchOn(false), + mShowParticipantsSpeaking(false) { //static LLUIColor output_monitor_muted_color = LLUIColorTable::instance().getColor("OutputMonitorMutedColor", LLColor4::orange); //static LLUIColor output_monitor_overdriven_color = LLUIColorTable::instance().getColor("OutputMonitorOverdrivenColor", LLColor4::red); @@ -157,6 +158,24 @@ void LLOutputMonitorCtrl::draw() } } + if ((mPower == 0.f && !mIsTalking) && mShowParticipantsSpeaking) + { + std::set<LLUUID> participant_uuids; + LLVoiceClient::instance().getParticipantList(participant_uuids); + std::set<LLUUID>::const_iterator part_it = participant_uuids.begin(); + + F32 power = 0; + for (; part_it != participant_uuids.end(); ++part_it) + { + power = LLVoiceClient::instance().getCurrentPower(*part_it); + if (power) + { + mPower = power; + break; + } + } + } + LLPointer<LLUIImage> icon; if (mIsMuted) { @@ -245,15 +264,19 @@ void LLOutputMonitorCtrl::draw() // virtual BOOL LLOutputMonitorCtrl::handleMouseUp(S32 x, S32 y, MASK mask) { - if (mSpeakerId != gAgentID) + if (mSpeakerId != gAgentID && !mShowParticipantsSpeaking) { LLFloaterReg::showInstance("floater_voice_volume", LLSD().with("avatar_id", mSpeakerId)); } + else if(mShowParticipantsSpeaking) + { + LLFloaterReg::showInstance("chat_voice", LLSD()); + } return TRUE; } -void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id/* = LLUUID::null*/) +void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id/* = LLUUID::null*/, bool show_other_participants_speaking /* = false */) { if (speaker_id.isNull() && mSpeakerId.notNull()) { @@ -268,6 +291,7 @@ void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& s LLSpeakingIndicatorManager::unregisterSpeakingIndicator(mSpeakerId, this); } + mShowParticipantsSpeaking = show_other_participants_speaking; mSpeakerId = speaker_id; LLSpeakingIndicatorManager::registerSpeakingIndicator(mSpeakerId, this, session_id); diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index 7b02e84744..1fa6ef41f8 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -82,6 +82,8 @@ public: void setIsTalking(bool val) { mIsTalking = val; } + void setShowParticipantsSpeaking(bool show) { mShowParticipantsSpeaking = show; } + /** * Sets avatar UUID to interact with voice channel. * @@ -90,7 +92,7 @@ public: * If this parameter is set registered indicator will be shown only in voice channel * which has the same session id (EXT-5562). */ - void setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id = LLUUID::null); + void setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id = LLUUID::null, bool show_other_participants_speaking = false); //called by mute list virtual void onChange(); @@ -132,6 +134,7 @@ private: bool mIsAgentControl; bool mIsMuted; bool mIsTalking; + bool mShowParticipantsSpeaking; LLPointer<LLUIImage> mImageMute; LLPointer<LLUIImage> mImageOff; LLPointer<LLUIImage> mImageOn; diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index c9eebe24d3..899771f3b9 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -69,6 +69,7 @@ LLContextMenu* NearbyMenu::createMenu() registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, id)); registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, id)); registrar.add("Avatar.InviteToGroup", boost::bind(&LLAvatarActions::inviteToGroup, id)); + registrar.add("Avatar.Calllog", boost::bind(&LLAvatarActions::viewChatHistory, id)); enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2)); enable_registrar.add("Avatar.CheckItem", boost::bind(&NearbyMenu::checkContextMenuItem, this, _2)); diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 339cee3f95..90226e7fba 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -34,6 +34,7 @@ #include "llagent.h" #include "llimview.h" +#include "llimfloatercontainer.h" #include "llpanelpeoplemenus.h" #include "llnotificationsutil.h" #include "llparticipantlist.h" @@ -224,12 +225,14 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, mSpeakerRemoveListener = new SpeakerRemoveListener(*this); mSpeakerClearListener = new SpeakerClearListener(*this); mSpeakerModeratorListener = new SpeakerModeratorUpdateListener(*this); + mSpeakerUpdateListener = new SpeakerUpdateListener(*this); mSpeakerMuteListener = new SpeakerMuteListener(*this); mSpeakerMgr->addListener(mSpeakerAddListener, "add"); mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove"); mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator"); + mSpeakerMgr->addListener(mSpeakerUpdateListener, "update_speaker"); setSessionID(mSpeakerMgr->getSessionID()); @@ -584,6 +587,21 @@ bool LLParticipantList::onClearListEvent(LLPointer<LLOldEvents::LLEvent> event, return true; } +bool LLParticipantList::onSpeakerUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) +{ + const LLSD& evt_data = event->getValue(); + if ( evt_data.has("id") ) + { + LLUUID participant_id = evt_data["id"]; + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->setTimeNow(mUUID,participant_id); + } + } + return true; +} + bool LLParticipantList::onModeratorUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) { const LLSD& evt_data = event->getValue(); @@ -660,8 +678,10 @@ void LLParticipantList::sort() void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) { + // Do not add if already in there or excluded for some reason if (mExcludeAgent && gAgent.getID() == avatar_id) return; if (mAvatarList && mAvatarList->contains(avatar_id)) return; + if (findParticipant(avatar_id)) return; bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(avatar_id); @@ -746,6 +766,14 @@ bool LLParticipantList::SpeakerClearListener::handleEvent(LLPointer<LLOldEvents: } // +// LLParticipantList::SpeakerUpdateListener +// +bool LLParticipantList::SpeakerUpdateListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) +{ + return mParent.onSpeakerUpdateEvent(event, userdata); +} + +// // LLParticipantList::SpeakerModeratorListener // bool LLParticipantList::SpeakerModeratorUpdateListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index f8165aa292..acee68873c 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -95,6 +95,7 @@ protected: bool onRemoveItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); bool onClearListEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); bool onModeratorUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + bool onSpeakerUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); bool onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); /** @@ -136,6 +137,13 @@ protected: /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); }; + class SpeakerUpdateListener : public BaseSpeakerListener + { + public: + SpeakerUpdateListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + }; + class SpeakerModeratorUpdateListener : public BaseSpeakerListener { public: @@ -264,6 +272,7 @@ private: LLPointer<SpeakerAddListener> mSpeakerAddListener; LLPointer<SpeakerRemoveListener> mSpeakerRemoveListener; LLPointer<SpeakerClearListener> mSpeakerClearListener; + LLPointer<SpeakerUpdateListener> mSpeakerUpdateListener; LLPointer<SpeakerModeratorUpdateListener> mSpeakerModeratorListener; LLPointer<SpeakerMuteListener> mSpeakerMuteListener; diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 07d2f1ad6f..2d2b5202e0 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -84,6 +84,19 @@ bool LLSpeaker::isInVoiceChannel() return mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || mStatus == LLSpeaker::STATUS_MUTED; } +LLSpeakerUpdateSpeakerEvent::LLSpeakerUpdateSpeakerEvent(LLSpeaker* source) +: LLEvent(source, "Speaker update speaker event"), + mSpeakerID (source->mID) +{ +} + +LLSD LLSpeakerUpdateSpeakerEvent::getValue() +{ + LLSD ret; + ret["id"] = mSpeakerID; + return ret; +} + LLSpeakerUpdateModeratorEvent::LLSpeakerUpdateModeratorEvent(LLSpeaker* source) : LLEvent(source, "Speaker add moderator event"), mSpeakerID (source->mID), @@ -374,6 +387,7 @@ void LLSpeakerMgr::update(BOOL resort_ok) { speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); speakerp->mHasSpoken = TRUE; + fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); } speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; // interpolate between active color and full speaking color based on power of speech output @@ -548,6 +562,7 @@ void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) { speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); speakerp->mHasSpoken = TRUE; + fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); } } diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index 1c6f51e131..8ab08661d3 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -79,6 +79,15 @@ public: BOOL mModeratorMutedText; }; +class LLSpeakerUpdateSpeakerEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerUpdateSpeakerEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +private: + const LLUUID& mSpeakerID; +}; + class LLSpeakerUpdateModeratorEvent : public LLOldEvents::LLEvent { public: diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 927ee8f380..c751550523 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -50,6 +50,7 @@ #include "llfloaterbump.h" #include "llfloaterbvhpreview.h" #include "llfloatercamera.h" +#include "llfloaterchatvoicevolume.h" #include "llfloaterconversationlog.h" #include "llfloaterconversationpreview.h" #include "llfloaterdeleteenvpreset.h" @@ -192,7 +193,8 @@ 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("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNearbyChat>); + LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>); + LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLNearbyChat::buildFloater); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>); LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationLog>); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index a993d195b5..6da9296ea3 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3466,7 +3466,8 @@ class LLTogglePanelPeopleTab : public view_listener_t if ( panel_name == "friends_panel" || panel_name == "groups_panel" - || panel_name == "nearby_panel") + || panel_name == "nearby_panel" + || panel_name == "blocked_panel") { return togglePeoplePanel(panel_name, param); } @@ -5490,16 +5491,6 @@ void toggle_debug_menus(void*) // gExportDialog = LLUploadDialog::modalUploadDialog("Exporting selected objects..."); // } // - -class LLCommunicateBlockList : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - return true; - } -}; - class LLWorldSetHomeLocation : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -8290,9 +8281,6 @@ void initialize_menus() // Me > Movement view_listener_t::addMenu(new LLAdvancedAgentFlyingInfo(), "Agent.getFlying"); - // Communicate - view_listener_t::addMenu(new LLCommunicateBlockList(), "Communicate.BlockList"); - // World menu view_listener_t::addMenu(new LLWorldAlwaysRun(), "World.AlwaysRun"); view_listener_t::addMenu(new LLWorldCreateLandmark(), "World.CreateLandmark"); diff --git a/indra/newview/skins/default/textures/icons/nearby_chat_icon.png b/indra/newview/skins/default/textures/icons/nearby_chat_icon.png Binary files differnew file mode 100644 index 0000000000..7c3ad40381 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/nearby_chat_icon.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 47b0c12fa0..a124041565 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -363,6 +363,8 @@ with the same filename but different name <texture name="NavBar_BG_NoFav_Bevel" file_name="navbar/NavBar_BG_NoFav_Bevel.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0" /> <texture name="NavBar_BG_NoNav_Bevel" file_name="navbar/NavBar_BG_NoNav_Bevel.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0" /> + <texture name="Nearby_chat_icon" file_name="icons/nearby_chat_icon.png" preload="false" /> + <texture name="Notices_Unread" file_name="bottomtray/Notices_Unread.png" preload="true" /> <texture name="NoEntryLines" file_name="world/NoEntryLines.png" use_mips="true" preload="false" /> diff --git a/indra/newview/skins/default/xui/en/floater_conversation_log.xml b/indra/newview/skins/default/xui/en/floater_conversation_log.xml index 12d17e6b37..c9c52e5ce5 100644 --- a/indra/newview/skins/default/xui/en/floater_conversation_log.xml +++ b/indra/newview/skins/default/xui/en/floater_conversation_log.xml @@ -49,6 +49,7 @@ menu_filename="menu_conversation_log_view.xml" menu_position="bottomleft" name="conversation_view_btn" + tool_tip="View/sort options" top="3" width="31" /> <menu_button @@ -61,6 +62,7 @@ layout="topleft" left_pad="2" name="conversations_gear_btn" + tool_tip="Actions on selected person or group" top="3" width="31" /> </panel> diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml index 413e66738d..590ce45c33 100644 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -11,6 +11,7 @@ save_rect="true" save_visibility="true" single_instance="true" + reuse_instance="true" title="CONVERSATIONS" width="680"> <string @@ -62,6 +63,7 @@ layout="topleft" left="10" name="sort_btn" + tool_tip="View/sort options" top="5" width="31" /> <button @@ -75,7 +77,7 @@ top="5" left_pad="4" name="add_btn" - tool_tip="Add button on the left panel" + tool_tip="Start a new conversation" width="31"/> </layout_panel> <layout_panel @@ -94,6 +96,7 @@ top="5" left="5" name="expand_collapse_btn" + tool_tip="Collapse/Expand this list" width="31" /> </layout_panel> </layout_stack> 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 d6d48130ab..5c74f7f9bb 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -37,6 +37,12 @@ <floater.string name="multiple_participants_added" value="[NAME] were invited to the conversation."/> + <floater.string + name="tooltip_to_separate_window" + value="Move this conversation to a separate window"/> + <floater.string + name="tooltip_to_main_window" + value="Move this conversation back to main window"/> <view follows="all" layout="topleft" @@ -64,6 +70,7 @@ layout="topleft" left="5" name="view_options_btn" + tool_tip="View/sort options" top="5" width="31" /> <button @@ -78,6 +85,7 @@ top="5" left_pad="4" name="add_btn" + tool_tip="Add someone to this conversation" width="31"/> <button follows="top|left" @@ -90,6 +98,7 @@ top="5" left_pad="4" name="voice_call_btn" + tool_tip="Open voice connection" width="31"/> <button follows="right|top" @@ -102,6 +111,7 @@ top="5" left="283" name="close_btn" + tool_tip="End this conversation" width="31" /> <button follows="right|top" @@ -114,6 +124,7 @@ top="5" left_pad="5" name="expand_collapse_btn" + tool_tip="Collapse/Expand this pane" width="31" /> <button follows="right|top" diff --git a/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml b/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml new file mode 100644 index 0000000000..5c71fd3bc6 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> + +<floater + legacy_header_height="25" + bevel_style="in" + bg_opaque_image="Inspector_Background" + can_close="false" + can_minimize="false" + height="90" + layout="topleft" + name="floater_voice_volume" + single_instance="true" + sound_flags="0" + title="VOICE CHAT VOLUME" + visible="true" + width="245"> + <slider + control_name="AudioLevelVoice" + disabled_control="MuteAudio" + follows="left|top" + height="16" + increment="0.025" + initial_value="0.5" + label="Voice Chat" + label_width="50" + layout="topleft" + left="15" + top="50" + name="chat_voice_volume" + show_text="false" + slider_label.halign="right" + volume="true" + width="200"> + </slider> + <button + control_name="MuteVoice" + disabled_control="MuteAudio" + follows="top|left" + height="16" + image_selected="AudioMute_Off" + image_unselected="Audio_Off" + is_toggle="true" + layout="topleft" + left_pad="5" + name="mute_audio" + tab_stop="false" + width="16" /> +</floater>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/menu_conversation.xml b/indra/newview/skins/default/xui/en/menu_conversation.xml new file mode 100644 index 0000000000..912ff811d9 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_conversation.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + bottom="806" + layout="topleft" + left="0" + mouse_opaque="false" + name="menu_conversation_participant" + visible="false"> + <menu_item_call + label="Close conversation" + layout="topleft" + name="close_conversation"> + <on_click function="Avatar.DoToSelected" parameter="close_conversation"/> + </menu_item_call> + <menu_item_call + label="Open voice conversation" + layout="topleft" + name="open_voice_conversation"> + <on_click function="Avatar.DoToSelected" parameter="open_voice_conversation"/> + </menu_item_call> + <menu_item_call + label="Disconnect from voice" + layout="topleft" + name="disconnect_from_voice"> + <on_click function="Avatar.DoToSelected" parameter="disconnect_from_voice"/> + </menu_item_call> + <menu_item_separator layout="topleft" name="separator_disconnect_from_voice"/> + <menu_item_call + label="View Profile" + layout="topleft" + name="view_profile"> + <on_click function="Avatar.DoToSelected" parameter="view_profile"/> + </menu_item_call> + <menu_item_call + label="IM" + layout="topleft" + name="im"> + <on_click function="Avatar.DoToSelected" parameter="im"/> + </menu_item_call> + <menu_item_call + label="Offer teleport" + layout="topleft" + name="offer_teleport"> + <on_click function="Avatar.DoToSelected" parameter="offer_teleport"/> + <on_enable function="Avatar.EnableItem" parameter="can_offer_teleport"/> + </menu_item_call> + <menu_item_call + label="Voice call" + layout="topleft" + name="voice_call"> + <on_click function="Avatar.DoToSelected" parameter="voice_call"/> + <on_enable function="Avatar.EnableItem" parameter="can_call" /> + </menu_item_call> + <menu_item_call + label="Chat history..." + layout="topleft" + name="chat_history"> + <on_click function="Avatar.DoToSelected" parameter="chat_history"/> + </menu_item_call> + <menu_item_separator layout="topleft" name="separator_chat_history"/> + <menu_item_call + label="Add friend" + layout="topleft" + name="add_friend"> + <on_click function="Avatar.DoToSelected" parameter="add_friend"/> + <on_enable function="Avatar.EnableItem" parameter="can_add" /> + </menu_item_call> + <menu_item_call + label="Remove friend" + layout="topleft" + name="remove_friend"> + <on_click function="Avatar.DoToSelected" parameter="remove_friend" /> + <on_enable function="Avatar.EnableItem" parameter="can_delete" /> + </menu_item_call> + <menu_item_call + label="Invite to group..." + layout="topleft" + name="invite_to_group"> + <on_click function="Avatar.DoToSelected" parameter="invite_to_group" /> + </menu_item_call> + <menu_item_separator layout="topleft" name="separator_invite_to_group"/> + <menu_item_call + label="Map" + layout="topleft" + name="map"> + <on_click function="Avatar.DoToSelected" parameter="map" /> + <on_enable function="Avatar.EnableItem" parameter="can_show_on_map" /> + </menu_item_call> + <menu_item_call + label="Share" + layout="topleft" + name="share"> + <on_click function="Avatar.DoToSelected" parameter="share" /> + </menu_item_call> + <menu_item_call + label="Pay" + layout="topleft" + name="pay"> + <on_click function="Avatar.DoToSelected" parameter="pay" /> + </menu_item_call> + <menu_item_check + label="Block / unblock" + layout="topleft" + name="block_unblock"> + <on_click function="Avatar.DoToSelected" parameter="block_unblock" /> + <on_check function="Avatar.CheckItem" parameter="is_blocked" /> + <on_enable function="Avatar.EnableItem" parameter="can_block" /> + </menu_item_check> + <menu_item_call + label="Group Profile" + layout="topleft" + name="group_profile"> + <on_click function="Group.DoToSelected" parameter="group_profile"/> + </menu_item_call> + <menu_item_call + label="Activate Group" + layout="topleft" + name="activate_group"> + <on_click function="Group.DoToSelected" parameter="activate_group"/> + </menu_item_call> + <menu_item_call + label="Leave Group" + layout="topleft" + name="leave_group"> + <on_click function="Group.DoToSelected" parameter="leave_group"/> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_friends_view.xml b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml index eab7b8c085..dde9432867 100644 --- a/indra/newview/skins/default/xui/en/menu_people_friends_view.xml +++ b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml @@ -40,4 +40,12 @@ function="CheckControl" parameter="FriendsListShowPermissions" /> </menu_item_check> + <menu_item_check name="view_conversation" label="View Conversation Log..."> + <menu_item_check.on_check + function="Floater.Visible" + parameter="conversation" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="conversation" /> + </menu_item_check> </toggleable_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 index b7c9ab1fe3..8014e81469 100644 --- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml +++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml @@ -37,6 +37,14 @@ </menu_item_call> <menu_item_separator /> <menu_item_call + label="View chat history..." + layout="topleft" + name="Chat history"> + <menu_item_call.on_click + function="Avatar.Calllog" /> + </menu_item_call> + <menu_item_separator /> + <menu_item_call label="Add Friend" layout="topleft" name="Add Friend"> @@ -101,5 +109,5 @@ function="Avatar.EnableItem" parameter="can_block" /> </menu_item_check> - + <menu_item_separator /> </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index aa131035ed..88b30c8272 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -244,16 +244,6 @@ parameter="speak" /> </menu_item_check> <menu_item_check - label="Voice settings..." - name="Nearby Voice"> - <menu_item_check.on_check - function="Floater.Visible" - parameter="voice_controls" /> - <menu_item_check.on_click - function="Floater.Toggle" - parameter="voice_controls" /> - </menu_item_check> - <menu_item_check label="Voice morphing..." name="ShowVoice" visibility_control="VoiceMorphingEnabled"> @@ -304,7 +294,8 @@ label="Block List" name="Block List"> <menu_item_call.on_click - function="Communicate.BlockList" /> + function="SideTray.PanelPeopleTab" + parameter="blocked_panel" /> </menu_item_call> </menu> <menu diff --git a/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml b/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml index 24f7d44cce..53d0252215 100644 --- a/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml +++ b/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml @@ -43,6 +43,7 @@ menu_filename="menu_people_blocked_gear.xml" menu_position="bottomleft" name="blocked_gear_btn" + tool_tip="Actions on selected person or object" top="3" width="31" /> <menu_button @@ -57,6 +58,7 @@ menu_filename="menu_people_blocked_view.xml" menu_position="bottomleft" name="view_btn" + tool_tip="Sort options" top_delta="0" width="31" /> <menu_button @@ -71,6 +73,7 @@ menu_filename="menu_people_blocked_plus.xml" menu_position="bottomleft" name="plus_btn" + tool_tip="Pick a Resident or an object to block" top_delta="0" width="31"/> <button @@ -83,6 +86,7 @@ left_pad="2" layout="topleft" name="unblock_btn" + tool_tip="Remove Resident or object from blocked list" top_delta="0" width="31"/> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml index 375ea79ebe..56056ed560 100644 --- a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml @@ -13,13 +13,33 @@ layout="topleft" left="5" top="2" + visible="false" width="20" /> + <group_icon + follows="top|left" + height="20" + default_icon_name="Generic_Group" + layout="topleft" + left="5" + top="2" + visible="false" + width="20" /> + <icon + follows="top|left" + height="20" + image_name="Nearby_chat_icon" + layout="topleft" + left="5" + name="nearby_chat_icon" + top="2" + visible="false" + width="20"/> <layout_stack animate="false" follows="all" height="24" layout="topleft" - left_pad="5" + left="30" mouse_opaque="false" name="conversation_item_stack" orientation="horizontal" @@ -36,7 +56,7 @@ <icon height="20" follows="top|right|left" - image_name="Conv_toolbar_hang_up" + image_name="Conv_toolbar_open_call" layout="topleft" left="0" name="selected_icon" @@ -70,7 +90,7 @@ layout="topleft" left_pad="5" mouse_opaque="true" - name="speaking_indicator" + name="speaking_indicatorn" visible="false" width="20" /> </layout_panel> diff --git a/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml index 8a58eb1ca6..78d4c174d2 100644 --- a/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml @@ -35,6 +35,7 @@ image_name="Conv_toolbar_open_call" mouse_opaque="true" name="voice_session_icon" + tool_tip="Included a voice conversation" top="2" visible="false" width="20" /> @@ -46,6 +47,7 @@ image_name="Conv_log_inbox" mouse_opaque="false" name="unread_ims_icon" + tool_tip="Messages arrived while you were logged out" top="2" visible="false" width="20" /> @@ -92,6 +94,7 @@ width="110"/> <button name="delete_btn" + tool_tip="Remove this entry" layout="topleft" follows="top|right" image_unselected="Conv_toolbar_close" diff --git a/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml b/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml index 203febbf18..c80e5b168a 100644 --- a/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml @@ -20,6 +20,13 @@ folder_indentation="8" item_height="20" item_top_pad="4" - selection_image="Rounded_Square"/> + selection_image="Rounded_Square" + left_pad="5" + icon_pad="2" + icon_width="16" + text_pad="1" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2"/> <item allow_open="false"/> </inventory_panel> diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 09156b41b5..7433ad828d 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -116,6 +116,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M layout="topleft" left_pad="7" name="gear_btn" + tool_tip="Actions on selected person" top="3" width="31" /> <menu_button @@ -130,6 +131,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M menu_filename="menu_people_nearby_view.xml" menu_position="bottomleft" name="nearby_view_btn" + tool_tip="View/sort options" top_delta="0" width="31" /> <button @@ -142,6 +144,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M layout="topleft" left_pad="2" name="add_friend_btn" + tool_tip="Offer friendship to a resident" top_delta="0" width="31"> <commit_callback @@ -158,6 +161,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left_pad="2" layout="topleft" name="nearby_del_btn" + tool_tip="Remove selected person as a friend" top_delta="0" width="31"> <commit_callback @@ -264,6 +268,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M layout="topleft" left_pad="8" name="gear_btn" + tool_tip="Actions on selected person" top="3" width="31" /> <menu_button @@ -278,6 +283,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M menu_filename="menu_people_friends_view.xml" menu_position="bottomleft" name="friends_view_btn" + tool_tip="View/sort options" top_delta="0" width="31" /> <button @@ -290,6 +296,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M layout="topleft" left_pad="2" name="friends_add_btn" + tool_tip="Offer friendship to a resident" top_delta="0" width="31"> <commit_callback @@ -305,6 +312,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left_pad="2" layout="topleft" name="friends_del_btn" + tool_tip="Remove selected person as a friend" top_delta="0" width="31"> <commit_callback @@ -419,6 +427,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M layout="topleft" left_pad="8" name="groups_gear_btn" + tool_tip="Actions on selected group" top="3" width="31" /> <menu_button @@ -433,6 +442,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M menu_filename="menu_people_groups_view.xml" menu_position="bottomleft" name="groups_view_btn" + tool_tip="View/sort options" top_delta="0" width="31" /> <menu_button @@ -447,6 +457,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M menu_filename="menu_group_plus.xml" menu_position="bottomleft" name="plus_btn" + tool_tip="Join group/Create new group" top_delta="0" width="31"> <validate_callback @@ -462,6 +473,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left_pad="2" layout="topleft" name="minus_btn" + tool_tip="Leave selected group" top_delta="0" width="31"> <commit_callback @@ -527,6 +539,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M layout="topleft" left_pad="8" name="gear_btn" + tool_tip="Actions on selected person" top="3" width="31" /> <menu_button @@ -541,6 +554,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M menu_filename="menu_people_recent_view.xml" menu_position="bottomleft" name="recent_view_btn" + tool_tip="View/sort options" top_delta="0" width="31" /> <button @@ -553,6 +567,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M layout="topleft" left_pad="2" name="add_friend_btn" + tool_tip="Offer friendship to a resident" top_delta="0" width="31"> <commit_callback @@ -569,6 +584,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left_pad="2" layout="topleft" name="recent_del_btn" + tool_tip="Remove selected person as a friend" top_delta="0" width="31"> <commit_callback diff --git a/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml index 7ddcfe3b03..0024decd4c 100755 --- a/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml +++ b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml @@ -1,18 +1,27 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <conversation_view_participant - folder_arrow_image="ForSale_Badge" + folder_arrow_image="Folder_Arrow" folder_indentation="0" item_height="24" - item_top_pad="4" + item_top_pad="0" selection_image="Rounded_Square" mouse_opaque="true" follows="left|top|right" + left_pad="0" + icon_pad="10" + icon_width="20" + text_pad="7" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2" > <avatar_icon follows="left" height="20" default_icon_name="Generic_Person" + layout="topleft" left="50" + top="2" width="20" /> <info_button follows="right" diff --git a/indra/newview/skins/default/xui/en/widgets/conversation_view_session.xml b/indra/newview/skins/default/xui/en/widgets/conversation_view_session.xml index f44731ea3d..b8c39eec1d 100644 --- a/indra/newview/skins/default/xui/en/widgets/conversation_view_session.xml +++ b/indra/newview/skins/default/xui/en/widgets/conversation_view_session.xml @@ -6,4 +6,11 @@ item_top_pad="4" selection_image="Rounded_Square" mouse_opaque="true" - follows="left|top|right"/> + follows="left|top|right" + left_pad="5" + icon_pad="2" + icon_width="16" + text_pad="1" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2"/> diff --git a/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml b/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml index 6fa74f403d..bbd53ccb12 100644 --- a/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml +++ b/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml @@ -7,4 +7,10 @@ selection_image="Rounded_Square" mouse_opaque="true" follows="left|top|right" - /> + left_pad="5" + icon_pad="2" + icon_width="16" + text_pad="1" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2"/> diff --git a/indra/newview/skins/default/xui/en/widgets/inbox_folder_view_folder.xml b/indra/newview/skins/default/xui/en/widgets/inbox_folder_view_folder.xml index 77d8024cb2..590a4730a9 100644 --- a/indra/newview/skins/default/xui/en/widgets/inbox_folder_view_folder.xml +++ b/indra/newview/skins/default/xui/en/widgets/inbox_folder_view_folder.xml @@ -5,7 +5,13 @@ item_height="20" item_top_pad="4" selection_image="Rounded_Square" - > + left_pad="5" + icon_pad="2" + icon_width="16" + text_pad="1" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2"> <new_badge label="New" label_offset_horiz="-1" |