diff options
Diffstat (limited to 'indra')
108 files changed, 5821 insertions, 1859 deletions
diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 26802bbd1c..457c074ef1 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -552,6 +552,23 @@ void LLPluginClassMedia::loadURI(const std::string &uri) sendMessage(message); } +const char* LLPluginClassMedia::priorityToString(EPriority priority) +{ + const char* result = "UNKNOWN"; + switch(priority) + { + case PRIORITY_UNLOADED: result = "unloaded"; break; + case PRIORITY_STOPPED: result = "stopped"; break; + case PRIORITY_HIDDEN: result = "hidden"; break; + case PRIORITY_SLIDESHOW: result = "slideshow"; break; + case PRIORITY_LOW: result = "low"; break; + case PRIORITY_NORMAL: result = "normal"; break; + case PRIORITY_HIGH: result = "high"; break; + } + + return result; +} + void LLPluginClassMedia::setPriority(EPriority priority) { if(mPriority != priority) @@ -560,35 +577,28 @@ void LLPluginClassMedia::setPriority(EPriority priority) LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "set_priority"); - std::string priority_string; + std::string priority_string = priorityToString(priority); switch(priority) { case PRIORITY_UNLOADED: - priority_string = "unloaded"; mSleepTime = 1.0f; break; case PRIORITY_STOPPED: - priority_string = "stopped"; mSleepTime = 1.0f; break; case PRIORITY_HIDDEN: - priority_string = "hidden"; mSleepTime = 1.0f; break; case PRIORITY_SLIDESHOW: - priority_string = "slideshow"; mSleepTime = 1.0f; break; case PRIORITY_LOW: - priority_string = "low"; mSleepTime = 1.0f / 50.0f; break; case PRIORITY_NORMAL: - priority_string = "normal"; mSleepTime = 1.0f / 100.0f; break; case PRIORITY_HIGH: - priority_string = "high"; mSleepTime = 1.0f / 100.0f; break; } @@ -794,6 +804,10 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) { mStatus = LLPluginClassMediaOwner::MEDIA_PAUSED; } + else if(status == "done") + { + mStatus = LLPluginClassMediaOwner::MEDIA_DONE; + } else { // empty string or any unknown string diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 4f9763474e..90ecd1e073 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -150,6 +150,7 @@ public: PRIORITY_HIGH // media has user focus and/or is taking up most of the screen }EPriority; + static const char* priorityToString(EPriority priority); void setPriority(EPriority priority); void setLowPrioritySizeLimit(int size); diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h index 4690f09172..c798af29ca 100644 --- a/indra/llplugin/llpluginclassmediaowner.h +++ b/indra/llplugin/llpluginclassmediaowner.h @@ -70,7 +70,8 @@ public: MEDIA_ERROR, // navigation/preroll failed MEDIA_PLAYING, // playing (only for time-based media) MEDIA_PAUSED, // paused (only for time-based media) - + MEDIA_DONE // finished playing (only for time-based media) + } EMediaStatus; virtual ~LLPluginClassMediaOwner() {}; diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index fd369730d6..a7946cacf5 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -486,6 +486,11 @@ void LLButton::onMouseLeave(S32 x, S32 y, MASK mask) mNeedsHighlight = FALSE; } +void LLButton::setHighlight(bool b) +{ + mNeedsHighlight = b; +} + BOOL LLButton::handleHover(S32 x, S32 y, MASK mask) { if (!childrenHandleHover(x, y, mask)) diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 7fc4997133..839b196466 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -176,6 +176,7 @@ public: BOOL getToggleState() const; void setToggleState(BOOL b); + void setHighlight(bool b); void setFlashing( BOOL b ); BOOL getFlashing() const { return mFlashing; } diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index e053477d58..75905d0927 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1544,18 +1544,24 @@ void LLLineEditor::drawBackground() image = mBgImage; } + F32 alpha = getDrawContext().mAlpha; // optionally draw programmatic border if (has_focus) { + LLColor4 tmp_color = gFocusMgr.getFocusColor(); + tmp_color.setAlpha(alpha); image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(), - gFocusMgr.getFocusColor(), + tmp_color, gFocusMgr.getFocusFlashWidth()); } - image->draw(getLocalRect()); + LLColor4 tmp_color = UI_VERTEX_COLOR; + tmp_color.setAlpha(alpha); + image->draw(getLocalRect(), tmp_color); } void LLLineEditor::draw() { + F32 alpha = getDrawContext().mAlpha; S32 text_len = mText.length(); static LLUICachedControl<S32> lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0); static LLUICachedControl<S32> lineeditor_v_pad ("UILineEditorVPad", 0); @@ -1608,8 +1614,10 @@ void LLLineEditor::draw() { text_color = mReadOnlyFgColor.get(); } + text_color.setAlpha(alpha); LLColor4 label_color = mTentativeFgColor.get(); - + label_color.setAlpha(alpha); + if (hasPreeditString()) { // Draw preedit markers. This needs to be before drawing letters. @@ -1632,7 +1640,7 @@ void LLLineEditor::draw() preedit_pixels_right - preedit_standout_gap - 1, background.mBottom + preedit_standout_position - preedit_standout_thickness, (text_color * preedit_standout_brightness - + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(1.0f)); + + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(alpha/*1.0f*/)); } else { @@ -1641,7 +1649,7 @@ void LLLineEditor::draw() preedit_pixels_right - preedit_marker_gap - 1, background.mBottom + preedit_marker_position - preedit_marker_thickness, (text_color * preedit_marker_brightness - + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(1.0f)); + + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(alpha/*1.0f*/)); } } } @@ -1684,15 +1692,17 @@ void LLLineEditor::draw() if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) ) { LLColor4 color = mHighlightColor; + color.setAlpha(alpha); // selected middle S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text); width = llmin(width, mMaxHPixels - llround(rendered_pixels_right)); gl_rect_2d(llround(rendered_pixels_right), cursor_top, llround(rendered_pixels_right)+width, cursor_bottom, color); + LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); rendered_text += mGLFont->render( mText, mScrollHPos + rendered_text, rendered_pixels_right, text_bottom, - LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ), + tmp_color, LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, @@ -1758,8 +1768,9 @@ void LLLineEditor::draw() cursor_right, cursor_bottom, text_color); if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) { + LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); mGLFont->render(mText, getCursor(), (F32)(cursor_left + lineeditor_cursor_thickness / 2), text_bottom, - LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ), + tmp_color, LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 732c01614b..cde4c75518 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -44,6 +44,7 @@ #include "lluictrlfactory.h" #include "llrender.h" #include "llfloater.h" +#include "lltrans.h" //---------------------------------------------------------------------------- @@ -153,6 +154,8 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p) mRightTabBtnOffset(p.tab_padding_right), mTotalTabWidth(0), mTabPosition(p.tab_position), + mFontHalign(p.font_halign), + mFont(p.font.isProvided() ? p.font() : (mIsVertical ? LLFontGL::getFontSansSerif() : LLFontGL::getFontSansSerifSmall())), mFirstTabParams(p.first_tab), mMiddleTabParams(p.middle_tab), mLastTabParams(p.last_tab) @@ -401,12 +404,6 @@ void LLTabContainer::draw() } } } - LLUI::pushMatrix(); - { - LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f); - tuple->mButton->draw(); - } - LLUI::popMatrix(); idx++; } @@ -641,12 +638,6 @@ BOOL LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask) } } } - - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( FALSE ); - } } return handled; } @@ -836,8 +827,6 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) // already a child of mine return; } - const LLFontGL* font = - (mIsVertical ? LLFontGL::getFontSansSerif() : LLFontGL::getFontSansSerifSmall()); // Store the original label for possible xml export. child->setLabel(label); @@ -847,7 +836,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) S32 button_width = mMinTabWidth; if (!mIsVertical) { - button_width = llclamp(font->getWidth(trimmed_label) + tab_padding, mMinTabWidth, mMaxTabWidth); + button_width = llclamp(mFont->getWidth(trimmed_label) + tab_padding, mMinTabWidth, mMaxTabWidth); } // Tab panel @@ -934,7 +923,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) params.name(trimmed_label); params.rect(btn_rect); params.initial_value(trimmed_label); - params.font(font); + params.font(mFont); textbox = LLUICtrlFactory::create<LLTextBox> (params); LLButton::Params p; @@ -950,12 +939,12 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) p.rect(btn_rect); p.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT); p.click_callback.function(boost::bind(&LLTabContainer::onTabBtn, this, _2, child)); - p.font(font); + p.font(mFont); p.label(trimmed_label); p.image_unselected(mMiddleTabParams.tab_left_image_unselected); p.image_selected(mMiddleTabParams.tab_left_image_selected); p.scale_image(true); - p.font_halign = LLFontGL::LEFT; + p.font_halign = mFontHalign; p.tab_stop(false); if (indent) { @@ -965,18 +954,13 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) } else { - std::string tooltip = trimmed_label; - tooltip += "\nAlt-Left arrow for previous tab"; - tooltip += "\nAlt-Right arrow for next tab"; - LLButton::Params p; p.name(std::string(child->getName()) + " tab"); p.rect(btn_rect); p.click_callback.function(boost::bind(&LLTabContainer::onTabBtn, this, _2, child)); - p.font(font); + p.font(mFont); p.label(trimmed_label); p.visible(false); - p.tool_tip(tooltip); p.scale_image(true); p.image_unselected(tab_img); p.image_selected(tab_selected_img); @@ -984,7 +968,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) // Try to squeeze in a bit more text p.pad_left(4); p.pad_right(2); - p.font_halign = LLFontGL::LEFT; + p.font_halign = mFontHalign; p.follows.flags = FOLLOWS_LEFT; p.follows.flags = FOLLOWS_LEFT; @@ -1505,7 +1489,6 @@ void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const L if (!mIsVertical) { - const LLFontGL* fontp = LLFontGL::getFontSansSerifSmall(); // remove current width from total tab strip width mTotalTabWidth -= tuple->mButton->getRect().getWidth(); @@ -1516,7 +1499,7 @@ void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const L tuple->mPadding = image_overlay_width; tuple->mButton->setRightHPad(6); - tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), + tuple->mButton->reshape(llclamp(mFont->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tuple->mButton->getRect().getHeight()); // add back in button width to total tab strip width mTotalTabWidth += tuple->mButton->getRect().getWidth(); diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index a81974cd42..be9c6c7d06 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -262,6 +262,9 @@ private: S32 mTabHeight; LLFrameTimer mDragAndDropDelayTimer; + + LLFontGL::HAlign mFontHalign; + const LLFontGL* mFont; TabParams mFirstTabParams; TabParams mMiddleTabParams; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 0add3fb500..2b1d677ffb 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -419,9 +419,6 @@ void LLTextBase::drawCursor() return; } - if (!mTextRect.contains(cursor_rect)) - return; - // Draw the cursor // (Flash the cursor every half second starting a fixed time after the last keystroke) F32 elapsed = mCursorBlinkTimer.getElapsedTimeF32(); @@ -973,7 +970,7 @@ void LLTextBase::draw() : hasFocus() ? mFocusBgColor.get() : mWriteableBgColor.get(); - gl_rect_2d(mDocumentView->getRect(), bg_color, TRUE); + gl_rect_2d(mTextRect, bg_color, TRUE); } // draw document view @@ -1034,13 +1031,13 @@ S32 LLTextBase::getLeftOffset(S32 width) switch (mHAlign) { case LLFontGL::LEFT: - return 0; + return mHPad; case LLFontGL::HCENTER: - return (mTextRect.getWidth() - width) / 2; + return mHPad + (mTextRect.getWidth() - width - mHPad) / 2; case LLFontGL::RIGHT: return mTextRect.getWidth() - width; default: - return 0; + return mHPad; } } @@ -1048,8 +1045,6 @@ S32 LLTextBase::getLeftOffset(S32 width) static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow"); void LLTextBase::reflow(S32 start_index) { - if (!mReflowNeeded) return; - LLFastTimer ft(FTM_TEXT_REFLOW); updateSegments(); @@ -1078,7 +1073,7 @@ void LLTextBase::reflow(S32 start_index) segment_set_t::iterator seg_iter = mSegments.begin(); S32 seg_offset = 0; S32 line_start_index = 0; - const S32 text_width = mTextRect.getWidth(); // optionally reserve room for margin + const S32 text_width = mTextRect.getWidth() - mHPad; // reserve room for margin S32 remaining_pixels = text_width; LLWString text(getWText()); S32 line_count = 0; @@ -2037,7 +2032,6 @@ void LLTextBase::updateRects() } else { - mContentsRect = mLineInfoList.begin()->mRect; for (line_list_t::const_iterator line_iter = ++mLineInfoList.begin(); line_iter != mLineInfoList.end(); @@ -2046,13 +2040,28 @@ void LLTextBase::updateRects() mContentsRect.unionWith(line_iter->mRect); } - mContentsRect.mRight += mHPad; + mContentsRect.mLeft = 0; mContentsRect.mTop += mVPad; - // get around rounding errors when clipping text against rectangle - mContentsRect.stretch(1); + + S32 delta_pos = -mContentsRect.mBottom; + // move line segments to fit new document rect + for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) + { + it->mRect.translate(0, delta_pos); + } + mContentsRect.translate(0, delta_pos); } + // update document container dimensions according to text contents + LLRect doc_rect = mContentsRect; + // use old mTextRect constraint document to width of viewable region + doc_rect.mRight = doc_rect.mLeft + mTextRect.getWidth(); + + mDocumentView->setShape(doc_rect); + //update mTextRect *after* mDocumentView has been resized + // so that scrollbars are added if document needs to scroll + // since mTextRect does not include scrollbars LLRect old_text_rect = mTextRect; mTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); //FIXME: replace border with image? @@ -2060,43 +2069,14 @@ void LLTextBase::updateRects() { mTextRect.stretch(-1); } - mTextRect.mLeft += mHPad; - mTextRect.mTop -= mVPad; if (mTextRect != old_text_rect) { needsReflow(); } - // change document rect size too - LLRect document_rect; - if (mScroller) - { - // document is size of scroller or size of text contents, whichever is larger - document_rect.setOriginAndSize(0, 0, - mScroller->getContentWindowRect().getWidth(), - llmax(mScroller->getContentWindowRect().getHeight(), mContentsRect.getHeight())); - } - else - { - // document size is just extents of reflowed text, reset to origin 0,0 - document_rect.set(0, - getLocalRect().getHeight(), - getLocalRect().getWidth(), - llmin(0, getLocalRect().getHeight() - mContentsRect.getHeight())); - } - mDocumentView->setShape(document_rect); - - // after making document big enough to hold all the text, move the text to fit in the document - if (!mLineInfoList.empty()) - { - S32 delta_pos = mDocumentView->getRect().getHeight() - mLineInfoList.begin()->mRect.mTop - mVPad; - // move line segments to fit new document rect - for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) - { - it->mRect.translate(0, delta_pos); - } - mContentsRect.translate(0, delta_pos); - } + // update document container again, using new mTextRect + doc_rect.mRight = doc_rect.mLeft + mTextRect.getWidth(); + mDocumentView->setShape(doc_rect); } @@ -2398,7 +2378,6 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin { if (text[last_char] == '\n') { - last_char++; break; } } @@ -2418,9 +2397,14 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin // If at the beginning of a line, and a single character won't fit, draw it anyway num_chars = 1; } - if (mStart + segment_offset + num_chars == mEditor.getLength()) + + // include *either* the EOF or newline character in this run of text + // but not both + S32 last_char_in_run = mStart + segment_offset + num_chars; + // check length first to avoid indexing off end of string + if (last_char_in_run >= mEditor.getLength() + || text[last_char_in_run] == '\n') { - // include terminating NULL num_chars++; } return num_chars; @@ -2442,12 +2426,14 @@ void LLNormalTextSegment::dump() const // LLInlineViewSegment // -LLInlineViewSegment::LLInlineViewSegment(LLView* view, S32 start, S32 end, bool force_new_line, S32 hpad, S32 vpad) +LLInlineViewSegment::LLInlineViewSegment(const Params& p, S32 start, S32 end) : LLTextSegment(start, end), - mView(view), - mForceNewLine(force_new_line), - mHPad(hpad), // one sided padding (applied to left and right) - mVPad(vpad) + mView(p.view), + mForceNewLine(p.force_newline), + mLeftPad(p.left_pad), + mRightPad(p.right_pad), + mTopPad(p.top_pad), + mBottomPad(p.bottom_pad) { } @@ -2467,8 +2453,8 @@ void LLInlineViewSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt } else { - width = mHPad * 2 + mView->getRect().getWidth(); - height = mVPad * 2 + mView->getRect().getHeight(); + width = mLeftPad + mRightPad + mView->getRect().getWidth(); + height = mBottomPad + mTopPad + mView->getRect().getHeight(); } } @@ -2491,14 +2477,14 @@ S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin void LLInlineViewSegment::updateLayout(const LLTextBase& editor) { LLRect start_rect = editor.getDocRectFromDocIndex(mStart); - mView->setOrigin(start_rect.mLeft + mHPad, start_rect.mBottom + mVPad); + mView->setOrigin(start_rect.mLeft + mLeftPad, start_rect.mBottom + mBottomPad); } F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) { // return padded width of widget // widget is actually drawn during mDocumentView's draw() - return (F32)(draw_rect.mLeft + mView->getRect().getWidth() + mHPad * 2); + return (F32)(draw_rect.mLeft + mView->getRect().getWidth() + mLeftPad + mRightPad); } void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor) diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index d0787f001e..14fd786127 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -459,7 +459,17 @@ public: class LLInlineViewSegment : public LLTextSegment { public: - LLInlineViewSegment(LLView* widget, S32 start, S32 end, bool force_new_line, S32 hpad = 0, S32 vpad = 0); + struct Params : public LLInitParam::Block<Params> + { + Mandatory<LLView*> view; + Optional<bool> force_newline; + Optional<S32> left_pad, + right_pad, + bottom_pad, + top_pad; + }; + + LLInlineViewSegment(const Params& p, S32 start, S32 end); ~LLInlineViewSegment(); /*virtual*/ void getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; @@ -470,8 +480,10 @@ public: /*virtual*/ void linkToDocument(class LLTextBase* editor); private: - S32 mHPad; - S32 mVPad; + S32 mLeftPad; + S32 mRightPad; + S32 mTopPad; + S32 mBottomPad; LLView* mView; bool mForceNewLine; }; diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 570ca4b998..f0238dba49 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -2307,7 +2307,7 @@ void LLTextEditor::insertText(const std::string &new_text) setEnabled( enabled ); } -void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool force_new_line, S32 hpad, S32 vpad) +void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo) { // Save old state S32 selection_start = mSelectionStart; @@ -2321,12 +2321,9 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, setCursorPos(old_length); - LLWString widget_wide_text; + LLWString widget_wide_text = utf8str_to_wstring(text); - // Add carriage return if not first line - widget_wide_text = utf8str_to_wstring(widget_text); - - LLTextSegmentPtr segment = new LLInlineViewSegment(widget, old_length, old_length + widget_text.size(), force_new_line, hpad, vpad); + LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size()); insert(getLength(), widget_wide_text, FALSE, segment); needsReflow(); @@ -2349,7 +2346,7 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, setCursorPos(cursor_pos); } - if( !allow_undo ) + if (!allow_undo) { blockUndo(); } diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 4847f4d117..10fc94dedc 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -166,7 +166,7 @@ public: // inserts text at cursor void insertText(const std::string &text); - void appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool force_newline, S32 hpad, S32 vpad); + void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo); // Non-undoable void setText(const LLStringExplicit &utf8str); diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index c8094f9c7c..2a30eb4b5b 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -38,10 +38,11 @@ // Library includes #include "lltextbox.h" #include "lliconctrl.h" +#include "llbutton.h" #include "llmenugl.h" // hideMenus() #include "llui.h" // positionViewNearMouse() #include "llwindow.h" - +#include "lltrans.h" // // Constants // @@ -155,7 +156,9 @@ LLToolTip::Params::Params() visible_time_near("visible_time_near", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeNear" )), visible_time_far("visible_time_far", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeFar" )), sticky_rect("sticky_rect"), - image("image") + image("image"), + time_based_media("time_based_media", false), + media_playing("media_playing", false) { name = "tooltip"; font = LLFontGL::getFontSansSerif(); @@ -167,7 +170,10 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p) : LLPanel(p), mMaxWidth(p.max_width), mHasClickCallback(p.click_callback.isProvided()), - mPadding(p.padding) + mPadding(p.padding), + mTextBox(NULL), + mInfoButton(NULL), + mPlayMediaButton(NULL) { LLTextBox::Params params; params.initial_value = "tip_text"; @@ -186,25 +192,60 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p) params.allow_html = false; // disallow hyperlinks in tooltips, as they want to spawn their own explanatory tooltips mTextBox = LLUICtrlFactory::create<LLTextBox> (params); addChild(mTextBox); - + + S32 TOOLTIP_ICON_SIZE = 0; + S32 TOOLTIP_PLAYBUTTON_SIZE = 0; if (p.image.isProvided()) { - LLIconCtrl::Params icon_params; - icon_params.name = "tooltip_icon"; + LLButton::Params icon_params; + icon_params.name = "tooltip_info"; LLRect icon_rect; LLUIImage* imagep = p.image; - const S32 TOOLTIP_ICON_SIZE = (imagep ? imagep->getWidth() : 16); + TOOLTIP_ICON_SIZE = (imagep ? imagep->getWidth() : 16); icon_rect.setOriginAndSize(mPadding, mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); icon_params.rect = icon_rect; - icon_params.follows.flags = FOLLOWS_LEFT | FOLLOWS_BOTTOM; - icon_params.image = p.image; - icon_params.mouse_opaque = false; - addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_params)); - + //icon_params.follows.flags = FOLLOWS_LEFT | FOLLOWS_BOTTOM; + icon_params.image_unselected(imagep); + icon_params.scale_image(true); + icon_params.flash_color(icon_params.highlight_color()); + mInfoButton = LLUICtrlFactory::create<LLButton>(icon_params); + if (p.click_callback.isProvided()) + { + mInfoButton->setCommitCallback(boost::bind(p.click_callback())); + } + addChild(mInfoButton); + // move text over to fit image in mTextBox->translate(TOOLTIP_ICON_SIZE + mPadding, 0); } - + + if (p.time_based_media.isProvided() && p.time_based_media == true) + { + LLButton::Params p_button; + p_button.name(std::string("play_media")); + TOOLTIP_PLAYBUTTON_SIZE = 16; + LLRect button_rect; + button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); + p_button.rect = button_rect; + p_button.image_selected.name("button_anim_pause.tga"); + p_button.image_unselected.name("button_anim_play.tga"); + p_button.scale_image(true); + + mPlayMediaButton = LLUICtrlFactory::create<LLButton>(p_button); + if(p.click_playmedia_callback.isProvided()) + { + mPlayMediaButton->setCommitCallback(boost::bind(p.click_playmedia_callback())); + } + if(p.media_playing.isProvided()) + { + mPlayMediaButton->setToggleState(p.media_playing); + } + addChild(mPlayMediaButton); + + // move text over to fit image in + mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0); + } + if (p.click_callback.isProvided()) { setMouseUpCallback(boost::bind(p.click_callback())); @@ -255,6 +296,9 @@ void LLToolTip::setVisible(BOOL visible) BOOL LLToolTip::handleHover(S32 x, S32 y, MASK mask) { + //mInfoButton->setFlashing(true); + mInfoButton->setHighlight(true); + LLPanel::handleHover(x, y, mask); if (mHasClickCallback) { @@ -263,6 +307,13 @@ BOOL LLToolTip::handleHover(S32 x, S32 y, MASK mask) return TRUE; } +void LLToolTip::onMouseLeave(S32 x, S32 y, MASK mask) +{ + //mInfoButton->setFlashing(true); + mInfoButton->setHighlight(false); + LLUICtrl::onMouseLeave(x, y, mask); +} + void LLToolTip::draw() { F32 alpha = 1.f; diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h index 63e7249a12..a81876eac1 100644 --- a/indra/llui/lltooltip.h +++ b/indra/llui/lltooltip.h @@ -81,6 +81,11 @@ public: Optional<click_callback_t> click_callback; Optional<LLUIImage*> image; + + + Optional<bool> time_based_media; + Optional<bool> media_playing; + Optional<click_callback_t> click_playmedia_callback; Optional<S32> max_width; Optional<S32> padding; Optional<bool> wrap; @@ -89,7 +94,7 @@ public: }; /*virtual*/ void draw(); /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); - + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); /*virtual*/ void setValue(const LLSD& value); /*virtual*/ void setVisible(BOOL visible); @@ -101,6 +106,8 @@ public: private: class LLTextBox* mTextBox; + class LLButton* mInfoButton; + class LLButton* mPlayMediaButton; LLFrameTimer mFadeTimer; LLFrameTimer mVisibleTimer; S32 mMaxWidth; diff --git a/indra/media_plugins/base/media_plugin_base.cpp b/indra/media_plugins/base/media_plugin_base.cpp index 0b7092fad6..6acac07423 100644 --- a/indra/media_plugins/base/media_plugin_base.cpp +++ b/indra/media_plugins/base/media_plugin_base.cpp @@ -64,6 +64,7 @@ std::string MediaPluginBase::statusString() case STATUS_ERROR: result = "error"; break; case STATUS_PLAYING: result = "playing"; break; case STATUS_PAUSED: result = "paused"; break; + case STATUS_DONE: result = "done"; break; default: // keep the empty string break; diff --git a/indra/media_plugins/base/media_plugin_base.h b/indra/media_plugins/base/media_plugin_base.h index 8f600cb8d6..f1e96335f9 100644 --- a/indra/media_plugins/base/media_plugin_base.h +++ b/indra/media_plugins/base/media_plugin_base.h @@ -56,6 +56,7 @@ protected: STATUS_ERROR, STATUS_PLAYING, STATUS_PAUSED, + STATUS_DONE } EStatus; class SharedSegmentInfo diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp index c9ee1c8ac7..fb6d5b2905 100644 --- a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -420,7 +420,7 @@ private: {
if ( mCommand == COMMAND_PLAY )
{
- if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING )
+ if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING || mStatus == STATUS_DONE )
{
long state = GetMovieLoadState( mMovieHandle );
@@ -446,7 +446,7 @@ private: else
if ( mCommand == COMMAND_STOP )
{
- if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED )
+ if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED || mStatus == STATUS_DONE )
{
if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK )
{
@@ -547,12 +547,12 @@ private: // see if title arrived and if so, update member variable with contents
checkTitle();
-
- // special code for looping - need to rewind at the end of the movie
- if ( mIsLooping )
+
+ // QT call to see if we are at the end - can't do with controller
+ if ( IsMovieDone( mMovieHandle ) )
{
- // QT call to see if we are at the end - can't do with controller
- if ( IsMovieDone( mMovieHandle ) )
+ // special code for looping - need to rewind at the end of the movie
+ if ( mIsLooping )
{
// go back to start
rewind();
@@ -565,8 +565,16 @@ private: // set the volume
MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
};
- };
- };
+ }
+ else
+ {
+ if(mStatus == STATUS_PLAYING)
+ {
+ setStatus(STATUS_DONE);
+ }
+ }
+ }
+
};
int getDataWidth() const
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index dd3937a6ef..a7681e4a1d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -63,13 +63,13 @@ include_directories( ) set(viewer_SOURCE_FILES - llaccordionctrltab.cpp llaccordionctrl.cpp + llaccordionctrltab.cpp llagent.cpp - llagentlistener.cpp llagentaccess.cpp llagentdata.cpp llagentlanguage.cpp + llagentlistener.cpp llagentpicksinfo.cpp llagentpilot.cpp llagentui.cpp @@ -78,8 +78,8 @@ set(viewer_SOURCE_FILES llappearancemgr.cpp llappviewer.cpp llappviewerlistener.cpp - llassetuploadresponders.cpp llassetuploadqueue.cpp + llassetuploadresponders.cpp llaudiosourcevo.cpp llavataractions.cpp llavatariconctrl.cpp @@ -95,8 +95,8 @@ set(viewer_SOURCE_FILES llcaphttpsender.cpp llchannelmanager.cpp llchatbar.cpp - llchatitemscontainerctrl.cpp llchathistory.cpp + llchatitemscontainerctrl.cpp llchatmsgbox.cpp llchiclet.cpp llclassifiedinfo.cpp @@ -116,10 +116,10 @@ set(viewer_SOURCE_FILES lldirpicker.cpp lldndbutton.cpp lldrawable.cpp + lldrawpool.cpp lldrawpoolalpha.cpp lldrawpoolavatar.cpp lldrawpoolbump.cpp - lldrawpool.cpp lldrawpoolground.cpp lldrawpoolsimple.cpp lldrawpoolsky.cpp @@ -151,8 +151,8 @@ set(viewer_SOURCE_FILES llfloaterbuildoptions.cpp llfloaterbulkpermission.cpp llfloaterbump.cpp - llfloaterbuycontents.cpp llfloaterbuy.cpp + llfloaterbuycontents.cpp llfloaterbuycurrency.cpp llfloaterbuyland.cpp llfloatercall.cpp @@ -163,8 +163,8 @@ set(viewer_SOURCE_FILES llfloatercustomize.cpp llfloaterdaycycle.cpp llfloaterenvsettings.cpp - llfloaterfriends.cpp llfloaterfonttest.cpp + llfloaterfriends.cpp llfloatergesture.cpp llfloatergodtools.cpp llfloatergroupinvite.cpp @@ -172,8 +172,6 @@ set(viewer_SOURCE_FILES llfloaterhandler.cpp llfloaterhardwaresettings.cpp llfloaterhelpbrowser.cpp - llfloatermediabrowser.cpp - llfloatermediasettings.cpp llfloaterhud.cpp llfloaterimagepreview.cpp llfloaterinspect.cpp @@ -183,6 +181,8 @@ set(viewer_SOURCE_FILES llfloaterland.cpp llfloaterlandholdings.cpp llfloatermap.cpp + llfloatermediabrowser.cpp + llfloatermediasettings.cpp llfloatermemleak.cpp llfloaternamedesc.cpp llfloaternotificationsconsole.cpp @@ -227,8 +227,8 @@ set(viewer_SOURCE_FILES llgroupmgr.cpp llgroupnotify.cpp llhomelocationresponder.cpp - llhudeffectbeam.cpp llhudeffect.cpp + llhudeffectbeam.cpp llhudeffectlookat.cpp llhudeffectpointat.cpp llhudeffecttrail.cpp @@ -238,11 +238,11 @@ set(viewer_SOURCE_FILES llhudrender.cpp llhudtext.cpp llhudview.cpp + llimcontrolpanel.cpp llimfloater.cpp llimhandler.cpp llimpanel.cpp llimview.cpp - llimcontrolpanel.cpp llinspect.cpp llinspectavatar.cpp llinspectgroup.cpp @@ -260,7 +260,6 @@ set(viewer_SOURCE_FILES lllocaltextureobject.cpp lllocationhistory.cpp lllocationinputctrl.cpp - llurllineeditorctrl.cpp lllogchat.cpp llloginhandler.cpp lllogininstance.cpp @@ -312,8 +311,8 @@ set(viewer_SOURCE_FILES llpanelgrouplandmoney.cpp llpanelgroupnotices.cpp llpanelgrouproles.cpp - llpanelinventory.cpp llpanelimcontrolpanel.cpp + llpanelinventory.cpp llpanelland.cpp llpanellandaudio.cpp llpanellandmarks.cpp @@ -322,11 +321,10 @@ set(viewer_SOURCE_FILES llpanellookinfo.cpp llpanellooks.cpp llpanelmedia.cpp - llpanelmediahud.cpp - llpanelmeprofile.cpp llpanelmediasettingsgeneral.cpp - llpanelmediasettingssecurity.cpp llpanelmediasettingspermissions.cpp + llpanelmediasettingssecurity.cpp + llpanelmeprofile.cpp llpanelobject.cpp llpanelpeople.cpp llpanelpeoplemenus.cpp @@ -335,11 +333,12 @@ set(viewer_SOURCE_FILES llpanelpicks.cpp llpanelplace.cpp llpanelplaceinfo.cpp - llpanelshower.cpp llpanelplaces.cpp llpanelplacestab.cpp + llpanelprimmediacontrols.cpp llpanelprofile.cpp llpanelprofileview.cpp + llpanelshower.cpp llpanelteleporthistory.cpp llpanelvolume.cpp llparcelselection.cpp @@ -348,8 +347,8 @@ set(viewer_SOURCE_FILES llplacesinventorybridge.cpp llpolymesh.cpp llpolymorph.cpp - llpreviewanim.cpp llpreview.cpp + llpreviewanim.cpp llpreviewgesture.cpp llpreviewnotecard.cpp llpreviewscript.cpp @@ -371,6 +370,7 @@ set(viewer_SOURCE_FILES llsky.cpp llslurl.cpp llspatialpartition.cpp + llspeakers.cpp llsplitbutton.cpp llsprite.cpp llstartup.cpp @@ -397,10 +397,10 @@ set(viewer_SOURCE_FILES lltoastimpanel.cpp lltoastnotifypanel.cpp lltoastpanel.cpp + lltool.cpp lltoolbar.cpp lltoolbrush.cpp lltoolcomp.cpp - lltool.cpp lltooldraganddrop.cpp lltoolface.cpp lltoolfocus.cpp @@ -424,6 +424,7 @@ set(viewer_SOURCE_FILES llurl.cpp llurldispatcher.cpp llurlhistory.cpp + llurllineeditorctrl.cpp llurlsimstring.cpp llurlwhitelist.cpp llvectorperfoptions.cpp @@ -440,18 +441,18 @@ set(viewer_SOURCE_FILES llviewerhelp.cpp llviewerhelputil.cpp llviewerinventory.cpp - llviewerjointattachment.cpp llviewerjoint.cpp + llviewerjointattachment.cpp llviewerjointmesh.cpp - llviewerjointmesh_sse2.cpp llviewerjointmesh_sse.cpp + llviewerjointmesh_sse2.cpp llviewerjointmesh_vec.cpp llviewerjoystick.cpp llviewerkeyboard.cpp llviewerlayer.cpp llviewermedia.cpp - llviewermediafocus.cpp llviewermedia_streamingaudio.cpp + llviewermediafocus.cpp llviewermenu.cpp llviewermenufile.cpp llviewermessage.cpp @@ -484,10 +485,11 @@ set(viewer_SOURCE_FILES llvoclouds.cpp llvograss.cpp llvoground.cpp + llvoicechannel.cpp llvoiceclient.cpp + llvoicecontrolpanel.cpp llvoiceremotectrl.cpp llvoicevisualizer.cpp - llvoicecontrolpanel.cpp llvoinventorylistener.cpp llvopartgroup.cpp llvosky.cpp @@ -538,25 +540,25 @@ endif (LINUX) set(viewer_HEADER_FILES CMakeLists.txt ViewerInstall.cmake - llaccordionctrltab.h llaccordionctrl.h + llaccordionctrltab.h llagent.h - llagentlistener.h llagentaccess.h llagentdata.h llagentlanguage.h + llagentlistener.h llagentpicksinfo.h llagentpilot.h llagentui.h llagentwearables.h llanimstatelabels.h llappearance.h + llappearancemgr.h llappviewer.h llappviewerlistener.h - llassetuploadresponders.h llassetuploadqueue.h + llassetuploadresponders.h llaudiosourcevo.h - llappearancemgr.h llavataractions.h llavatariconctrl.h llavatarlist.h @@ -572,8 +574,8 @@ set(viewer_HEADER_FILES llcaphttpsender.h llchannelmanager.h llchatbar.h - llchatitemscontainerctrl.h llchathistory.h + llchatitemscontainerctrl.h llchatmsgbox.h llchiclet.h llclassifiedinfo.h @@ -650,8 +652,6 @@ set(viewer_HEADER_FILES llfloaterhandler.h llfloaterhardwaresettings.h llfloaterhelpbrowser.h - llfloatermediabrowser.h - llfloatermediasettings.h llfloaterhud.h llfloaterimagepreview.h llfloaterinspect.h @@ -661,16 +661,18 @@ set(viewer_HEADER_FILES llfloaterland.h llfloaterlandholdings.h llfloatermap.h + llfloatermediabrowser.h + llfloatermediasettings.h llfloatermemleak.h llfloaternamedesc.h llfloaternotificationsconsole.h llfloateropenobject.h llfloaterparcel.h llfloaterpay.h + llfloaterperms.h llfloaterpostcard.h llfloaterpostprocess.h llfloaterpreference.h - llfloaterperms.h llfloaterproperties.h llfloaterregioninfo.h llfloaterreporter.h @@ -716,12 +718,12 @@ set(viewer_HEADER_FILES llhudrender.h llhudtext.h llhudview.h + llimcontrolpanel.h llimfloater.h llimpanel.h llimview.h - llimcontrolpanel.h - llinspectavatar.h llinspect.h + llinspectavatar.h llinspectgroup.h llinspectobject.h llinventorybridge.h @@ -738,7 +740,6 @@ set(viewer_HEADER_FILES lllocaltextureobject.h lllocationhistory.h lllocationinputctrl.h - llurllineeditorctrl.h lllogchat.h llloginhandler.h lllogininstance.h @@ -747,6 +748,7 @@ set(viewer_HEADER_FILES llmanipscale.h llmaniptranslate.h llmapresponders.h + llmediactrl.h llmediadataclient.h llmediaremotectrl.h llmemoryview.h @@ -786,8 +788,8 @@ set(viewer_HEADER_FILES llpanelgrouplandmoney.h llpanelgroupnotices.h llpanelgrouproles.h - llpanelinventory.h llpanelimcontrolpanel.h + llpanelinventory.h llpanelland.h llpanellandaudio.h llpanellandmarks.h @@ -796,11 +798,10 @@ set(viewer_HEADER_FILES llpanellookinfo.h llpanellooks.h llpanelmedia.h - llpanelmediahud.h - llpanelmeprofile.h llpanelmediasettingsgeneral.h - llpanelmediasettingssecurity.h llpanelmediasettingspermissions.h + llpanelmediasettingssecurity.h + llpanelmeprofile.h llpanelobject.h llpanelpeople.h llpanelpeoplemenus.h @@ -809,11 +810,12 @@ set(viewer_HEADER_FILES llpanelpicks.h llpanelplace.h llpanelplaceinfo.h - llpanelshower.h llpanelplaces.h llpanelplacestab.h + llpanelprimmediacontrols.h llpanelprofile.h llpanelprofileview.h + llpanelshower.h llpanelteleporthistory.h llpanelvolume.h llparcelselection.h @@ -836,9 +838,9 @@ set(viewer_HEADER_FILES llremoteparcelrequest.h llresourcedata.h llrootview.h + llsavedsettingsglue.h llscreenchannel.h llscrollingpanelparam.h - llsavedsettingsglue.h llsearchcombobox.h llsearchhistory.h llselectmgr.h @@ -847,6 +849,7 @@ set(viewer_HEADER_FILES llsky.h llslurl.h llspatialpartition.h + llspeakers.h llsplitbutton.h llsprite.h llstartup.h @@ -902,6 +905,7 @@ set(viewer_HEADER_FILES llurl.h llurldispatcher.h llurlhistory.h + llurllineeditorctrl.h llurlsimstring.h llurlwhitelist.h llvectorperfoptions.h @@ -925,8 +929,8 @@ set(viewer_HEADER_FILES llviewerkeyboard.h llviewerlayer.h llviewermedia.h - llviewermediaobserver.h llviewermediafocus.h + llviewermediaobserver.h llviewermenu.h llviewermenufile.h llviewermessage.h @@ -960,10 +964,11 @@ set(viewer_HEADER_FILES llvoclouds.h llvograss.h llvoground.h + llvoicechannel.h llvoiceclient.h + llvoicecontrolpanel.h llvoiceremotectrl.h llvoicevisualizer.h - llvoicecontrolpanel.h llvoinventorylistener.h llvopartgroup.h llvosky.h @@ -981,7 +986,6 @@ set(viewer_HEADER_FILES llwearabledictionary.h llwearablelist.h llweb.h - llmediactrl.h llwind.h llwindebug.h llwlanimator.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 3682d48577..6bc95b9cdb 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7742,6 +7742,39 @@ <key>Value</key> <integer>1</integer> </map> + <key>FriendsListShowIcons</key> + <map> + <key>Comment</key> + <string>Show/hide online and all friends icons in the friend list</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>NearbyListShowIcons</key> + <map> + <key>Comment</key> + <string>Show/hide people icons in nearby list</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>RecentListShowIcons</key> + <map> + <key>Comment</key> + <string>Show/hide people icons in recent list</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>FriendsSortOrder</key> <map> <key>Comment</key> @@ -10460,6 +10493,17 @@ <key>Value</key> <real>90.0</real> </map> + <key>YouAreHereDistance</key> + <map> + <key>Comment</key> + <string>Radius of distance for banner that indicates if the resident is "on" the Place.(meters from avatar to requested place)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>10.0</real> + </map> <key>YieldTime</key> <map> <key>Comment</key> diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml index f3bfa37cea..c43ba27984 100644 --- a/indra/newview/character/avatar_lad.xml +++ b/indra/newview/character/avatar_lad.xml @@ -5589,6 +5589,13 @@ </layer> <layer + name="hair texture alpha layer" + visibility_mask="TRUE"> + <texture + local_texture="hair_grain" /> + </layer> + + <layer name="hair alpha" visibility_mask="TRUE"> <texture diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index f62606cc50..75a72e5b17 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -42,7 +42,7 @@ #include "lldrawable.h" #include "llfirstuse.h" #include "llfloaterreg.h" -#include "llfloateractivespeakers.h" +#include "llspeakers.h" #include "llfloatercamera.h" #include "llfloatercustomize.h" @@ -5391,12 +5391,6 @@ void update_group_floaters(const LLUUID& group_id) //*TODO Implement group update for Profile View // still actual as of July 31, 2009 (DZ) - if (gIMMgr) - { - // update the talk view - gIMMgr->refresh(); - } - gAgent.fireEvent(new LLOldEvents::LLEvent(&gAgent, "new group"), ""); } diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp index 1a69f1d975..09f7c49f23 100644 --- a/indra/newview/llagentui.cpp +++ b/indra/newview/llagentui.cpp @@ -89,6 +89,11 @@ std::string LLAgentUI::buildSLURL(const bool escaped /*= true*/) return slurl; } +//static +BOOL LLAgentUI::checkAgentDistance(const LLVector3& pole, F32 radius) +{ + return (gAgent.getPositionAgent() - pole).length() < radius; +} BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt,const LLVector3& agent_pos_region) { LLViewerRegion* region = gAgent.getRegion(); diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h index 47ecb04547..c7aafb71e7 100644 --- a/indra/newview/llagentui.h +++ b/indra/newview/llagentui.h @@ -52,6 +52,11 @@ public: static BOOL buildLocationString(std::string& str, ELocationFormat fmt = LOCATION_FORMAT_LANDMARK); //build location string using a region position of the avatar. static BOOL buildLocationString(std::string& str, ELocationFormat fmt,const LLVector3& agent_pos_region); + /** + * @brief Check whether the agent is in neighborhood of the pole Within same region + * @return true if the agent is in neighborhood. + */ + static BOOL checkAgentDistance(const LLVector3& local_pole, F32 radius); }; #endif //LLAGENTUI_H diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e184d99ffc..06c9171d67 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -49,7 +49,6 @@ #include "llviewerstats.h" #include "llmd5.h" #include "llpumpio.h" -#include "llimpanel.h" #include "llmimetypes.h" #include "llslurl.h" #include "llstartup.h" @@ -76,6 +75,7 @@ #include "llteleporthistory.h" #include "lllocationhistory.h" #include "llfasttimerview.h" +#include "llvoicechannel.h" #include "llweb.h" #include "llsecondlifeurls.h" diff --git a/indra/newview/llavatariconctrl.h b/indra/newview/llavatariconctrl.h index 426fcec514..65b5c86ed5 100644 --- a/indra/newview/llavatariconctrl.h +++ b/indra/newview/llavatariconctrl.h @@ -103,6 +103,8 @@ public: const std::string& getFirstName() const { return mFirstName; } const std::string& getLastName() const { return mLastName; } + void setDrawTooltip(bool value) { mDrawTooltip = value;} + protected: LLUUID mAvatarId; std::string mFirstName; diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 3a07c6e5ef..e93d0dfa50 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -38,6 +38,7 @@ #include "llcallingcard.h" // for LLAvatarTracker #include "llcachename.h" #include "llvoiceclient.h" +#include "llviewercontrol.h" // for gSavedSettings static LLDefaultChildRegistry::Register<LLAvatarList> r("avatar_list"); @@ -45,6 +46,21 @@ static LLDefaultChildRegistry::Register<LLAvatarList> r("avatar_list"); // Used to limit time spent for avatar list update per frame. static const unsigned ADD_LIMIT = 50; +void LLAvatarList::toggleIcons() +{ + // Save the new value for new items to use. + mShowIcons = !mShowIcons; + gSavedSettings.setBOOL(mIconParamName, mShowIcons); + + // Show/hide icons for all existing items. + std::vector<LLPanel*> items; + getItems(items); + for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++) + { + static_cast<LLAvatarListItem*>(*it)->setAvatarIconVisible(mShowIcons); + } +} + static bool findInsensitive(std::string haystack, const std::string& needle_upper) { LLStringUtil::toUpper(haystack); @@ -73,13 +89,22 @@ LLAvatarList::LLAvatarList(const Params& p) setComparator(&NAME_COMPARATOR); } +void LLAvatarList::setShowIcons(std::string param_name) +{ + mIconParamName= param_name; + mShowIcons = gSavedSettings.getBOOL(mIconParamName); +} + // virtual void LLAvatarList::draw() { - if (mDirty) - refresh(); + // *NOTE dzaporozhan + // Call refresh() after draw() to avoid flickering of avatar list items. LLFlatListView::draw(); + + if (mDirty) + refresh(); } void LLAvatarList::setNameFilter(const std::string& filter) @@ -202,6 +227,7 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is item->setContextMenu(mContextMenu); item->childSetVisible("info_btn", false); + item->setAvatarIconVisible(mShowIcons); addItem(item, id, pos); } diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index a83a72b26c..f60f1f00f3 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -70,7 +70,11 @@ public: void setContextMenu(LLAvatarListItem::ContextMenu* menu) { mContextMenu = menu; } + void toggleIcons(); void sortByName(); + void setShowIcons(std::string param_name); + bool getIconsVisible() const { return mShowIcons; } + const std::string getIconParamName() const{return mIconParamName;} virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); protected: @@ -86,7 +90,9 @@ private: bool mIgnoreOnlineStatus; bool mDirty; + bool mShowIcons; + std::string mIconParamName; std::string mNameFilter; uuid_vector_t mIDs; diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index ebc79aae48..4ecb9537ba 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -42,6 +42,7 @@ #include "llavatariconctrl.h" #include "llbutton.h" +S32 LLAvatarListItem::sIconWidth = 0; LLAvatarListItem::LLAvatarListItem() : LLPanel(), @@ -55,6 +56,12 @@ LLAvatarListItem::LLAvatarListItem() mOnlineStatus(E_UNKNOWN) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_list_item.xml"); + // Remember avatar icon width including its padding from the name text box, + // so that we can hide and show the icon again later. + if (!sIconWidth) + { + sIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft; + } } LLAvatarListItem::~LLAvatarListItem() @@ -188,6 +195,21 @@ void LLAvatarListItem::setAvatarId(const LLUUID& id, bool ignore_status_changes) gCacheName->get(id, FALSE, boost::bind(&LLAvatarListItem::onNameCache, this, _2, _3)); } +void LLAvatarListItem::setAvatarIconVisible(bool visible) +{ + // Already done? Then do nothing. + if (mAvatarIcon->getVisible() == (BOOL)visible) + return; + + // Show/hide avatar icon. + mAvatarIcon->setVisible(visible); + + // Move the avatar name horizontally by icon size + its distance from the avatar name. + LLRect name_rect = mAvatarName->getRect(); + name_rect.mLeft += visible ? sIconWidth : -sIconWidth; + mAvatarName->setRect(name_rect); +} + void LLAvatarListItem::onInfoBtnClick() { LLFloaterReg::showInstance("inspect_avatar", LLSD().insert("avatar_id", mAvatarId)); diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index b9cfed4b7b..a8d3919217 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -64,6 +64,7 @@ public: void setOnline(bool online); void setName(const std::string& name); void setAvatarId(const LLUUID& id, bool ignore_status_changes = false); + void setAvatarIconVisible(bool visible); const LLUUID& getAvatarId() const; const std::string getAvatarName() const; @@ -87,7 +88,7 @@ private: void onNameCache(const std::string& first_name, const std::string& last_name); - LLAvatarIconCtrl*mAvatarIcon; + LLAvatarIconCtrl* mAvatarIcon; LLTextBox* mAvatarName; LLTextBox* mStatus; @@ -98,6 +99,7 @@ private: LLUUID mAvatarId; EOnlineStatus mOnlineStatus; + static S32 sIconWidth; // icon width + padding }; #endif //LL_LLAVATARLISTITEM_H diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index 77f941eef0..6427422572 100644 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -40,6 +40,8 @@ #include "llbottomtray.h" #include "llviewerwindow.h" #include "llrootview.h" +#include "llsyswellwindow.h" +#include "llfloaterreg.h" #include <algorithm> @@ -128,7 +130,7 @@ void LLChannelManager::onLoginCompleted() S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound); - mStartUpChannel->setShowToasts(true); + mStartUpChannel->setMouseDownCallback(boost::bind(&LLSysWellWindow::onStartUpToastClick, LLFloaterReg::getTypedInstance<LLSysWellWindow>("syswell_window"), _2, _3, _4)); mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this)); mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("StartUpToastLifeTime")); diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 94058365be..aaca568320 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -38,9 +38,215 @@ #include "llscrollcontainer.h" #include "llavatariconctrl.h" +#include "llimview.h" +#include "llcallingcard.h" //for LLAvatarTracker +#include "llagentdata.h" +#include "llavataractions.h" +#include "lltrans.h" + static LLDefaultChildRegistry::Register<LLChatHistory> r("chat_history"); static const std::string MESSAGE_USERNAME_DATE_SEPARATOR(" ----- "); +std::string formatCurrentTime() +{ + time_t utc_time; + utc_time = time_corrected(); + std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" + +LLTrans::getString("TimeMin")+"] "; + + LLSD substitution; + + substitution["datetime"] = (S32) utc_time; + LLStringUtil::format (timeStr, substitution); + + return timeStr; +} + +class LLChatHistoryHeader: public LLPanel +{ +public: + static LLChatHistoryHeader* createInstance(const std::string& file_name) + { + LLChatHistoryHeader* pInstance = new LLChatHistoryHeader; + LLUICtrlFactory::getInstance()->buildPanel(pInstance, file_name); + return pInstance; + } + + BOOL handleMouseUp(S32 x, S32 y, MASK mask) + { + return LLPanel::handleMouseUp(x,y,mask); + } + + void onAvatarIconContextMenuItemClicked(const LLSD& userdata) + { + std::string level = userdata.asString(); + + if (level == "profile") + { + LLAvatarActions::showProfile(getAvatarId()); + } + else if (level == "im") + { + LLAvatarActions::startIM(getAvatarId()); + } + else if (level == "add") + { + std::string name; + name.assign(getFirstName()); + name.append(" "); + name.append(getLastName()); + + LLAvatarActions::requestFriendshipDialog(getAvatarId(), name); + } + else if (level == "remove") + { + LLAvatarActions::removeFriendDialog(getAvatarId()); + } + } + + BOOL postBuild() + { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + + registrar.add("AvatarIcon.Action", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemClicked, this, _2)); + + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_avatar_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + + mPopupMenuHandleAvatar = menu->getHandle(); + + return LLPanel::postBuild(); + } + + bool pointInChild(const std::string& name,S32 x,S32 y) + { + LLUICtrl* child = findChild<LLUICtrl>(name); + if(!child) + return false; + + LLView* parent = child->getParent(); + if(parent!=this) + { + x-=parent->getRect().mLeft; + y-=parent->getRect().mBottom; + } + + S32 local_x = x - child->getRect().mLeft ; + S32 local_y = y - child->getRect().mBottom ; + return child->pointInView(local_x, local_y); + } + + BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) + { + if(pointInChild("avatar_icon",x,y) || pointInChild("user_name",x,y)) + { + showContextMenu(x,y); + return TRUE; + } + + return LLPanel::handleRightMouseDown(x,y,mask); + } + const LLUUID& getAvatarId () const { return mAvatarID;} + const std::string& getFirstName() const { return mFirstName; } + const std::string& getLastName () const { return mLastName; } + + void setup(const LLChat& chat) + { + mAvatarID = chat.mFromID; + mSourceType = chat.mSourceType; + gCacheName->get(mAvatarID, FALSE, boost::bind(&LLChatHistoryHeader::nameUpdatedCallback, this, _1, _2, _3, _4)); + if(chat.mFromID.isNull()) + { + mSourceType = CHAT_SOURCE_SYSTEM; + } + + + LLTextBox* userName = getChild<LLTextBox>("user_name"); + + if(!chat.mFromName.empty()) + userName->setValue(chat.mFromName); + else + { + std::string SL = LLTrans::getString("SECOND_LIFE"); + userName->setValue(SL); + } + + LLTextBox* timeBox = getChild<LLTextBox>("time_box"); + timeBox->setValue(formatCurrentTime()); + + LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon"); + + if(mSourceType != CHAT_SOURCE_AGENT) + icon->setDrawTooltip(false); + + if(!chat.mFromID.isNull()) + { + icon->setValue(chat.mFromID); + } + + } + + void nameUpdatedCallback(const LLUUID& id,const std::string& first,const std::string& last,BOOL is_group) + { + if (id != mAvatarID) + return; + mFirstName = first; + mLastName = last; + } +protected: + void showContextMenu(S32 x,S32 y) + { + if(mSourceType == CHAT_SOURCE_SYSTEM) + showSystemContextMenu(x,y); + if(mSourceType == CHAT_SOURCE_AGENT) + showAvatarContextMenu(x,y); + if(mSourceType == CHAT_SOURCE_OBJECT) + showObjectContextMenu(x,y); + } + + void showSystemContextMenu(S32 x,S32 y) + { + } + void showObjectContextMenu(S32 x,S32 y) + { + } + + void showAvatarContextMenu(S32 x,S32 y) + { + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandleAvatar.get(); + + if(menu) + { + bool is_friend = LLAvatarTracker::instance().getBuddyInfo(mAvatarID) != NULL; + + menu->setItemEnabled("Add Friend", !is_friend); + menu->setItemEnabled("Remove Friend", is_friend); + + if(gAgentID == mAvatarID) + { + menu->setItemEnabled("Add Friend", false); + menu->setItemEnabled("Send IM", false); + menu->setItemEnabled("Remove Friend", false); + } + + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, menu, x, y); + } + } + + + +protected: + LLHandle<LLView> mPopupMenuHandleAvatar; + + LLUUID mAvatarID; + EChatSourceType mSourceType; + std::string mFirstName; + std::string mLastName; + +}; + + LLChatHistory::LLChatHistory(const LLChatHistory::Params& p) : LLTextEditor(p), mMessageHeaderFilename(p.message_header), @@ -48,7 +254,7 @@ mMessageSeparatorFilename(p.message_separator), mLeftTextPad(p.left_text_pad), mRightTextPad(p.right_text_pad), mLeftWidgetPad(p.left_widget_pad), -mRightWidgetPad(p.rigth_widget_pad) +mRightWidgetPad(p.right_widget_pad) { } @@ -78,50 +284,49 @@ LLView* LLChatHistory::getSeparator() return separator; } -LLView* LLChatHistory::getHeader(const LLUUID& avatar_id, std::string& from, std::string& time) +LLView* LLChatHistory::getHeader(const LLChat& chat) { - LLPanel* header = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>(mMessageHeaderFilename, NULL, LLPanel::child_registry_t::instance()); - LLTextBox* userName = header->getChild<LLTextBox>("user_name"); - userName->setValue(from); - LLTextBox* timeBox = header->getChild<LLTextBox>("time_box"); - timeBox->setValue(time); - if(!avatar_id.isNull()) - { - LLAvatarIconCtrl* icon = header->getChild<LLAvatarIconCtrl>("avatar_icon"); - icon->setValue(avatar_id); - } + LLChatHistoryHeader* header = LLChatHistoryHeader::createInstance(mMessageHeaderFilename); + header->setup(chat); return header; } -void LLChatHistory::appendWidgetMessage(const LLUUID& avatar_id, std::string& from, std::string& time, std::string& message, LLStyle::Params& style_params) +void LLChatHistory::appendWidgetMessage(const LLChat& chat, LLStyle::Params& style_params) { LLView* view = NULL; std::string view_text; - if (mLastFromName == from) + if (mLastFromName == chat.mFromName) { view = getSeparator(); view_text = "\n"; } else { - view = getHeader(avatar_id, from, time); - view_text = from + MESSAGE_USERNAME_DATE_SEPARATOR + time + '\n'; + view = getHeader(chat); + view_text = chat.mFromName + MESSAGE_USERNAME_DATE_SEPARATOR + formatCurrentTime() + '\n'; } //Prepare the rect for the view LLRect target_rect = getDocumentView()->getRect(); - target_rect.mLeft += mLeftWidgetPad; + // squeeze down the widget by subtracting padding off left and right + target_rect.mLeft += mLeftWidgetPad + mHPad; target_rect.mRight -= mRightWidgetPad; view->reshape(target_rect.getWidth(), view->getRect().getHeight()); view->setOrigin(target_rect.mLeft, view->getRect().mBottom); - appendWidget(view, view_text, FALSE, TRUE, mLeftWidgetPad, 0); + LLInlineViewSegment::Params p; + p.view = view; + p.force_newline = true; + p.left_pad = mLeftWidgetPad; + p.right_pad = mRightWidgetPad; + + appendWidget(p, view_text, false); //Append the text message - message += '\n'; + std::string message = chat.mText + '\n'; appendText(message, FALSE, style_params); - mLastFromName = from; + mLastFromName = chat.mFromName; blockUndo(); setCursorAndScrollToEnd(); } diff --git a/indra/newview/llchathistory.h b/indra/newview/llchathistory.h index d6eccf896a..92dcfdd958 100644 --- a/indra/newview/llchathistory.h +++ b/indra/newview/llchathistory.h @@ -34,6 +34,7 @@ #define LLCHATHISTORY_H_ #include "lltexteditor.h" +#include "llchat.h" //Chat log widget allowing addition of a message as a widget class LLChatHistory : public LLTextEditor @@ -52,7 +53,7 @@ class LLChatHistory : public LLTextEditor //Widget left padding from the scroll rect Optional<S32> left_widget_pad; //Widget right padding from the scroll rect - Optional<S32> rigth_widget_pad; + Optional<S32> right_widget_pad; Params() : message_header("message_header"), @@ -60,7 +61,7 @@ class LLChatHistory : public LLTextEditor left_text_pad("left_text_pad"), right_text_pad("right_text_pad"), left_widget_pad("left_widget_pad"), - rigth_widget_pad("rigth_widget_pad") + right_widget_pad("right_widget_pad") { } @@ -85,7 +86,7 @@ class LLChatHistory : public LLTextEditor * @param time time of a message. * @return pointer to LLView header object. */ - LLView* getHeader(const LLUUID& avatar_id, std::string& from, std::string& time); + LLView* getHeader(const LLChat& chat); public: ~LLChatHistory(); @@ -94,11 +95,11 @@ class LLChatHistory : public LLTextEditor * Appends a widget message. * If last user appended message, concurs with current user, * separator is added before the message, otherwise header is added. - * @param from owner of a message. + * @param chat - base chat message. * @param time time of a message. * @param message message itself. */ - void appendWidgetMessage(const LLUUID& avatar_id, std::string& from, std::string& time, std::string& message, LLStyle::Params& style_params); + void appendWidgetMessage(const LLChat& chat, LLStyle::Params& style_params); private: std::string mLastFromName; diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 3b5b7f570e..a6afbc05be 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -503,13 +503,15 @@ void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, con return; } + LLPointer<LLViewerInventoryItem> viewer_item = new LLViewerInventoryItem(item); + if (dest) { - insertBeforeItem(mItems, dest->getLandmarkId(), item->getUUID()); + insertBeforeItem(mItems, dest->getLandmarkId(), viewer_item); } else { - mItems.push_back(gInventory.getItem(item->getUUID())); + mItems.push_back(viewer_item); } int sortField = 0; @@ -534,13 +536,22 @@ void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, con } } - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - favorites_id, - std::string(), - cb); + LLToolDragAndDrop* tool_dad = LLToolDragAndDrop::getInstance(); + if (tool_dad->getSource() == LLToolDragAndDrop::SOURCE_NOTECARD) + { + viewer_item->setType(LLAssetType::AT_FAVORITE); + copy_inventory_from_notecard(tool_dad->getObjectID(), tool_dad->getSourceID(), viewer_item.get(), gInventoryCallbacks.registerCB(cb)); + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + favorites_id, + std::string(), + cb); + } llinfos << "Copied inventory item #" << item->getUUID() << " to favorites." << llendl; } @@ -1263,10 +1274,9 @@ void LLFavoritesBarCtrl::updateItemsOrder(LLInventoryModel::item_array_t& items, items.insert(findItemByUUID(items, destItem->getUUID()), srcItem); } -void LLFavoritesBarCtrl::insertBeforeItem(LLInventoryModel::item_array_t& items, const LLUUID& beforeItemId, const LLUUID& insertedItemId) +void LLFavoritesBarCtrl::insertBeforeItem(LLInventoryModel::item_array_t& items, const LLUUID& beforeItemId, LLViewerInventoryItem* insertedItem) { LLViewerInventoryItem* beforeItem = gInventory.getItem(beforeItemId); - LLViewerInventoryItem* insertedItem = gInventory.getItem(insertedItemId); items.insert(findItemByUUID(items, beforeItem->getUUID()), insertedItem); } diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index ea2a3d08e2..e90d13f9d5 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -141,7 +141,7 @@ private: * inserts an item identified by insertedItemId BEFORE an item identified by beforeItemId. * this function assumes that an item identified by insertedItemId doesn't exist in items array. */ - void insertBeforeItem(LLInventoryModel::item_array_t& items, const LLUUID& beforeItemId, const LLUUID& insertedItemId); + void insertBeforeItem(LLInventoryModel::item_array_t& items, const LLUUID& beforeItemId, LLViewerInventoryItem* insertedItem); // finds an item by it's UUID in the items array LLInventoryModel::item_array_t::iterator findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id); diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp index dea656b0e4..fbf09207fe 100644 --- a/indra/newview/llfloaterchatterbox.cpp +++ b/indra/newview/llfloaterchatterbox.cpp @@ -42,7 +42,7 @@ #include "llfloaterfriends.h" #include "llfloatergroups.h" #include "llviewercontrol.h" -#include "llimview.h" +#include "llvoicechannel.h" #include "llimpanel.h" // diff --git a/indra/newview/llfloaterinventory.cpp b/indra/newview/llfloaterinventory.cpp index c890f9f122..a47916b7d7 100644 --- a/indra/newview/llfloaterinventory.cpp +++ b/indra/newview/llfloaterinventory.cpp @@ -1438,7 +1438,11 @@ void LLInventoryPanel::modelChanged(U32 mask) } LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolders->getItemByID(model_item->getParentUUID()); - if (view_item->getParentFolder() != new_parent) + + // added check against NULL for cases when Inventory panel contains startFolder. + // in this case parent is LLFolderView (LLInventoryPanel::mFolders) itself. + // this check is a fix for bug EXT-1859. + if (NULL != new_parent && view_item->getParentFolder() != new_parent) { view_item->getParentFolder()->extractItem(view_item); view_item->addToFolder(new_parent, mFolders); diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp index b64257b11d..aca9198f59 100644 --- a/indra/newview/llfloatervoicedevicesettings.cpp +++ b/indra/newview/llfloatervoicedevicesettings.cpp @@ -43,7 +43,7 @@ #include "llsliderctrl.h" #include "llviewercontrol.h" #include "llvoiceclient.h" -#include "llimpanel.h" +#include "llvoicechannel.h" // Library includes (after viewer) #include "lluictrlfactory.h" diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index a20b5ea66c..b86795f696 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -42,7 +42,6 @@ #include "llchiclet.h" #include "llfloaterchat.h" #include "llfloaterreg.h" -#include "llimview.h" #include "lllineeditor.h" #include "lllogchat.h" #include "llpanelimcontrolpanel.h" @@ -50,6 +49,7 @@ #include "lltrans.h" #include "llchathistory.h" #include "llviewerwindow.h" +#include "llvoicechannel.h" #include "lltransientfloatermgr.h" @@ -61,7 +61,14 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mLastMessageIndex(-1), mDialog(IM_NOTHING_SPECIAL), mChatHistory(NULL), - mInputEditor(NULL), + mInputEditor(NULL), + mSavedTitle(), + mTypingStart(), + mShouldSendTypingState(false), + mMeTyping(false), + mOtherTyping(false), + mTypingTimer(), + mTypingTimeoutTimer(), mPositioned(false), mSessionInitialized(false) { @@ -71,12 +78,15 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mSessionInitialized = im_session->mSessionInitialized; mDialog = im_session->mType; - if (IM_NOTHING_SPECIAL == mDialog || IM_SESSION_P2P_INVITE == mDialog) - { + switch(mDialog){ + case IM_NOTHING_SPECIAL: + case IM_SESSION_P2P_INVITE: mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); - } - else - { + break; + case IM_SESSION_CONFERENCE_START: + mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); + break; + default: mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); } } @@ -95,6 +105,7 @@ void LLIMFloater::onFocusReceived() // virtual void LLIMFloater::onClose(bool app_quitting) { + setTyping(false); gIMMgr->leaveSession(mSessionID); } @@ -141,6 +152,7 @@ void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) { LLIMFloater* self = (LLIMFloater*) userdata; self->sendMsg(); + self->setTyping(false); } void LLIMFloater::sendMsg() @@ -193,9 +205,10 @@ BOOL LLIMFloater::postBuild() if (other_party_id.notNull()) { mOtherParticipantUUID = other_party_id; - mControlPanel->setID(mOtherParticipantUUID); } + mControlPanel->setSessionId(mSessionID); + LLButton* slide_left = getChild<LLButton>("slide_left_btn"); slide_left->setVisible(mControlPanel->getVisible()); slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); @@ -228,12 +241,37 @@ BOOL LLIMFloater::postBuild() LLLogChat::loadHistory(getTitle(), &chatFromLogFile, (void *)this); } + mTypingStart = LLTrans::getString("IM_typing_start_string"); + + // Disable input editor if session cannot accept text + LLIMModel::LLIMSession* im_session = + LLIMModel::instance().findIMSession(mSessionID); + if( im_session && !im_session->mTextIMPossible ) + { + mInputEditor->setEnabled(FALSE); + mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); + } + //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla" //see LLFloaterIMPanel for how it is done (IB) return LLDockableFloater::postBuild(); } +// virtual +void LLIMFloater::draw() +{ + if ( mMeTyping ) + { + // Time out if user hasn't typed for a while. + if ( mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS ) + { + setTyping(false); + } + } + + LLFloater::draw(); +} // static @@ -255,6 +293,15 @@ void* LLIMFloater::createPanelGroupControl(void* userdata) return self->mControlPanel; } +// static +void* LLIMFloater::createPanelAdHocControl(void* userdata) +{ + LLIMFloater *self = (LLIMFloater*)userdata; + self->mControlPanel = new LLPanelAdHocControlPanel(self->mSessionID); + self->mControlPanel->setXMLFilename("panel_adhoc_control_panel.xml"); + return self->mControlPanel; +} + void LLIMFloater::onSlide() { LLPanel* im_control_panel = getChild<LLPanel>("panel_im_control_panel"); @@ -379,10 +426,12 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) { mSessionInitialized = true; + //will be different only for an ad-hoc im session if (mSessionID != im_session_id) { mSessionID = im_session_id; setKey(im_session_id); + mControlPanel->setSessionId(im_session_id); } //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) @@ -402,7 +451,8 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) void LLIMFloater::updateMessages() { - std::list<LLSD> messages = LLIMModel::instance().getMessages(mSessionID, mLastMessageIndex+1); + std::list<LLSD> messages; + LLIMModel::instance().getMessages(mSessionID, messages, mLastMessageIndex+1); std::string agent_name; gCacheName->getFullName(gAgentID, agent_name); @@ -428,7 +478,11 @@ void LLIMFloater::updateMessages() if (from == agent_name) from = LLTrans::getString("You"); - mChatHistory->appendWidgetMessage(from_id, from, time, message, style_params); + LLChat chat(message); + chat.mFromID = from_id; + chat.mFromName = from; + + mChatHistory->appendWidgetMessage(chat, style_params); mLastMessageIndex = msg["index"].asInteger(); } @@ -440,9 +494,14 @@ void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* { LLIMFloater* self= (LLIMFloater*) userdata; - //in disconnected state IM input editor should be disabled - self->mInputEditor->setEnabled(!gDisconnected); - + // Allow enabling the LLIMFloater input editor only if session can accept text + LLIMModel::LLIMSession* im_session = + LLIMModel::instance().findIMSession(self->mSessionID); + if( im_session && im_session->mTextIMPossible ) + { + //in disconnected state IM input editor should be disabled + self->mInputEditor->setEnabled(!gDisconnected); + } self->mChatHistory->setCursorAndScrollToEnd(); } @@ -450,7 +509,7 @@ void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata) { LLIMFloater* self = (LLIMFloater*) userdata; - self->setTyping(FALSE); + self->setTyping(false); } // static @@ -460,19 +519,142 @@ void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) std::string text = self->mInputEditor->getText(); if (!text.empty()) { - self->setTyping(TRUE); + self->setTyping(true); } else { // Deleting all text counts as stopping typing. - self->setTyping(FALSE); + self->setTyping(false); } } +void LLIMFloater::setTyping(bool typing) +{ + if ( typing ) + { + // Started or proceeded typing, reset the typing timeout timer + mTypingTimeoutTimer.reset(); + } + + if ( mMeTyping != typing ) + { + // Typing state is changed + mMeTyping = typing; + // So, should send current state + mShouldSendTypingState = true; + // In case typing is started, send state after some delay + mTypingTimer.reset(); + } + + // Don't want to send typing indicators to multiple people, potentially too + // much network traffic. Only send in person-to-person IMs. + if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL ) + { + if ( mMeTyping ) + { + if ( mTypingTimer.getElapsedTimeF32() > 1.f ) + { + // Still typing, send 'start typing' notification + LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE); + mShouldSendTypingState = false; + } + } + else + { + // Send 'stop typing' notification immediately + LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, FALSE); + mShouldSendTypingState = false; + } + } -//just a stub for now -void LLIMFloater::setTyping(BOOL typing) + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); + +} + +void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) +{ + if ( typing ) + { + // other user started typing + addTypingIndicator(im_info); + } + else + { + // other user stopped typing + removeTypingIndicator(im_info); + } +} + +void LLIMFloater::processSessionUpdate(const LLSD& session_update) +{ + // *TODO : verify following code when moderated mode will be implemented + if ( false && session_update.has("moderated_mode") && + session_update["moderated_mode"].has("voice") ) + { + BOOL voice_moderated = session_update["moderated_mode"]["voice"]; + const std::string session_label = LLIMModel::instance().getName(mSessionID); + + if (voice_moderated) + { + setTitle(session_label + std::string(" ") + LLTrans::getString("IM_moderated_chat_label")); + } + else + { + setTitle(session_label); + } + + // *TODO : uncomment this when/if LLPanelActiveSpeakers panel will be added + //update the speakers dropdown too + //mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated); + } +} + +void LLIMFloater::addTypingIndicator(const LLIMInfo* im_info) { + // We may have lost a "stop-typing" packet, don't add it twice + if ( im_info && !mOtherTyping ) + { + mOtherTyping = true; + + // Create typing is started title string + LLUIString typing_start(mTypingStart); + typing_start.setArg("[NAME]", im_info->mName); + + // Save and set new title + mSavedTitle = getTitle(); + setTitle (typing_start); + + // Update speaker + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if ( speaker_mgr ) + { + speaker_mgr->setSpeakerTyping(im_info->mFromID, TRUE); + } + } +} + +void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info) +{ + if ( mOtherTyping ) + { + mOtherTyping = false; + + // Revert the title to saved one + setTitle(mSavedTitle); + + if ( im_info ) + { + // Update speaker + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if ( speaker_mgr ) + { + speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE); + } + } + + } } void LLIMFloater::chatFromLogFile(LLLogChat::ELogLineType type, std::string line, void* userdata) diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 99810b6d6d..3da27ac941 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -55,6 +55,8 @@ public: // LLView overrides /*virtual*/ BOOL postBuild(); /*virtual*/ void setVisible(BOOL visible); + // Check typing timeout timer. + /*virtual*/ void draw(); // LLFloater overrides /*virtual*/ void onClose(bool app_quitting); @@ -85,6 +87,8 @@ public: void setPositioned(bool b) { mPositioned = b; }; void onVisibilityChange(const LLSD& new_visibility); + void processIMTyping(const LLIMInfo* im_info, BOOL typing); + void processSessionUpdate(const LLSD& session_update); private: // process focus events to set a currently active session @@ -94,15 +98,21 @@ private: static void onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ); static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); - void setTyping(BOOL typing); + void setTyping(bool typing); void onSlide(); static void* createPanelIMControl(void* userdata); static void* createPanelGroupControl(void* userdata); + static void* createPanelAdHocControl(void* userdata); // gets a rect that bounds possible positions for the LLIMFloater on a screen (EXT-1111) void getAllowedRect(LLRect& rect); static void chatFromLogFile(LLLogChat::ELogLineType type, std::string line, void* userdata); + // Add the "User is typing..." indicator. + void addTypingIndicator(const LLIMInfo* im_info); + + // Remove the "User is typing..." indicator. + void removeTypingIndicator(const LLIMInfo* im_info = NULL); LLPanelChatControlPanel* mControlPanel; LLUUID mSessionID; @@ -114,6 +124,14 @@ private: LLLineEditor* mInputEditor; bool mPositioned; + std::string mSavedTitle; + LLUIString mTypingStart; + bool mMeTyping; + bool mOtherTyping; + bool mShouldSendTypingState; + LLFrameTimer mTypingTimer; + LLFrameTimer mTypingTimeoutTimer; + bool mSessionInitialized; LLSD mQueuedMsgsForInit; }; diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 163984f740..77ee90f681 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -54,6 +54,7 @@ #include "llconsole.h" #include "llgroupactions.h" #include "llfloater.h" +#include "llfloateractivespeakers.h" #include "llfloatercall.h" #include "llavataractions.h" #include "llimview.h" @@ -77,6 +78,7 @@ #include "llviewercontrol.h" #include "lluictrlfactory.h" #include "llviewerwindow.h" +#include "llvoicechannel.h" #include "lllogchat.h" #include "llweb.h" #include "llhttpclient.h" @@ -90,7 +92,6 @@ const S32 LINE_HEIGHT = 16; const S32 MIN_WIDTH = 200; const S32 MIN_HEIGHT = 130; -const U32 DEFAULT_RETRIES_COUNT = 3; // // Statics @@ -100,831 +101,6 @@ static std::string sTitleString = "Instant Message with [NAME]"; static std::string sTypingStartString = "[NAME]: ..."; static std::string sSessionStartString = "Starting session with [NAME] please wait."; -LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; -LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; -LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL; -LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL; - -BOOL LLVoiceChannel::sSuspended = FALSE; - - - -class LLVoiceCallCapResponder : public LLHTTPClient::Responder -{ -public: - LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {}; - - virtual void error(U32 status, const std::string& reason); // called with bad status codes - virtual void result(const LLSD& content); - -private: - LLUUID mSessionID; -}; - - -void LLVoiceCallCapResponder::error(U32 status, const std::string& reason) -{ - llwarns << "LLVoiceCallCapResponder::error(" - << status << ": " << reason << ")" - << llendl; - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if ( channelp ) - { - if ( 403 == status ) - { - //403 == no ability - LLNotifications::instance().add( - "VoiceNotAllowed", - channelp->getNotifyArgs()); - } - else - { - LLNotifications::instance().add( - "VoiceCallGenericError", - channelp->getNotifyArgs()); - } - channelp->deactivate(); - } -} - -void LLVoiceCallCapResponder::result(const LLSD& content) -{ - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if (channelp) - { - //*TODO: DEBUG SPAM - LLSD::map_const_iterator iter; - for(iter = content.beginMap(); iter != content.endMap(); ++iter) - { - llinfos << "LLVoiceCallCapResponder::result got " - << iter->first << llendl; - } - - channelp->setChannelInfo( - content["voice_credentials"]["channel_uri"].asString(), - content["voice_credentials"]["channel_credentials"].asString()); - } -} - -// -// LLVoiceChannel -// -LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& session_name) : - mSessionID(session_id), - mState(STATE_NO_CHANNEL_INFO), - mSessionName(session_name), - mIgnoreNextSessionLeave(FALSE) -{ - mNotifyArgs["VOICE_CHANNEL_NAME"] = mSessionName; - - if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second) - { - // a voice channel already exists for this session id, so this instance will be orphaned - // the end result should simply be the failure to make voice calls - llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl; - } - - LLVoiceClient::getInstance()->addObserver(this); -} - -LLVoiceChannel::~LLVoiceChannel() -{ - // Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed. - if(gVoiceClient) - { - gVoiceClient->removeObserver(this); - } - - sVoiceChannelMap.erase(mSessionID); - sVoiceChannelURIMap.erase(mURI); -} - -void LLVoiceChannel::setChannelInfo( - const std::string& uri, - const std::string& credentials) -{ - setURI(uri); - - mCredentials = credentials; - - if (mState == STATE_NO_CHANNEL_INFO) - { - if (mURI.empty()) - { - LLNotifications::instance().add("VoiceChannelJoinFailed", mNotifyArgs); - llwarns << "Received empty URI for channel " << mSessionName << llendl; - deactivate(); - } - else if (mCredentials.empty()) - { - LLNotifications::instance().add("VoiceChannelJoinFailed", mNotifyArgs); - llwarns << "Received empty credentials for channel " << mSessionName << llendl; - deactivate(); - } - else - { - setState(STATE_READY); - - // if we are supposed to be active, reconnect - // this will happen on initial connect, as we request credentials on first use - if (sCurrentVoiceChannel == this) - { - // just in case we got new channel info while active - // should move over to new channel - activate(); - } - } - } -} - -void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal) -{ - if (channelURI != mURI) - { - return; - } - - if (type < BEGIN_ERROR_STATUS) - { - handleStatusChange(type); - } - else - { - handleError(type); - } -} - -void LLVoiceChannel::handleStatusChange(EStatusType type) -{ - // status updates - switch(type) - { - case STATUS_LOGIN_RETRY: - //mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle(); - LLNotifications::instance().add("VoiceLoginRetry"); - break; - case STATUS_LOGGED_IN: - //if (!mLoginNotificationHandle.isDead()) - //{ - // LLNotifyBox* notifyp = (LLNotifyBox*)mLoginNotificationHandle.get(); - // if (notifyp) - // { - // notifyp->close(); - // } - // mLoginNotificationHandle.markDead(); - //} - break; - case STATUS_LEFT_CHANNEL: - if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) - { - // if forceably removed from channel - // update the UI and revert to default channel - LLNotifications::instance().add("VoiceChannelDisconnected", mNotifyArgs); - deactivate(); - } - mIgnoreNextSessionLeave = FALSE; - break; - case STATUS_JOINING: - if (callStarted()) - { - setState(STATE_RINGING); - } - break; - case STATUS_JOINED: - if (callStarted()) - { - setState(STATE_CONNECTED); - } - default: - break; - } -} - -// default behavior is to just deactivate channel -// derived classes provide specific error messages -void LLVoiceChannel::handleError(EStatusType type) -{ - deactivate(); - setState(STATE_ERROR); -} - -BOOL LLVoiceChannel::isActive() -{ - // only considered active when currently bound channel matches what our channel - return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI; -} - -BOOL LLVoiceChannel::callStarted() -{ - return mState >= STATE_CALL_STARTED; -} - -void LLVoiceChannel::deactivate() -{ - if (mState >= STATE_RINGING) - { - // ignore session leave event - mIgnoreNextSessionLeave = TRUE; - } - - if (callStarted()) - { - setState(STATE_HUNG_UP); - // mute the microphone if required when returning to the proximal channel - if (gSavedSettings.getBOOL("AutoDisengageMic") && sCurrentVoiceChannel == this) - { - gSavedSettings.setBOOL("PTTCurrentlyEnabled", true); - } - } - - if (sCurrentVoiceChannel == this) - { - // default channel is proximal channel - sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); - sCurrentVoiceChannel->activate(); - } -} - -void LLVoiceChannel::activate() -{ - if (callStarted()) - { - return; - } - - // deactivate old channel and mark ourselves as the active one - if (sCurrentVoiceChannel != this) - { - // mark as current before deactivating the old channel to prevent - // activating the proximal channel between IM calls - LLVoiceChannel* old_channel = sCurrentVoiceChannel; - sCurrentVoiceChannel = this; - if (old_channel) - { - old_channel->deactivate(); - } - } - - if (mState == STATE_NO_CHANNEL_INFO) - { - // responsible for setting status to active - getChannelInfo(); - } - else - { - setState(STATE_CALL_STARTED); - } -} - -void LLVoiceChannel::getChannelInfo() -{ - // pretend we have everything we need - if (sCurrentVoiceChannel == this) - { - setState(STATE_CALL_STARTED); - } -} - -//static -LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id) -{ - voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id); - if (found_it == sVoiceChannelMap.end()) - { - return NULL; - } - else - { - return found_it->second; - } -} - -//static -LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri) -{ - voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri); - if (found_it == sVoiceChannelURIMap.end()) - { - return NULL; - } - else - { - return found_it->second; - } -} - -void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id) -{ - sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID)); - mSessionID = new_session_id; - sVoiceChannelMap.insert(std::make_pair(mSessionID, this)); -} - -void LLVoiceChannel::setURI(std::string uri) -{ - sVoiceChannelURIMap.erase(mURI); - mURI = uri; - sVoiceChannelURIMap.insert(std::make_pair(mURI, this)); -} - -void LLVoiceChannel::setState(EState state) -{ - switch(state) - { - case STATE_RINGING: - gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); - break; - case STATE_CONNECTED: - gIMMgr->addSystemMessage(mSessionID, "connected", mNotifyArgs); - break; - case STATE_HUNG_UP: - gIMMgr->addSystemMessage(mSessionID, "hang_up", mNotifyArgs); - break; - default: - break; - } - - mState = state; -} - -void LLVoiceChannel::toggleCallWindowIfNeeded(EState state) -{ - if (state == STATE_CONNECTED) - { - LLFloaterReg::showInstance("voice_call", mSessionID); - } - // By checking that current state is CONNECTED we make sure that the call window - // has been shown, hence there's something to hide. This helps when user presses - // the "End call" button right after initiating the call. - // *TODO: move this check to LLFloaterCall? - else if (state == STATE_HUNG_UP && mState == STATE_CONNECTED) - { - LLFloaterReg::hideInstance("voice_call", mSessionID); - } -} - -//static -void LLVoiceChannel::initClass() -{ - sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); -} - - -//static -void LLVoiceChannel::suspend() -{ - if (!sSuspended) - { - sSuspendedVoiceChannel = sCurrentVoiceChannel; - sSuspended = TRUE; - } -} - -//static -void LLVoiceChannel::resume() -{ - if (sSuspended) - { - if (gVoiceClient->voiceEnabled()) - { - if (sSuspendedVoiceChannel) - { - sSuspendedVoiceChannel->activate(); - } - else - { - LLVoiceChannelProximal::getInstance()->activate(); - } - } - sSuspended = FALSE; - } -} - - -// -// LLVoiceChannelGroup -// - -LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name) : - LLVoiceChannel(session_id, session_name) -{ - mRetries = DEFAULT_RETRIES_COUNT; - mIsRetrying = FALSE; -} - -void LLVoiceChannelGroup::deactivate() -{ - if (callStarted()) - { - LLVoiceClient::getInstance()->leaveNonSpatialChannel(); - } - LLVoiceChannel::deactivate(); -} - -void LLVoiceChannelGroup::activate() -{ - if (callStarted()) return; - - LLVoiceChannel::activate(); - - if (callStarted()) - { - // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel( - mURI, - mCredentials); - -#if 0 // *TODO - if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel - { - // Add the party to the list of people with which we've recently interacted. - for (/*people in the chat*/) - LLRecentPeople::instance().add(buddy_id); - } -#endif - } -} - -void LLVoiceChannelGroup::getChannelInfo() -{ - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - std::string url = region->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "call"; - data["session-id"] = mSessionID; - LLHTTPClient::post(url, - data, - new LLVoiceCallCapResponder(mSessionID)); - } -} - -void LLVoiceChannelGroup::setChannelInfo( - const std::string& uri, - const std::string& credentials) -{ - setURI(uri); - - mCredentials = credentials; - - if (mState == STATE_NO_CHANNEL_INFO) - { - if(!mURI.empty() && !mCredentials.empty()) - { - setState(STATE_READY); - - // if we are supposed to be active, reconnect - // this will happen on initial connect, as we request credentials on first use - if (sCurrentVoiceChannel == this) - { - // just in case we got new channel info while active - // should move over to new channel - activate(); - } - } - else - { - //*TODO: notify user - llwarns << "Received invalid credentials for channel " << mSessionName << llendl; - deactivate(); - } - } - else if ( mIsRetrying ) - { - // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel( - mURI, - mCredentials); - } -} - -void LLVoiceChannelGroup::handleStatusChange(EStatusType type) -{ - // status updates - switch(type) - { - case STATUS_JOINED: - mRetries = 3; - mIsRetrying = FALSE; - default: - break; - } - - LLVoiceChannel::handleStatusChange(type); -} - -void LLVoiceChannelGroup::handleError(EStatusType status) -{ - std::string notify; - switch(status) - { - case ERROR_CHANNEL_LOCKED: - case ERROR_CHANNEL_FULL: - notify = "VoiceChannelFull"; - break; - case ERROR_NOT_AVAILABLE: - //clear URI and credentials - //set the state to be no info - //and activate - if ( mRetries > 0 ) - { - mRetries--; - mIsRetrying = TRUE; - mIgnoreNextSessionLeave = TRUE; - - getChannelInfo(); - return; - } - else - { - notify = "VoiceChannelJoinFailed"; - mRetries = DEFAULT_RETRIES_COUNT; - mIsRetrying = FALSE; - } - - break; - - case ERROR_UNKNOWN: - default: - break; - } - - // notification - if (!notify.empty()) - { - LLNotificationPtr notification = LLNotifications::instance().add(notify, mNotifyArgs); - // echo to im window - gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, notification->getMessage()); - } - - LLVoiceChannel::handleError(status); -} - -void LLVoiceChannelGroup::setState(EState state) -{ - // HACK: Open/close the call window if needed. - toggleCallWindowIfNeeded(state); - - switch(state) - { - case STATE_RINGING: - if ( !mIsRetrying ) - { - gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); - } - - mState = state; - break; - default: - LLVoiceChannel::setState(state); - } -} - -// -// LLVoiceChannelProximal -// -LLVoiceChannelProximal::LLVoiceChannelProximal() : - LLVoiceChannel(LLUUID::null, LLStringUtil::null) -{ - activate(); -} - -BOOL LLVoiceChannelProximal::isActive() -{ - return callStarted() && LLVoiceClient::getInstance()->inProximalChannel(); -} - -void LLVoiceChannelProximal::activate() -{ - if (callStarted()) return; - - LLVoiceChannel::activate(); - - if (callStarted()) - { - // this implicitly puts you back in the spatial channel - LLVoiceClient::getInstance()->leaveNonSpatialChannel(); - } -} - -void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) -{ - if (!proximal) - { - return; - } - - if (type < BEGIN_ERROR_STATUS) - { - handleStatusChange(type); - } - else - { - handleError(type); - } -} - -void LLVoiceChannelProximal::handleStatusChange(EStatusType status) -{ - // status updates - switch(status) - { - case STATUS_LEFT_CHANNEL: - // do not notify user when leaving proximal channel - return; - case STATUS_VOICE_DISABLED: - gIMMgr->addSystemMessage(LLUUID::null, "unavailable", mNotifyArgs); - return; - default: - break; - } - LLVoiceChannel::handleStatusChange(status); -} - - -void LLVoiceChannelProximal::handleError(EStatusType status) -{ - std::string notify; - switch(status) - { - case ERROR_CHANNEL_LOCKED: - case ERROR_CHANNEL_FULL: - notify = "ProximalVoiceChannelFull"; - break; - default: - break; - } - - // notification - if (!notify.empty()) - { - LLNotifications::instance().add(notify, mNotifyArgs); - } - - LLVoiceChannel::handleError(status); -} - -void LLVoiceChannelProximal::deactivate() -{ - if (callStarted()) - { - setState(STATE_HUNG_UP); - } -} - - -// -// LLVoiceChannelP2P -// -LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) : - LLVoiceChannelGroup(session_id, session_name), - mOtherUserID(other_user_id), - mReceivedCall(FALSE) -{ - // make sure URI reflects encoded version of other user's agent id - setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); -} - -void LLVoiceChannelP2P::handleStatusChange(EStatusType type) -{ - // status updates - switch(type) - { - case STATUS_LEFT_CHANNEL: - if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) - { - if (mState == STATE_RINGING) - { - // other user declined call - LLNotifications::instance().add("P2PCallDeclined", mNotifyArgs); - } - else - { - // other user hung up - LLNotifications::instance().add("VoiceChannelDisconnectedP2P", mNotifyArgs); - } - deactivate(); - } - mIgnoreNextSessionLeave = FALSE; - return; - default: - break; - } - - LLVoiceChannel::handleStatusChange(type); -} - -void LLVoiceChannelP2P::handleError(EStatusType type) -{ - switch(type) - { - case ERROR_NOT_AVAILABLE: - LLNotifications::instance().add("P2PCallNoAnswer", mNotifyArgs); - break; - default: - break; - } - - LLVoiceChannel::handleError(type); -} - -void LLVoiceChannelP2P::activate() -{ - if (callStarted()) return; - - LLVoiceChannel::activate(); - - if (callStarted()) - { - // no session handle yet, we're starting the call - if (mSessionHandle.empty()) - { - mReceivedCall = FALSE; - LLVoiceClient::getInstance()->callUser(mOtherUserID); - } - // otherwise answering the call - else - { - LLVoiceClient::getInstance()->answerInvite(mSessionHandle); - - // using the session handle invalidates it. Clear it out here so we can't reuse it by accident. - mSessionHandle.clear(); - } - - // Add the party to the list of people with which we've recently interacted. - LLRecentPeople::instance().add(mOtherUserID); - } -} - -void LLVoiceChannelP2P::getChannelInfo() -{ - // pretend we have everything we need, since P2P doesn't use channel info - if (sCurrentVoiceChannel == this) - { - setState(STATE_CALL_STARTED); - } -} - -// receiving session from other user who initiated call -void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::string &inURI) -{ - BOOL needs_activate = FALSE; - if (callStarted()) - { - // defer to lower agent id when already active - if (mOtherUserID < gAgent.getID()) - { - // pretend we haven't started the call yet, so we can connect to this session instead - deactivate(); - needs_activate = TRUE; - } - else - { - // we are active and have priority, invite the other user again - // under the assumption they will join this new session - mSessionHandle.clear(); - LLVoiceClient::getInstance()->callUser(mOtherUserID); - return; - } - } - - mSessionHandle = handle; - - // The URI of a p2p session should always be the other end's SIP URI. - if(!inURI.empty()) - { - setURI(inURI); - } - else - { - setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); - } - - mReceivedCall = TRUE; - - if (needs_activate) - { - activate(); - } -} - -void LLVoiceChannelP2P::setState(EState state) -{ - // HACK: Open/close the call window if needed. - toggleCallWindowIfNeeded(state); - - // you only "answer" voice invites in p2p mode - // so provide a special purpose message here - if (mReceivedCall && state == STATE_RINGING) - { - gIMMgr->addSystemMessage(mSessionID, "answering", mNotifyArgs); - mState = state; - return; - } - LLVoiceChannel::setState(state); -} - // // LLFloaterIMPanel @@ -1836,75 +1012,8 @@ void LLFloaterIMPanel::chatFromLogFile(LLLogChat::ELogLineType type, std::string self->mHistoryEditor->blockUndo(); } -void LLFloaterIMPanel::showSessionStartError( - const std::string& error_string) -{ - LLSD args; - args["REASON"] = LLTrans::getString(error_string); - args["RECIPIENT"] = getTitle(); - - LLSD payload; - payload["session_id"] = mSessionUUID; - - LLNotifications::instance().add( - "ChatterBoxSessionStartError", - args, - payload, - onConfirmForceCloseError); -} - -void LLFloaterIMPanel::showSessionEventError( - const std::string& event_string, - const std::string& error_string) -{ - LLSD args; - args["REASON"] = - LLTrans::getString(error_string); - args["EVENT"] = - LLTrans::getString(event_string); - args["RECIPIENT"] = getTitle(); - - LLNotifications::instance().add( - "ChatterBoxSessionEventError", - args); -} - -void LLFloaterIMPanel::showSessionForceClose( - const std::string& reason_string) -{ - LLSD args; - - args["NAME"] = getTitle(); - args["REASON"] = LLTrans::getString(reason_string); - - LLSD payload; - payload["session_id"] = mSessionUUID; - - LLNotifications::instance().add( - "ForceCloseChatterBoxSession", - args, - payload, - LLFloaterIMPanel::onConfirmForceCloseError); - -} - //static void LLFloaterIMPanel::onKickSpeaker(void* user_data) { } - -bool LLFloaterIMPanel::onConfirmForceCloseError(const LLSD& notification, const LLSD& response) -{ - //only 1 option really - LLUUID session_id = notification["payload"]["session_id"]; - - if ( gIMMgr ) - { - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession( - session_id); - - if ( floaterp ) floaterp->closeFloater(FALSE); - } - return false; -} diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index 4e306c7fab..39107d9a22 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -50,133 +50,6 @@ class LLIMSpeakerMgr; class LLPanelActiveSpeakers; class LLPanelChatControlPanel; -class LLVoiceChannel : public LLVoiceClientStatusObserver -{ -public: - typedef enum e_voice_channel_state - { - STATE_NO_CHANNEL_INFO, - STATE_ERROR, - STATE_HUNG_UP, - STATE_READY, - STATE_CALL_STARTED, - STATE_RINGING, - STATE_CONNECTED - } EState; - - LLVoiceChannel(const LLUUID& session_id, const std::string& session_name); - virtual ~LLVoiceChannel(); - - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - - virtual void handleStatusChange(EStatusType status); - virtual void handleError(EStatusType status); - virtual void deactivate(); - virtual void activate(); - virtual void setChannelInfo( - const std::string& uri, - const std::string& credentials); - virtual void getChannelInfo(); - virtual BOOL isActive(); - virtual BOOL callStarted(); - const std::string& getSessionName() const { return mSessionName; } - - const LLUUID getSessionID() { return mSessionID; } - EState getState() { return mState; } - - void updateSessionID(const LLUUID& new_session_id); - const LLSD& getNotifyArgs() { return mNotifyArgs; } - - static LLVoiceChannel* getChannelByID(const LLUUID& session_id); - static LLVoiceChannel* getChannelByURI(std::string uri); - static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; } - static void initClass(); - - static void suspend(); - static void resume(); - -protected: - virtual void setState(EState state); - void toggleCallWindowIfNeeded(EState state); - void setURI(std::string uri); - - std::string mURI; - std::string mCredentials; - LLUUID mSessionID; - EState mState; - std::string mSessionName; - LLSD mNotifyArgs; - BOOL mIgnoreNextSessionLeave; - LLHandle<LLPanel> mLoginNotificationHandle; - - typedef std::map<LLUUID, LLVoiceChannel*> voice_channel_map_t; - static voice_channel_map_t sVoiceChannelMap; - - typedef std::map<std::string, LLVoiceChannel*> voice_channel_map_uri_t; - static voice_channel_map_uri_t sVoiceChannelURIMap; - - static LLVoiceChannel* sCurrentVoiceChannel; - static LLVoiceChannel* sSuspendedVoiceChannel; - static BOOL sSuspended; -}; - -class LLVoiceChannelGroup : public LLVoiceChannel -{ -public: - LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name); - - /*virtual*/ void handleStatusChange(EStatusType status); - /*virtual*/ void handleError(EStatusType status); - /*virtual*/ void activate(); - /*virtual*/ void deactivate(); - /*vritual*/ void setChannelInfo( - const std::string& uri, - const std::string& credentials); - /*virtual*/ void getChannelInfo(); - -protected: - virtual void setState(EState state); - -private: - U32 mRetries; - BOOL mIsRetrying; -}; - -class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoiceChannelProximal> -{ -public: - LLVoiceChannelProximal(); - - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - /*virtual*/ void handleStatusChange(EStatusType status); - /*virtual*/ void handleError(EStatusType status); - /*virtual*/ BOOL isActive(); - /*virtual*/ void activate(); - /*virtual*/ void deactivate(); - -}; - -class LLVoiceChannelP2P : public LLVoiceChannelGroup -{ -public: - LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id); - - /*virtual*/ void handleStatusChange(EStatusType status); - /*virtual*/ void handleError(EStatusType status); - /*virtual*/ void activate(); - /*virtual*/ void getChannelInfo(); - - void setSessionHandle(const std::string& handle, const std::string &inURI); - -protected: - virtual void setState(EState state); - -private: - std::string mSessionHandle; - LLUUID mOtherUserID; - BOOL mReceivedCall; -}; - class LLFloaterIMPanel : public LLFloater { public: @@ -256,15 +129,6 @@ public: void processIMTyping(const LLIMInfo* im_info, BOOL typing); static void chatFromLogFile(LLLogChat::ELogLineType type, std::string line, void* userdata); - //show error statuses to the user - void showSessionStartError(const std::string& error_string); - void showSessionEventError( - const std::string& event_string, - const std::string& error_string); - void showSessionForceClose(const std::string& reason); - - static bool onConfirmForceCloseError(const LLSD& notification, const LLSD& response); - private: // Called by UI methods. void sendMsg(); diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index b631c991ae..8a55ab41b9 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -70,6 +70,7 @@ #include "llviewerwindow.h" #include "llnotify.h" #include "llviewerregion.h" +#include "llvoicechannel.h" #include "lltrans.h" #include "llrecentpeople.h" @@ -144,7 +145,10 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& mInitialTargetIDs(ids), mVoiceChannel(NULL), mSpeakers(NULL), - mSessionInitialized(false) + mSessionInitialized(false), + mCallBackEnabled(true), + mTextIMPossible(true), + mOtherParticipantIsAvatar(true) { if (IM_NOTHING_SPECIAL == type || IM_SESSION_P2P_INVITE == type) { @@ -168,6 +172,13 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& //so we're already initialized mSessionInitialized = true; } + + if (IM_NOTHING_SPECIAL == type) + { + mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionID); + mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID); + mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID); + } } LLIMModel::LLIMSession::~LLIMSession() @@ -272,7 +283,8 @@ void LLIMModel::testMessages() } -bool LLIMModel::newSession(LLUUID session_id, std::string name, EInstantMessage type, LLUUID other_participant_id, const std::vector<LLUUID>& ids) +bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, + const LLUUID& other_participant_id, const std::vector<LLUUID>& ids) { if (is_in_map(sSessionsMap, session_id)) { @@ -289,7 +301,7 @@ bool LLIMModel::newSession(LLUUID session_id, std::string name, EInstantMessage } -bool LLIMModel::clearSession(LLUUID session_id) +bool LLIMModel::clearSession(const LLUUID& session_id) { if (sSessionsMap.find(session_id) == sSessionsMap.end()) return false; delete (sSessionsMap[session_id]); @@ -297,16 +309,13 @@ bool LLIMModel::clearSession(LLUUID session_id) return true; } -//*TODO remake it, instead of returing the list pass it as as parameter (IB) -std::list<LLSD> LLIMModel::getMessages(LLUUID session_id, int start_index) +void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) { - std::list<LLSD> return_list; - LLIMSession* session = findIMSession(session_id); if (!session) { llwarns << "session " << session_id << "does not exist " << llendl; - return return_list; + return; } int i = session->mMsgs.size() - start_index; @@ -317,7 +326,7 @@ std::list<LLSD> LLIMModel::getMessages(LLUUID session_id, int start_index) { LLSD msg; msg = *iter; - return_list.push_back(*iter); + messages.push_back(*iter); i--; } @@ -327,14 +336,9 @@ std::list<LLSD> LLIMModel::getMessages(LLUUID session_id, int start_index) arg["session_id"] = session_id; arg["num_unread"] = 0; mNoUnreadMsgsSignal(arg); - - // TODO: in the future is there a more efficient way to return these - //of course there is - return as parameter (IB) - return return_list; - } -bool LLIMModel::addToHistory(LLUUID session_id, std::string from, LLUUID from_id, std::string utf8_text) { +bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { LLIMSession* session = findIMSession(session_id); @@ -383,8 +387,8 @@ bool LLIMModel::logToFile(const LLUUID& session_id, const std::string& from, con return false; } -//*TODO add const qualifier and pass by references (IB) -bool LLIMModel::addMessage(LLUUID session_id, std::string from, LLUUID from_id, std::string utf8_text, bool log2file /* = true */) { +bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, + const std::string& utf8_text, bool log2file /* = true */) { LLIMSession* session = findIMSession(session_id); if (!session) @@ -506,7 +510,7 @@ void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, gAgent.sendReliableMessage(); } -void LLIMModel::sendLeaveSession(LLUUID session_id, LLUUID other_participant_id) +void LLIMModel::sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id) { if(session_id.notNull()) { @@ -890,20 +894,11 @@ public: { gIMMgr->clearPendingAgentListUpdates(mSessionID); gIMMgr->clearPendingInvitation(mSessionID); - - LLFloaterIMPanel* floaterp = - gIMMgr->findFloaterBySession(mSessionID); - - if ( floaterp ) + if ( 404 == statusNum ) { - if ( 404 == statusNum ) - { - std::string error_string; - error_string = "does not exist"; - - floaterp->showSessionStartError( - error_string); - } + std::string error_string; + error_string = "does not exist"; + gIMMgr->showSessionStartError(error_string, mSessionID); } } } @@ -955,6 +950,106 @@ LLUUID LLIMMgr::computeSessionID( return session_id; } +inline LLFloater* getFloaterBySessionID(const LLUUID session_id) +{ + LLFloater* floater = NULL; + if ( gIMMgr ) + { + floater = dynamic_cast < LLFloater* > + ( gIMMgr->findFloaterBySession(session_id) ); + } + if ( !floater ) + { + floater = dynamic_cast < LLFloater* > + ( LLIMFloater::findInstance(session_id) ); + } + return floater; +} + +void +LLIMMgr::showSessionStartError( + const std::string& error_string, + const LLUUID session_id) +{ + const LLFloater* floater = getFloaterBySessionID (session_id); + if (!floater) return; + + LLSD args; + args["REASON"] = LLTrans::getString(error_string); + args["RECIPIENT"] = floater->getTitle(); + + LLSD payload; + payload["session_id"] = session_id; + + LLNotifications::instance().add( + "ChatterBoxSessionStartError", + args, + payload, + LLIMMgr::onConfirmForceCloseError); +} + +void +LLIMMgr::showSessionEventError( + const std::string& event_string, + const std::string& error_string, + const LLUUID session_id) +{ + const LLFloater* floater = getFloaterBySessionID (session_id); + if (!floater) return; + + LLSD args; + args["REASON"] = + LLTrans::getString(error_string); + args["EVENT"] = + LLTrans::getString(event_string); + args["RECIPIENT"] = floater->getTitle(); + + LLNotifications::instance().add( + "ChatterBoxSessionEventError", + args); +} + +void +LLIMMgr::showSessionForceClose( + const std::string& reason_string, + const LLUUID session_id) +{ + const LLFloater* floater = getFloaterBySessionID (session_id); + if (!floater) return; + + LLSD args; + + args["NAME"] = floater->getTitle(); + args["REASON"] = LLTrans::getString(reason_string); + + LLSD payload; + payload["session_id"] = session_id; + + LLNotifications::instance().add( + "ForceCloseChatterBoxSession", + args, + payload, + LLIMMgr::onConfirmForceCloseError); +} + +//static +bool +LLIMMgr::onConfirmForceCloseError( + const LLSD& notification, + const LLSD& response) +{ + //only 1 option really + LLUUID session_id = notification["payload"]["session_id"]; + + LLFloater* floater = getFloaterBySessionID (session_id); + if ( floater ) + { + floater->closeFloater(FALSE); + } + return false; +} + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLIncomingCallDialog //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1114,29 +1209,6 @@ void LLIncomingCallDialog::processCallResponse(S32 response) } } -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLIMViewFriendObserver -// -// Bridge to suport knowing when the inventory has changed. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLIMViewFriendObserver : public LLFriendObserver -{ -public: - LLIMViewFriendObserver(LLIMMgr* tv) : mTV(tv) {} - virtual ~LLIMViewFriendObserver() {} - virtual void changed(U32 mask) - { - if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE)) - { - mTV->refresh(); - } - } -protected: - LLIMMgr* mTV; -}; - - bool inviteUserResponse(const LLSD& notification, const LLSD& response) { const LLSD& payload = notification["payload"]; @@ -1237,7 +1309,6 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) // LLIMMgr::LLIMMgr() : - mFriendObserver(NULL), mIMReceived(FALSE) { static bool registered_dialog = false; @@ -1246,21 +1317,11 @@ LLIMMgr::LLIMMgr() : LLFloaterReg::add("incoming_call", "floater_incoming_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLIncomingCallDialog>); registered_dialog = true; } - - mFriendObserver = new LLIMViewFriendObserver(this); - LLAvatarTracker::instance().addObserver(mFriendObserver); mPendingInvitations = LLSD::emptyMap(); mPendingAgentListUpdates = LLSD::emptyMap(); } -LLIMMgr::~LLIMMgr() -{ - LLAvatarTracker::instance().removeObserver(mFriendObserver); - delete mFriendObserver; - // Children all cleaned up by default view destructor. -} - // Add a message to a session. void LLIMMgr::addMessage( const LLUUID& session_id, @@ -1299,9 +1360,15 @@ void LLIMMgr::addMessage( fixed_session_name = session_name; } - if (!LLIMModel::getInstance()->findIMSession(new_session_id)) + bool new_session = !hasSession(session_id); + if (new_session) { - LLIMModel::getInstance()->newSession(session_id, fixed_session_name, dialog, other_participant_id); + // *NOTE dzaporozhan + // Workaround for critical bug EXT-1918 + + // *TODO + // Investigate cases when session_id == NULL and find solution to handle those cases + LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id); } floater = findFloaterBySession(new_session_id); @@ -1318,15 +1385,16 @@ void LLIMMgr::addMessage( // create IM window as necessary if(!floater) { - - floater = createFloater( new_session_id, other_participant_id, fixed_session_name, dialog, FALSE); + } + if (new_session) + { // When we get a new IM, and if you are a god, display a bit // of information about the source. This is to help liaisons // when answering questions. @@ -1336,7 +1404,7 @@ void LLIMMgr::addMessage( std::ostringstream bonus_info; bonus_info << LLTrans::getString("***")+ " "+ LLTrans::getString("IMParentEstate") + ":" + " " << parent_estate_id - << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "") + << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "") << ((parent_estate_id == 5) ? "," + LLTrans::getString ("IMTeen") : ""); // once we have web-services (or something) which returns @@ -1439,10 +1507,7 @@ S32 LLIMMgr::getNumberOfUnreadIM() S32 num = 0; for(it = LLIMModel::sSessionsMap.begin(); it != LLIMModel::sSessionsMap.end(); ++it) { - if((*it).first != mBeingRemovedSessionID) - { - num += (*it).second->mNumUnread; - } + num += (*it).second->mNumUnread; } return num; @@ -1458,15 +1523,6 @@ BOOL LLIMMgr::getIMReceived() const return mIMReceived; } -// This method returns TRUE if the local viewer has a session -// currently open keyed to the uuid. -BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid) -{ - LLFloaterIMPanel* floater = findFloaterBySession(uuid); - if(floater) return TRUE; - return FALSE; -} - LLUUID LLIMMgr::addP2PSession(const std::string& name, const LLUUID& other_participant_id, const std::string& voice_session_handle, @@ -1560,41 +1616,25 @@ bool LLIMMgr::leaveSession(const LLUUID& session_id) return true; } -// This removes the panel referenced by the uuid, and then restores -// internal consistency. The internal pointer is not deleted? Did you mean -// a pointer to the corresponding LLIMSession? Session data is cleared now. -// Put a copy of UUID to avoid problem when passed reference becames invalid -// if it has been come from the object removed in observer. -void LLIMMgr::removeSession(LLUUID session_id) +// Removes data associated with a particular session specified by session_id +void LLIMMgr::removeSession(const LLUUID& session_id) { - if (mBeingRemovedSessionID == session_id) - { - return; - } + llassert_always(hasSession(session_id)); + //*TODO remove this floater thing when Communicate Floater is being deleted (IB) LLFloaterIMPanel* floater = findFloaterBySession(session_id); if(floater) { mFloaters.erase(floater->getHandle()); LLFloaterChatterBox::getInstance()->removeFloater(floater); - //mTabContainer->removeTabPanel(floater); - - clearPendingInvitation(session_id); - clearPendingAgentListUpdates(session_id); } - // for some purposes storing ID of a sessios that is being removed - mBeingRemovedSessionID = session_id; - notifyObserverSessionRemoved(session_id); + clearPendingInvitation(session_id); + clearPendingAgentListUpdates(session_id); - //if we don't clear session data on removing the session - //we can't use LLBottomTray as observer of session creation/delettion and - //creating chiclets only on session created even, we need to handle chiclets creation - //the same way as LLFloaterIMPanels were managed. LLIMModel::getInstance()->clearSession(session_id); - // now this session is completely removed - mBeingRemovedSessionID.setNull(); + notifyObserverSessionRemoved(session_id); } void LLIMMgr::inviteToSession( @@ -1723,10 +1763,6 @@ void LLIMMgr::onInviteNameLookup(LLSD payload, const LLUUID& id, const std::stri } } -void LLIMMgr::refresh() -{ -} - void LLIMMgr::disconnectAllSessions() { LLFloaterIMPanel* floater = NULL; @@ -2038,6 +2074,12 @@ void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing) { floater->processIMTyping(im_info, typing); } + + LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + if ( im_floater ) + { + im_floater->processIMTyping(im_info, typing); + } } class LLViewerChatterBoxSessionStartReply : public LLHTTPNode @@ -2087,19 +2129,21 @@ public: } } + LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + if ( im_floater ) + { + if ( body.has("session_info") ) + { + im_floater->processSessionUpdate(body["session_info"]); + } + } + gIMMgr->clearPendingAgentListUpdates(session_id); } else { - //throw an error dialog and close the temp session's - //floater - LLFloaterIMPanel* floater = - gIMMgr->findFloaterBySession(temp_session_id); - - if ( floater ) - { - floater->showSessionStartError(body["error"].asString()); - } + //throw an error dialog and close the temp session's floater + gIMMgr->showSessionStartError(body["error"].asString(), temp_session_id); } gIMMgr->clearPendingAgentListUpdates(session_id); @@ -2132,15 +2176,10 @@ public: if ( !success ) { //throw an error dialog - LLFloaterIMPanel* floater = - gIMMgr->findFloaterBySession(session_id); - - if (floater) - { - floater->showSessionEventError( - body["event"].asString(), - body["error"].asString()); - } + gIMMgr->showSessionEventError( + body["event"].asString(), + body["error"].asString(), + session_id); } } }; @@ -2158,13 +2197,7 @@ public: session_id = input["body"]["session_id"].asUUID(); reason = input["body"]["reason"].asString(); - LLFloaterIMPanel* floater = - gIMMgr ->findFloaterBySession(session_id); - - if ( floater ) - { - floater->showSessionForceClose(reason); - } + gIMMgr->showSessionForceClose(reason, session_id); } }; @@ -2202,11 +2235,17 @@ public: const LLSD& context, const LLSD& input) const { - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID()); + LLUUID session_id = input["body"]["session_id"].asUUID(); + LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); if (floaterp) { floaterp->processSessionUpdate(input["body"]["info"]); } + LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + if ( im_floater ) + { + im_floater->processSessionUpdate(input["body"]["info"]); + } } }; diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 68beb29034..ae8fd355ea 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -34,7 +34,7 @@ #define LL_LLIMVIEW_H #include "lldarray.h" -#include "llfloateractivespeakers.h" //for LLIMSpeakerMgr +#include "llspeakers.h" //for LLIMSpeakerMgr #include "llimpanel.h" //for voice channels #include "llmodaldialog.h" #include "llinstantmessage.h" @@ -70,6 +70,13 @@ public: LLIMSpeakerMgr* mSpeakers; bool mSessionInitialized; + + //true if calling back the session URI after the session has closed is possible. + //Currently this will be false only for PSTN P2P calls. + bool mCallBackEnabled; + + bool mTextIMPossible; + bool mOtherParticipantIsAvatar; }; @@ -104,18 +111,35 @@ public: boost::signals2::connection addNewMsgCallback( session_callback_t cb ) { return mNewMsgSignal.connect(cb); } boost::signals2::connection addNoUnreadMsgsCallback( session_callback_t cb ) { return mNoUnreadMsgsSignal.connect(cb); } - bool newSession(LLUUID session_id, std::string name, EInstantMessage type, LLUUID other_participant_id, + /** + * Create new session object in a model + */ + bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids = std::vector<LLUUID>()); - bool clearSession(LLUUID session_id); - std::list<LLSD> getMessages(LLUUID session_id, int start_index = 0); - bool addMessage(LLUUID session_id, std::string from, LLUUID other_participant_id, std::string utf8_text, bool log2file = true); - bool addToHistory(LLUUID session_id, std::string from, LLUUID from_id, std::string utf8_text); + /** + * Remove all session data associated with a session specified by session_id + */ + bool clearSession(const LLUUID& session_id); - bool logToFile(const LLUUID& session_id, const std::string& from, const std::string& utf8_text); + /** + * Populate supplied std::list with messages starting from index specified by start_index + */ + void getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); - //used to get the name of the session, for use as the title - //currently just the other avatar name + /** + * Add a message to an IM Model - the message is saved in a message store associated with a session specified by session_id + * and also saved into a file if log2file is specified. + * It sends new message signal for each added message. + */ + bool addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& other_participant_id, const std::string& utf8_text, bool log2file = true); + + /** + * Get a session's name. + * For a P2P chat - it's an avatar's name, + * For a group chat - it's a group's name + * For an ad-hoc chat - is received from the server and is in a from of "<Avatar's name> conference" + */ const std::string& getName(const LLUUID& session_id) const; /** @@ -150,7 +174,7 @@ public: */ LLIMSpeakerMgr* getSpeakerManager(const LLUUID& session_id) const; - static void sendLeaveSession(LLUUID session_id, LLUUID other_participant_id); + static void sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id); static bool sendStartSession(const LLUUID& temp_session_id, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids, EInstantMessage dialog); static void sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing); @@ -158,6 +182,19 @@ public: const LLUUID& other_participant_id, EInstantMessage dialog); void testMessages(); + +private: + + /** + * Add message to a list of message associated with session specified by session_id + */ + bool addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text); + + /** + * Save an IM message into a file + */ + //*TODO should also save uuid of a sender + bool logToFile(const LLUUID& session_id, const std::string& from, const std::string& utf8_text); }; class LLIMSessionObserver @@ -183,7 +220,7 @@ public: }; LLIMMgr(); - virtual ~LLIMMgr(); + virtual ~LLIMMgr() {}; // Add a message to a session. The session can keyed to sesion id // or agent id. @@ -200,11 +237,6 @@ public: void addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args); - // This method returns TRUE if the local viewer has a session - // currently open keyed to the uuid. The uuid can be keyed by - // either session id or agent id. - BOOL isIMSessionOpen(const LLUUID& uuid); - // This adds a session to the talk view. The name is the local // name of the session, dialog specifies the type of // session. Since sessions can be keyed off of first recipient or @@ -250,9 +282,6 @@ public: void processIMTypingStart(const LLIMInfo* im_info); void processIMTypingStop(const LLIMInfo* im_info); - // Rebuild stuff - void refresh(); - void notifyNewIM(); void clearNewIMNotification(); @@ -268,10 +297,6 @@ public: // good connection. void disconnectAllSessions(); - // This is a helper function to determine what kind of im session - // should be used for the given agent. - static EInstantMessage defaultIMTypeForAgent(const LLUUID& agent_id); - BOOL hasSession(const LLUUID& session_id); // This method returns the im panel corresponding to the uuid @@ -290,11 +315,18 @@ public: void clearPendingAgentListUpdates(const LLUUID& session_id); //HACK: need a better way of enumerating existing session, or listening to session create/destroy events + //@deprecated, is used only by LLToolBox, which is not used anywhere, right? (IB) const std::set<LLHandle<LLFloater> >& getIMFloaterHandles() { return mFloaters; } void addSessionObserver(LLIMSessionObserver *); void removeSessionObserver(LLIMSessionObserver *); + //show error statuses to the user + void showSessionStartError(const std::string& error_string, const LLUUID session_id); + void showSessionEventError(const std::string& event_string, const std::string& error_string, const LLUUID session_id); + void showSessionForceClose(const std::string& reason, const LLUUID session_id); + static bool onConfirmForceCloseError(const LLSD& notification, const LLSD& response); + /** * Start call in a session * @return false if voice channel doesn't exist @@ -308,10 +340,11 @@ public: bool endCall(const LLUUID& session_id); private: - // This removes the panel referenced by the uuid, and then - // restores internal consistency. The internal pointer is not - // deleted. - void removeSession(LLUUID session_id); + + /** + * Remove data associated with a particular session specified by session_id + */ + void removeSession(const LLUUID& session_id); // create a panel and update internal representation for // consistency. Returns the pointer, caller (the class instance @@ -340,8 +373,9 @@ private: void notifyObserverSessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); private: + + //*TODO should be deleted when Communicate Floater is being deleted std::set<LLHandle<LLFloater> > mFloaters; - LLFriendObserver* mFriendObserver; typedef std::list <LLIMSessionObserver *> session_observers_list_t; session_observers_list_t mSessionObservers; @@ -351,9 +385,6 @@ private: LLSD mPendingInvitations; LLSD mPendingAgentListUpdates; - // ID of a session that is being removed: observers are already told - // that this session is being removed, but it is still present in the sessions' map - LLUUID mBeingRemovedSessionID; }; class LLIncomingCallDialog : public LLModalDialog diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp index 29cca14a7b..81544904e3 100644 --- a/indra/newview/llinspectobject.cpp +++ b/indra/newview/llinspectobject.cpp @@ -106,12 +106,14 @@ private: private: LLUUID mObjectID; + S32 mObjectFace; LLSafeHandle<LLObjectSelection> mObjectSelection; }; LLInspectObject::LLInspectObject(const LLSD& sd) : LLInspect( LLSD() ), // single_instance, doesn't really need key mObjectID(), // set in onOpen() + mObjectFace(0), mObjectSelection() { // can't make the properties request until the widgets are constructed @@ -182,7 +184,11 @@ void LLInspectObject::onOpen(const LLSD& data) // Extract appropriate avatar id mObjectID = data["object_id"]; - + + if(data.has("object_face")) + { + mObjectFace = data["object_face"]; + } // Position the inspector relative to the mouse cursor // Similar to how tooltips are positioned // See LLToolTipMgr::createToolTip diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp index b36b7cf50e..091346d3b4 100644 --- a/indra/newview/lllandmarkactions.cpp +++ b/indra/newview/lllandmarkactions.cpp @@ -348,7 +348,7 @@ LLLandmark* LLLandmarkActions::getLandmark(const LLUUID& landmarkInventoryItemID { LLViewerInventoryItem* item = gInventory.getItem(landmarkInventoryItemID); if (NULL == item) - return false; + return NULL; const LLUUID& asset_id = item->getAssetUUID(); return gLandmarkList.getAsset(asset_id, NULL); diff --git a/indra/newview/lllandmarkactions.h b/indra/newview/lllandmarkactions.h index d651259790..32f05e702b 100644 --- a/indra/newview/lllandmarkactions.h +++ b/indra/newview/lllandmarkactions.h @@ -108,7 +108,7 @@ public: /** * @brief Retrieve a landmark from gLandmarkList by inventory item's id * - * @return pointer to loaded landmark from gLandmarkList or NULL if landmark does not exist. + * @return pointer to loaded landmark from gLandmarkList or NULL if landmark does not exist or wasn't loaded. */ static LLLandmark* getLandmark(const LLUUID& landmarkInventoryItemID); diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp index 3802d13f8b..c32ef2f22b 100644 --- a/indra/newview/llnavigationbar.cpp +++ b/indra/newview/llnavigationbar.cpp @@ -261,6 +261,15 @@ void LLNavigationBar::draw() onTeleportHistoryChanged(); mPurgeTPHistoryItems = false; } + + if (isBackgroundVisible()) + { + static LLUICachedControl<S32> drop_shadow_floater ("DropShadowFloater", 0); + static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow"); + gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, + color_drop_shadow, drop_shadow_floater ); + } + LLPanel::draw(); } diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 148f72703c..12638ab855 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -170,22 +170,6 @@ LLColor4 nearbychat_get_text_color(const LLChat& chat) return text_color; } -std::string formatCurrentTime() -{ - time_t utc_time; - utc_time = time_corrected(); - std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"] "; - - LLSD substitution; - - substitution["datetime"] = (S32) utc_time; - LLStringUtil::format (timeStr, substitution); - - return timeStr; -} - - void LLNearbyChat::add_timestamped_line(const LLChat& chat, const LLColor4& color) { S32 font_size = gSavedSettings.getS32("ChatFontSize"); @@ -210,16 +194,14 @@ void LLNearbyChat::add_timestamped_line(const LLChat& chat, const LLColor4& colo style_params.font(fontp); LLUUID uuid = chat.mFromID; std::string from = chat.mFromName; - std::string time = formatCurrentTime(); std::string message = chat.mText; - mChatHistory->appendWidgetMessage(uuid, from, time, message, style_params); + mChatHistory->appendWidgetMessage(chat, style_params); } void LLNearbyChat::addMessage(const LLChat& chat) { LLColor4 color = nearbychat_get_text_color(chat); - if (chat.mChatType == CHAT_TYPE_DEBUG_MSG) { if(gSavedSettings.getBOOL("ShowScriptErrors") == FALSE) diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index 5fa97926a5..217007fb15 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -45,6 +45,7 @@ #include "llviewerstats.h" #include "llcommandhandler.h" #include "llviewercontrol.h" +#include "llnavigationbar.h" S32 LLNearbyChatBar::sLastSpecialChatChannel = 0; @@ -177,6 +178,31 @@ void LLGestureComboBox::draw() LLComboBox::draw(); } +//virtual +void LLGestureComboBox::showList() +{ + LLComboBox::showList(); + + // Calculating amount of space between the navigation bar and gestures combo + LLNavigationBar* nb = LLNavigationBar::getInstance(); + S32 x, nb_bottom; + nb->localPointToScreen(0, 0, &x, &nb_bottom); + + S32 list_bottom; + mList->localPointToScreen(0, 0, &x, &list_bottom); + + S32 max_height = nb_bottom - list_bottom; + + LLRect rect = mList->getRect(); + // List overlapped navigation bar, downsize it + if (rect.getHeight() > max_height) + { + rect.setOriginAndSize(rect.mLeft, rect.mBottom, rect.getWidth(), max_height); + mList->setRect(rect); + mList->reshape(rect.getWidth(), rect.getHeight()); + } +} + LLNearbyChatBar::LLNearbyChatBar() : LLPanel() , mChatBox(NULL) diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index b902ff86cc..06204e6367 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -40,7 +40,7 @@ #include "llchiclet.h" #include "llvoiceclient.h" #include "lloutputmonitorctrl.h" -#include "llfloateractivespeakers.h" +#include "llspeakers.h" class LLGestureComboBox : public LLComboBox @@ -62,6 +62,9 @@ public: virtual void changed() { refreshGestures(); } protected: + + virtual void showList(); + LLFrameTimer mGestureLabelTimer; std::vector<LLMultiGesture*> mGestures; std::string mLabel; diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index 7239f49b7f..543198c1d2 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -86,6 +86,20 @@ bool LLTipHandler::processNotification(const LLSD& notify) if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") { + // archive message in nearby chat + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); + if(nearby_chat) + { + LLChat chat_msg(notification->getMessage()); + nearby_chat->addMessage(chat_msg); + + // don't show toast if Nearby Chat is opened + if (nearby_chat->getVisible()) + { + return true; + } + } + LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); LLToast::Params p; @@ -99,14 +113,6 @@ bool LLTipHandler::processNotification(const LLSD& notify) LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel); if(channel) channel->addToast(p); - - // archive message in nearby chat - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); - if(nearby_chat) - { - LLChat chat_msg(notification->getMessage()); - nearby_chat->addMessage(chat_msg); - } } else if (notify["sigtype"].asString() == "delete") { diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 6eed956eb8..f9eeaf1e9e 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -34,6 +34,7 @@ #include "llpanelimcontrolpanel.h" +#include "llagent.h" #include "llavataractions.h" #include "llavatariconctrl.h" #include "llbutton.h" @@ -41,6 +42,53 @@ #include "llavatarlist.h" #include "llparticipantlist.h" #include "llimview.h" +#include "llvoicechannel.h" + +void LLPanelChatControlPanel::onCallButtonClicked() +{ + gIMMgr->startCall(mSessionId); +} + +void LLPanelChatControlPanel::onEndCallButtonClicked() +{ + gIMMgr->endCall(mSessionId); +} + +BOOL LLPanelChatControlPanel::postBuild() +{ + childSetAction("call_btn", boost::bind(&LLPanelChatControlPanel::onCallButtonClicked, this)); + childSetAction("end_call_btn", boost::bind(&LLPanelChatControlPanel::onEndCallButtonClicked, this)); + + return TRUE; +} + +void LLPanelChatControlPanel::draw() +{ + // hide/show start call and end call buttons + bool voice_enabled = LLVoiceClient::voiceEnabled(); + + LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId); + if (!session) return; + + LLVoiceChannel* voice_channel = session->mVoiceChannel; + if (voice_channel && voice_enabled) + { + childSetVisible("end_call_btn", voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); + childSetVisible("call_btn", voice_channel->getState() < LLVoiceChannel::STATE_CALL_STARTED); + } + + bool session_initialized = session->mSessionInitialized; + bool callback_enabled = session->mCallBackEnabled; + LLViewerRegion* region = gAgent.getRegion(); + + BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "") + && session_initialized + && voice_enabled + && callback_enabled; + childSetEnabled("call_btn", enable_connect); + + LLPanel::draw(); +} LLPanelIMControlPanel::LLPanelIMControlPanel() { @@ -54,11 +102,11 @@ BOOL LLPanelIMControlPanel::postBuild() { childSetAction("view_profile_btn", boost::bind(&LLPanelIMControlPanel::onViewProfileButtonClicked, this)); childSetAction("add_friend_btn", boost::bind(&LLPanelIMControlPanel::onAddFriendButtonClicked, this)); - childSetAction("call_btn", boost::bind(&LLPanelIMControlPanel::onCallButtonClicked, this)); + childSetAction("share_btn", boost::bind(&LLPanelIMControlPanel::onShareButtonClicked, this)); childSetEnabled("add_friend_btn", !LLAvatarActions::isFriend(getChild<LLAvatarIconCtrl>("avatar_icon")->getAvatarId())); - - return TRUE; + + return LLPanelChatControlPanel::postBuild(); } void LLPanelIMControlPanel::onViewProfileButtonClicked() @@ -73,22 +121,29 @@ void LLPanelIMControlPanel::onAddFriendButtonClicked() LLAvatarActions::requestFriendshipDialog(avatar_icon->getAvatarId(), full_name); } -void LLPanelIMControlPanel::onCallButtonClicked() -{ - // *TODO: Implement -} - void LLPanelIMControlPanel::onShareButtonClicked() { // *TODO: Implement } -void LLPanelIMControlPanel::setID(const LLUUID& avatar_id) +void LLPanelIMControlPanel::setSessionId(const LLUUID& session_id) { + LLPanelChatControlPanel::setSessionId(session_id); + + LLIMModel& im_model = LLIMModel::instance(); + + LLUUID avatar_id = im_model.getOtherParticipantID(session_id); + // Disable "Add friend" button for friends. childSetEnabled("add_friend_btn", !LLAvatarActions::isFriend(avatar_id)); getChild<LLAvatarIconCtrl>("avatar_icon")->setValue(avatar_id); + + // Disable profile button if participant is not realy SL avatar + LLIMModel::LLIMSession* im_session = + im_model.findIMSession(session_id); + if( im_session && !im_session->mOtherParticipantIsAvatar ) + childSetEnabled("view_profile_btn", FALSE); } @@ -100,12 +155,11 @@ LLPanelGroupControlPanel::LLPanelGroupControlPanel(const LLUUID& session_id) BOOL LLPanelGroupControlPanel::postBuild() { childSetAction("group_info_btn", boost::bind(&LLPanelGroupControlPanel::onGroupInfoButtonClicked, this)); - childSetAction("call_btn", boost::bind(&LLPanelGroupControlPanel::onCallButtonClicked, this)); mAvatarList = getChild<LLAvatarList>("speakers_list"); mParticipantList = new LLParticipantList(mSpeakerManager, mAvatarList); - return TRUE; + return LLPanelChatControlPanel::postBuild(); } LLPanelGroupControlPanel::~LLPanelGroupControlPanel() @@ -127,13 +181,23 @@ void LLPanelGroupControlPanel::onGroupInfoButtonClicked() } -void LLPanelGroupControlPanel::onCallButtonClicked() +void LLPanelGroupControlPanel::setSessionId(const LLUUID& session_id) { - // *TODO: Implement + LLPanelChatControlPanel::setSessionId(session_id); + + mGroupID = LLIMModel::getInstance()->getOtherParticipantID(session_id); } -void LLPanelGroupControlPanel::setID(const LLUUID& id) +LLPanelAdHocControlPanel::LLPanelAdHocControlPanel(const LLUUID& session_id):LLPanelGroupControlPanel(session_id) +{ +} + +BOOL LLPanelAdHocControlPanel::postBuild() { - mGroupID = id; + mAvatarList = getChild<LLAvatarList>("speakers_list"); + mParticipantList = new LLParticipantList(mSpeakerManager, mAvatarList); + + return LLPanelChatControlPanel::postBuild(); } + diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index 138b1630c4..220b7b14ba 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -45,8 +45,16 @@ public: LLPanelChatControlPanel() {}; ~LLPanelChatControlPanel() {}; - // sets the group or avatar UUID - virtual void setID(const LLUUID& avatar_id)= 0; + virtual BOOL postBuild(); + virtual void draw(); + + void onCallButtonClicked(); + void onEndCallButtonClicked(); + + virtual void setSessionId(const LLUUID& session_id) { mSessionId = session_id; } + +private: + LLUUID mSessionId; }; @@ -58,13 +66,14 @@ public: BOOL postBuild(); - void setID(const LLUUID& avatar_id); + void setSessionId(const LLUUID& session_id); private: void onViewProfileButtonClicked(); void onAddFriendButtonClicked(); - void onCallButtonClicked(); void onShareButtonClicked(); + + LLUUID mAvatarID; }; @@ -76,19 +85,26 @@ public: BOOL postBuild(); - void setID(const LLUUID& id); + void setSessionId(const LLUUID& session_id); /*virtual*/ void draw(); -private: - void onGroupInfoButtonClicked(); - void onCallButtonClicked(); - +protected: LLUUID mGroupID; LLSpeakerMgr* mSpeakerManager; LLAvatarList* mAvatarList; LLParticipantList* mParticipantList; + +private: + void onGroupInfoButtonClicked(); }; +class LLPanelAdHocControlPanel : public LLPanelGroupControlPanel +{ +public: + LLPanelAdHocControlPanel(const LLUUID& session_id); + + BOOL postBuild(); +}; #endif // LL_LLPANELIMCONTROLPANEL_H diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 83fb147a49..3d0db71045 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -38,10 +38,12 @@ #include "llsdutil.h" #include "llsdutil_math.h" +#include "llaccordionctrl.h" #include "llaccordionctrltab.h" #include "llagent.h" #include "llagentpicksinfo.h" #include "llagentui.h" +#include "llcallbacklist.h" #include "lldndbutton.h" #include "llfloaterworldmap.h" #include "llfolderviewitem.h" @@ -56,7 +58,7 @@ //static LLRegisterPanelClassWrapper<LLLandmarksPanel> t_landmarks("panel_landmarks"); static const std::string OPTIONS_BUTTON_NAME = "options_gear_btn"; -static const std::string ADD_LANDMARK_BUTTON_NAME = "add_landmark_btn"; +static const std::string ADD_BUTTON_NAME = "add_btn"; static const std::string ADD_FOLDER_BUTTON_NAME = "add_folder_btn"; static const std::string TRASH_BUTTON_NAME = "trash_btn"; @@ -75,6 +77,7 @@ LLLandmarksPanel::LLLandmarksPanel() , mListCommands(NULL) , mGearFolderMenu(NULL) , mGearLandmarkMenu(NULL) + , mDirtyFilter(false) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_landmarks.xml"); } @@ -98,16 +101,37 @@ BOOL LLLandmarksPanel::postBuild() initMyInventroyPanel(); initLibraryInventroyPanel(); + gIdleCallbacks.addFunction(LLLandmarksPanel::doIdle, this); return TRUE; } // virtual void LLLandmarksPanel::onSearchEdit(const std::string& string) { + static std::string prev_string(""); + + if (prev_string == string) return; + + // show all folders in Landmarks Accordion for empty filter + mLandmarksInventoryPanel->setShowFolderState(string.empty() ? + LLInventoryFilter::SHOW_ALL_FOLDERS : + LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS + ); + filter_list(mFavoritesInventoryPanel, string); filter_list(mLandmarksInventoryPanel, string); filter_list(mMyInventoryPanel, string); filter_list(mLibraryInventoryPanel, string); + + prev_string = string; + mDirtyFilter = true; + + // give FolderView a chance to be refreshed. So, made all accordions visible + for (accordion_tabs_t::const_iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter) + { + LLAccordionCtrlTab* tab = *iter; + tab->setVisible(true); + } } // virtual @@ -300,6 +324,8 @@ void LLLandmarksPanel::processParcelInfo(const LLParcelData& parcel_data) panel_pick, panel_places,params)); panel_pick->setSaveCallback(boost::bind(&LLLandmarksPanel::onPickPanelExit,this, panel_pick, panel_places,params)); + panel_pick->setCancelCallback(boost::bind(&LLLandmarksPanel::onPickPanelExit,this, + panel_pick, panel_places,params)); } } } @@ -389,6 +415,7 @@ void LLLandmarksPanel::initLandmarksPanel(LLInventorySubTreePanel* inventory_lis void LLLandmarksPanel::initAccordion(const std::string& accordion_tab_name, LLInventorySubTreePanel* inventory_list) { LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>(accordion_tab_name); + mAccordionTabs.push_back(accordion_tab); accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLLandmarksPanel::onAccordionExpandedCollapsed, this, _2, inventory_list)); } @@ -433,11 +460,11 @@ void LLLandmarksPanel::initListCommandsHandlers() mListCommands = getChild<LLPanel>("bottom_panel"); mListCommands->childSetAction(OPTIONS_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onActionsButtonClick, this)); - mListCommands->childSetAction(ADD_LANDMARK_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onAddLandmarkButtonClick, this)); - mListCommands->childSetAction(ADD_FOLDER_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onAddFolderButtonClick, this)); mListCommands->childSetAction(TRASH_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onTrashButtonClick, this)); + mListCommands->getChild<LLButton>(ADD_BUTTON_NAME)->setHeldDownCallback(boost::bind(&LLLandmarksPanel::onAddButtonHeldDown, this)); + static const LLSD add_landmark_command("add_landmark"); + mListCommands->childSetAction(ADD_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onAddAction, this, add_landmark_command)); - LLDragAndDropButton* trash_btn = mListCommands->getChild<LLDragAndDropButton>(TRASH_BUTTON_NAME); trash_btn->setDragAndDropHandler(boost::bind(&LLLandmarksPanel::handleDragAndDropToTrash, this , _4 // BOOL drop @@ -453,6 +480,7 @@ void LLLandmarksPanel::initListCommandsHandlers() mEnableCallbackRegistrar.add("Places.LandmarksGear.Enable", boost::bind(&LLLandmarksPanel::isActionEnabled, this, _2)); mGearLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_places_gear_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); mGearFolderMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_places_gear_folder.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mMenuAdd = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_place_add_button.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); } @@ -488,52 +516,26 @@ void LLLandmarksPanel::onActionsButtonClick() mGearFolderMenu->getChild<LLMenuItemCallGL>("collapse")->setVisible(cur_item->isOpen()); menu = mGearFolderMenu; } - if(menu) - { - menu->buildDrawLabels(); - menu->updateParent(LLMenuGL::sMenuContainer); - LLView* actions_btn = getChild<LLView>(OPTIONS_BUTTON_NAME); - S32 menu_x, menu_y; - actions_btn->localPointToOtherView(0,actions_btn->getRect().getHeight(),&menu_x,&menu_y, this); - menu_y += menu->getRect().getHeight(); - LLMenuGL::showPopup(this, menu, menu_x,menu_y); - } + showActionMenu(menu,OPTIONS_BUTTON_NAME); } -void LLLandmarksPanel::onAddLandmarkButtonClick() const +void LLLandmarksPanel::onAddButtonHeldDown() { - if(LLLandmarkActions::landmarkAlreadyExists()) - { - std::string location; - LLAgentUI::buildLocationString(location, LLAgentUI::LOCATION_FORMAT_FULL); - llwarns<<" Landmark already exists at location: "<< location<<llendl; - return; - } - LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); + showActionMenu(mMenuAdd,ADD_BUTTON_NAME); } -void LLLandmarksPanel::onAddFolderButtonClick() const +void LLLandmarksPanel::showActionMenu(LLMenuGL* menu, std::string spawning_view_name) { - LLFolderViewItem* item = getCurSelectedItem(); - if(item && mCurrentSelectedList == mLandmarksInventoryPanel) + if (menu) { - LLFolderViewEventListener* folder_bridge = NULL; - if(item-> getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) - { - // for a landmark get parent folder bridge - folder_bridge = item->getParentFolder()->getListener(); - } - else if (item-> getListener()->getInventoryType() == LLInventoryType::IT_CATEGORY) - { - // for a folder get its own bridge - folder_bridge = item->getListener(); - } - - menu_create_inventory_item(mCurrentSelectedList->getRootFolder() - , dynamic_cast<LLFolderBridge*>(folder_bridge) - , LLSD("category") - , gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK) - ); + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + LLView* spawning_view = getChild<LLView> (spawning_view_name); + S32 menu_x, menu_y; + //show menu in co-ordinates of panel + spawning_view->localPointToOtherView(0, spawning_view->getRect().getHeight(), &menu_x, &menu_y, this); + menu_y += menu->getRect().getHeight(); + LLMenuGL::showPopup(this, menu, menu_x, menu_y); } } @@ -547,11 +549,39 @@ void LLLandmarksPanel::onAddAction(const LLSD& userdata) const std::string command_name = userdata.asString(); if("add_landmark" == command_name) { - onAddLandmarkButtonClick(); + if(LLLandmarkActions::landmarkAlreadyExists()) + { + std::string location; + LLAgentUI::buildLocationString(location, LLAgentUI::LOCATION_FORMAT_FULL); + llwarns<<" Landmark already exists at location: "<< location<<llendl; + return; + } + LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); } else if ("category" == command_name) { - onAddFolderButtonClick(); + LLFolderViewItem* item = getCurSelectedItem(); + if (item && mCurrentSelectedList == mLandmarksInventoryPanel) + { + LLFolderViewEventListener* folder_bridge = NULL; + if (item-> getListener()->getInventoryType() + == LLInventoryType::IT_LANDMARK) + { + // for a landmark get parent folder bridge + folder_bridge = item->getParentFolder()->getListener(); + } + else if (item-> getListener()->getInventoryType() + == LLInventoryType::IT_CATEGORY) + { + // for a folder get its own bridge + folder_bridge = item->getListener(); + } + + menu_create_inventory_item(mCurrentSelectedList->getRootFolder(), + dynamic_cast<LLFolderBridge*> (folder_bridge), LLSD( + "category"), gInventory.findCategoryUUIDForType( + LLAssetType::AT_LANDMARK)); + } } } @@ -854,6 +884,43 @@ bool LLLandmarksPanel::handleDragAndDropToTrash(BOOL drop, EDragAndDropType carg } +void LLLandmarksPanel::doIdle(void* landmarks_panel) +{ + LLLandmarksPanel* panel = (LLLandmarksPanel* ) landmarks_panel; + + if (panel->mDirtyFilter) + { + panel->updateFilteredAccordions(); + } + +} + +void LLLandmarksPanel::updateFilteredAccordions() +{ + LLInventoryPanel* inventory_list = NULL; + LLAccordionCtrlTab* accordion_tab = NULL; + for (accordion_tabs_t::const_iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter) + { + accordion_tab = *iter; + inventory_list = dynamic_cast<LLInventorySubTreePanel*> (accordion_tab->getAccordionView()); + if (NULL == inventory_list) continue; + LLFolderView* fv = inventory_list->getRootFolder(); + + bool has_visible_children = fv->hasVisibleChildren(); + + accordion_tab->setVisible(has_visible_children); + } + + // we have to arrange accordion tabs for cases when filter string is less restrictive but + // all items are still filtered. + static LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("landmarks_accordion"); + accordion->arrange(); + + // now filter state is applied to accordion tabs + mDirtyFilter = false; +} + + ////////////////////////////////////////////////////////////////////////// // HELPER FUNCTIONS ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index 47c9f7647c..0e7abb4865 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -41,6 +41,7 @@ #include "llpanelpick.h" #include "llremoteparcelrequest.h" +class LLAccordionCtrlTab; class LLFolderViewItem; class LLMenuGL; class LLInventoryPanel; @@ -90,8 +91,8 @@ private: void initListCommandsHandlers(); void updateListCommands(); void onActionsButtonClick(); - void onAddLandmarkButtonClick() const; - void onAddFolderButtonClick() const; + void showActionMenu(LLMenuGL* menu, std::string spawning_view_name); + void onAddButtonHeldDown(); void onTrashButtonClick() const; void onAddAction(const LLSD& command_name) const; void onClipboardAction(const LLSD& command_name) const; @@ -114,6 +115,18 @@ private: */ bool handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept); + /** + * Static callback for gIdleCallbacks to perform actions out of drawing + */ + static void doIdle(void* landmarks_panel); + + /** + * Updates accordions according to filtered items in lists. + * + * It hides accordion for empty lists + */ + void updateFilteredAccordions(); + private: LLInventorySubTreePanel* mFavoritesInventoryPanel; LLInventorySubTreePanel* mLandmarksInventoryPanel; @@ -121,10 +134,15 @@ private: LLInventorySubTreePanel* mLibraryInventoryPanel; LLMenuGL* mGearLandmarkMenu; LLMenuGL* mGearFolderMenu; + LLMenuGL* mMenuAdd; LLInventorySubTreePanel* mCurrentSelectedList; LLPanel* mListCommands; bool mSortByDate; + bool mDirtyFilter; + + typedef std::vector<LLAccordionCtrlTab*> accordion_tabs_t; + accordion_tabs_t mAccordionTabs; }; #endif //LL_LLPANELLANDMARKS_H diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 61d66873ea..4580eeb336 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -413,13 +413,17 @@ BOOL LLPanelPeople::postBuild() mOnlineFriendList = getChild<LLPanel>(FRIENDS_TAB_NAME)->getChild<LLAvatarList>("avatars_online"); mAllFriendList = getChild<LLPanel>(FRIENDS_TAB_NAME)->getChild<LLAvatarList>("avatars_all"); mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online")); + mOnlineFriendList->setShowIcons("FriendsListShowIcons"); mAllFriendList->setNoItemsCommentText(getString("no_friends")); + mAllFriendList->setShowIcons("FriendsListShowIcons"); mNearbyList = getChild<LLPanel>(NEARBY_TAB_NAME)->getChild<LLAvatarList>("avatar_list"); mNearbyList->setNoItemsCommentText(getString("no_one_near")); + mNearbyList->setShowIcons("NearbyListShowIcons"); mRecentList = getChild<LLPanel>(RECENT_TAB_NAME)->getChild<LLAvatarList>("avatar_list"); mRecentList->setNoItemsCommentText(getString("no_people")); + mRecentList->setShowIcons("RecentListShowIcons"); mGroupList = getChild<LLGroupList>("group_list"); mGroupList->setNoItemsCommentText(getString("no_groups")); @@ -611,6 +615,10 @@ void LLPanelPeople::updateButtons() bool recent_tab_active = (cur_tab == RECENT_TAB_NAME); LLUUID selected_id; + std::vector<LLUUID> selected_uuids; + getCurrentItemIDs(selected_uuids); + bool item_selected = (selected_uuids.size() == 1); + buttonSetVisible("group_info_btn", group_tab_active); buttonSetVisible("chat_btn", group_tab_active); buttonSetVisible("add_friend_btn", nearby_tab_active || recent_tab_active); @@ -621,7 +629,6 @@ void LLPanelPeople::updateButtons() if (group_tab_active) { - bool item_selected = mGroupList->getSelectedItem() != NULL; bool cur_group_active = true; if (item_selected) @@ -629,7 +636,7 @@ void LLPanelPeople::updateButtons() selected_id = mGroupList->getSelectedUUID(); cur_group_active = (gAgent.getGroupID() == selected_id); } - + LLPanel* groups_panel = mTabContainer->getCurrentPanel(); groups_panel->childSetEnabled("activate_btn", item_selected && !cur_group_active); // "none" or a non-active group selected groups_panel->childSetEnabled("plus_btn", item_selected); @@ -640,18 +647,18 @@ void LLPanelPeople::updateButtons() bool is_friend = true; // Check whether selected avatar is our friend. - if ((selected_id = getCurrentItemID()).notNull()) + if (item_selected) { + selected_id = selected_uuids.front(); is_friend = LLAvatarTracker::instance().getBuddyInfo(selected_id) != NULL; } childSetEnabled("add_friend_btn", !is_friend); } - bool item_selected = selected_id.notNull(); buttonSetEnabled("teleport_btn", friends_tab_active && item_selected); buttonSetEnabled("view_profile_btn", item_selected); - buttonSetEnabled("im_btn", item_selected); + buttonSetEnabled("im_btn", (selected_uuids.size() >= 1)); // allow starting the friends conference for multiple selection buttonSetEnabled("call_btn", item_selected && false); // not implemented yet buttonSetEnabled("share_btn", item_selected && false); // not implemented yet buttonSetEnabled("group_info_btn", item_selected); @@ -877,7 +884,17 @@ void LLPanelPeople::onAddFriendWizButtonClicked() void LLPanelPeople::onDeleteFriendButtonClicked() { - LLAvatarActions::removeFriendDialog(getCurrentItemID()); + std::vector<LLUUID> selected_uuids; + getCurrentItemIDs(selected_uuids); + + if (selected_uuids.size() == 1) + { + LLAvatarActions::removeFriendDialog( selected_uuids.front() ); + } + else if (selected_uuids.size() > 1) + { + LLAvatarActions::removeFriendsDialog( selected_uuids ); + } } void LLPanelPeople::onGroupInfoButtonClicked() @@ -963,6 +980,8 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) } else if (chosen_item == "view_icons") { + mAllFriendList->toggleIcons(); + mOnlineFriendList->toggleIcons(); } else if (chosen_item == "organize_offline") { @@ -992,6 +1011,7 @@ void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) } else if (chosen_item == "view_icons") { + mNearbyList->toggleIcons(); } else if (chosen_item == "sort_distance") { @@ -1011,7 +1031,7 @@ void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata) } else if (chosen_item == "view_icons") { - // *TODO: implement showing/hiding icons + mRecentList->toggleIcons(); } } diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index cb9f7184f0..5af27a5ec1 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -55,6 +55,8 @@ #include "llagent.h" #include "llagentui.h" #include "llavatarpropertiesprocessor.h" +#include "llcallbacklist.h" +#include "llexpandabletextbox.h" #include "llfloaterworldmap.h" #include "llfloaterbuycurrency.h" #include "llinventorymodel.h" @@ -65,6 +67,7 @@ #include "llviewerinventory.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" +#include "llviewercontrol.h" #include "llviewertexteditor.h" #include "llworldmap.h" #include "llsdutil_math.h" @@ -76,7 +79,6 @@ typedef std::pair<LLUUID, std::string> folder_pair_t; static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right); -static std::string getFullFolderName(const LLViewerInventoryCategory* cat); static void collectLandmarkFolders(LLInventoryModel::cat_array_t& cats); static LLRegisterPanelClassWrapper<LLPanelPlaceInfo> t_place_info("panel_place_info"); @@ -111,7 +113,7 @@ BOOL LLPanelPlaceInfo::postBuild() mForSalePanel = getChild<LLPanel>("for_sale_panel"); mYouAreHerePanel = getChild<LLPanel>("here_panel"); - LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(boost::bind(&LLPanelPlaceInfo::updateYouAreHereBanner,this)); + gIdleCallbacks.addFunction(&LLPanelPlaceInfo::updateYouAreHereBanner, this); //Icon value should contain sale price of last selected parcel. mForSalePanel->getChild<LLIconCtrl>("icon_for_sale")-> @@ -120,7 +122,7 @@ BOOL LLPanelPlaceInfo::postBuild() mSnapshotCtrl = getChild<LLTextureCtrl>("logo"); mRegionName = getChild<LLTextBox>("region_title"); mParcelName = getChild<LLTextBox>("parcel_title"); - mDescEditor = getChild<LLTextEditor>("description"); + mDescEditor = getChild<LLExpandableTextBox>("description"); mMaturityRatingText = getChild<LLTextBox>("maturity_value"); mParcelOwner = getChild<LLTextBox>("owner_value"); @@ -456,13 +458,13 @@ void LLPanelPlaceInfo::processParcelInfo(const LLParcelData& parcel_data) mSnapshotCtrl->setImageAssetID(parcel_data.snapshot_id); } - if(!parcel_data.name.empty()) + if(!parcel_data.sim_name.empty()) { - mParcelName->setText(parcel_data.name); + mRegionName->setText(parcel_data.sim_name); } else { - mParcelName->setText(LLStringUtil::null); + mRegionName->setText(LLStringUtil::null); } if(!parcel_data.desc.empty()) @@ -509,19 +511,22 @@ void LLPanelPlaceInfo::processParcelInfo(const LLParcelData& parcel_data) region_z = llround(mPosRegion.mV[VZ]); } - std::string name = getString("not_available"); - if (!parcel_data.sim_name.empty()) + if (!parcel_data.name.empty()) { - name = llformat("%s (%d, %d, %d)", - parcel_data.sim_name.c_str(), region_x, region_y, region_z); - mRegionName->setText(name); + mParcelName->setText(llformat("%s (%d, %d, %d)", + parcel_data.name.c_str(), region_x, region_y, region_z)); + } + else + { + mParcelName->setText(getString("not_available")); } if (mInfoType == CREATE_LANDMARK) { if (parcel_data.name.empty()) { - mTitleEditor->setText(name); + mTitleEditor->setText(llformat("%s (%d, %d, %d)", + parcel_data.sim_name.c_str(), region_x, region_y, region_z)); } else { @@ -610,6 +615,9 @@ void LLPanelPlaceInfo::displaySelectedParcelInfo(LLParcel* parcel, parcel_data.name = parcel->getName(); parcel_data.sim_name = region->getName(); parcel_data.snapshot_id = parcel->getSnapshotID(); + mPosRegion.setVec((F32)fmod(pos_global.mdV[VX], (F64)REGION_WIDTH_METERS), + (F32)fmod(pos_global.mdV[VY], (F64)REGION_WIDTH_METERS), + (F32)pos_global.mdV[VZ]); parcel_data.global_x = pos_global.mdV[VX]; parcel_data.global_y = pos_global.mdV[VY]; parcel_data.global_z = pos_global.mdV[VZ]; @@ -986,18 +994,22 @@ void LLPanelPlaceInfo::populateFoldersList() mFolderCombo->add(it->second, LLSD(it->first)); } -void LLPanelPlaceInfo::updateYouAreHereBanner() +//static +void LLPanelPlaceInfo::updateYouAreHereBanner(void* userdata) { //YouAreHere Banner should be displayed only for selected places, // If you want to display it for landmark or teleport history item, you should check by mParcelId - bool is_you_are_here = false; - if (mSelectedParcelID != S32(-1) && !mLastSelectedRegionID.isNull()) - { - is_you_are_here = gAgent.getRegion()->getRegionID()== mLastSelectedRegionID && - mSelectedParcelID == LLViewerParcelMgr::getInstance()->getAgentParcel()->getLocalID(); - } - mYouAreHerePanel->setVisible(is_you_are_here); + LLPanelPlaceInfo* self = static_cast<LLPanelPlaceInfo*>(userdata); + if(!self->getVisible()) + return; + + static F32 radius = gSavedSettings.getF32("YouAreHereDistance"); + + BOOL display_banner = self->mLastSelectedRegionID == gAgent.getRegion()->getRegionID() && + LLAgentUI::checkAgentDistance(self->mPosRegion, radius); + + self->mYouAreHerePanel->setVisible(display_banner); } void LLPanelPlaceInfo::onForSaleBannerClick() @@ -1028,14 +1040,9 @@ void LLPanelPlaceInfo::onForSaleBannerClick() } - - -static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right) -{ - return left.second < right.second; -} -static std::string getFullFolderName(const LLViewerInventoryCategory* cat) +/*static*/ +std::string LLPanelPlaceInfo::getFullFolderName(const LLViewerInventoryCategory* cat) { std::string name = cat->getName(); LLUUID parent_id; @@ -1057,6 +1064,11 @@ static std::string getFullFolderName(const LLViewerInventoryCategory* cat) return name; } +static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right) +{ + return left.second < right.second; +} + static void collectLandmarkFolders(LLInventoryModel::cat_array_t& cats) { LLUUID landmarks_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK); diff --git a/indra/newview/llpanelplaceinfo.h b/indra/newview/llpanelplaceinfo.h index 7b3a8f050b..07a2434d59 100644 --- a/indra/newview/llpanelplaceinfo.h +++ b/indra/newview/llpanelplaceinfo.h @@ -43,6 +43,7 @@ class LLButton; class LLComboBox; +class LLExpandableTextBox; class LLInventoryItem; class LLLineEditor; class LLPanelPickEdit; @@ -52,6 +53,7 @@ class LLTextBox; class LLTextEditor; class LLTextureCtrl; class LLViewerRegion; +class LLViewerInventoryCategory; class LLPanelPlaceInfo : public LLPanel, LLRemoteParcelInfoObserver { @@ -131,11 +133,13 @@ public: /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); /*virtual*/ void handleVisibilityChange (BOOL new_visibility); + + static std::string getFullFolderName(const LLViewerInventoryCategory* cat); private: void populateFoldersList(); - void updateYouAreHereBanner(); + static void updateYouAreHereBanner(void*);// added to gIdleCallbacks void onForSaleBannerClick(); /** @@ -161,7 +165,7 @@ private: LLTextureCtrl* mSnapshotCtrl; LLTextBox* mRegionName; LLTextBox* mParcelName; - LLTextEditor* mDescEditor; + LLExpandableTextBox*mDescEditor; LLTextBox* mMaturityRatingText; LLTextBox* mParcelOwner; LLTextBox* mLastVisited; diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 5ab823b6e5..b2e9110e96 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -351,7 +351,16 @@ void LLPanelPlaces::setItem(LLInventoryItem* item) if (is_landmark_editable) { - mPlaceInfo->setLandmarkFolder(mItem->getParentUUID()); + if(!mPlaceInfo->setLandmarkFolder(mItem->getParentUUID()) && !mItem->getParentUUID().isNull()) + { + const LLViewerInventoryCategory* cat = gInventory.getCategory(mItem->getParentUUID()); + if(cat) + { + std::string cat_fullname = LLPanelPlaceInfo::getFullFolderName(cat); + LLComboBox* folderList = mPlaceInfo->getChild<LLComboBox>("folder_combo"); + folderList->add(cat_fullname, cat->getUUID(),ADD_TOP); + } + } } mPlaceInfo->displayItemInfo(mItem); diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp new file mode 100644 index 0000000000..ca7ebb1ad8 --- /dev/null +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -0,0 +1,1095 @@ +/** + * @file llpanelprimmediacontrols.cpp + * @brief media controls popup panel + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +//LLPanelPrimMediaControls +#include "llagent.h" +#include "llparcel.h" +#include "llpanel.h" +#include "llselectmgr.h" +#include "llmediaentry.h" +#include "llrender.h" +#include "lldrawable.h" +#include "llviewerwindow.h" +#include "lluictrlfactory.h" +#include "llbutton.h" +#include "llface.h" +#include "llcombobox.h" +#include "llslider.h" +#include "llhudview.h" +#include "lliconctrl.h" +#include "lltoolpie.h" +#include "llviewercamera.h" +#include "llviewerobjectlist.h" +#include "llpanelprimmediacontrols.h" +#include "llpluginclassmedia.h" +#include "llprogressbar.h" +#include "llviewercontrol.h" +#include "llviewerparcelmgr.h" +#include "llviewermedia.h" +#include "llviewermediafocus.h" +#include "llvovolume.h" +#include "llweb.h" +#include "llwindow.h" + +glh::matrix4f glh_get_current_modelview(); +glh::matrix4f glh_get_current_projection(); + +const F32 ZOOM_NEAR_PADDING = 1.0f; +const F32 ZOOM_MEDIUM_PADDING = 1.15f; +const F32 ZOOM_FAR_PADDING = 1.5f; + +// Warning: make sure these two match! +const LLPanelPrimMediaControls::EZoomLevel LLPanelPrimMediaControls::kZoomLevels[] = { ZOOM_NONE, ZOOM_MEDIUM }; +const int LLPanelPrimMediaControls::kNumZoomLevels = 2; + +// +// LLPanelPrimMediaControls +// + +LLPanelPrimMediaControls::LLPanelPrimMediaControls() : + mAlpha(1.f), + mCurrentURL(""), + mPreviousURL(""), + mPauseFadeout(false), + mUpdateSlider(true), + mClearFaceOnFade(false), + mCurrentRate(0.0), + mMovieDuration(0.0), + mUpdatePercent(0) +{ + mCommitCallbackRegistrar.add("MediaCtrl.Close", boost::bind(&LLPanelPrimMediaControls::onClickClose, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Back", boost::bind(&LLPanelPrimMediaControls::onClickBack, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Forward", boost::bind(&LLPanelPrimMediaControls::onClickForward, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Home", boost::bind(&LLPanelPrimMediaControls::onClickHome, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Stop", boost::bind(&LLPanelPrimMediaControls::onClickStop, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Reload", boost::bind(&LLPanelPrimMediaControls::onClickReload, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Play", boost::bind(&LLPanelPrimMediaControls::onClickPlay, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Pause", boost::bind(&LLPanelPrimMediaControls::onClickPause, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Open", boost::bind(&LLPanelPrimMediaControls::onClickOpen, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Zoom", boost::bind(&LLPanelPrimMediaControls::onClickZoom, this)); + mCommitCallbackRegistrar.add("MediaCtrl.CommitURL", boost::bind(&LLPanelPrimMediaControls::onCommitURL, this)); + mCommitCallbackRegistrar.add("MediaCtrl.JumpProgress", boost::bind(&LLPanelPrimMediaControls::onCommitSlider, this)); + mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeUp", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeUp, this)); + mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeDown", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeDown, this)); + mCommitCallbackRegistrar.add("MediaCtrl.ToggleMute", boost::bind(&LLPanelPrimMediaControls::onToggleMute, this)); + + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_prim_media_controls.xml"); + mInactivityTimer.reset(); + mFadeTimer.stop(); + mCurrentZoom = ZOOM_NONE; + mScrollState = SCROLL_NONE; + + mPanelHandle.bind(this); +} +LLPanelPrimMediaControls::~LLPanelPrimMediaControls() +{ +} + +BOOL LLPanelPrimMediaControls::postBuild() +{ + LLButton* scroll_up_ctrl = getChild<LLButton>("scrollup"); + scroll_up_ctrl->setClickedCallback(onScrollUp, this); + scroll_up_ctrl->setHeldDownCallback(onScrollUpHeld, this); + scroll_up_ctrl->setMouseUpCallback(onScrollStop, this); + LLButton* scroll_left_ctrl = getChild<LLButton>("scrollleft"); + scroll_left_ctrl->setClickedCallback(onScrollLeft, this); + scroll_left_ctrl->setHeldDownCallback(onScrollLeftHeld, this); + scroll_left_ctrl->setMouseUpCallback(onScrollStop, this); + LLButton* scroll_right_ctrl = getChild<LLButton>("scrollright"); + scroll_right_ctrl->setClickedCallback(onScrollRight, this); + scroll_right_ctrl->setHeldDownCallback(onScrollLeftHeld, this); + scroll_right_ctrl->setMouseUpCallback(onScrollStop, this); + LLButton* scroll_down_ctrl = getChild<LLButton>("scrolldown"); + scroll_down_ctrl->setClickedCallback(onScrollDown, this); + scroll_down_ctrl->setHeldDownCallback(onScrollDownHeld, this); + scroll_down_ctrl->setMouseUpCallback(onScrollStop, this); + + LLUICtrl* media_address = getChild<LLUICtrl>("media_address"); + media_address->setFocusReceivedCallback(boost::bind(&LLPanelPrimMediaControls::onInputURL, _1, this )); + mInactiveTimeout = gSavedSettings.getF32("MediaControlTimeout"); + mControlFadeTime = gSavedSettings.getF32("MediaControlFadeTime"); + + mCurrentZoom = ZOOM_NONE; + // clicks on HUD buttons do not remove keyboard focus from media + setIsChrome(TRUE); + return TRUE; +} + +void LLPanelPrimMediaControls::setMediaFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ + if (media_impl.notNull() && objectp.notNull()) + { + mTargetImplID = media_impl->getMediaTextureID(); + mTargetObjectID = objectp->getID(); + mTargetObjectFace = face; + mTargetObjectNormal = pick_normal; + mClearFaceOnFade = false; + } + else + { + // This happens on a timer now. +// mTargetImplID = LLUUID::null; +// mTargetObjectID = LLUUID::null; +// mTargetObjectFace = 0; + mClearFaceOnFade = true; + } + + updateShape(); +} + +void LLPanelPrimMediaControls::focusOnTarget() +{ + // Sets the media focus to the current target of the LLPanelPrimMediaControls. + // This is how we transition from hover to focus when the user clicks on a control. + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if(media_impl) + { + if(!media_impl->hasFocus()) + { + // The current target doesn't have media focus -- focus on it. + LLViewerObject* objectp = getTargetObject(); + LLViewerMediaFocus::getInstance()->setFocusFace(objectp, mTargetObjectFace, media_impl, mTargetObjectNormal); + } + } +} + +LLViewerMediaImpl* LLPanelPrimMediaControls::getTargetMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mTargetImplID); +} + +LLViewerObject* LLPanelPrimMediaControls::getTargetObject() +{ + return gObjectList.findObject(mTargetObjectID); +} + +LLPluginClassMedia* LLPanelPrimMediaControls::getTargetMediaPlugin() +{ + LLViewerMediaImpl* impl = getTargetMediaImpl(); + if(impl && impl->hasMedia()) + { + return impl->getMediaPlugin(); + } + + return NULL; +} + +void LLPanelPrimMediaControls::updateShape() +{ + const S32 MIN_HUD_WIDTH=400; + const S32 MIN_HUD_HEIGHT=120; + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + LLViewerObject* objectp = getTargetObject(); + + if(!media_impl) + { + setVisible(FALSE); + return; + } + + LLPluginClassMedia* media_plugin = NULL; + if(media_impl->hasMedia()) + { + media_plugin = media_impl->getMediaPlugin(); + } + + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + bool can_navigate = parcel->getMediaAllowNavigate(); + bool enabled = false; + bool has_focus = media_impl->hasFocus(); + setVisible(enabled); + + if (objectp) + { + bool mini_controls = false; + LLMediaEntry *media_data = objectp->getTE(mTargetObjectFace)->getMediaData(); + if (media_data && NULL != dynamic_cast<LLVOVolume*>(objectp)) + { + // Don't show the media HUD if we do not have permissions + enabled = dynamic_cast<LLVOVolume*>(objectp)->hasMediaPermission(media_data, LLVOVolume::MEDIA_PERM_CONTROL); + mini_controls = (LLMediaEntry::MINI == media_data->getControls()); + } + + // + // Set the state of the buttons + // + LLUICtrl* back_ctrl = getChild<LLUICtrl>("back"); + LLUICtrl* fwd_ctrl = getChild<LLUICtrl>("fwd"); + LLUICtrl* reload_ctrl = getChild<LLUICtrl>("reload"); + LLUICtrl* play_ctrl = getChild<LLUICtrl>("play"); + LLUICtrl* pause_ctrl = getChild<LLUICtrl>("pause"); + LLUICtrl* stop_ctrl = getChild<LLUICtrl>("stop"); + LLUICtrl* media_stop_ctrl = getChild<LLUICtrl>("media_stop"); + LLUICtrl* home_ctrl = getChild<LLUICtrl>("home"); + LLUICtrl* close_ctrl = getChild<LLUICtrl>("close"); + LLUICtrl* open_ctrl = getChild<LLUICtrl>("new_window"); + LLUICtrl* zoom_ctrl = getChild<LLUICtrl>("zoom_frame"); + LLPanel* media_loading_panel = getChild<LLPanel>("media_progress_indicator"); + LLUICtrl* media_address_ctrl = getChild<LLUICtrl>("media_address"); + LLUICtrl* media_play_slider_ctrl = getChild<LLUICtrl>("media_play_position"); + LLUICtrl* volume_ctrl = getChild<LLUICtrl>("media_volume"); + LLButton* volume_btn = getChild<LLButton>("media_volume_button"); + LLUICtrl* volume_up_ctrl = getChild<LLUICtrl>("volume_up"); + LLUICtrl* volume_down_ctrl = getChild<LLUICtrl>("volume_down"); + LLIconCtrl* whitelist_icon = getChild<LLIconCtrl>("media_whitelist_flag"); + LLIconCtrl* secure_lock_icon = getChild<LLIconCtrl>("media_secure_lock_flag"); + + LLUICtrl* media_panel_scroll = getChild<LLUICtrl>("media_panel_scroll"); + LLUICtrl* scroll_up_ctrl = getChild<LLUICtrl>("scrollup"); + LLUICtrl* scroll_left_ctrl = getChild<LLUICtrl>("scrollleft"); + LLUICtrl* scroll_right_ctrl = getChild<LLUICtrl>("scrollright"); + LLUICtrl* scroll_down_ctrl = getChild<LLUICtrl>("scrolldown"); + + // XXX RSP: TODO: FIXME: clean this up so that it is clearer what mode we are in, + // and that only the proper controls get made visible/enabled according to that mode. + back_ctrl->setVisible(has_focus); + fwd_ctrl->setVisible(has_focus); + reload_ctrl->setVisible(has_focus); + stop_ctrl->setVisible(false); + home_ctrl->setVisible(has_focus); + close_ctrl->setVisible(has_focus); + open_ctrl->setVisible(true); + media_address_ctrl->setVisible(has_focus && !mini_controls); + media_play_slider_ctrl->setVisible(has_focus && !mini_controls); + volume_ctrl->setVisible(false); + volume_up_ctrl->setVisible(false); + volume_down_ctrl->setVisible(false); + + whitelist_icon->setVisible(!mini_controls && (media_data)?media_data->getWhiteListEnable():false); + // Disable zoom if HUD + zoom_ctrl->setEnabled(!objectp->isHUDAttachment()); + secure_lock_icon->setVisible(false); + mCurrentURL = media_impl->getMediaURL(); + + back_ctrl->setEnabled((media_impl != NULL) && media_impl->canNavigateBack() && can_navigate); + fwd_ctrl->setEnabled((media_impl != NULL) && media_impl->canNavigateForward() && can_navigate); + stop_ctrl->setEnabled(has_focus && can_navigate); + home_ctrl->setEnabled(has_focus && can_navigate); + LLPluginClassMediaOwner::EMediaStatus result = ((media_impl != NULL) && media_impl->hasMedia()) ? media_plugin->getStatus() : LLPluginClassMediaOwner::MEDIA_NONE; + + if(media_plugin && media_plugin->pluginSupportsMediaTime()) + { + reload_ctrl->setEnabled(FALSE); + reload_ctrl->setVisible(FALSE); + media_stop_ctrl->setVisible(has_focus); + home_ctrl->setVisible(FALSE); + back_ctrl->setEnabled(has_focus); + fwd_ctrl->setEnabled(has_focus); + media_address_ctrl->setVisible(false); + media_address_ctrl->setEnabled(false); + media_play_slider_ctrl->setVisible(!mini_controls); + media_play_slider_ctrl->setEnabled(!mini_controls); + + volume_ctrl->setVisible(has_focus); + volume_up_ctrl->setVisible(has_focus); + volume_down_ctrl->setVisible(has_focus); + volume_ctrl->setEnabled(has_focus); + + whitelist_icon->setVisible(false); + secure_lock_icon->setVisible(false); + scroll_up_ctrl->setVisible(false); + scroll_left_ctrl->setVisible(false); + scroll_right_ctrl->setVisible(false); + scroll_down_ctrl->setVisible(false); + media_panel_scroll->setVisible(false); + + F32 volume = media_impl->getVolume(); + // movie's url changed + if(mCurrentURL!=mPreviousURL) + { + mMovieDuration = media_plugin->getDuration(); + mPreviousURL = mCurrentURL; + } + + if(mMovieDuration == 0) + { + mMovieDuration = media_plugin->getDuration(); + media_play_slider_ctrl->setValue(0); + media_play_slider_ctrl->setEnabled(false); + } + // TODO: What if it's not fully loaded + + if(mUpdateSlider && mMovieDuration!= 0) + { + F64 current_time = media_plugin->getCurrentTime(); + F32 percent = current_time / mMovieDuration; + media_play_slider_ctrl->setValue(percent); + media_play_slider_ctrl->setEnabled(true); + } + + // video vloume + if(volume <= 0.0) + { + volume_up_ctrl->setEnabled(TRUE); + volume_down_ctrl->setEnabled(FALSE); + media_impl->setVolume(0.0); + volume_btn->setToggleState(true); + } + else if (volume >= 1.0) + { + volume_up_ctrl->setEnabled(FALSE); + volume_down_ctrl->setEnabled(TRUE); + media_impl->setVolume(1.0); + volume_btn->setToggleState(false); + } + else + { + volume_up_ctrl->setEnabled(TRUE); + volume_down_ctrl->setEnabled(TRUE); + } + + switch(result) + { + case LLPluginClassMediaOwner::MEDIA_PLAYING: + play_ctrl->setEnabled(FALSE); + play_ctrl->setVisible(FALSE); + pause_ctrl->setEnabled(TRUE); + pause_ctrl->setVisible(has_focus); + media_stop_ctrl->setEnabled(TRUE); + + break; + case LLPluginClassMediaOwner::MEDIA_PAUSED: + default: + pause_ctrl->setEnabled(FALSE); + pause_ctrl->setVisible(FALSE); + play_ctrl->setEnabled(TRUE); + play_ctrl->setVisible(has_focus); + media_stop_ctrl->setEnabled(FALSE); + break; + } + } + else // web based + { + if(media_plugin) + { + mCurrentURL = media_plugin->getLocation(); + } + else + { + mCurrentURL.clear(); + } + + play_ctrl->setVisible(FALSE); + pause_ctrl->setVisible(FALSE); + media_stop_ctrl->setVisible(FALSE); + media_address_ctrl->setVisible(has_focus && !mini_controls); + media_address_ctrl->setEnabled(has_focus && !mini_controls); + media_play_slider_ctrl->setVisible(FALSE); + media_play_slider_ctrl->setEnabled(FALSE); + + volume_ctrl->setVisible(FALSE); + volume_up_ctrl->setVisible(FALSE); + volume_down_ctrl->setVisible(FALSE); + volume_ctrl->setEnabled(FALSE); + volume_up_ctrl->setEnabled(FALSE); + volume_down_ctrl->setEnabled(FALSE); + + scroll_up_ctrl->setVisible(has_focus); + scroll_left_ctrl->setVisible(has_focus); + scroll_right_ctrl->setVisible(has_focus); + scroll_down_ctrl->setVisible(has_focus); + media_panel_scroll->setVisible(has_focus); + // TODO: get the secure lock bool from media plug in + std::string prefix = std::string("https://"); + std::string test_prefix = mCurrentURL.substr(0, prefix.length()); + LLStringUtil::toLower(test_prefix); + if(test_prefix == prefix) + { + secure_lock_icon->setVisible(has_focus); + } + + if(mCurrentURL!=mPreviousURL) + { + setCurrentURL(); + mPreviousURL = mCurrentURL; + } + + if(result == LLPluginClassMediaOwner::MEDIA_LOADING) + { + reload_ctrl->setEnabled(FALSE); + reload_ctrl->setVisible(FALSE); + stop_ctrl->setEnabled(TRUE); + stop_ctrl->setVisible(has_focus); + } + else + { + reload_ctrl->setEnabled(TRUE); + reload_ctrl->setVisible(has_focus); + stop_ctrl->setEnabled(FALSE); + stop_ctrl->setVisible(FALSE); + } + } + + + if(media_plugin) + { + // + // Handle progress bar + // + mUpdatePercent = media_plugin->getProgressPercent(); + if(mUpdatePercent<100.0f) + { + media_loading_panel->setVisible(true); + getChild<LLProgressBar>("media_progress_bar")->setPercent(mUpdatePercent); + gFocusMgr.setTopCtrl(media_loading_panel); + } + else + { + media_loading_panel->setVisible(false); + gFocusMgr.setTopCtrl(NULL); + } + } + + if(media_plugin) + { + // + // Handle Scrolling + // + switch (mScrollState) + { + case SCROLL_UP: + media_plugin->scrollEvent(0, -1, MASK_NONE); + break; + case SCROLL_DOWN: + media_plugin->scrollEvent(0, 1, MASK_NONE); + break; + case SCROLL_LEFT: + media_impl->handleKeyHere(KEY_LEFT, MASK_NONE); + break; + case SCROLL_RIGHT: + media_impl->handleKeyHere(KEY_RIGHT, MASK_NONE); + break; + case SCROLL_NONE: + default: + break; + } + } + + setVisible(enabled); + + // + // Calculate position and shape of the controls + // + glh::matrix4f mat = glh_get_current_projection()*glh_get_current_modelview(); + std::vector<LLVector3>::iterator vert_it; + std::vector<LLVector3>::iterator vert_end; + std::vector<LLVector3> vect_face; + + LLVolume* volume = objectp->getVolume(); + + if (volume) + { + const LLVolumeFace& vf = volume->getVolumeFace(mTargetObjectFace); + + const LLVector3* ext = vf.mExtents; + + LLVector3 center = (ext[0]+ext[1])*0.5f; + LLVector3 size = (ext[1]-ext[0])*0.5f; + LLVector3 vert[] = + { + center + size.scaledVec(LLVector3(1,1,1)), + center + size.scaledVec(LLVector3(-1,1,1)), + center + size.scaledVec(LLVector3(1,-1,1)), + center + size.scaledVec(LLVector3(-1,-1,1)), + center + size.scaledVec(LLVector3(1,1,-1)), + center + size.scaledVec(LLVector3(-1,1,-1)), + center + size.scaledVec(LLVector3(1,-1,-1)), + center + size.scaledVec(LLVector3(-1,-1,-1)), + }; + + LLVOVolume* vo = (LLVOVolume*) objectp; + + for (U32 i = 0; i < 8; i++) + { + vect_face.push_back(vo->volumePositionToAgent(vert[i])); + } + } + vert_it = vect_face.begin(); + vert_end = vect_face.end(); + + LLVector3 min = LLVector3(1,1,1); + LLVector3 max = LLVector3(-1,-1,-1); + for(; vert_it != vert_end; ++vert_it) + { + // project silhouette vertices into screen space + glh::vec3f screen_vert = glh::vec3f(vert_it->mV); + mat.mult_matrix_vec(screen_vert); + + // add to screenspace bounding box + update_min_max(min, max, LLVector3(screen_vert.v)); + } + + LLCoordGL screen_min; + screen_min.mX = llround((F32)gViewerWindow->getWorldViewWidth() * (min.mV[VX] + 1.f) * 0.5f); + screen_min.mY = llround((F32)gViewerWindow->getWorldViewHeight() * (min.mV[VY] + 1.f) * 0.5f); + + LLCoordGL screen_max; + screen_max.mX = llround((F32)gViewerWindow->getWorldViewWidth() * (max.mV[VX] + 1.f) * 0.5f); + screen_max.mY = llround((F32)gViewerWindow->getWorldViewHeight() * (max.mV[VY] + 1.f) * 0.5f); + + // grow panel so that screenspace bounding box fits inside "media_region" element of HUD + LLRect media_controls_rect; + getParent()->screenRectToLocal(LLRect(screen_min.mX, screen_max.mY, screen_max.mX, screen_min.mY), &media_controls_rect); + LLView* media_region = getChild<LLView>("media_region"); + media_controls_rect.mLeft -= media_region->getRect().mLeft; + media_controls_rect.mBottom -= media_region->getRect().mBottom; + media_controls_rect.mTop += getRect().getHeight() - media_region->getRect().mTop; + media_controls_rect.mRight += getRect().getWidth() - media_region->getRect().mRight; + + LLRect old_hud_rect = media_controls_rect; + // keep all parts of HUD on-screen + media_controls_rect.intersectWith(getParent()->getLocalRect()); + + // clamp to minimum size, keeping centered + media_controls_rect.setCenterAndSize(media_controls_rect.getCenterX(), media_controls_rect.getCenterY(), + llmax(MIN_HUD_WIDTH, media_controls_rect.getWidth()), llmax(MIN_HUD_HEIGHT, media_controls_rect.getHeight())); + + setShape(media_controls_rect, true); + + // Test mouse position to see if the cursor is stationary + LLCoordWindow cursor_pos_window; + getWindow()->getCursorPosition(&cursor_pos_window); + + // If last pos is not equal to current pos, the mouse has moved + // We need to reset the timer, and make sure the panel is visible + if(cursor_pos_window.mX != mLastCursorPos.mX || + cursor_pos_window.mY != mLastCursorPos.mY || + mScrollState != SCROLL_NONE) + { + mInactivityTimer.start(); + mLastCursorPos = cursor_pos_window; + } + + if(isMouseOver()) + { + // Never fade the controls if the mouse is over them. + mFadeTimer.stop(); + } + else if(!mClearFaceOnFade && (mInactivityTimer.getElapsedTimeF32() < mInactiveTimeout)) + { + // Mouse is over the object, but has not been stationary for long enough to fade the UI + mFadeTimer.stop(); + } + else if(! mFadeTimer.getStarted() ) + { + // we need to start fading the UI (and we have not already started) + mFadeTimer.reset(); + mFadeTimer.start(); + } + else + { + // I don't think this is correct anymore. This is done in draw() after the fade has completed. +// setVisible(FALSE); + } + } +} + +/*virtual*/ +void LLPanelPrimMediaControls::draw() +{ + F32 alpha = 1.f; + if(mFadeTimer.getStarted()) + { + F32 time = mFadeTimer.getElapsedTimeF32(); + alpha = llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); + + if(mFadeTimer.getElapsedTimeF32() >= mControlFadeTime) + { + setVisible(FALSE); + if(mClearFaceOnFade) + { + mClearFaceOnFade = false; + mTargetImplID = LLUUID::null; + mTargetObjectID = LLUUID::null; + mTargetObjectFace = 0; + } + } + } + + { + LLViewDrawContext context(alpha); + LLPanel::draw(); + } +} + +BOOL LLPanelPrimMediaControls::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + mInactivityTimer.start(); + return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); +} + +BOOL LLPanelPrimMediaControls::handleMouseDown(S32 x, S32 y, MASK mask) +{ + mInactivityTimer.start(); + return LLPanel::handleMouseDown(x, y, mask); +} + +BOOL LLPanelPrimMediaControls::handleMouseUp(S32 x, S32 y, MASK mask) +{ + mInactivityTimer.start(); + return LLPanel::handleMouseUp(x, y, mask); +} + +BOOL LLPanelPrimMediaControls::handleKeyHere( KEY key, MASK mask ) +{ + mInactivityTimer.start(); + return LLPanel::handleKeyHere(key, mask); +} + +bool LLPanelPrimMediaControls::isMouseOver() +{ + bool result = false; + + if( getVisible() ) + { + LLCoordWindow cursor_pos_window; + LLCoordScreen cursor_pos_screen; + LLCoordGL cursor_pos_gl; + S32 x, y; + getWindow()->getCursorPosition(&cursor_pos_window); + getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl); + + LLPanel* controls_panel = NULL; + controls_panel = getChild<LLPanel>("media_hover_controls"); + if(controls_panel && !controls_panel->getVisible()) + { + // The hover controls aren't visible -- use the focused controls instead. + controls_panel = getChild<LLPanel>("media_focused_controls"); + } + + if(controls_panel && controls_panel->getVisible()) + { + controls_panel->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &x, &y); + + LLView *hit_child = controls_panel->childFromPoint(x, y); + if(hit_child) + { + // This was useful for debugging both coordinate translation and view hieararchy problems... +// llinfos << "mouse coords: " << x << ", " << y << " hit child " << hit_child->getName() << llendl; + result = true; + } + } + } + + return result; +} + + +void LLPanelPrimMediaControls::onClickClose() +{ + close(); +} + +void LLPanelPrimMediaControls::close() +{ + LLViewerMediaFocus::getInstance()->clearFocus(); + resetZoomLevel(); + setVisible(FALSE); +} + + +void LLPanelPrimMediaControls::onClickBack() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl =getTargetMediaImpl(); + + if (impl) + { + impl->navigateBack(); + } +} + +void LLPanelPrimMediaControls::onClickForward() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if (impl) + { + impl->navigateForward(); + } +} + +void LLPanelPrimMediaControls::onClickHome() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->navigateHome(); + } +} + +void LLPanelPrimMediaControls::onClickOpen() +{ + LLViewerMediaImpl* impl =getTargetMediaImpl(); + if(impl) + { + if(impl->getMediaPlugin()) + { + if(impl->getMediaPlugin()->getLocation().empty()) + { + LLWeb::loadURL(impl->getMediaURL()); + } + else + { + LLWeb::loadURL( impl->getMediaPlugin()->getLocation()); + } + } + } +} + +void LLPanelPrimMediaControls::onClickReload() +{ + focusOnTarget(); + + //LLViewerMedia::navigateHome(); + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->navigateReload(); + } +} + +void LLPanelPrimMediaControls::onClickPlay() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->play(); + } +} + +void LLPanelPrimMediaControls::onClickPause() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->pause(); + } +} + +void LLPanelPrimMediaControls::onClickStop() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->stop(); + } +} + +void LLPanelPrimMediaControls::onClickZoom() +{ + focusOnTarget(); + + nextZoomLevel(); +} +void LLPanelPrimMediaControls::nextZoomLevel() +{ + int index = 0; + while (index < kNumZoomLevels) + { + if (kZoomLevels[index] == mCurrentZoom) + { + index++; + break; + } + index++; + } + mCurrentZoom = kZoomLevels[index % kNumZoomLevels]; + updateZoom(); +} + +void LLPanelPrimMediaControls::resetZoomLevel() +{ + if(mCurrentZoom != ZOOM_NONE) + { + mCurrentZoom = ZOOM_NONE; + updateZoom(); + } +} + +void LLPanelPrimMediaControls::updateZoom() +{ + F32 zoom_padding = 0.0f; + switch (mCurrentZoom) + { + case ZOOM_NONE: + { + gAgent.setFocusOnAvatar(TRUE, ANIMATE); + break; + } + case ZOOM_FAR: + { + zoom_padding = ZOOM_FAR_PADDING; + break; + } + case ZOOM_MEDIUM: + { + zoom_padding = ZOOM_MEDIUM_PADDING; + break; + } + case ZOOM_NEAR: + { + zoom_padding = ZOOM_NEAR_PADDING; + break; + } + default: + { + gAgent.setFocusOnAvatar(TRUE, ANIMATE); + break; + } + } + + if (zoom_padding > 0.0f) + LLViewerMediaFocus::setCameraZoom(getTargetObject(), mTargetObjectNormal, zoom_padding); +} +void LLPanelPrimMediaControls::onScrollUp(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (user_data); + this_panel->focusOnTarget(); + + LLPluginClassMedia* plugin = this_panel->getTargetMediaPlugin(); + + if(plugin) + { + plugin->scrollEvent(0, -1, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollUpHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (user_data); + this_panel->mScrollState = SCROLL_UP; +} +void LLPanelPrimMediaControls::onScrollRight(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->handleKeyHere(KEY_RIGHT, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollRightHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (user_data); + this_panel->mScrollState = SCROLL_RIGHT; +} + +void LLPanelPrimMediaControls::onScrollLeft(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->handleKeyHere(KEY_LEFT, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollLeftHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (user_data); + this_panel->mScrollState = SCROLL_LEFT; +} + +void LLPanelPrimMediaControls::onScrollDown(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (user_data); + this_panel->focusOnTarget(); + + LLPluginClassMedia* plugin = this_panel->getTargetMediaPlugin(); + + if(plugin) + { + plugin->scrollEvent(0, 1, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollDownHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (user_data); + this_panel->mScrollState = SCROLL_DOWN; +} + +void LLPanelPrimMediaControls::onScrollStop(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (user_data); + this_panel->mScrollState = SCROLL_NONE; +} + +void LLPanelPrimMediaControls::onCommitURL() +{ + focusOnTarget(); + + LLUICtrl *media_address_ctrl = getChild<LLUICtrl>("media_address_url"); + std::string url = media_address_ctrl->getValue().asString(); + if(getTargetMediaImpl() && !url.empty()) + { + getTargetMediaImpl()->navigateTo( url, "", true); + + // Make sure keyboard focus is set to the media focus object. + gFocusMgr.setKeyboardFocus(LLViewerMediaFocus::getInstance()); + + } + mPauseFadeout = false; + mFadeTimer.start(); +} + + +void LLPanelPrimMediaControls::onInputURL(LLFocusableElement* caller, void *userdata) +{ + + LLPanelPrimMediaControls* this_panel = static_cast<LLPanelPrimMediaControls*> (userdata); + this_panel->focusOnTarget(); + + this_panel->mPauseFadeout = true; + this_panel->mFadeTimer.stop(); + this_panel->mFadeTimer.reset(); + +} + +void LLPanelPrimMediaControls::setCurrentURL() +{ + LLComboBox* media_address_combo = getChild<LLComboBox>("media_address_combo"); + // redirects will navigate momentarily to about:blank, don't add to history + if (media_address_combo && mCurrentURL != "about:blank") + { + media_address_combo->remove(mCurrentURL); + media_address_combo->add(mCurrentURL, ADD_SORTED); + media_address_combo->selectByValue(mCurrentURL); + } +} + +void LLPanelPrimMediaControls::onCommitSlider() +{ + focusOnTarget(); + + LLSlider* media_play_slider_ctrl = getChild<LLSlider>("media_play_slider"); + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + // get slider value + F64 slider_value = media_play_slider_ctrl->getValue().asReal(); + if(slider_value <= 0.0) + { + media_impl->stop(); + } + else + { + media_impl->seek(slider_value*mMovieDuration); + //mUpdateSlider= false; + } + } +} + +void LLPanelPrimMediaControls::onCommitVolumeUp() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + F32 volume = media_impl->getVolume(); + + volume += 0.1f; + if(volume >= 1.0f) + { + volume = 1.0f; + } + + media_impl->setVolume(volume); + getChild<LLButton>("media_volume")->setToggleState(false); + } +} + +void LLPanelPrimMediaControls::onCommitVolumeDown() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + F32 volume = media_impl->getVolume(); + + volume -= 0.1f; + if(volume <= 0.0f) + { + volume = 0.0f; + } + + media_impl->setVolume(volume); + getChild<LLButton>("media_volume")->setToggleState(false); + } +} + + +void LLPanelPrimMediaControls::onToggleMute() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + F32 volume = media_impl->getVolume(); + + if(volume > 0.0) + { + media_impl->setVolume(0.0); + } + else + { + media_impl->setVolume(0.5); + } + } +} + diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h new file mode 100644 index 0000000000..3ec7aa2356 --- /dev/null +++ b/indra/newview/llpanelprimmediacontrols.h @@ -0,0 +1,148 @@ +/** + * @file llpanelprimmediacontrols.h + * @brief Pop-up media controls panel + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_PANELPRIMMEDIACONTROLS_H +#define LL_PANELPRIMMEDIACONTROLS_H + +#include "llpanel.h" +#include "llviewermedia.h" + +class LLCoordWindow; +class LLViewerMediaImpl; + +class LLPanelPrimMediaControls : public LLPanel +{ +public: + LLPanelPrimMediaControls(); + virtual ~LLPanelPrimMediaControls(); + /*virtual*/ BOOL postBuild(); + virtual void draw(); + virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + + virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); + virtual BOOL handleKeyHere(KEY key, MASK mask); + + void updateShape(); + bool isMouseOver(); + void nextZoomLevel(); + void resetZoomLevel(); + void close(); + + LLHandle<LLPanelPrimMediaControls> getHandle() const { return mPanelHandle; } + void setMediaFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); + + + enum EZoomLevel + { + ZOOM_NONE = 0, + ZOOM_FAR, + ZOOM_MEDIUM, + ZOOM_NEAR + }; + static const EZoomLevel kZoomLevels[]; + static const int kNumZoomLevels; + + enum EScrollDir + { + SCROLL_UP = 0, + SCROLL_DOWN, + SCROLL_LEFT, + SCROLL_RIGHT, + SCROLL_NONE + }; + +private: + void onClickClose(); + void onClickBack(); + void onClickForward(); + void onClickHome(); + void onClickOpen(); + void onClickReload(); + void onClickPlay(); + void onClickPause(); + void onClickStop(); + void onClickZoom(); + void onCommitURL(); + + void updateZoom(); + void setCurrentURL(); + void onCommitSlider(); + + void onCommitVolumeUp(); + void onCommitVolumeDown(); + void onToggleMute(); + + static void onScrollUp(void* user_data); + static void onScrollUpHeld(void* user_data); + static void onScrollLeft(void* user_data); + static void onScrollLeftHeld(void* user_data); + static void onScrollRight(void* user_data); + static void onScrollRightHeld(void* user_data); + static void onScrollDown(void* user_data); + static void onScrollDownHeld(void* user_data); + static void onScrollStop(void* user_data); + + static void onInputURL(LLFocusableElement* caller, void *userdata); + static bool hasControlsPermission(LLViewerObject *obj, const LLMediaEntry *media_entry); + + void focusOnTarget(); + + LLViewerMediaImpl* getTargetMediaImpl(); + LLViewerObject* getTargetObject(); + LLPluginClassMedia* getTargetMediaPlugin(); + bool mPauseFadeout; + bool mUpdateSlider; + bool mClearFaceOnFade; + + LLMatrix4 mLastCameraMat; + EZoomLevel mCurrentZoom; + EScrollDir mScrollState; + LLCoordWindow mLastCursorPos; + LLFrameTimer mInactivityTimer; + LLFrameTimer mFadeTimer; + F32 mInactiveTimeout; + F32 mControlFadeTime; + LLRootHandle<LLPanelPrimMediaControls> mPanelHandle; + F32 mAlpha; + std::string mCurrentURL; + std::string mPreviousURL; + F64 mCurrentRate; + F64 mMovieDuration; + int mUpdatePercent; + + LLUUID mTargetObjectID; + S32 mTargetObjectFace; + LLUUID mTargetImplID; + LLVector3 mTargetObjectNormal; +}; + +#endif // LL_PANELPRIMMEDIACONTROLS_H diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 25e773e8b8..e97eb1df2b 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -34,7 +34,7 @@ #include "llparticipantlist.h" #include "llavatarlist.h" -#include "llfloateractivespeakers.h" +#include "llspeakers.h" //LLParticipantList retrieves add, clear and remove events and updates view accordingly LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list): @@ -48,6 +48,18 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av mSpeakerMgr->addListener(mSpeakerAddListener, "add"); mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove"); mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); + + //Lets fill avatarList with existing speakers + LLAvatarList::uuid_vector_t& group_members = mAvatarList->getIDs(); + + LLSpeakerMgr::speaker_list_t speaker_list; + mSpeakerMgr->getSpeakerList(&speaker_list, true); + for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++) + { + group_members.push_back((*it)->mID); + } + mAvatarList->setDirty(); + mAvatarList->sortByName(); } LLParticipantList::~LLParticipantList() @@ -87,8 +99,12 @@ bool LLParticipantList::SpeakerAddListener::handleEvent(LLPointer<LLOldEvents::L bool LLParticipantList::SpeakerRemoveListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) { LLAvatarList::uuid_vector_t& group_members = mAvatarList->getIDs(); - group_members.erase(std::find(group_members.begin(), group_members.end(), event->getValue().asUUID())); - mAvatarList->setDirty(); + LLAvatarList::uuid_vector_t::iterator pos = std::find(group_members.begin(), group_members.end(), event->getValue().asUUID()); + if(pos != group_members.end()) + { + group_members.erase(pos); + mAvatarList->setDirty(); + } return true; } diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index 1683d113a9..73dcd1dd92 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -191,19 +191,22 @@ void LLScreenChannel::onToastFade(LLToast* toast) { std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), static_cast<LLPanel*>(toast)); - bool delete_toast = !mCanStoreToasts || !toast->getCanBeStored(); - if(delete_toast) - { - mToastList.erase(it); - deleteToast(toast); - } - else + if(it != mToastList.end()) { - storeToast((*it)); - mToastList.erase(it); - } + bool delete_toast = !mCanStoreToasts || !toast->getCanBeStored(); + if(delete_toast) + { + mToastList.erase(it); + deleteToast(toast); + } + else + { + storeToast((*it)); + mToastList.erase(it); + } - redrawToasts(); + redrawToasts(); + } } //-------------------------------------------------------------------------- @@ -247,6 +250,7 @@ void LLScreenChannel::loadStoredToastsToChannel() for(it = mStoredToastList.begin(); it != mStoredToastList.end(); ++it) { + (*it).toast->setIsHidden(false); (*it).toast->resetTimer(); mToastList.push_back((*it)); } @@ -266,6 +270,7 @@ void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) mOverflowToastHidden = false; LLToast* toast = (*it).toast; + toast->setIsHidden(false); toast->resetTimer(); mToastList.push_back((*it)); mStoredToastList.erase(it); @@ -519,6 +524,7 @@ void LLScreenChannel::createStartUpToast(S32 notif_num, F32 timer) LLRect toast_rect; LLToast::Params p; p.lifetime_secs = timer; + p.enable_hide_btn = false; mStartUpToastPanel = new LLToast(p); if(!mStartUpToastPanel) diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp new file mode 100644 index 0000000000..2341fcfc6d --- /dev/null +++ b/indra/newview/llspeakers.cpp @@ -0,0 +1,639 @@ +/** + * @file llspeakers.cpp + * @brief Management interface for muting and controlling volume of residents currently speaking + * + * $LicenseInfo:firstyear=2005&license=viewergpl$ + * + * Copyright (c) 2005-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llspeakers.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llmutelist.h" +#include "llsdutil.h" +#include "lluicolortable.h" +#include "llviewerobjectlist.h" +#include "llvoavatar.h" +#include "llworld.h" + +const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers +const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f); +const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f); + +LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerType type) : + mStatus(LLSpeaker::STATUS_TEXT_ONLY), + mLastSpokeTime(0.f), + mSpeechVolume(0.f), + mHasSpoken(FALSE), + mHasLeftCurrentCall(FALSE), + mDotColor(LLColor4::white), + mID(id), + mTyping(FALSE), + mSortIndex(0), + mType(type), + mIsModerator(FALSE), + mModeratorMutedVoice(FALSE), + mModeratorMutedText(FALSE) +{ + if (name.empty() && type == SPEAKER_AGENT) + { + lookupName(); + } + else + { + mDisplayName = name; + } + + gVoiceClient->setUserVolume(id, LLMuteList::getInstance()->getSavedResidentVolume(id)); + + mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); +} + + +void LLSpeaker::lookupName() +{ + gCacheName->get(mID, FALSE, boost::bind(&LLSpeaker::onAvatarNameLookup, this, _1, _2, _3, _4)); +} + +void LLSpeaker::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) +{ + mDisplayName = first + " " + last; +} + +LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source) +: LLEvent(source, "Speaker text moderation event") +{ +} + +LLSD LLSpeakerTextModerationEvent::getValue() +{ + return std::string("text"); +} + + +LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source) +: LLEvent(source, "Speaker voice moderation event") +{ +} + +LLSD LLSpeakerVoiceModerationEvent::getValue() +{ + return std::string("voice"); +} + +LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id) +: LLEvent(source, "Speaker added/removed from speaker mgr"), + mSpeakerID(speaker_id) +{ +} + +LLSD LLSpeakerListChangeEvent::getValue() +{ + return mSpeakerID; +} + +// helper sort class +struct LLSortRecentSpeakers +{ + bool operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const; +}; + +bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const +{ + // Sort first on status + if (lhs->mStatus != rhs->mStatus) + { + return (lhs->mStatus < rhs->mStatus); + } + + // and then on last speaking time + if(lhs->mLastSpokeTime != rhs->mLastSpokeTime) + { + return (lhs->mLastSpokeTime > rhs->mLastSpokeTime); + } + + // and finally (only if those are both equal), on name. + return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 ); +} + + +// +// LLSpeakerMgr +// + +LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : + mVoiceChannel(channelp) +{ +} + +LLSpeakerMgr::~LLSpeakerMgr() +{ +} + +LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) +{ + if (id.isNull()) return NULL; + + LLPointer<LLSpeaker> speakerp; + if (mSpeakers.find(id) == mSpeakers.end()) + { + speakerp = new LLSpeaker(id, name, type); + speakerp->mStatus = status; + mSpeakers.insert(std::make_pair(speakerp->mID, speakerp)); + mSpeakersSorted.push_back(speakerp); + fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add"); + } + else + { + speakerp = findSpeaker(id); + if (speakerp.notNull()) + { + // keep highest priority status (lowest value) instead of overriding current value + speakerp->mStatus = llmin(speakerp->mStatus, status); + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id + // we need to override speakers that we think are objects when we find out they are really + // residents + if (type == LLSpeaker::SPEAKER_AGENT) + { + speakerp->mType = LLSpeaker::SPEAKER_AGENT; + speakerp->lookupName(); + } + } + } + + return speakerp; +} + +void LLSpeakerMgr::update(BOOL resort_ok) +{ + if (!gVoiceClient) + { + return; + } + + LLColor4 speaking_color = LLUIColorTable::instance().getColor("SpeakingColor"); + LLColor4 overdriven_color = LLUIColorTable::instance().getColor("OverdrivenColor"); + + if(resort_ok) // only allow list changes when user is not interacting with it + { + updateSpeakerList(); + } + + // update status of all current speakers + BOOL voice_channel_active = (!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + + speaker_map_t::iterator cur_speaker_it = speaker_it++; + + if (voice_channel_active && gVoiceClient->getVoiceEnabled(speaker_id)) + { + speakerp->mSpeechVolume = gVoiceClient->getCurrentPower(speaker_id); + BOOL moderator_muted_voice = gVoiceClient->getIsModeratorMuted(speaker_id); + if (moderator_muted_voice != speakerp->mModeratorMutedVoice) + { + speakerp->mModeratorMutedVoice = moderator_muted_voice; + speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp)); + } + + if (gVoiceClient->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) + { + speakerp->mStatus = LLSpeaker::STATUS_MUTED; + } + else if (gVoiceClient->getIsSpeaking(speaker_id)) + { + // reset inactivity expiration + if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) + { + speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->mHasSpoken = TRUE; + } + speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; + // interpolate between active color and full speaking color based on power of speech output + speakerp->mDotColor = speaking_color; + if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL) + { + speakerp->mDotColor = overdriven_color; + } + } + else + { + speakerp->mSpeechVolume = 0.f; + speakerp->mDotColor = ACTIVE_COLOR; + + if (speakerp->mHasSpoken) + { + // have spoken once, not currently speaking + speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN; + } + else + { + // default state for being in voice channel + speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE; + } + } + } + // speaker no longer registered in voice channel, demote to text only + else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL) + { + if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL) + { + // external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice) + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + } + else + { + speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY; + speakerp->mSpeechVolume = 0.f; + speakerp->mDotColor = ACTIVE_COLOR; + } + } + } + + if(resort_ok) // only allow list changes when user is not interacting with it + { + // sort by status then time last spoken + std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers()); + } + + // for recent speakers who are not currently speaking, show "recent" color dot for most recent + // fading to "active" color + + S32 recent_speaker_count = 0; + S32 sort_index = 0; + speaker_list_t::iterator sorted_speaker_it; + for(sorted_speaker_it = mSpeakersSorted.begin(); + sorted_speaker_it != mSpeakersSorted.end(); ) + { + LLPointer<LLSpeaker> speakerp = *sorted_speaker_it; + + // color code recent speakers who are not currently speaking + if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN) + { + speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f)); + recent_speaker_count++; + } + + // stuff sort ordinal into speaker so the ui can sort by this value + speakerp->mSortIndex = sort_index++; + + // remove speakers that have been gone too long + if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL && speakerp->mActivityTimer.hasExpired()) + { + fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "remove"); + + mSpeakers.erase(speakerp->mID); + sorted_speaker_it = mSpeakersSorted.erase(sorted_speaker_it); + } + else + { + ++sorted_speaker_it; + } + } +} + +void LLSpeakerMgr::updateSpeakerList() +{ + // are we bound to the currently active voice channel? + if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) + { + LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList(); + if(participants) + { + LLVoiceClient::participantMap::iterator participant_it; + + // add new participants to our list of known speakers + for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it) + { + LLVoiceClient::participantState* participantp = participant_it->second; + setSpeaker(participantp->mAvatarID, participantp->mDisplayName, LLSpeaker::STATUS_VOICE_ACTIVE, (participantp->isAvatar()?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); + } + } + } +} + +LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) +{ + speaker_map_t::iterator found_it = mSpeakers.find(speaker_id); + if (found_it == mSpeakers.end()) + { + return NULL; + } + return found_it->second; +} + +void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, BOOL include_text) +{ + speaker_list->clear(); + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLPointer<LLSpeaker> speakerp = speaker_it->second; + // what about text only muted or inactive? + if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY) + { + speaker_list->push_back(speakerp); + } + } +} + +const LLUUID LLSpeakerMgr::getSessionID() +{ + return mVoiceChannel->getSessionID(); +} + + +void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, BOOL typing) +{ + LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id); + if (speakerp.notNull()) + { + speakerp->mTyping = typing; + } +} + +// speaker has chatted via either text or voice +void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) +{ + LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id); + if (speakerp.notNull()) + { + speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->mHasSpoken = TRUE; + } +} + +BOOL LLSpeakerMgr::isVoiceActive() +{ + // mVoiceChannel = NULL means current voice channel, whatever it is + return LLVoiceClient::voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); +} + + +// +// LLIMSpeakerMgr +// +LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel) +{ +} + +void LLIMSpeakerMgr::updateSpeakerList() +{ + // don't do normal updates which are pulled from voice channel + // rely on user list reported by sim + + // We need to do this to allow PSTN callers into group chats to show in the list. + LLSpeakerMgr::updateSpeakerList(); + + return; +} + +void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) +{ + if ( !speakers.isMap() ) return; + + if ( speakers.has("agent_info") && speakers["agent_info"].isMap() ) + { + LLSD::map_const_iterator speaker_it; + for(speaker_it = speakers["agent_info"].beginMap(); + speaker_it != speakers["agent_info"].endMap(); + ++speaker_it) + { + LLUUID agent_id(speaker_it->first); + + LLPointer<LLSpeaker> speakerp = setSpeaker( + agent_id, + LLStringUtil::null, + LLSpeaker::STATUS_TEXT_ONLY); + + if ( speaker_it->second.isMap() ) + { + speakerp->mIsModerator = speaker_it->second["is_moderator"]; + speakerp->mModeratorMutedText = + speaker_it->second["mutes"]["text"]; + } + } + } + else if ( speakers.has("agents" ) && speakers["agents"].isArray() ) + { + //older, more decprecated way. Need here for + //using older version of servers + LLSD::array_const_iterator speaker_it; + for(speaker_it = speakers["agents"].beginArray(); + speaker_it != speakers["agents"].endArray(); + ++speaker_it) + { + const LLUUID agent_id = (*speaker_it).asUUID(); + + LLPointer<LLSpeaker> speakerp = setSpeaker( + agent_id, + LLStringUtil::null, + LLSpeaker::STATUS_TEXT_ONLY); + } + } +} + +void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) +{ + if ( !update.isMap() ) return; + + if ( update.has("agent_updates") && update["agent_updates"].isMap() ) + { + LLSD::map_const_iterator update_it; + for( + update_it = update["agent_updates"].beginMap(); + update_it != update["agent_updates"].endMap(); + ++update_it) + { + LLUUID agent_id(update_it->first); + LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id); + + LLSD agent_data = update_it->second; + + if (agent_data.isMap() && agent_data.has("transition")) + { + if (agent_data["transition"].asString() == "LEAVE" && speakerp.notNull()) + { + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + } + else if (agent_data["transition"].asString() == "ENTER") + { + // add or update speaker + speakerp = setSpeaker(agent_id); + } + else + { + llwarns << "bad membership list update " << ll_print_sd(agent_data["transition"]) << llendl; + } + } + + if (speakerp.isNull()) continue; + + // should have a valid speaker from this point on + if (agent_data.isMap() && agent_data.has("info")) + { + LLSD agent_info = agent_data["info"]; + + if (agent_info.has("is_moderator")) + { + speakerp->mIsModerator = agent_info["is_moderator"]; + } + + if (agent_info.has("mutes")) + { + speakerp->mModeratorMutedText = agent_info["mutes"]["text"]; + } + } + } + } + else if ( update.has("updates") && update["updates"].isMap() ) + { + LLSD::map_const_iterator update_it; + for ( + update_it = update["updates"].beginMap(); + update_it != update["updates"].endMap(); + ++update_it) + { + LLUUID agent_id(update_it->first); + LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id); + + std::string agent_transition = update_it->second.asString(); + if (agent_transition == "LEAVE" && speakerp.notNull()) + { + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + } + else if ( agent_transition == "ENTER") + { + // add or update speaker + speakerp = setSpeaker(agent_id); + } + else + { + llwarns << "bad membership list update " + << agent_transition << llendl; + } + } + } +} + + +// +// LLActiveSpeakerMgr +// + +LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL) +{ +} + +void LLActiveSpeakerMgr::updateSpeakerList() +{ + // point to whatever the current voice channel is + mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); + + // always populate from active voice channel + if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) + { + fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear"); + mSpeakers.clear(); + mSpeakersSorted.clear(); + mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); + } + LLSpeakerMgr::updateSpeakerList(); + + // clean up text only speakers + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) + { + // automatically flag text only speakers for removal + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + } + } + +} + + + +// +// LLLocalSpeakerMgr +// + +LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance()) +{ +} + +LLLocalSpeakerMgr::~LLLocalSpeakerMgr () +{ +} + +void LLLocalSpeakerMgr::updateSpeakerList() +{ + // pull speakers from voice channel + LLSpeakerMgr::updateSpeakerList(); + + if (gDisconnected)//the world is cleared. + { + return ; + } + + // pick up non-voice speakers in chat range + std::vector<LLUUID> avatar_ids; + std::vector<LLVector3d> positions; + LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS); + for(U32 i=0; i<avatar_ids.size(); i++) + { + setSpeaker(avatar_ids[i]); + } + + // check if text only speakers have moved out of chat range + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) + { + LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id); + if (!avatarp || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS) + { + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + } + } + } +} diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h new file mode 100644 index 0000000000..e0f22bff4f --- /dev/null +++ b/indra/newview/llspeakers.h @@ -0,0 +1,172 @@ +/** + * @file llspeakers.h + * @brief Management interface for muting and controlling volume of residents currently speaking + * + * $LicenseInfo:firstyear=2005&license=viewergpl$ + * + * Copyright (c) 2005-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLSPEAKERS_H +#define LL_LLSPEAKERS_H + +#include "llevent.h" +#include "llspeakers.h" +#include "llvoicechannel.h" + +class LLSpeakerMgr; + +// data for a given participant in a voice channel +class LLSpeaker : public LLRefCount, public LLOldEvents::LLObservable, public LLHandleProvider<LLSpeaker>, public boost::signals2::trackable +{ +public: + typedef enum e_speaker_type + { + SPEAKER_AGENT, + SPEAKER_OBJECT, + SPEAKER_EXTERNAL // Speaker that doesn't map to an avatar or object (i.e. PSTN caller in a group) + } ESpeakerType; + + typedef enum e_speaker_status + { + STATUS_SPEAKING, + STATUS_HAS_SPOKEN, + STATUS_VOICE_ACTIVE, + STATUS_TEXT_ONLY, + STATUS_NOT_IN_CHANNEL, + STATUS_MUTED + } ESpeakerStatus; + + + LLSpeaker(const LLUUID& id, const std::string& name = LLStringUtil::null, const ESpeakerType type = SPEAKER_AGENT); + ~LLSpeaker() {}; + void lookupName(); + + void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); + + ESpeakerStatus mStatus; // current activity status in speech group + F32 mLastSpokeTime; // timestamp when this speaker last spoke + F32 mSpeechVolume; // current speech amplitude (timea average rms amplitude?) + std::string mDisplayName; // cache user name for this speaker + LLFrameTimer mActivityTimer; // time out speakers when they are not part of current voice channel + BOOL mHasSpoken; // has this speaker said anything this session? + BOOL mHasLeftCurrentCall; // has this speaker left the current voice call? + LLColor4 mDotColor; + LLUUID mID; + BOOL mTyping; + S32 mSortIndex; + ESpeakerType mType; + BOOL mIsModerator; + BOOL mModeratorMutedVoice; + BOOL mModeratorMutedText; +}; + +class LLSpeakerTextModerationEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerTextModerationEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +}; + +class LLSpeakerVoiceModerationEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerVoiceModerationEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +}; + +class LLSpeakerListChangeEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id); + /*virtual*/ LLSD getValue(); + +private: + const LLUUID& mSpeakerID; +}; + +class LLSpeakerMgr : public LLOldEvents::LLObservable +{ +public: + LLSpeakerMgr(LLVoiceChannel* channelp); + virtual ~LLSpeakerMgr(); + + LLPointer<LLSpeaker> findSpeaker(const LLUUID& avatar_id); + void update(BOOL resort_ok); + void setSpeakerTyping(const LLUUID& speaker_id, BOOL typing); + void speakerChatted(const LLUUID& speaker_id); + LLPointer<LLSpeaker> setSpeaker(const LLUUID& id, + const std::string& name = LLStringUtil::null, + LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, + LLSpeaker::ESpeakerType = LLSpeaker::SPEAKER_AGENT); + + BOOL isVoiceActive(); + + typedef std::vector<LLPointer<LLSpeaker> > speaker_list_t; + void getSpeakerList(speaker_list_t* speaker_list, BOOL include_text); + LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; } + const LLUUID getSessionID(); + +protected: + virtual void updateSpeakerList(); + + typedef std::map<LLUUID, LLPointer<LLSpeaker> > speaker_map_t; + speaker_map_t mSpeakers; + + speaker_list_t mSpeakersSorted; + LLFrameTimer mSpeechTimer; + LLVoiceChannel* mVoiceChannel; +}; + +class LLIMSpeakerMgr : public LLSpeakerMgr +{ +public: + LLIMSpeakerMgr(LLVoiceChannel* channel); + + void updateSpeakers(const LLSD& update); + void setSpeakers(const LLSD& speakers); +protected: + virtual void updateSpeakerList(); +}; + +class LLActiveSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLActiveSpeakerMgr> +{ +public: + LLActiveSpeakerMgr(); +protected: + virtual void updateSpeakerList(); +}; + +class LLLocalSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLLocalSpeakerMgr> +{ +public: + LLLocalSpeakerMgr(); + ~LLLocalSpeakerMgr (); +protected: + virtual void updateSpeakerList(); +}; + +#endif // LL_LLSPEAKERS_H diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 86290e6695..93a931dc78 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -113,6 +113,12 @@ void LLSysWellWindow::connectListUpdaterToSignal(std::string notification_type) } //--------------------------------------------------------------------------------- +void LLSysWellWindow::onStartUpToastClick(S32 x, S32 y, MASK mask) +{ + onChicletClick(); +} + +//--------------------------------------------------------------------------------- void LLSysWellWindow::onChicletClick() { // 1 - remove StartUp toast and channel if present diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index fa6a1abea4..cbc5f7358f 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -76,6 +76,7 @@ public: void onItemClose(LLSysWellItem* item); void onStoreToast(LLPanel* info_panel, LLUUID id); void onChicletClick(); + void onStartUpToastClick(S32 x, S32 y, MASK mask); // size constants for the window and for its elements static const S32 MAX_WINDOW_HEIGHT = 200; diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp index 17547cae39..5d9046ac90 100644 --- a/indra/newview/lltexlayer.cpp +++ b/indra/newview/lltexlayer.cpp @@ -804,8 +804,9 @@ void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height, gGL.setColorMask(false, true); gGL.setSceneBlendType(LLRender::BT_REPLACE); + // (Optionally) replace alpha with a single component image from a tga file. - if (!info->mStaticAlphaFileName.empty() && mMaskLayerList.empty()) + if (!info->mStaticAlphaFileName.empty()) { LLGLSNoAlphaTest gls_no_alpha_test; gGL.flush(); diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index eba43d76a6..24824a095c 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -63,6 +63,7 @@ LLToast::LLToast(const LLToast::Params& p) mHideBtnEnabled(p.enable_hide_btn), mHideBtn(NULL), mNotification(p.notification), + mIsHidden(false), mHideBtnPressed(false) { LLUICtrlFactory::getInstance()->buildFloater(this, "panel_toast.xml", NULL); @@ -143,7 +144,8 @@ void LLToast::hide() { setVisible(FALSE); mTimer.stop(); - mOnFadeSignal(this); + mIsHidden = true; + mOnFadeSignal(this); } //-------------------------------------------------------------------------- @@ -159,9 +161,7 @@ void LLToast::tick() { if(mCanFade) { - setVisible(FALSE); - mTimer.stop(); - mOnFadeSignal(this); + hide(); } } @@ -206,6 +206,16 @@ void LLToast::draw() //-------------------------------------------------------------------------- void LLToast::setVisible(BOOL show) { + if(mIsHidden) + { + // this toast is invisible after fade until its ScreenChannel will allow it + // + // (EXT-1849) according to this bug a toast can be resurrected from + // invisible state if it faded during a teleportation + // then it fades a second time and causes a crash + return; + } + if(show) { setBackgroundOpaque(TRUE); diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h index 1826c13ebc..0698c94880 100644 --- a/indra/newview/lltoast.h +++ b/indra/newview/lltoast.h @@ -125,6 +125,8 @@ public: void setCanBeStored(bool can_be_stored) { mCanBeStored = can_be_stored; } // bool getCanBeStored() { return mCanBeStored; } + // set whether this toast considered as hidden or not + void setIsHidden( bool is_toast_hidden ) { mIsHidden = is_toast_hidden; } // Registers signals/callbacks for events @@ -164,6 +166,7 @@ private: bool mCanBeStored; bool mHideBtnEnabled; bool mHideBtnPressed; + bool mIsHidden; // this flag is TRUE when a toast has faded or was hidden with (x) button (EXT-1849) }; } diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 7c17699bf9..42ecfa8cde 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -732,7 +732,44 @@ BOOL LLToolPie::handleToolTip(S32 local_x, S32 local_y, MASK mask) { tooltip_msg.append( nodep->mName ); } - + + bool is_time_based_media = false; + bool is_media_playing = false; + + // Does this face have media? + const LLTextureEntry* tep = hover_object->getTE(mHoverPick.mObjectFace); + + if(tep) + { + const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL; + if (mep) + { + viewer_media_t media_impl = mep ? LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()) : NULL; + LLPluginClassMedia* media_plugin = NULL; + + if (media_impl.notNull() && (media_impl->hasMedia())) + { + LLStringUtil::format_map_t args; + + media_plugin = media_impl->getMediaPlugin(); + if(media_plugin) + { if(media_plugin->pluginSupportsMediaTime()) + { + is_time_based_media = true; + args["[CurrentURL]"] = media_impl->getMediaURL(); + is_media_playing = media_impl->isMediaPlaying(); + } + else + { + is_time_based_media = false; + args["[CurrentURL]"] = media_plugin->getLocation(); + } + //tooltip_msg.append(LLTrans::getString("CurrentURL", args)); + } + } + } + } + bool needs_tip = needs_tooltip(nodep); if (show_all_object_tips || needs_tip) @@ -741,8 +778,11 @@ BOOL LLToolPie::handleToolTip(S32 local_x, S32 local_y, MASK mask) mPick = mHoverPick; LLToolTipMgr::instance().show(LLToolTip::Params() .message(tooltip_msg) - .image(LLUI::getUIImage("Info")) + .image(LLUI::getUIImage("Info_Off")) .click_callback(boost::bind(showObjectInspector, hover_object->getID())) + .time_based_media(is_time_based_media) + .media_playing(is_media_playing) + .click_playmedia_callback(boost::bind(playCurrentMedia, mHoverPick)) .visible_time_near(6.f) .visible_time_far(3.f) .wrap(false)); @@ -925,6 +965,20 @@ static void show_inspector(const char* inspector, const char* param, const LLUUI LLFloaterReg::showInstance(inspector, params); } + +static void show_inspector(const char* inspector, LLSD& params) +{ + if (LLToolTipMgr::instance().toolTipVisible()) + { + LLRect rect = LLToolTipMgr::instance().getToolTipRect(); + params["pos"]["x"] = rect.mLeft; + params["pos"]["y"] = rect.mTop; + } + + LLFloaterReg::showInstance(inspector, params); +} + + // static void LLToolPie::showAvatarInspector(const LLUUID& avatar_id) { @@ -937,6 +991,72 @@ void LLToolPie::showObjectInspector(const LLUUID& object_id) show_inspector("inspect_object", "object_id", object_id); } + +// static +void LLToolPie::showObjectInspector(const LLUUID& object_id, const S32& object_face) +{ + LLSD params; + params["object_id"] = object_id; + params["object_face"] = object_face; + show_inspector("inspect_object", params); +} + +// static +void LLToolPie::playCurrentMedia(const LLPickInfo& info) +{ + //FIXME: how do we handle object in different parcel than us? + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (!parcel) return; + + LLPointer<LLViewerObject> objectp = info.getObject(); + + // Early out cases. Must clear media hover. + // did not hit an object or did not hit a valid face + if ( objectp.isNull() || + info.mObjectFace < 0 || + info.mObjectFace >= objectp->getNumTEs() ) + { + return; + } + + // Does this face have media? + const LLTextureEntry* tep = objectp->getTE(info.mObjectFace); + if (!tep) + return; + + const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL; + if(!mep) + return; + + LLPluginClassMedia* media_plugin = NULL; + + if (gSavedSettings.getBOOL("MediaOnAPrimUI")) + { + viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); + + if(media_impl.notNull() && media_impl->hasMedia()) + { + media_plugin = media_impl->getMediaPlugin(); + + if (media_plugin && media_plugin->pluginSupportsMediaTime()) + { + if(media_impl->isMediaPlaying()) + { + media_impl->pause(); + } + else //if(media_impl->isMediaPaused()) + { + media_impl->play(); + } + + } + + } + } + +} + + void LLToolPie::handleDeselect() { if( hasMouseCapture() ) @@ -1035,12 +1155,17 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) // Does this face have media? const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); + if(!tep) + return false; + LLMediaEntry* mep = (tep->hasMedia()) ? tep->getMediaData() : NULL; + + if(!mep) + return false; + viewer_media_t media_impl = mep ? LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()) : NULL; - if (tep - && mep - && gSavedSettings.getBOOL("MediaOnAPrimUI") + if (gSavedSettings.getBOOL("MediaOnAPrimUI") && media_impl.notNull()) { if (!LLViewerMediaFocus::getInstance()->isFocusedOnFace(pick.getObject(), pick.mObjectFace) ) @@ -1085,6 +1210,9 @@ bool LLToolPie::handleMediaHover(const LLPickInfo& pick) // Does this face have media? const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); + if(!tep) + return false; + const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL; if (mep && gSavedSettings.getBOOL("MediaOnAPrimUI")) diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index 5faedbec5a..3cf9cbde55 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -78,6 +78,8 @@ public: static void showAvatarInspector(const LLUUID& avatar_id); static void showObjectInspector(const LLUUID& object_id); + static void showObjectInspector(const LLUUID& object_id, const S32& object_face); + static void playCurrentMedia(const LLPickInfo& info); private: BOOL outsideSlop (S32 x, S32 y, S32 start_x, S32 start_y); BOOL pickLeftMouseDownCallback(); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 57a4117d5d..366e5602bd 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -42,7 +42,6 @@ #include "llconsole.h" #include "llinventorymodel.h" #include "llnotify.h" -#include "llimview.h" #include "llgesturemgr.h" #include "llinventorybridge.h" diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 02fda191be..8bd74dcb04 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -156,10 +156,10 @@ public: { if(!mInitialized && ! mime_type.empty()) { - if (mMediaImpl->initializeMedia(mime_type)) + if(mMediaImpl->initializeMedia(mime_type)) { mInitialized = true; - mMediaImpl->play(); + mMediaImpl->loadURI(); } } } @@ -267,10 +267,6 @@ viewer_media_t LLViewerMedia::updateMediaImpl(LLMediaEntry* media_entry, const s { needs_navigate = (media_entry->getCurrentURL() != previous_url); } - else if(!media_entry->getHomeURL().empty()) - { - needs_navigate = (media_entry->getHomeURL() != previous_url); - } } } else @@ -293,8 +289,6 @@ viewer_media_t LLViewerMedia::updateMediaImpl(LLMediaEntry* media_entry, const s if(media_impl && needs_navigate) { std::string url = media_entry->getCurrentURL(); - if(url.empty()) - url = media_entry->getHomeURL(); media_impl->navigateTo(url, "", true, true); } @@ -639,13 +633,14 @@ LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id, mUsedInUI(false), mHasFocus(false), mPriority(LLPluginClassMedia::PRIORITY_UNLOADED), - mDoNavigateOnLoad(false), - mDoNavigateOnLoadRediscoverType(false), - mDoNavigateOnLoadServerRequest(false), + mNavigateRediscoverType(false), + mNavigateServerRequest(false), mMediaSourceFailed(false), mRequestedVolume(1.0f), mIsMuted(false), mNeedsMuteCheck(false), + mPreviousMediaState(MEDIA_NONE), + mPreviousMediaTime(0.0f), mIsUpdated(false) { @@ -716,7 +711,6 @@ bool LLViewerMediaImpl::initializeMedia(const std::string& mime_type) mMimeType = mime_type; } - // play(); return (mMediaSource != NULL); } @@ -729,16 +723,13 @@ void LLViewerMediaImpl::createMediaSource() return; } - if(mDoNavigateOnLoad) + if(! mMediaURL.empty()) { - if(! mMediaURL.empty()) - { - navigateTo(mMediaURL, mMimeType, mDoNavigateOnLoadRediscoverType, mDoNavigateOnLoadServerRequest); - } - else if(! mMimeType.empty()) - { - initializeMedia(mMimeType); - } + navigateInternal(); + } + else if(! mMimeType.empty()) + { + initializeMedia(mMimeType); } } @@ -869,6 +860,46 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) return false; } +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::loadURI() +{ + if(mMediaSource) + { + mMediaSource->loadURI( mMediaURL ); + + if(mPreviousMediaState == MEDIA_PLAYING) + { + // This media was playing before this instance was unloaded. + + if(mPreviousMediaTime != 0.0f) + { + // Seek back to where we left off, if possible. + seek(mPreviousMediaTime); + } + + start(); + } + else if(mPreviousMediaState == MEDIA_PAUSED) + { + // This media was paused before this instance was unloaded. + + if(mPreviousMediaTime != 0.0f) + { + // Seek back to where we left off, if possible. + seek(mPreviousMediaTime); + } + + pause(); + } + else + { + // No relevant previous media play state -- if we're loading the URL, we want to start playing. + start(); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::setSize(int width, int height) { mMediaWidth = width; @@ -882,24 +913,21 @@ void LLViewerMediaImpl::setSize(int width, int height) ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::play() { - // first stop any previously playing media - // stop(); - - // mMediaSource->addObserver( this ); + // If the media source isn't there, try to initialize it and load an URL. if(mMediaSource == NULL) { - if(!initializePlugin(mMimeType)) + if(!initializeMedia(mMimeType)) { // This may be the case where the plugin's priority is PRIORITY_UNLOADED return; } + + // Only do this if the media source was just loaded. + loadURI(); } - mMediaSource->loadURI( mMediaURL ); - if(/*mMediaSource->pluginSupportsMediaTime()*/ true) - { - start(); - } + // always start the media + start(); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1165,27 +1193,21 @@ void LLViewerMediaImpl::navigateHome() ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type, bool server_request) { - if(server_request) - { - setNavState(MEDIANAVSTATE_SERVER_SENT); - } - else + if(mMediaURL != url) { - setNavState(MEDIANAVSTATE_NONE); + // Don't carry media play state across distinct URLs. + resetPreviousMediaState(); } // Always set the current URL and MIME type. mMediaURL = url; mMimeType = mime_type; - // If the current URL is not null, make the instance do a navigate on load. - mDoNavigateOnLoad = !mMediaURL.empty(); - // if mime type discovery was requested, we'll need to do it when the media loads - mDoNavigateOnLoadRediscoverType = rediscover_type; + mNavigateRediscoverType = rediscover_type; // and if this was a server request, the navigate on load will also need to be one. - mDoNavigateOnLoadServerRequest = server_request; + mNavigateServerRequest = server_request; // An explicit navigate resets the "failed" flag. mMediaSourceFailed = false; @@ -1193,7 +1215,7 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi if(mPriority == LLPluginClassMedia::PRIORITY_UNLOADED) { // Helpful to have media urls in log file. Shouldn't be spammy. - llinfos << "UNLOADED media id= " << mTextureId << " url=" << url << " mime_type=" << mime_type << llendl; + llinfos << "NOT LOADING media id= " << mTextureId << " url=" << url << " mime_type=" << mime_type << llendl; // This impl should not be loaded at this time. LL_DEBUGS("PluginPriority") << this << "Not loading (PRIORITY_UNLOADED)" << LL_ENDL; @@ -1201,10 +1223,24 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi return; } + navigateInternal(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateInternal() +{ // Helpful to have media urls in log file. Shouldn't be spammy. - llinfos << "media id= " << mTextureId << " url=" << url << " mime_type=" << mime_type << llendl; - - + llinfos << "media id= " << mTextureId << " url=" << mMediaURL << " mime_type=" << mMimeType << llendl; + + if(mNavigateServerRequest) + { + setNavState(MEDIANAVSTATE_SERVER_SENT); + } + else + { + setNavState(MEDIANAVSTATE_NONE); + } + // If the caller has specified a non-empty MIME type, look that up in our MIME types list. // If we have a plugin for that MIME type, use that instead of attempting auto-discovery. // This helps in supporting legacy media content where the server the media resides on returns a bogus MIME type @@ -1216,11 +1252,11 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi if(!plugin_basename.empty()) { // We have a plugin for this mime type - rediscover_type = false; + mNavigateRediscoverType = false; } } - if(rediscover_type) + if(mNavigateRediscoverType) { LLURI uri(mMediaURL); @@ -1236,7 +1272,7 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi // We use "data" internally for a text/html url for loading the login screen if(initializeMedia("text/html")) { - mMediaSource->loadURI( mMediaURL ); + loadURI(); } } else @@ -1244,24 +1280,18 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi // This catches 'rtsp://' urls if(initializeMedia(scheme)) { - mMediaSource->loadURI( mMediaURL ); + loadURI(); } } } - else if (mMediaSource) + else if(initializeMedia(mMimeType)) { - mMediaSource->loadURI( mMediaURL ); - } - else if(initializeMedia(mime_type) && mMediaSource) - { - mMediaSource->loadURI( mMediaURL ); + loadURI(); } else { - LL_WARNS("Media") << "Couldn't navigate to: " << url << " as there is no media type for: " << mime_type << LL_ENDL; - return; + LL_WARNS("Media") << "Couldn't navigate to: " << mMediaURL << " as there is no media type for: " << mMimeType << LL_ENDL; } - } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1390,6 +1420,7 @@ void LLViewerMediaImpl::update() if(mMediaSource->isPluginExited()) { + resetPreviousMediaState(); destroyMediaSource(); return; } @@ -1586,6 +1617,14 @@ bool LLViewerMediaImpl::hasMedia() } ////////////////////////////////////////////////////////////////////////////////////////// +// +void LLViewerMediaImpl::resetPreviousMediaState() +{ + mPreviousMediaState = MEDIA_NONE; + mPreviousMediaTime = 0.0f; +} + +////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginClassMediaOwner::EMediaEvent event) { switch(event) @@ -1595,6 +1634,9 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla // The plugin failed to load properly. Make sure the timer doesn't retry. // TODO: maybe mark this plugin as not loadable somehow? mMediaSourceFailed = true; + + // Reset the last known state of the media to defaults. + resetPreviousMediaState(); // TODO: may want a different message for this case? LLSD args; @@ -1608,6 +1650,9 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla // The plugin crashed. mMediaSourceFailed = true; + // Reset the last known state of the media to defaults. + resetPreviousMediaState(); + LLSD args; args["PLUGIN"] = LLMIMETypes::implType(mMimeType); // SJB: This is getting called every frame if the plugin fails to load, continuously respawining the alert! @@ -1833,11 +1878,11 @@ void LLViewerMediaImpl::setUsedInUI(bool used_in_ui) { if(getVisible()) { - mPriority = LLPluginClassMedia::PRIORITY_NORMAL; + setPriority(LLPluginClassMedia::PRIORITY_NORMAL); } else { - mPriority = LLPluginClassMedia::PRIORITY_HIDDEN; + setPriority(LLPluginClassMedia::PRIORITY_HIDDEN); } createMediaSource(); @@ -1858,6 +1903,15 @@ F64 LLViewerMediaImpl::getCPUUsage() const void LLViewerMediaImpl::setPriority(LLPluginClassMedia::EPriority priority) { + if(mPriority != priority) + { + LL_INFOS("PluginPriority") + << "changing priority of media id " << mTextureId + << " from " << LLPluginClassMedia::priorityToString(mPriority) + << " to " << LLPluginClassMedia::priorityToString(priority) + << LL_ENDL; + } + mPriority = priority; if(priority == LLPluginClassMedia::PRIORITY_UNLOADED) @@ -1865,6 +1919,11 @@ void LLViewerMediaImpl::setPriority(LLPluginClassMedia::EPriority priority) if(mMediaSource) { // Need to unload the media source + + // First, save off previous media state + mPreviousMediaState = mMediaSource->getStatus(); + mPreviousMediaTime = mMediaSource->getCurrentTime(); + destroyMediaSource(); } } diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index b15314e954..4f0d39dd80 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -123,6 +123,7 @@ public: void setMediaType(const std::string& media_type); bool initializeMedia(const std::string& mime_type); bool initializePlugin(const std::string& media_type); + void loadURI(); LLPluginClassMedia* getMediaPlugin() { return mMediaSource; } void setSize(int width, int height); @@ -151,6 +152,7 @@ public: void navigateReload(); void navigateHome(); void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false, bool server_request = false); + void navigateInternal(); void navigateStop(); bool handleKeyHere(KEY key, MASK mask); bool handleUnicodeCharHere(llwchar uni_char); @@ -174,6 +176,7 @@ public: bool isMediaPaused(); bool hasMedia(); bool isMediaFailed() { return mMediaSourceFailed; }; + void resetPreviousMediaState(); ECursorType getLastSetCursor() { return mLastSetCursor; }; @@ -287,14 +290,14 @@ public: bool mUsedInUI; bool mHasFocus; LLPluginClassMedia::EPriority mPriority; - bool mDoNavigateOnLoad; - bool mDoNavigateOnLoadRediscoverType; - bool mDoNavigateOnLoadServerRequest; + bool mNavigateRediscoverType; + bool mNavigateServerRequest; bool mMediaSourceFailed; F32 mRequestedVolume; bool mIsMuted; bool mNeedsMuteCheck; - + int mPreviousMediaState; + F64 mPreviousMediaTime; private: BOOL mIsUpdated ; diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index b47e0b8406..5d0b77d4fb 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -35,7 +35,7 @@ //LLViewerMediaFocus #include "llviewerobjectlist.h" -#include "llpanelmediahud.h" +#include "llpanelprimmediacontrols.h" #include "llpluginclassmedia.h" #include "llagent.h" #include "lltoolpie.h" @@ -106,19 +106,19 @@ void LLViewerMediaFocus::setFocusFace(LLPointer<LLViewerObject> objectp, S32 fac // We must do this before processing the media HUD zoom, or it may zoom to the wrong face. update(); - if(mMediaHUD.get() && face_auto_zoom && ! parcel->getMediaPreventCameraZoom()) + if(mMediaControls.get() && face_auto_zoom && ! parcel->getMediaPreventCameraZoom()) { - mMediaHUD.get()->resetZoomLevel(); - mMediaHUD.get()->nextZoomLevel(); + mMediaControls.get()->resetZoomLevel(); + mMediaControls.get()->nextZoomLevel(); } } else { if(mFocusedImplID != LLUUID::null) { - if(mMediaHUD.get()) + if(mMediaControls.get()) { - mMediaHUD.get()->resetZoomLevel(); + mMediaControls.get()->resetZoomLevel(); } gFocusMgr.setKeyboardFocus(NULL); @@ -327,20 +327,20 @@ void LLViewerMediaFocus::update() // We have an object and impl to point at. // Make sure the media HUD object exists. - if(! mMediaHUD.get()) + if(! mMediaControls.get()) { - LLPanelMediaHUD* media_hud = new LLPanelMediaHUD(); - mMediaHUD = media_hud->getHandle(); - gHUDView->addChild(media_hud); + LLPanelPrimMediaControls* media_controls = new LLPanelPrimMediaControls(); + mMediaControls = media_controls->getHandle(); + gHUDView->addChild(media_controls); } - mMediaHUD.get()->setMediaFace(viewer_object, face, media_impl, normal); + mMediaControls.get()->setMediaFace(viewer_object, face, media_impl, normal); } else { // The media HUD is no longer needed. - if(mMediaHUD.get()) + if(mMediaControls.get()) { - mMediaHUD.get()->setMediaFace(NULL, 0, NULL); + mMediaControls.get()->setMediaFace(NULL, 0, NULL); } } } diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h index c77533ba5a..c1179de39d 100644 --- a/indra/newview/llviewermediafocus.h +++ b/indra/newview/llviewermediafocus.h @@ -40,7 +40,7 @@ #include "llselectmgr.h" class LLViewerMediaImpl; -class LLPanelMediaHUD; +class LLPanelPrimMediaControls; class LLViewerMediaFocus : public LLFocusableElement, @@ -88,7 +88,7 @@ protected: private: - LLHandle<LLPanelMediaHUD> mMediaHUD; + LLHandle<LLPanelPrimMediaControls> mMediaControls; LLUUID mFocusedObjectID; S32 mFocusedObjectFace; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 058f44ef57..864cf9d57b 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3483,7 +3483,6 @@ void set_god_level(U8 god_level) { U8 old_god_level = gAgent.getGodLevel(); gAgent.setGodLevel( god_level ); - gIMMgr->refresh(); LLViewerParcelMgr::getInstance()->notifyObservers(); // God mode changes sim visibility diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 791ec07349..320f0f83ff 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -72,7 +72,6 @@ #include "llviewercontrol.h" #include "lldrawpool.h" #include "llfirstuse.h" -#include "llfloateractivespeakers.h" #include "llfloateranimpreview.h" #include "llfloaterbuycurrency.h" #include "llfloaterbuyland.h" @@ -5680,7 +5679,7 @@ void onCovenantLoadComplete(LLVFS *vfs, LLPanelLandCovenant::updateCovenantText(covenant_text); LLFloaterBuyLand::updateCovenantText(covenant_text, asset_uuid); - LLPanelPlaceInfo* panel = dynamic_cast<LLPanelPlaceInfo*>(LLSideTray::getInstance()->showPanel("panel_place_info", LLSD())); + LLPanelPlaceInfo* panel = LLSideTray::getInstance()->findChild<LLPanelPlaceInfo>("panel_place_info"); if (panel) { panel->updateCovenantText(covenant_text); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index f141d33729..b0b69fbae6 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -142,7 +142,6 @@ #include "llstatview.h" #include "llsurface.h" #include "llsurfacepatch.h" -#include "llimview.h" #include "lltexlayer.h" #include "lltextbox.h" #include "lltexturecache.h" diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index a402aff8ab..f9c95afc31 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -6400,6 +6400,11 @@ LLBBox LLVOAvatar::getHUDBBox() const ++attachment_iter) { const LLViewerObject* attached_object = (*attachment_iter); + if (attached_object == NULL) + { + llwarns << "HUD attached object is NULL!" << llendl; + continue; + } // initialize bounding box to contain identity orientation and center point for attached object bbox.addPointLocal(attached_object->getPosition()); // add rotated bounding box for attached object diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp new file mode 100644 index 0000000000..96fcf61e62 --- /dev/null +++ b/indra/newview/llvoicechannel.cpp @@ -0,0 +1,872 @@ +/** + * @file llvoicechannel.cpp + * @brief Voice Channel related classes + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llfloaterreg.h" +#include "llimview.h" +#include "llnotifications.h" +#include "llpanel.h" +#include "llrecentpeople.h" +#include "llviewercontrol.h" +#include "llvoicechannel.h" + + +LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; +LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; +LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL; +LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL; + +BOOL LLVoiceChannel::sSuspended = FALSE; + +// +// Constants +// +const U32 DEFAULT_RETRIES_COUNT = 3; + + +class LLVoiceCallCapResponder : public LLHTTPClient::Responder +{ +public: + LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {}; + + virtual void error(U32 status, const std::string& reason); // called with bad status codes + virtual void result(const LLSD& content); + +private: + LLUUID mSessionID; +}; + + +void LLVoiceCallCapResponder::error(U32 status, const std::string& reason) +{ + llwarns << "LLVoiceCallCapResponder::error(" + << status << ": " << reason << ")" + << llendl; + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); + if ( channelp ) + { + if ( 403 == status ) + { + //403 == no ability + LLNotifications::instance().add( + "VoiceNotAllowed", + channelp->getNotifyArgs()); + } + else + { + LLNotifications::instance().add( + "VoiceCallGenericError", + channelp->getNotifyArgs()); + } + channelp->deactivate(); + } +} + +void LLVoiceCallCapResponder::result(const LLSD& content) +{ + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); + if (channelp) + { + //*TODO: DEBUG SPAM + LLSD::map_const_iterator iter; + for(iter = content.beginMap(); iter != content.endMap(); ++iter) + { + llinfos << "LLVoiceCallCapResponder::result got " + << iter->first << llendl; + } + + channelp->setChannelInfo( + content["voice_credentials"]["channel_uri"].asString(), + content["voice_credentials"]["channel_credentials"].asString()); + } +} + +// +// LLVoiceChannel +// +LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& session_name) : + mSessionID(session_id), + mState(STATE_NO_CHANNEL_INFO), + mSessionName(session_name), + mIgnoreNextSessionLeave(FALSE) +{ + mNotifyArgs["VOICE_CHANNEL_NAME"] = mSessionName; + + if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second) + { + // a voice channel already exists for this session id, so this instance will be orphaned + // the end result should simply be the failure to make voice calls + llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl; + } + + LLVoiceClient::getInstance()->addObserver(this); +} + +LLVoiceChannel::~LLVoiceChannel() +{ + // Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed. + if(gVoiceClient) + { + gVoiceClient->removeObserver(this); + } + + sVoiceChannelMap.erase(mSessionID); + sVoiceChannelURIMap.erase(mURI); +} + +void LLVoiceChannel::setChannelInfo( + const std::string& uri, + const std::string& credentials) +{ + setURI(uri); + + mCredentials = credentials; + + if (mState == STATE_NO_CHANNEL_INFO) + { + if (mURI.empty()) + { + LLNotifications::instance().add("VoiceChannelJoinFailed", mNotifyArgs); + llwarns << "Received empty URI for channel " << mSessionName << llendl; + deactivate(); + } + else if (mCredentials.empty()) + { + LLNotifications::instance().add("VoiceChannelJoinFailed", mNotifyArgs); + llwarns << "Received empty credentials for channel " << mSessionName << llendl; + deactivate(); + } + else + { + setState(STATE_READY); + + // if we are supposed to be active, reconnect + // this will happen on initial connect, as we request credentials on first use + if (sCurrentVoiceChannel == this) + { + // just in case we got new channel info while active + // should move over to new channel + activate(); + } + } + } +} + +void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal) +{ + if (channelURI != mURI) + { + return; + } + + if (type < BEGIN_ERROR_STATUS) + { + handleStatusChange(type); + } + else + { + handleError(type); + } +} + +void LLVoiceChannel::handleStatusChange(EStatusType type) +{ + // status updates + switch(type) + { + case STATUS_LOGIN_RETRY: + //mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle(); + LLNotifications::instance().add("VoiceLoginRetry"); + break; + case STATUS_LOGGED_IN: + //if (!mLoginNotificationHandle.isDead()) + //{ + // LLNotifyBox* notifyp = (LLNotifyBox*)mLoginNotificationHandle.get(); + // if (notifyp) + // { + // notifyp->close(); + // } + // mLoginNotificationHandle.markDead(); + //} + break; + case STATUS_LEFT_CHANNEL: + if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) + { + // if forceably removed from channel + // update the UI and revert to default channel + LLNotifications::instance().add("VoiceChannelDisconnected", mNotifyArgs); + deactivate(); + } + mIgnoreNextSessionLeave = FALSE; + break; + case STATUS_JOINING: + if (callStarted()) + { + setState(STATE_RINGING); + } + break; + case STATUS_JOINED: + if (callStarted()) + { + setState(STATE_CONNECTED); + } + default: + break; + } +} + +// default behavior is to just deactivate channel +// derived classes provide specific error messages +void LLVoiceChannel::handleError(EStatusType type) +{ + deactivate(); + setState(STATE_ERROR); +} + +BOOL LLVoiceChannel::isActive() +{ + // only considered active when currently bound channel matches what our channel + return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI; +} + +BOOL LLVoiceChannel::callStarted() +{ + return mState >= STATE_CALL_STARTED; +} + +void LLVoiceChannel::deactivate() +{ + if (mState >= STATE_RINGING) + { + // ignore session leave event + mIgnoreNextSessionLeave = TRUE; + } + + if (callStarted()) + { + setState(STATE_HUNG_UP); + // mute the microphone if required when returning to the proximal channel + if (gSavedSettings.getBOOL("AutoDisengageMic") && sCurrentVoiceChannel == this) + { + gSavedSettings.setBOOL("PTTCurrentlyEnabled", true); + } + } + + if (sCurrentVoiceChannel == this) + { + // default channel is proximal channel + sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); + sCurrentVoiceChannel->activate(); + } +} + +void LLVoiceChannel::activate() +{ + if (callStarted()) + { + return; + } + + // deactivate old channel and mark ourselves as the active one + if (sCurrentVoiceChannel != this) + { + // mark as current before deactivating the old channel to prevent + // activating the proximal channel between IM calls + LLVoiceChannel* old_channel = sCurrentVoiceChannel; + sCurrentVoiceChannel = this; + if (old_channel) + { + old_channel->deactivate(); + } + } + + if (mState == STATE_NO_CHANNEL_INFO) + { + // responsible for setting status to active + getChannelInfo(); + } + else + { + setState(STATE_CALL_STARTED); + } +} + +void LLVoiceChannel::getChannelInfo() +{ + // pretend we have everything we need + if (sCurrentVoiceChannel == this) + { + setState(STATE_CALL_STARTED); + } +} + +//static +LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id) +{ + voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id); + if (found_it == sVoiceChannelMap.end()) + { + return NULL; + } + else + { + return found_it->second; + } +} + +//static +LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri) +{ + voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri); + if (found_it == sVoiceChannelURIMap.end()) + { + return NULL; + } + else + { + return found_it->second; + } +} + +void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id) +{ + sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID)); + mSessionID = new_session_id; + sVoiceChannelMap.insert(std::make_pair(mSessionID, this)); +} + +void LLVoiceChannel::setURI(std::string uri) +{ + sVoiceChannelURIMap.erase(mURI); + mURI = uri; + sVoiceChannelURIMap.insert(std::make_pair(mURI, this)); +} + +void LLVoiceChannel::setState(EState state) +{ + switch(state) + { + case STATE_RINGING: + gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); + break; + case STATE_CONNECTED: + gIMMgr->addSystemMessage(mSessionID, "connected", mNotifyArgs); + break; + case STATE_HUNG_UP: + gIMMgr->addSystemMessage(mSessionID, "hang_up", mNotifyArgs); + break; + default: + break; + } + + mState = state; +} + +void LLVoiceChannel::toggleCallWindowIfNeeded(EState state) +{ + if (state == STATE_CONNECTED) + { + LLFloaterReg::showInstance("voice_call", mSessionID); + } + // By checking that current state is CONNECTED we make sure that the call window + // has been shown, hence there's something to hide. This helps when user presses + // the "End call" button right after initiating the call. + // *TODO: move this check to LLFloaterCall? + else if (state == STATE_HUNG_UP && mState == STATE_CONNECTED) + { + LLFloaterReg::hideInstance("voice_call", mSessionID); + } +} + +//static +void LLVoiceChannel::initClass() +{ + sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); +} + + +//static +void LLVoiceChannel::suspend() +{ + if (!sSuspended) + { + sSuspendedVoiceChannel = sCurrentVoiceChannel; + sSuspended = TRUE; + } +} + +//static +void LLVoiceChannel::resume() +{ + if (sSuspended) + { + if (gVoiceClient->voiceEnabled()) + { + if (sSuspendedVoiceChannel) + { + sSuspendedVoiceChannel->activate(); + } + else + { + LLVoiceChannelProximal::getInstance()->activate(); + } + } + sSuspended = FALSE; + } +} + + +// +// LLVoiceChannelGroup +// + +LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name) : + LLVoiceChannel(session_id, session_name) +{ + mRetries = DEFAULT_RETRIES_COUNT; + mIsRetrying = FALSE; +} + +void LLVoiceChannelGroup::deactivate() +{ + if (callStarted()) + { + LLVoiceClient::getInstance()->leaveNonSpatialChannel(); + } + LLVoiceChannel::deactivate(); +} + +void LLVoiceChannelGroup::activate() +{ + if (callStarted()) return; + + LLVoiceChannel::activate(); + + if (callStarted()) + { + // we have the channel info, just need to use it now + LLVoiceClient::getInstance()->setNonSpatialChannel( + mURI, + mCredentials); + +#if 0 // *TODO + if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel + { + // Add the party to the list of people with which we've recently interacted. + for (/*people in the chat*/) + LLRecentPeople::instance().add(buddy_id); + } +#endif + } +} + +void LLVoiceChannelGroup::getChannelInfo() +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string url = region->getCapability("ChatSessionRequest"); + LLSD data; + data["method"] = "call"; + data["session-id"] = mSessionID; + LLHTTPClient::post(url, + data, + new LLVoiceCallCapResponder(mSessionID)); + } +} + +void LLVoiceChannelGroup::setChannelInfo( + const std::string& uri, + const std::string& credentials) +{ + setURI(uri); + + mCredentials = credentials; + + if (mState == STATE_NO_CHANNEL_INFO) + { + if(!mURI.empty() && !mCredentials.empty()) + { + setState(STATE_READY); + + // if we are supposed to be active, reconnect + // this will happen on initial connect, as we request credentials on first use + if (sCurrentVoiceChannel == this) + { + // just in case we got new channel info while active + // should move over to new channel + activate(); + } + } + else + { + //*TODO: notify user + llwarns << "Received invalid credentials for channel " << mSessionName << llendl; + deactivate(); + } + } + else if ( mIsRetrying ) + { + // we have the channel info, just need to use it now + LLVoiceClient::getInstance()->setNonSpatialChannel( + mURI, + mCredentials); + } +} + +void LLVoiceChannelGroup::handleStatusChange(EStatusType type) +{ + // status updates + switch(type) + { + case STATUS_JOINED: + mRetries = 3; + mIsRetrying = FALSE; + default: + break; + } + + LLVoiceChannel::handleStatusChange(type); +} + +void LLVoiceChannelGroup::handleError(EStatusType status) +{ + std::string notify; + switch(status) + { + case ERROR_CHANNEL_LOCKED: + case ERROR_CHANNEL_FULL: + notify = "VoiceChannelFull"; + break; + case ERROR_NOT_AVAILABLE: + //clear URI and credentials + //set the state to be no info + //and activate + if ( mRetries > 0 ) + { + mRetries--; + mIsRetrying = TRUE; + mIgnoreNextSessionLeave = TRUE; + + getChannelInfo(); + return; + } + else + { + notify = "VoiceChannelJoinFailed"; + mRetries = DEFAULT_RETRIES_COUNT; + mIsRetrying = FALSE; + } + + break; + + case ERROR_UNKNOWN: + default: + break; + } + + // notification + if (!notify.empty()) + { + LLNotificationPtr notification = LLNotifications::instance().add(notify, mNotifyArgs); + // echo to im window + gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, notification->getMessage()); + } + + LLVoiceChannel::handleError(status); +} + +void LLVoiceChannelGroup::setState(EState state) +{ + // HACK: Open/close the call window if needed. + toggleCallWindowIfNeeded(state); + + switch(state) + { + case STATE_RINGING: + if ( !mIsRetrying ) + { + gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); + } + + mState = state; + break; + default: + LLVoiceChannel::setState(state); + } +} + +// +// LLVoiceChannelProximal +// +LLVoiceChannelProximal::LLVoiceChannelProximal() : + LLVoiceChannel(LLUUID::null, LLStringUtil::null) +{ + activate(); +} + +BOOL LLVoiceChannelProximal::isActive() +{ + return callStarted() && LLVoiceClient::getInstance()->inProximalChannel(); +} + +void LLVoiceChannelProximal::activate() +{ + if (callStarted()) return; + + LLVoiceChannel::activate(); + + if (callStarted()) + { + // this implicitly puts you back in the spatial channel + LLVoiceClient::getInstance()->leaveNonSpatialChannel(); + } +} + +void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) +{ + if (!proximal) + { + return; + } + + if (type < BEGIN_ERROR_STATUS) + { + handleStatusChange(type); + } + else + { + handleError(type); + } +} + +void LLVoiceChannelProximal::handleStatusChange(EStatusType status) +{ + // status updates + switch(status) + { + case STATUS_LEFT_CHANNEL: + // do not notify user when leaving proximal channel + return; + case STATUS_VOICE_DISABLED: + gIMMgr->addSystemMessage(LLUUID::null, "unavailable", mNotifyArgs); + return; + default: + break; + } + LLVoiceChannel::handleStatusChange(status); +} + + +void LLVoiceChannelProximal::handleError(EStatusType status) +{ + std::string notify; + switch(status) + { + case ERROR_CHANNEL_LOCKED: + case ERROR_CHANNEL_FULL: + notify = "ProximalVoiceChannelFull"; + break; + default: + break; + } + + // notification + if (!notify.empty()) + { + LLNotifications::instance().add(notify, mNotifyArgs); + } + + LLVoiceChannel::handleError(status); +} + +void LLVoiceChannelProximal::deactivate() +{ + if (callStarted()) + { + setState(STATE_HUNG_UP); + } +} + + +// +// LLVoiceChannelP2P +// +LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) : + LLVoiceChannelGroup(session_id, session_name), + mOtherUserID(other_user_id), + mReceivedCall(FALSE) +{ + // make sure URI reflects encoded version of other user's agent id + setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); +} + +void LLVoiceChannelP2P::handleStatusChange(EStatusType type) +{ + // status updates + switch(type) + { + case STATUS_LEFT_CHANNEL: + if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) + { + if (mState == STATE_RINGING) + { + // other user declined call + LLNotifications::instance().add("P2PCallDeclined", mNotifyArgs); + } + else + { + // other user hung up + LLNotifications::instance().add("VoiceChannelDisconnectedP2P", mNotifyArgs); + } + deactivate(); + } + mIgnoreNextSessionLeave = FALSE; + return; + default: + break; + } + + LLVoiceChannel::handleStatusChange(type); +} + +void LLVoiceChannelP2P::handleError(EStatusType type) +{ + switch(type) + { + case ERROR_NOT_AVAILABLE: + LLNotifications::instance().add("P2PCallNoAnswer", mNotifyArgs); + break; + default: + break; + } + + LLVoiceChannel::handleError(type); +} + +void LLVoiceChannelP2P::activate() +{ + if (callStarted()) return; + + LLVoiceChannel::activate(); + + if (callStarted()) + { + // no session handle yet, we're starting the call + if (mSessionHandle.empty()) + { + mReceivedCall = FALSE; + LLVoiceClient::getInstance()->callUser(mOtherUserID); + } + // otherwise answering the call + else + { + LLVoiceClient::getInstance()->answerInvite(mSessionHandle); + + // using the session handle invalidates it. Clear it out here so we can't reuse it by accident. + mSessionHandle.clear(); + } + + // Add the party to the list of people with which we've recently interacted. + LLRecentPeople::instance().add(mOtherUserID); + } +} + +void LLVoiceChannelP2P::getChannelInfo() +{ + // pretend we have everything we need, since P2P doesn't use channel info + if (sCurrentVoiceChannel == this) + { + setState(STATE_CALL_STARTED); + } +} + +// receiving session from other user who initiated call +void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::string &inURI) +{ + BOOL needs_activate = FALSE; + if (callStarted()) + { + // defer to lower agent id when already active + if (mOtherUserID < gAgent.getID()) + { + // pretend we haven't started the call yet, so we can connect to this session instead + deactivate(); + needs_activate = TRUE; + } + else + { + // we are active and have priority, invite the other user again + // under the assumption they will join this new session + mSessionHandle.clear(); + LLVoiceClient::getInstance()->callUser(mOtherUserID); + return; + } + } + + mSessionHandle = handle; + + // The URI of a p2p session should always be the other end's SIP URI. + if(!inURI.empty()) + { + setURI(inURI); + } + else + { + setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); + } + + mReceivedCall = TRUE; + + if (needs_activate) + { + activate(); + } +} + +void LLVoiceChannelP2P::setState(EState state) +{ + // HACK: Open/close the call window if needed. + toggleCallWindowIfNeeded(state); + + // you only "answer" voice invites in p2p mode + // so provide a special purpose message here + if (mReceivedCall && state == STATE_RINGING) + { + gIMMgr->addSystemMessage(mSessionID, "answering", mNotifyArgs); + mState = state; + return; + } + LLVoiceChannel::setState(state); +} diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h new file mode 100644 index 0000000000..9966bdd5ab --- /dev/null +++ b/indra/newview/llvoicechannel.h @@ -0,0 +1,168 @@ +/** + * @file llvoicechannel.h + * @brief Voice channel related classes + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_VOICECHANNEL_H +#define LL_VOICECHANNEL_H + +#include "llhandle.h" +#include "llvoiceclient.h" + +class LLPanel; + +class LLVoiceChannel : public LLVoiceClientStatusObserver +{ +public: + typedef enum e_voice_channel_state + { + STATE_NO_CHANNEL_INFO, + STATE_ERROR, + STATE_HUNG_UP, + STATE_READY, + STATE_CALL_STARTED, + STATE_RINGING, + STATE_CONNECTED + } EState; + + LLVoiceChannel(const LLUUID& session_id, const std::string& session_name); + virtual ~LLVoiceChannel(); + + /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + + virtual void handleStatusChange(EStatusType status); + virtual void handleError(EStatusType status); + virtual void deactivate(); + virtual void activate(); + virtual void setChannelInfo( + const std::string& uri, + const std::string& credentials); + virtual void getChannelInfo(); + virtual BOOL isActive(); + virtual BOOL callStarted(); + const std::string& getSessionName() const { return mSessionName; } + + const LLUUID getSessionID() { return mSessionID; } + EState getState() { return mState; } + + void updateSessionID(const LLUUID& new_session_id); + const LLSD& getNotifyArgs() { return mNotifyArgs; } + + static LLVoiceChannel* getChannelByID(const LLUUID& session_id); + static LLVoiceChannel* getChannelByURI(std::string uri); + static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; } + static void initClass(); + + static void suspend(); + static void resume(); + +protected: + virtual void setState(EState state); + void toggleCallWindowIfNeeded(EState state); + void setURI(std::string uri); + + std::string mURI; + std::string mCredentials; + LLUUID mSessionID; + EState mState; + std::string mSessionName; + LLSD mNotifyArgs; + BOOL mIgnoreNextSessionLeave; + LLHandle<LLPanel> mLoginNotificationHandle; + + typedef std::map<LLUUID, LLVoiceChannel*> voice_channel_map_t; + static voice_channel_map_t sVoiceChannelMap; + + typedef std::map<std::string, LLVoiceChannel*> voice_channel_map_uri_t; + static voice_channel_map_uri_t sVoiceChannelURIMap; + + static LLVoiceChannel* sCurrentVoiceChannel; + static LLVoiceChannel* sSuspendedVoiceChannel; + static BOOL sSuspended; +}; + +class LLVoiceChannelGroup : public LLVoiceChannel +{ +public: + LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name); + + /*virtual*/ void handleStatusChange(EStatusType status); + /*virtual*/ void handleError(EStatusType status); + /*virtual*/ void activate(); + /*virtual*/ void deactivate(); + /*vritual*/ void setChannelInfo( + const std::string& uri, + const std::string& credentials); + /*virtual*/ void getChannelInfo(); + +protected: + virtual void setState(EState state); + +private: + U32 mRetries; + BOOL mIsRetrying; +}; + +class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoiceChannelProximal> +{ +public: + LLVoiceChannelProximal(); + + /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + /*virtual*/ void handleStatusChange(EStatusType status); + /*virtual*/ void handleError(EStatusType status); + /*virtual*/ BOOL isActive(); + /*virtual*/ void activate(); + /*virtual*/ void deactivate(); + +}; + +class LLVoiceChannelP2P : public LLVoiceChannelGroup +{ +public: + LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id); + + /*virtual*/ void handleStatusChange(EStatusType status); + /*virtual*/ void handleError(EStatusType status); + /*virtual*/ void activate(); + /*virtual*/ void getChannelInfo(); + + void setSessionHandle(const std::string& handle, const std::string &inURI); + +protected: + virtual void setState(EState state); + +private: + std::string mSessionHandle; + LLUUID mOtherUserID; + BOOL mReceivedCall; +}; + +#endif // LL_VOICECHANNEL_H diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 02f63a848b..2834284a9b 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -57,13 +57,13 @@ #include "llagent.h" #include "llcachename.h" #include "llimview.h" // for LLIMMgr -#include "llimpanel.h" // for LLVoiceChannel #include "llparcel.h" #include "llviewerparcelmgr.h" #include "llfirstuse.h" #include "llviewerwindow.h" #include "llviewercamera.h" #include "llvoavatarself.h" +#include "llvoicechannel.h" #include "llfloaterfriends.h" //VIVOX, inorder to refresh communicate panel #include "llfloaterchat.h" // for LLFloaterChat::addChat() diff --git a/indra/newview/llwearable.cpp b/indra/newview/llwearable.cpp index 8f74ea29ac..a091028ec2 100644 --- a/indra/newview/llwearable.cpp +++ b/indra/newview/llwearable.cpp @@ -545,7 +545,7 @@ BOOL LLWearable::isDirty() const else { // image found in current image list but not saved image list - return FALSE; + return TRUE; } } } diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index b46b766fc0..d3366cdcaa 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -144,6 +144,7 @@ <texture name="Info" file_name="icons/Info.png" preload="false" /> <texture name="Info_Small" file_name="icons/Info_Small.png" preload="false" /> <texture name="Info_Off" file_name="navbar/Info_Off.png" preload="false" /> + <texture name="Info_Over" file_name="icons/Info_Over.png" preload="false" /> <texture name="Info_Press" file_name="navbar/Info_Press.png" preload="false" /> <texture name="Inspector_Background" file_name="windows/Inspector_Background.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 072fafd06e..aa0b4094b4 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -28,7 +28,9 @@ follows="left|top|right|bottom" height="400" layout="topleft" + font="SansSerifSmall" left="1" + tab_padding_right="5" name="landtab" tab_position="top" top="20" diff --git a/indra/newview/skins/default/xui/en/floater_camera.xml b/indra/newview/skins/default/xui/en/floater_camera.xml index 520249c2a2..a713cc32a0 100644 --- a/indra/newview/skins/default/xui/en/floater_camera.xml +++ b/indra/newview/skins/default/xui/en/floater_camera.xml @@ -1,17 +1,17 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater can_dock="true" - can_minimize="true" - can_close="true" + can_minimize="false" + can_close="true" center_horiz="true" - follows="top" - height="110" + follows="bottom" + height="152" layout="topleft" name="camera_floater" help_topic="camera_floater" save_rect="true" save_visibility="true" - width="105"> + width="150"> <floater.string name="rotate_tooltip"> Rotate Camera Around Focus @@ -25,69 +25,71 @@ Move Camera Up and Down, Left and Right </floater.string> <panel - border="true" - height="79" + border="false" + height="110" layout="topleft" - left="0" + left="2" top="0" - mouse_opaque="false" + mouse_opaque="false" name="controls" - width="105"> - <joystick_rotate - follows="top|left" - height="64" - image_selected="cam_rotate_in.tga" - image_unselected="cam_rotate_out.tga" - layout="topleft" - left="2" - name="cam_rotate_stick" - picture_style="true" - quadrant="left" - scale_image="false" - sound_flags="3" - tool_tip="Orbit camera around focus" - top="15" - width="64" /> + width="148"> <joystick_track follows="top|left" - height="64" - image_selected="cam_tracking_in.tga" - image_unselected="cam_tracking_out.tga" + height="78" + image_selected="Cam_Tracking_In" + image_unselected="Cam_Tracking_Out" layout="topleft" - left="2" + left="45" name="cam_track_stick" picture_style="true" quadrant="left" scale_image="false" sound_flags="3" tool_tip="Move camera up and down, left and right" - top="15" + top="22" visible="false" - width="64" /> + width="78" /> + <!--TODO: replace with slider, + - images --> <joystick_zoom follows="top|left" - height="64" - image_unselected="cam_zoom_out.tga" + height="78" + image_unselected="ScrollThumb_Vert" layout="topleft" - left_delta="70" - minus_image="cam_zoom_minus_in.tga" + left="7" + minus_image="ScrollThumb_Vert" name="zoom" picture_style="true" - plus_image="cam_zoom_plus_in.tga" + plus_image="ScrollThumb_Vert" quadrant="left" scale_image="false" sound_flags="3" tool_tip="Zoom camera toward focus" - top_delta="0" - width="16" /> + top="22" + width="20" /> + <joystick_rotate + follows="top|left" + height="78" + image_selected="Cam_Rotate_In" + image_unselected="Cam_Rotate_Out" + layout="topleft" + left="45" + name="cam_rotate_stick" + picture_style="true" + quadrant="left" + scale_image="false" + sound_flags="3" + visible="true" + tool_tip="Orbit camera around focus" + top="22" + width="78" /> <panel - height="70" + height="78" layout="topleft" - left="15" + left="36" name="camera_presets" - top="15" + top="30" visible="false" - width="75"> + width="78"> <button height="30" image_selected="CameraPreset_Rear" @@ -127,7 +129,7 @@ name="front_view" picture_style="true" tool_tip="Front View" - top_pad="2" + top_pad="5" width="30"> <click_callback function="CameraPresets.ChangeView" @@ -151,21 +153,21 @@ </panel> </panel> <panel - border="true" - height="25" + border="false" + height="42" layout="topleft" - left="0" - top_pad="1" + left="2" + top_pad="0" name="buttons" - width="105"> + width="148"> <button height="23" label="" layout="topleft" - left="2" + left="23" is_toggle="true" image_overlay="Cam_Orbit_Off" - image_selected="PushButton_Selected_Press" + image_selected="PushButton_Selected_Press" name="orbit_btn" tab_stop="false" tool_tip="Orbit camera" @@ -179,7 +181,7 @@ left_pad="0" is_toggle="true" image_overlay="Cam_Pan_Off" - image_selected="PushButton_Selected_Press" + image_selected="PushButton_Selected_Press" name="pan_btn" tab_stop="false" tool_tip="Pan camera" @@ -191,7 +193,7 @@ layout="topleft" left_pad="0" image_overlay="Cam_Avatar_Off" - image_selected="PushButton_Selected_Press" + image_selected="PushButton_Selected_Press" name="avatarview_btn" tab_stop="false" tool_tip="See as avatar" @@ -204,12 +206,11 @@ left_pad="0" is_toggle="true" image_overlay="Cam_FreeCam_Off" - image_selected="PushButton_Selected_Press" + image_selected="PushButton_Selected_Press" name="freecamera_btn" tab_stop="false" tool_tip="View object" width="25"> </button> - </panel> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml index 0bd4b441c6..d24d1b7064 100644 --- a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml @@ -14,7 +14,7 @@ save_rect="true" title="Nearby Chat" save_visibility="true" - single_instance="true" + single_instance="true" width="320"> <chat_history allow_html="true" diff --git a/indra/newview/skins/default/xui/en/floater_pay.xml b/indra/newview/skins/default/xui/en/floater_pay.xml index 5f70f09a34..69525d48d2 100644 --- a/indra/newview/skins/default/xui/en/floater_pay.xml +++ b/indra/newview/skins/default/xui/en/floater_pay.xml @@ -34,7 +34,7 @@ type="string" length="1" follows="left|top" - font="SansSerif" + font="SansSerifSmall" height="16" layout="topleft" left_pad="7" @@ -44,6 +44,7 @@ </text> <button height="23" + font="SansSerifSmall" label="L$1" label_selected="L$1" layout="topleft" @@ -53,7 +54,8 @@ width="80" /> <button height="23" - label="L$5" + label="L$1" + font="SansSerif" label_selected="L$5" layout="topleft" left_pad="15" @@ -62,6 +64,7 @@ <button height="23" label="L$10" + font="SansSerifHuge" label_selected="L$10" layout="topleft" left="25" diff --git a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml index 8cdafe110a..d2b8455eab 100644 --- a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml +++ b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml @@ -83,7 +83,7 @@ Loading... </text_editor> <button - follows="left|bottom" + follows="right|bottom" height="22" label="Save" label_selected="Save" diff --git a/indra/newview/skins/default/xui/en/floater_report_abuse.xml b/indra/newview/skins/default/xui/en/floater_report_abuse.xml index abde4ba5fa..884532c7a3 100644 --- a/indra/newview/skins/default/xui/en/floater_report_abuse.xml +++ b/indra/newview/skins/default/xui/en/floater_report_abuse.xml @@ -14,19 +14,19 @@ allow_no_texture="true" default_image_name="None" follows="left|top" - height="125" + height="150" layout="topleft" - left="10" - name="screenshot" - top="23" - width="160" /> + left="60" + name="" + top="15" + width="220" /> <check_box height="15" label="Use this screenshot" layout="topleft" - left_pad="5" + left="8" name="screen_check" - top="120" + top_pad="-12" width="116" /> <text type="string" @@ -38,8 +38,8 @@ layout="topleft" left="10" name="reporter_title" - top="140" - width="60"> + top_pad="0" + width="100"> Reporter: </text> <text @@ -48,24 +48,25 @@ follows="left|top" height="16" layout="topleft" - left_pad="10" + left_pad="5" name="reporter_field" top_delta="0" - width="193"> - Loremipsum Dolorsitamut + use_ellipses="true" + width="200"> + Loremipsum Dolorsitamut Longnamez </text> <text type="string" length="1" follows="left|top" height="16" - font.name="SansSerif" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="10" name="sim_title" - top_pad="5" - width="60"> + top_pad="2" + width="100"> Region: </text> <text @@ -74,10 +75,11 @@ follows="left|top" height="16" layout="topleft" - left_pad="2" + left_pad="5" name="sim_field" top_delta="0" - width="193"> + use_ellipses="true" + width="200"> Region Name </text> <text @@ -85,13 +87,13 @@ length="1" follows="left|top" height="16" - font.name="SansSerif" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="10" name="pos_title" - top_pad="5" - width="50"> + top_pad="2" + width="100"> Position: </text> <text @@ -100,10 +102,10 @@ follows="left|top" height="16" layout="topleft" - left_pad="12" + left_pad="5" name="pos_field" top_delta="0" - width="193"> + width="200"> {128.1, 128.1, 15.4} </text> <text @@ -114,7 +116,7 @@ layout="topleft" left="10" name="select_object_label" - top_pad="5" + top_pad="2" width="310"> Click the button, then the abusive object: </text> @@ -133,13 +135,13 @@ length="1" follows="left|top" height="16" - font.name="SansSerif" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="48" name="object_name_label" top_delta="0" - width="60"> + width="80"> Object: </text> <text @@ -151,7 +153,8 @@ left_pad="6" name="object_name" top_delta="0" - width="157"> + use_ellipses="true" + width="185"> Consetetur Sadipscing </text> <text @@ -159,13 +162,13 @@ length="1" follows="left|top" height="16" - font.name="SansSerif" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="48" name="owner_name_label" top_pad="0" - width="60"> + width="80"> Owner: </text> <text @@ -177,8 +180,9 @@ left_pad="6" name="owner_name" top_delta="0" - width="157"> - Hendrerit Vulputate + use_ellipses="true" + width="185"> + Hendrerit Vulputate Kamawashi Longname </text> <combo_box height="23" @@ -349,8 +353,8 @@ type="string" length="1" follows="left|top" - height="16" - font.name="SansSerif" + height="14" + font.name="SansSerif" font.style="BOLD" layout="topleft" left_delta="0" @@ -368,11 +372,10 @@ left_delta="0" max_length="32" name="abuser_name_edit" - top_pad="2" + top_pad="0" width="195" /> <button height="23" - font="SansSerifSmall" label="Choose" layout="topleft" left_pad="5" @@ -394,13 +397,13 @@ type="string" length="1" follows="left|top" - height="16" - font.name="SansSerif" + height="14" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="10" name="abuser_name_title2" - top_pad="5" + top_pad="2" width="313"> Location of Abuse: </text> @@ -413,19 +416,19 @@ left="10" max_length="256" name="abuse_location_edit" - top_pad="2" + top_pad="0" width="313" /> <text type="string" length="1" follows="left|top" height="16" - font.name="SansSerif" + font.name="SansSerif" font.style="BOLD" layout="topleft" left_delta="0" name="sum_title" - top_pad="5" + top_pad="2" width="313"> Summary: </text> @@ -438,14 +441,14 @@ left_delta="0" max_length="64" name="summary_edit" - top_pad="2" + top_pad="0" width="313" /> <text type="string" length="1" follows="left|top" - height="16" - font.name="SansSerif" + height="14" + font.name="SansSerif" font.style="BOLD" layout="topleft" left_delta="0" @@ -461,9 +464,9 @@ height="16" layout="topleft" name="bug_aviso" - left_pad="0" + left_pad="10" width="200"> - Please be as specific as possible. + Please be as specific as possible </text> <text_editor follows="left|top" @@ -479,16 +482,15 @@ type="string" length="1" follows="left|top" - height="50" + height="30" layout="topleft" left="10" - font.name="SansSerif" - font.style="BOLD" + font.name="SansSerifSmall" name="incomplete_title" - top_pad="5" + top_pad="2" word_wrap="true" width="313"> - Note: Incomplete reports won't be investigated. + * Incomplete reports won't be investigated </text> <button left="80" diff --git a/indra/newview/skins/default/xui/en/floater_test_textbox.xml b/indra/newview/skins/default/xui/en/floater_test_textbox.xml index 8305452c85..c33ab8aa70 100644 --- a/indra/newview/skins/default/xui/en/floater_test_textbox.xml +++ b/indra/newview/skins/default/xui/en/floater_test_textbox.xml @@ -114,16 +114,59 @@ Escaped greater than > </text> <text - type="string" - length="1" - bottom="390" - label="N" - layout="topleft" - left="10" - name="floater_map_north" - right="30" - text_color="1 1 1 0.7" - top="370"> - N - </text> + type="string" + length="1" + bottom="390" + label="N" + layout="topleft" + left="10" + name="right_aligned_text" + width="380" + halign="right" + text_color="1 1 1 0.7" + top_pad="10"> + Right aligned text + </text> + <text + type="string" + length="1" + bottom="390" + label="N" + layout="topleft" + left="10" + name="centered_text" + width="380" + halign="center" + text_color="1 1 1 0.7" + top_pad="10"> + Centered text + </text> + <text + type="string" + length="1" + bottom="390" + label="N" + layout="topleft" + left="10" + name="centered_text" + width="380" + halign="left" + text_color="1 1 1 0.7" + top_pad="10"> + Left aligned text + </text> + <text + type="string" + length="1" + bottom="390" + label="N" + layout="topleft" + left="10" + name="floater_map_north" + right="30" + text_color="1 1 1 0.7" + top="370"> + N + </text> + </floater> diff --git a/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml index cc17e9dd4b..eedb4383bb 100644 --- a/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml @@ -23,9 +23,14 @@ parameter="sort_status" /> </menu_item_check> <menu_item_separator layout="topleft" /> - <menu_item_call name="view_icons" label="View People Icons"> - <menu_item_call.on_click function="People.Friends.ViewSort.Action" userdata="view_icons" /> - </menu_item_call> + <menu_item_check name="view_icons" label="View People Icons"> + <menu_item_check.on_click + function="People.Friends.ViewSort.Action" + parameter="view_icons" /> + <menu_item_check.on_check + function="CheckControl" + parameter="FriendsListShowIcons" /> + </menu_item_check> <menu_item_call name="organize_offline" label="Organize Offline Friends"> <menu_item_call.on_click function="People.Friends.ViewSort.Action" userdata="organize_offline" /> </menu_item_call> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml index f91a961388..c002cd078f 100644 --- a/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml @@ -12,9 +12,14 @@ <menu_item_call.on_click function="People.Nearby.ViewSort.Action" userdata="sort_distance" /> </menu_item_call> <menu_item_separator layout="topleft" /> - <menu_item_call name="view_icons" label="View People Icons"> - <menu_item_call.on_click function="People.Nearby.ViewSort.Action" userdata="view_icons" /> - </menu_item_call> + <menu_item_check name="view_icons" label="View People Icons"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="view_icons" /> + <menu_item_check.on_check + function="CheckControl" + parameter="NearbyListShowIcons" /> + </menu_item_check> <menu_item_separator layout="topleft" /> <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> <menu_item_call.on_click function="SideTray.ShowPanel" userdata="panel_block_list_sidetray" /> diff --git a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml index d09871cff3..cfd6dc78b6 100644 --- a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml @@ -23,9 +23,14 @@ parameter="sort_name" /> </menu_item_check> <menu_item_separator layout="topleft" /> - <menu_item_call name="view_icons" label="View People Icons"> - <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="view_icons" /> - </menu_item_call> + <menu_item_check name="view_icons" label="View People Icons"> + <menu_item_check.on_click + function="People.Recent.ViewSort.Action" + parameter="view_icons" /> + <menu_item_check.on_check + function="CheckControl" + parameter="RecentListShowIcons" /> + </menu_item_check> <menu_item_separator layout="topleft" /> <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> <menu_item_call.on_click function="SideTray.ShowPanel" userdata="panel_block_list_sidetray" /> diff --git a/indra/newview/skins/default/xui/en/menu_place_add_button.xml b/indra/newview/skins/default/xui/en/menu_place_add_button.xml new file mode 100644 index 0000000000..e3a39a1242 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_place_add_button.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu + layout="topleft" + left="0" + mouse_opaque="false" + name="menu_folder_gear" + visible="false"> + <menu_item_call + label="Add Folder" + layout="topleft" + name="add_folder"> + <on_click + function="Places.LandmarksGear.Add.Action" + parameter="category" /> + <on_enable + function="Places.LandmarksGear.Enable" + parameter="category" /> + </menu_item_call> + <menu_item_call + label="Add Landmark" + layout="topleft" + name="add_landmark"> + <on_click + function="Places.LandmarksGear.Add.Action" + parameter="add_landmark" /> + </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml b/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml new file mode 100644 index 0000000000..8db745fab7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + name="panel_im_control_panel" + width="146" + height="215" + border="false"> + <avatar_list + color="DkGray2" + follows="left|top|right|bottom" + height="130" + ignore_online_status="true" + layout="topleft" + left="3" + name="speakers_list" + opaque="false" + top="10" + width="140" /> + <button + name="call_btn" + label="Call" + width="90" + height="20" /> + <button + name="end_call_btn" + label="End Call" + width="90" + height="20" + visible="false"/> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml b/indra/newview/skins/default/xui/en/panel_group_control_panel.xml index 9767a673f6..15b6b2a00d 100644 --- a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml +++ b/indra/newview/skins/default/xui/en/panel_group_control_panel.xml @@ -7,12 +7,14 @@ <avatar_list color="DkGray2" follows="left|top|right|bottom" - height="150" + height="130" + ignore_online_status="true" layout="topleft" left="3" name="speakers_list" + opaque="false" top="10" - width="140"/> + width="140" /> <button name="group_info_btn" label="Group Info" @@ -24,4 +26,10 @@ label="Call" width="90" height="20" /> + <button + name="end_call_btn" + label="End Call" + width="90" + height="20" + visible="false"/> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_group_roles.xml b/indra/newview/skins/default/xui/en/panel_group_roles.xml index 75ded4f249..af1919bd8f 100644 --- a/indra/newview/skins/default/xui/en/panel_group_roles.xml +++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml @@ -486,7 +486,7 @@ things in this group. There's a broad variety of Abilities. </panel> </tab_container> <panel - height="170" + height="190" layout="topleft" follows="left|top" left="10" @@ -556,7 +556,7 @@ things in this group. There's a broad variety of Abilities. </scroll_list> </panel> <panel - height="215" + height="252" layout="topleft" left_delta="0" name="roles_footer" diff --git a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml index 7dc94d1141..dca52def49 100644 --- a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml +++ b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel name="panel_im_control_panel" width="96" - height="215" + height="225" border="false"> <avatar_icon name="avatar_icon" @@ -24,6 +24,13 @@ width="90" height="20" /> + <button + height="20" + label="End Call" + name="end_call_btn" + visible="false" + width="90" /> + <button name="share_btn" label="Share" width="90" diff --git a/indra/newview/skins/default/xui/en/panel_landmarks.xml b/indra/newview/skins/default/xui/en/panel_landmarks.xml index c33f68eaf7..5293043ba7 100644 --- a/indra/newview/skins/default/xui/en/panel_landmarks.xml +++ b/indra/newview/skins/default/xui/en/panel_landmarks.xml @@ -114,22 +114,10 @@ image_disabled="AddItem_Disabled" layout="topleft" left_pad="5" - name="add_landmark_btn" + name="add_btn" picture_style="true" tool_tip="Add new landmark" width="18" /> - <button - follows="bottom|left" - height="18" - image_selected="AddItem_Press" - image_unselected="AddItem_Off" - image_disabled="AddItem_Disabled" - layout="topleft" - left_pad="5" - name="add_folder_btn" - picture_style="true" - tool_tip="Add new folder" - width="18" /> <dnd_button follows="bottom|right" height="18" diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 7b19ab1a1c..69089e0e26 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -49,11 +49,13 @@ background_visible="true" height="500" layout="topleft" left="10" + font="SansSerifBigBold" name="tabs" tab_min_width="70" tab_height="30" tab_position="top" top_pad="10" + halign="center" width="313"> <panel follows="all" diff --git a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml new file mode 100644 index 0000000000..b21fbc1795 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml @@ -0,0 +1,594 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="left|right|top|bottom" + name="MediaControls" + bg_alpha_color="1 1 1 0" + height="160" + layout="topleft" + mouse_opaque="false" + width="800"> + <panel + name="media_region" + bottom="125" + follows="left|right|top|bottom" + layout="topleft" + left="20" + mouse_opaque="false" + right="-20" + top="20" /> + <layout_stack + follows="left|right|bottom" + height="32" + layout="topleft" + animate="false" + left="0" + orientation="horizontal" + top="96"> + <!-- outer layout_panels center the inner one --> + <layout_panel + width="0" + layout="topleft" + user_resize="false" /> + <panel + name="media_progress_indicator" + height="22" + layout="topleft" + left="0" + top="0" + auto_resize="false" + user_resize="false" + min_width="100" + width="200"> + <progress_bar + name="media_progress_bar" + color_bar="1 1 1 0.96" + follows="left|right|top" + height="16" + layout="topleft" + left="0" + tool_tip="Media is Loading"/> + </panel> + <layout_panel + width="0" + layout="topleft" + user_resize="false" /> + </layout_stack> + <layout_stack + name="media_controls" + follows="left|right" + animate="false" + height="32" + layout="topleft" + left="0" + orientation="horizontal" + top="128"> + <!-- outer layout_panels center the inner one --> + <layout_panel + width="0" + layout="topleft" + user_resize="false" /> + <layout_panel + name="back" + auto_resize="false" + user_resize="false" + layout="topleft" + min_width="22" + width="22" + top="4"> + <button + auto_resize="false" + height="22" + image_selected="media_btn_back.png" + image_unselected="media_btn_back.png" + layout="topleft" + tool_tip="Step back" + picture_style="true" + width="22" + top_delta="4"> + <button.commit_callback + function="MediaCtrl.Back" /> + </button> + </layout_panel> + <layout_panel + name="fwd" + auto_resize="false" + user_resize="false" + layout="topleft" + top="10" + min_width="17" + width="17"> + <button + height="22" + image_selected="media_btn_forward.png" + image_unselected="media_btn_forward.png" + layout="topleft" + tool_tip="Step forward" + picture_style="true" + top_delta="0" + min_width="17" + width="17"> + <button.commit_callback + function="MediaCtrl.Forward" /> + </button> + </layout_panel> +<!-- + <panel + height="22" + layout="topleft" + auto_resize="false" + min_width="3" + width="3"> + <icon + height="22" + image_name="media_panel_divider.png" + layout="topleft" + top="0" + min_width="3" + width="3" /> + </panel> +--> + <layout_panel + name="home" + auto_resize="false" + user_resize="false" + layout="topleft" + top="-2" + min_width="22" + width="22"> + <button + height="22" + image_selected="media_btn_home.png" + image_unselected="media_btn_home.png" + layout="topleft" + tool_tip="Home page" + picture_style="true" + min_width="22" + width="22"> + <button.commit_callback + function="MediaCtrl.Home" /> + </button> + </layout_panel> + <layout_panel + name="media_stop" + auto_resize="false" + user_resize="false" + layout="topleft" + top="2" + min_width="22" + width="22"> + <button + height="22" + image_selected="button_anim_stop.tga" + image_unselected="button_anim_stop.tga" + layout="topleft" + tool_tip="Stop media" + picture_style="true" + min_width="22" + width="22"> + <button.commit_callback + function="MediaCtrl.Stop" /> + </button> + </layout_panel> +<!-- + <panel + height="22" + layout="topleft" + auto_resize="false" + min_width="3" + width="3"> + <icon + height="22" + image_name="media_panel_divider.png" + layout="topleft" + top="0" + min_width="3" + width="3" /> + </panel> +--> + <layout_panel + name="reload" + auto_resize="false" + user_resize="false" + layout="topleft" + top="6" + min_width="22" + width="22"> + <button + height="22" + image_selected="media_btn_reload.png" + image_unselected="media_btn_reload.png" + layout="topleft" + tool_tip="Reload" + picture_style="true" + min_width="22" + width="22"> + <button.commit_callback + function="MediaCtrl.Reload" /> + </button> + </layout_panel> + <layout_panel + name="stop" + auto_resize="false" + user_resize="false" + layout="topleft" + top="10" + min_width="22" + width="22"> + <button + height="22" + image_selected="media_btn_stoploading.png" + image_unselected="media_btn_stoploading.png" + layout="topleft" + picture_style="true" + tool_tip = "Stop loading" + min_width="22" + width="22"> + <button.commit_callback + function="MediaCtrl.Stop" /> + </button> + </layout_panel> + <layout_panel + name="play" + auto_resize="false" + user_resize="false" + layout="topleft" + top="14" + min_width="22" + width="22"> + <button + height="22" + image_selected="button_anim_play.tga" + image_unselected="button_anim_play.tga" + layout="topleft" + tool_tip = "Play media" + picture_style="true" + min_width="22" + width="22"> + <button.commit_callback + function="MediaCtrl.Play" /> + </button> + </layout_panel> + <layout_panel + name="pause" + auto_resize="false" + user_resize="false" + layout="topleft" + top="18" + min_width="22" + width="22"> + <button + height="22" + image_selected="button_anim_pause.tga" + image_unselected="button_anim_pause.tga" + layout="topleft" + tool_tip = "Pause media" + picture_style="true"> + <button.commit_callback + function="MediaCtrl.Pause" /> + </button> + </layout_panel> + <!-- media URL entry --> + <layout_panel + name="media_address" + auto_resize="true" + user_resize="false" + height="22" + follows="left|right|bottom" + layout="topleft" + width="190" + min_width="90"> + <!-- + RE-ENABLE THIS WHEN WE HAVE A HISTORY DROP-DOWN AGAIN + +<combo_box +name="media_address_url" +allow_text_entry="true" +height="22" +layout="topleft" +max_chars="1024" +tool_tip = "Media URL" +<combo_box.commit_callback +function="MediaCtrl.CommitURL" /> +</combo_box> + --> + <line_editor + name="media_address_url" + follows="left|right" + height="22" + top="0" + tool_tip="Media URL" + text_pad_right="16"> + <line_editor.commit_callback + function="MediaCtrl.CommitURL"/> + </line_editor> + <layout_stack + animate="false" + follows="right" + width="32" + min_width="32" + height="16" + top="3" + orientation="horizontal" + left_pad="-38"> + <icon + name="media_whitelist_flag" + follows="top|right" + height="16" + image_name="smicon_warn.tga" + layout="topleft" + tool_tip="White List enabled" + min_width="16" + width="16" /> + <icon + name="media_secure_lock_flag" + height="16" + image_name="inv_item_eyes.tga" + layout="topleft" + tool_tip="Secured Browsing" + min_width="16" + width="16" /> + </layout_stack> + </layout_panel> + <layout_panel + name="media_play_position" + auto_resize="true" + user_resize="false" + follows="left|right|top|bottom" + layout="topleft" + min_width="100" + width="200"> + <slider_bar + name="media_play_slider" + follows="left|right|top" + height="22" + increment="0.05" + initial_value="0.5" + layout="topleft" + tool_tip="Movie play progress" + min_width="100" + width="200"> + <slider_bar.commit_callback + function="MediaCtrl.JumpProgress" /> + </slider_bar> + </layout_panel> + <layout_panel + name="media_volume" + auto_resize="false" + user_resize="false" + layout="topleft" + height="24" + min_width="24" + width="24"> + <button + name="media_volume_button" + height="22" + image_selected="icn_speaker-muted_dark.tga" + image_unselected="icn_speaker_dark.tga" + is_toggle="true" + layout="topleft" + scale_image="false" + picture_style="true" + tool_tip="Mute This Media" + top_delta="22" + min_width="24" + width="24" > + <button.commit_callback + function="MediaCtrl.ToggleMute" /> + </button> + </layout_panel> + <layout_panel + name="volume_up" + auto_resize="false" + user_resize="false" + layout="topleft" + min_width="20" + height="14" + width="20"> + <button + top="-3" + height="14" + image_selected="media_btn_scrollup.png" + image_unselected="media_btn_scrollup.png" + layout="topleft" + tool_tip="Volume up" + picture_style="true" + scale_image="true" + min_width="20" + width="20" > + <button.commit_callback + function="MediaCtrl.CommitVolumeUp" /> + </button> + </layout_panel> + <layout_panel + name="volume_down" + auto_resize="false" + user_resize="false" + layout="topleft" + min_width="20" + height="14" + width="20"> + <button + top="-5" + height="14" + image_selected="media_btn_scrolldown.png" + image_unselected="media_btn_scrolldown.png" + layout="topleft" + tool_tip="Volume down" + picture_style="true" + scale_image="true" + min_width="20" + width="20"> + <button.commit_callback + function="MediaCtrl.CommitVolumeDown" /> + </button> + </layout_panel> + <!-- Scroll pad --> + <layout_panel + name="media_panel_scroll" + auto_resize="false" + user_resize="false" + height="32" + follows="left|right|top|bottom" + layout="topleft" + min_width="32" + width="32"> + <icon + height="32" + image_name="media_panel_scrollbg.png" + layout="topleft" + top="0" + min_width="32" + width="32" /> + <button + name="scrollup" + height="8" + image_selected="media_btn_scrollup.png" + image_unselected="media_btn_scrollup.png" + layout="topleft" + tool_tip="Scroll up" + picture_style="true" + scale_image="false" + left="12" + top_delta="4" + min_width="8" + width="8" /> + <button + name="scrollleft" + height="8" + image_selected="media_btn_scrollleft.png" + image_unselected="media_btn_scrollleft.png" + layout="topleft" + left="3" + tool_tip="Scroll left" + picture_style="true" + scale_image="false" + top="12" + min_width="8" + width="8" /> + <button + name="scrollright" + height="8" + image_selected="media_btn_scrollright.png" + image_unselected="media_btn_scrollright.png" + layout="topleft" + left_pad="9" + tool_tip="Scroll right" + picture_style="true" + scale_image="false" + top_delta="0" + min_width="8" + width="8" /> + <button + name="scrolldown" + height="8" + image_selected="media_btn_scrolldown.png" + image_unselected="media_btn_scrolldown.png" + layout="topleft" + left="12" + tool_tip="Scroll down" + picture_style="true" + scale_image="false" + top="20" + min_width="8" + width="8" /> + </layout_panel> + <layout_panel + name="zoom_frame" + auto_resize="false" + user_resize="false" + layout="topleft" + height="28" + min_width="22" + width="22"> + <button + height="22" + image_selected="media_btn_optimalzoom.png" + image_unselected="media_btn_optimalzoom.png" + layout="topleft" + tool_tip="Zoom" + picture_style="true" + min_width="22" + width="22"> + <button.commit_callback + function="MediaCtrl.Zoom" /> + </button> + </layout_panel> +<!-- + <panel + height="22" + layout="topleft" + auto_resize="false" + min_width="3" + width="3"> + <icon + height="22" + image_name="media_panel_divider.png" + layout="topleft" + top="0" + min_width="3" + width="3" /> + </panel> +--> + <layout_panel + name="new_window" + auto_resize="false" + user_resize="false" + layout="topleft" + min_width="22" + width="22"> + <button + height="22" + image_selected="media_btn_newwindow.png" + image_unselected="media_btn_newwindow.png" + layout="topleft" + tool_tip = "Open URL in browser" + picture_style="true" + top_delta="-3" + min_width="24" + width="24" > + <button.commit_callback + function="MediaCtrl.Open" /> + </button> + </layout_panel> +<!-- + <panel + height="22" + layout="topleft" + auto_resize="false" + min_width="3" + width="3"> + <icon + height="22" + image_name="media_panel_divider.png" + layout="topleft" + top="0" + min_width="3" + width="3" /> + </panel> +--> + <layout_panel + name="close" + auto_resize="false" + user_resize="false" + layout="topleft" + min_width="21" + width="21" > + <button + height="22" + image_selected="media_btn_done.png" + image_unselected="media_btn_done.png" + layout="topleft" + tool_tip ="Close media control" + picture_style="true" + top_delta="-4" + width="21" > + <button.commit_callback + function="MediaCtrl.Close" /> + </button> + </layout_panel> + <layout_panel + width="0" + layout="topleft" + user_resize="false" /> + </layout_stack> +</panel> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 81bc12c33c..e842517853 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -61,9 +61,7 @@ <string name="TooltipFlagGroupScripts">Group Scripts</string> <string name="TooltipFlagNoScripts">No Scripts</string> <string name="TooltipLand">Land:</string> - <string name="TooltipMustSingleDrop">Only a single item can be dragged here</string> - <string name="TooltipAltLeft">Alt+← for previous tab</string> - <string name="TooltipAltRight">Alt+→ for next tab</string> + <string name="TooltipMustSingleDrop">Only a single item can be dragged here</string> <!-- tooltips for Urls --> <string name="TooltipHttpUrl">Click to view this web page</string> @@ -76,6 +74,7 @@ <string name="TooltipTeleportUrl">Click to teleport to this location</string> <string name="TooltipObjectIMUrl">Click to view this object's description</string> <string name="TooltipSLAPP">Click to run the secondlife:// command</string> + <string name="CurrentURL" value=" CurrentURL: [CurrentURL]" /> <!-- ButtonToolTips, llfloater.cpp --> <string name="BUTTON_CLOSE_DARWIN">Close (⌘W)</string> @@ -264,10 +263,6 @@ <string name="TrackYourCamera">Track your camera</string> <string name="ControlYourCamera">Control your camera</string> - <!-- IM --> - <string name="IM_logging_string">-- Instant message logging enabled --</string> - <string name="Unnamed">(Unnamed)</string> - <!-- Sim Access labels --> <string name="SIM_ACCESS_PG">PG</string> <string name="SIM_ACCESS_MATURE">Mature</string> @@ -642,7 +637,7 @@ Sets the script timer to zero float llGetAndResetTime() Returns the script time in seconds and then resets the script timer to zero </string> - <string name="LSLTipText_llSound" translate="false"> + <string name="LSLTipText_llSoplayund" translate="false"> llSound(string sound, float volume, integer queue, integer loop) Plays sound at volume and whether it should loop or not </string> @@ -2022,15 +2017,14 @@ this texture in your inventory <string name="IMTeen">teen</string> <!-- floater region info --> + <!-- The following will replace variable [ALL_ESTATES] in notifications EstateAllowed*, EstateBanned*, EstateManager* --> <string name="RegionInfoError">error</string> <string name="RegionInfoAllEstatesOwnedBy"> - all estates -owned by [OWNER] + all estates owned by [OWNER] </string> - <string name="RegionInfoAllEstatesYouOwn">all estates you owned</string> + <string name="RegionInfoAllEstatesYouOwn">all estates that you own</string> <string name="RegionInfoAllEstatesYouManage"> - all estates that -you managed for [OWNER] + all estates that you manage for [OWNER] </string> <string name="RegionInfoAllowedResidents">Allowed residents: ([ALLOWEDAGENTS], max [MAXACCESS])</string> <string name="RegionInfoAllowedGroups">Allowed groups: ([ALLOWEDGROUPS], max [MAXACCESS])</string> @@ -2194,6 +2188,7 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh <!-- media --> <string name="Multiple Media">Multiple Media</string> + <string name="Play Media">Play/Pause Media</string> <!-- OSMessageBox messages --> <string name="MBCmdLineError"> @@ -2884,7 +2879,14 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. Failed to start viewer </string> - <!-- IM system messages --> + <!-- IM system messages --> + <string name="IM_logging_string">-- Instant message logging enabled --</string> + <string name="IM_typing_start_string">[NAME] is typing...</string> + <string name="Unnamed">(Unnamed)</string> + <string name="IM_moderated_chat_label">(Moderated: Voices off by default)</string> + <string name="IM_unavailable_text_label">Text chat is not available for this call.</string> + + <string name="ringing-im"> Joining Voice Chat... </string> diff --git a/indra/newview/skins/default/xui/en/widgets/chat_history.xml b/indra/newview/skins/default/xui/en/widgets/chat_history.xml index b72d59524e..ea6997ebd5 100644 --- a/indra/newview/skins/default/xui/en/widgets/chat_history.xml +++ b/indra/newview/skins/default/xui/en/widgets/chat_history.xml @@ -4,8 +4,8 @@ message_separator="panel_chat_separator.xml" left_text_pad="10" right_text_pad="15" - left_widget_pad="5" - rigth_widget_pad="10" + left_widget_pad="0" + right_widget_pad="10" max_length="2147483647" enabled="false" track_bottom="true" diff --git a/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml b/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml index 89e442baec..ab4ad94089 100644 --- a/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml +++ b/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml @@ -23,7 +23,8 @@ image_selected="DropDown_Selected" image_disabled="DropDown_Disabled" image_disabled_selected="DropDown_Disabled_Selected" /> - <gesture_combo_box.combo_list bg_writeable_color="MenuDefaultBgColor" /> + <gesture_combo_box.combo_list bg_writeable_color="MenuDefaultBgColor" + scroll_bar_bg_visible="true" /> <gesture_combo_box.combo_editor name="Combo Text Entry" select_on_focus="true" font="SansSerifSmall" /> diff --git a/indra/newview/skins/default/xui/en/widgets/tab_container.xml b/indra/newview/skins/default/xui/en/widgets/tab_container.xml index 2fe5f517a2..7d10df1af7 100644 --- a/indra/newview/skins/default/xui/en/widgets/tab_container.xml +++ b/indra/newview/skins/default/xui/en/widgets/tab_container.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <tab_container tab_min_width="60" tab_max_width="150" + font_halign="left" tab_height="16"> <first_tab tab_top_image_unselected="TabTop_Left_Off" tab_top_image_selected="TabTop_Left_Selected" |