diff options
| author | Rick Pasetto <rick@lindenlab.com> | 2009-10-28 17:33:29 -0700 | 
|---|---|---|
| committer | Rick Pasetto <rick@lindenlab.com> | 2009-10-28 17:33:29 -0700 | 
| commit | d6a2c5965e6b0b07014e8fc2dbb16edc463e0566 (patch) | |
| tree | 879d891f1c6a743529d7683db3d322541e5d39f5 | |
| parent | 5db497e398d537a7f3b23c50336e2825006e0907 (diff) | |
| parent | 437449044583d50c8cad694e65e5a4a23a55d768 (diff) | |
merge from remote repo
80 files changed, 3332 insertions, 1553 deletions
| diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 732c01614b..08f16a12d4 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 653505a12e..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(); @@ -2381,7 +2378,6 @@ S32	LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin  	{  		if (text[last_char] == '\n')   		{ -			last_char++;  			break;  		}  	} @@ -2401,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; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index dd3937a6ef..5210ff66ed 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -371,6 +371,7 @@ set(viewer_SOURCE_FILES      llsky.cpp      llslurl.cpp      llspatialpartition.cpp +    llspeakers.cpp      llsplitbutton.cpp      llsprite.cpp      llstartup.cpp @@ -484,6 +485,7 @@ set(viewer_SOURCE_FILES      llvoclouds.cpp      llvograss.cpp      llvoground.cpp +    llvoicechannel.cpp      llvoiceclient.cpp      llvoiceremotectrl.cpp      llvoicevisualizer.cpp @@ -847,6 +849,7 @@ set(viewer_HEADER_FILES      llsky.h      llslurl.h      llspatialpartition.h +    llspeakers.h      llsplitbutton.h      llsprite.h      llstartup.h @@ -960,6 +963,7 @@ set(viewer_HEADER_FILES      llvoclouds.h      llvograss.h      llvoground.h +    llvoicechannel.h      llvoiceclient.h      llvoiceremotectrl.h      llvoicevisualizer.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 fbb57e35f4..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), @@ -78,35 +284,27 @@ 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(); @@ -125,10 +323,10 @@ void LLChatHistory::appendWidgetMessage(const LLUUID& avatar_id, std::string& fr  	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 69066a0110..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 @@ -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/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/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/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/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_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_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/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/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 81bc12c33c..4eacd72a7d 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> @@ -264,10 +262,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> @@ -2022,15 +2016,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> @@ -2884,7 +2877,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/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" | 
