Commits

Kent Quirk  committed d30e35c Merge

merge changes from backing out the directory change of default to base

  • Participants
  • Parent commits 1e042b6, a732669

Comments (0)

Files changed (21)

File indra/llrender/llfontfreetype.cpp

 	return (F32)mFontBitmapCachep->getMaxCharWidth();
 }
 
+F32 LLFontFreetype::getXAdvance(const LLFontGlyphInfo* glyph) const
+{
+	if (mFTFace == NULL)
+		return 0.0;
+
+	return glyph->mXAdvance;
+}
+
 F32 LLFontFreetype::getXKerning(llwchar char_left, llwchar char_right) const
 {
 	if (mFTFace == NULL)
 	return delta.x*(1.f/64.f);
 }
 
+F32 LLFontFreetype::getXKerning(const LLFontGlyphInfo* left_glyph_info, const LLFontGlyphInfo* right_glyph_info) const
+{
+	if (mFTFace == NULL)
+		return 0.0;
+
+	U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;
+	U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0;
+
+	FT_Vector  delta;
+
+	llverify(!FT_Get_Kerning(mFTFace, left_glyph, right_glyph, ft_kerning_unfitted, &delta));
+
+	return delta.x*(1.f/64.f);
+}
+
 BOOL LLFontFreetype::hasGlyph(llwchar wch) const
 {
 	llassert(!mIsFallback);

File indra/llrender/llfontfreetype.h

 	};
 
 	F32 getXAdvance(llwchar wc) const;
+	F32 getXAdvance(const LLFontGlyphInfo* glyph) const;
 	F32 getXKerning(llwchar char_left, llwchar char_right) const; // Get the kerning between the two characters
+	F32 getXKerning(const LLFontGlyphInfo* left_glyph_info, const LLFontGlyphInfo* right_glyph_info) const; // Get the kerning between the two characters
 
 	LLFontGlyphInfo* getGlyphInfo(llwchar wch) const;
 

File indra/llrender/llfontgl.cpp

 	// Remember last-used texture to avoid unnecesssary bind calls.
 	LLImageGL *last_bound_texture = NULL;
 
+	const LLFontGlyphInfo* next_glyph = NULL;
+
 	for (i = begin_offset; i < begin_offset + length; i++)
 	{
 		llwchar wch = wstr[i];
 
-		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch);
+		const LLFontGlyphInfo* fgi = next_glyph;
+		next_glyph = NULL;
+		if(!fgi)
+		{
+			fgi = mFontFreetype->getGlyphInfo(wch);
+		}
 		if (!fgi)
 		{
 			llerrs << "Missing Glyph Info" << llendl;
 		if (next_char && (next_char < LAST_CHARACTER))
 		{
 			// Kern this puppy.
-			cur_x += mFontFreetype->getXKerning(wch, next_char);
+			next_glyph = mFontFreetype->getGlyphInfo(next_char);
+			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
 		}
 
 		// Round after kerning.
 	F32 cur_x = 0;
 	const S32 max_index = begin_offset + max_chars;
 
+	const LLFontGlyphInfo* next_glyph = NULL;
+
 	F32 width_padding = 0.f;
 	for (S32 i = begin_offset; i < max_index && wchars[i] != 0; i++)
 	{
 		llwchar wch = wchars[i];
 
-		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch);
+		const LLFontGlyphInfo* fgi = next_glyph;
+		next_glyph = NULL;
+		if(!fgi)
+		{
+			fgi = mFontFreetype->getGlyphInfo(wch);
+		}
 
-		F32 advance = mFontFreetype->getXAdvance(wch);
+		F32 advance = mFontFreetype->getXAdvance(fgi);
 
 		// for the last character we want to measure the greater of its width and xadvance values
 		// so keep track of the difference between these values for the each character we measure
 			&& (next_char < LAST_CHARACTER))
 		{
 			// Kern this puppy.
-			cur_x += mFontFreetype->getXKerning(wch, next_char);
+			next_glyph = mFontFreetype->getGlyphInfo(next_char);
+			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
 		}
 		// Round after kerning.
 		cur_x = (F32)llround(cur_x);
 	// avoid S32 overflow when max_pixels == S32_MAX by staying in floating point
 	F32 scaled_max_pixels =	ceil(max_pixels * sScaleX);
 	F32 width_padding = 0.f;
+	
+	LLFontGlyphInfo* next_glyph = NULL;
 
 	S32 i;
 	for (i=0; (i < max_chars); i++)
 				in_word = TRUE;
 			}
 		}
-
-		LLFontGlyphInfo* fgi = mFontFreetype->getGlyphInfo(wch);
+		
+		LLFontGlyphInfo* fgi = next_glyph;
+		next_glyph = NULL;
+		if(!fgi)
+		{
+			fgi = mFontFreetype->getGlyphInfo(wch);
+		}
 
 		// account for glyphs that run beyond the starting point for the next glyphs
 		width_padding = llmax(	0.f,													// always use positive padding amount
 		if (((i+1) < max_chars) && wchars[i+1])
 		{
 			// Kern this puppy.
-			cur_x += mFontFreetype->getXKerning(wch, wchars[i+1]);
+			next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1]);
+			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
 		}
 
 		// Round after kerning.
 	const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars);
 
 	F32 scaled_max_pixels =	max_pixels * sScaleX;
+	
+	const LLFontGlyphInfo* next_glyph = NULL;
 
 	S32 pos;
 	for (pos = begin_offset; pos < max_index; pos++)
 		{
 			break; // done
 		}
-		F32 char_width = mFontFreetype->getXAdvance(wch);
+		
+		const LLFontGlyphInfo* glyph = next_glyph;
+		next_glyph = NULL;
+		if(!glyph)
+		{
+			glyph = mFontFreetype->getGlyphInfo(wch);
+		}
+		
+		F32 char_width = mFontFreetype->getXAdvance(glyph);
 
 		if (round)
 		{
 		if (((pos + 1) < max_index)
 			&& (wchars[(pos + 1)]))
 		{
-			llwchar next_char = wchars[pos + 1];
 			// Kern this puppy.
-			cur_x += mFontFreetype->getXKerning(wch, next_char);
+			next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1]);
+			cur_x += mFontFreetype->getXKerning(glyph, next_glyph);
 		}
 
+
 		// Round after kerning.
 		cur_x = llround(cur_x);
 	}

File indra/llui/llfloater.cpp

 			LLRect new_rect;
 			new_rect.setLeftTopAndSize(view_rect.mLeft,view_rect.mTop,new_width, new_height);
 
-			floater->reshape( new_width, new_height, TRUE );
-			floater->setRect(new_rect);
+			floater->setShape(new_rect);
 
-			floater->translateIntoRect( getLocalRect(), false );
+			if (floater->followsRight())
+			{
+				floater->translate(old_width - new_width, 0);
+			}
+
+			if (floater->followsTop())
+			{
+				floater->translate(0, old_height - new_height);
+			}
 		}
 	}
 

File indra/llui/llkeywords.cpp

 		llassert(0);
 	}
 }
+LLKeywords::WStringMapIndex::WStringMapIndex(const WStringMapIndex& other)
+{
+	if(other.mOwner)
+	{
+		copyData(other.mData, other.mLength);
+	}
+	else
+	{
+		mOwner = false;
+		mLength = other.mLength;
+		mData = other.mData;
+	}
+}
+
+LLKeywords::WStringMapIndex::WStringMapIndex(const LLWString& str)
+{
+	copyData(str.data(), str.size());
+}
+
+LLKeywords::WStringMapIndex::WStringMapIndex(const llwchar *start, size_t length):
+mData(start), mLength(length), mOwner(false)
+{
+}
+
+LLKeywords::WStringMapIndex::~WStringMapIndex()
+{
+	if(mOwner)
+		delete[] mData;
+}
+
+void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length)
+{
+	llwchar *data = new llwchar[length];
+	memcpy((void*)data, (const void*)start, length * sizeof(llwchar));
+
+	mOwner = true;
+	mLength = length;
+	mData = data;
+}
+
+bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &other) const
+{
+	// NOTE: Since this is only used to organize a std::map, it doesn't matter if it uses correct collate order or not.
+	// The comparison only needs to strictly order all possible strings, and be stable.
+	
+	bool result = false;
+	const llwchar* self_iter = mData;
+	const llwchar* self_end = mData + mLength;
+	const llwchar* other_iter = other.mData;
+	const llwchar* other_end = other.mData + other.mLength;
+	
+	while(true)
+	{
+		if(other_iter >= other_end)
+		{
+			// We've hit the end of other.
+			// This covers two cases: other being shorter than self, or the strings being equal.
+			// In either case, we want to return false.
+			result = false;
+			break;
+		}
+		else if(self_iter >= self_end)
+		{
+			// self is shorter than other.
+			result = true;
+			break; 
+		}
+		else if(*self_iter != *other_iter)
+		{
+			// The current character differs.  The strings are not equal.
+			result = *self_iter < *other_iter;
+			break;
+		}
+
+		self_iter++;
+		other_iter++;
+	}
+	
+	return result;
+}
 
 LLColor3 LLKeywords::readColor( const std::string& s )
 {
 				S32 seg_len = p - cur;
 				if( seg_len > 0 )
 				{
-					LLWString word( cur, 0, seg_len );
+					WStringMapIndex word( cur, seg_len );
 					word_token_map_t::iterator map_iter = mWordTokenMap.find(word);
 					if( map_iter != mWordTokenMap.end() )
 					{

File indra/llui/llkeywords.h

 					const std::string& key,
 					const LLColor3& color,
 					const std::string& tool_tip = LLStringUtil::null);
+	
+	// This class is here as a performance optimization.
+	// The word token map used to be defined as std::map<LLWString, LLKeywordToken*>.
+	// This worked, but caused a performance bottleneck due to memory allocation and string copies
+	//  because it's not possible to search such a map without creating an LLWString.
+	// Using this class as the map index instead allows us to search using segments of an existing
+	//  text run without copying them first, which greatly reduces overhead in LLKeywords::findSegments().
+	class WStringMapIndex
+	{
+	public:
+		// copy constructor
+		WStringMapIndex(const WStringMapIndex& other);
+		// constructor from a string (copies the string's data into the new object)
+		WStringMapIndex(const LLWString& str);
+		// constructor from pointer and length
+		// NOTE: does NOT copy data, caller must ensure that the lifetime of the pointer exceeds that of the new object!
+		WStringMapIndex(const llwchar *start, size_t length);
+		~WStringMapIndex();
+		bool operator<(const WStringMapIndex &other) const;
+	private:
+		void copyData(const llwchar *start, size_t length);
+		const llwchar *mData;
+		size_t mLength;
+		bool mOwner;
+	};
 
-	typedef std::map<LLWString, LLKeywordToken*> word_token_map_t;
+	typedef std::map<WStringMapIndex, LLKeywordToken*> word_token_map_t;
 	typedef word_token_map_t::const_iterator keyword_iterator_t;
 	keyword_iterator_t begin() const { return mWordTokenMap.begin(); }
 	keyword_iterator_t end() const { return mWordTokenMap.end(); }

File indra/llui/llmenugl.cpp

 // Class LLMenuItemTearOffGL
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p) 
-:	LLMenuItemGL(p), 
-	mParentHandle(p.parent_floater_handle)
+:	LLMenuItemGL(p)
 {
 }
 
+// Returns the first floater ancestor if there is one
+LLFloater* LLMenuItemTearOffGL::getParentFloater()
+{
+	LLView* parent_view = getMenu();
+
+	while (parent_view)
+	{
+		if (dynamic_cast<LLFloater*>(parent_view))
+		{
+			return dynamic_cast<LLFloater*>(parent_view);
+		}
+
+		bool parent_is_menu = dynamic_cast<LLMenuGL*>(parent_view) && !dynamic_cast<LLMenuBarGL*>(parent_view);
+
+		if (parent_is_menu)
+		{
+			// use menu parent
+			parent_view =  dynamic_cast<LLMenuGL*>(parent_view)->getParentMenuItem();
+		}
+		else
+		{
+			// just use regular view parent
+			parent_view = parent_view->getParent();
+		}
+	}
+
+	return NULL;
+}
 
 void LLMenuItemTearOffGL::onCommit()
 {
 
 		getMenu()->needsArrange();
 
-		LLFloater* parent_floater = mParentHandle.get();
+		LLFloater* parent_floater = getParentFloater();
 		LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu());
 
 		if (tear_off_menu)
 	mSpilloverMenu(NULL),
 	mJumpKey(p.jump_key),
 	mCreateJumpKeys(p.create_jump_keys),
-	mParentFloaterHandle(p.parent_floater),
 	mNeedsArrange(FALSE), 
 	mShortcutPad(p.shortcut_pad)
 {
 void LLMenuGL::initFromParams(const LLMenuGL::Params& p)
 {
 	LLUICtrl::initFromParams(p);
-	setCanTearOff(p.can_tear_off, p.parent_floater);
+	setCanTearOff(p.can_tear_off);
 }
 
 // Destroys the object
 	mJumpKeys.clear();
 }
 
-void LLMenuGL::setCanTearOff(BOOL tear_off, LLHandle<LLFloater> parent_floater_handle )
+void LLMenuGL::setCanTearOff(BOOL tear_off)
 {
 	if (tear_off && mTearOffItem == NULL)
 	{
 		LLMenuItemTearOffGL::Params p;
-		p.parent_floater_handle = parent_floater_handle;
 		mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p);
 		addChildInBack(mTearOffItem);
 	}
 		LLMenuGL::Params p;
 		p.name("More");
 		p.label("More"); // *TODO: Translate
-		p.parent_floater(mParentFloaterHandle);
 		p.bg_color(mBackgroundColor);
 		p.bg_visible(true);
 		p.can_tear_off(false);

File indra/llui/llmenugl.h

 public:
 	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
 	{
-		Optional<LLHandle<LLFloater> >	parent_floater;
 		Optional<KEY>					jump_key;
 		Optional<bool>					horizontal_layout,
 										can_tear_off,
 	void setBackgroundColor( const LLUIColor& color ) { mBackgroundColor = color; }
 	const LLUIColor& getBackgroundColor() const { return mBackgroundColor; }
 	void setBackgroundVisible( BOOL b )	{ mBgVisible = b; }
-	void setCanTearOff(BOOL tear_off, LLHandle<LLFloater> parent_floater_handle = LLHandle<LLFloater>());
+	void setCanTearOff(BOOL tear_off);
 
 	// add a separator to this menu
 	virtual BOOL addSeparator();
 	class LLMenuItemTearOffGL* mTearOffItem;
 	class LLMenuItemBranchGL* mSpilloverBranch;
 	LLMenuGL*		mSpilloverMenu;
-	LLHandle<LLFloater>	mParentFloaterHandle;
 	KEY				mJumpKey;
 	BOOL			mCreateJumpKeys;
 	S32				mShortcutPad;
 public:
 	struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
 	{
-		Optional<LLHandle<LLFloater> > parent_floater_handle;
 		Params()
 		{
 			name = "tear off";
 	};
 
 	LLMenuItemTearOffGL( const Params& );
-
+	
 	virtual void onCommit(void);
 	virtual void draw(void);
 	virtual U32 getNominalHeight() const;
 
-private:
-	LLHandle<LLFloater> mParentHandle;
+	LLFloater* getParentFloater();
 };
 
 

File indra/llui/llresizehandle.cpp

 	{
 		// Make sure the mouse in still over the application.  We don't want to make the parent
 		// so big that we can't see the resize handle any more.
-
+	
 		S32 screen_x;
 		S32 screen_y;
 		localPointToScreen(x, y, &screen_x, &screen_y);
 		if( resizing_view )
 		{
 			// undock floater when user resize it
-			if (((LLFloater*)getParent())->isDocked())
+			LLFloater* floater_parent = dynamic_cast<LLFloater*>(getParent());
+			if (floater_parent && floater_parent->isDocked()) 
 			{
-				((LLFloater*)getParent())->setDocked(false, false);
+				floater_parent->setDocked(false, false);
 			}
 
 			// Resize the parent
 			LLRect scaled_rect = orig_rect;
 			S32 delta_x = screen_x - mDragLastScreenX;
 			S32 delta_y = screen_y - mDragLastScreenY;
-
-			if(delta_x == 0 && delta_y == 0)
-				return FALSE;
-
 			LLCoordGL mouse_dir;
 			// use hysteresis on mouse motion to preserve user intent when mouse stops moving
 			mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
 			mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
-			
 			mLastMouseScreenX = screen_x;
 			mLastMouseScreenY = screen_y;
 			mLastMouseDir = mouse_dir;
 
-			S32 new_width = orig_rect.getWidth();
-			S32 new_height = orig_rect.getHeight();
+			S32 x_multiple = 1;
+			S32 y_multiple = 1;
+			switch( mCorner )
+			{
+			case LEFT_TOP:
+				x_multiple = -1; 
+				y_multiple =  1;	
+				break;
+			case LEFT_BOTTOM:	
+				x_multiple = -1; 
+				y_multiple = -1;	
+				break;
+			case RIGHT_TOP:		
+				x_multiple =  1; 
+				y_multiple =  1;	
+				break;
+			case RIGHT_BOTTOM:	
+				x_multiple =  1; 
+				y_multiple = -1;	
+				break;
+			}
 
-			S32 new_pos_x = orig_rect.mLeft;
-			S32 new_pos_y = orig_rect.mTop;
+			S32 new_width = orig_rect.getWidth() + x_multiple * delta_x;
+			if( new_width < mMinWidth )
+			{
+				new_width = mMinWidth;
+				delta_x = x_multiple * (mMinWidth - orig_rect.getWidth());
+			}
+
+			S32 new_height = orig_rect.getHeight() + y_multiple * delta_y;
+			if( new_height < mMinHeight )
+			{
+				new_height = mMinHeight;
+				delta_y = y_multiple * (mMinHeight - orig_rect.getHeight());
+			}
 
 			switch( mCorner )
 			{
-			case LEFT_TOP:
-				new_width-=delta_x;
-				new_height+=delta_y;
-				new_pos_x+=delta_x;
-				new_pos_y+=delta_y;
+			case LEFT_TOP:		
+				scaled_rect.translate(delta_x, 0);			
 				break;
 			case LEFT_BOTTOM:	
-				new_width-=delta_x;
-				new_height-=delta_y;
-				new_pos_x+=delta_x;
+				scaled_rect.translate(delta_x, delta_y);	
 				break;
 			case RIGHT_TOP:		
-				new_width+=delta_x;
-				new_height+=delta_y;
-				new_pos_y+=delta_y;
 				break;
 			case RIGHT_BOTTOM:	
-				new_width+=delta_x;
-				new_height-=delta_y;
+				scaled_rect.translate(0, delta_y);			
 				break;
 			}
 
-			new_width = llmax(new_width,mMinWidth);
-			new_height = llmax(new_height,mMinHeight);
-			
-			LLRect::tCoordType screen_width = resizing_view->getParent()->getSnapRect().getWidth();
-			LLRect::tCoordType screen_height = resizing_view->getParent()->getSnapRect().getHeight();
-			
-			new_width = llmin(new_width, screen_width);
-			new_height = llmin(new_height, screen_height);
-			
 			// temporarily set new parent rect
-			scaled_rect.setLeftTopAndSize(new_pos_x,new_pos_y,new_width,new_height);
-				
+			scaled_rect.mRight = scaled_rect.mLeft + new_width;
+			scaled_rect.mTop = scaled_rect.mBottom + new_height;
 			resizing_view->setRect(scaled_rect);
 
 			LLView* snap_view = NULL;
 			resizing_view->setRect(orig_rect);
 
 			// translate and scale to new shape
-			resizing_view->reshape(scaled_rect.getWidth(),scaled_rect.getHeight());
-			resizing_view->setRect(scaled_rect);
-			//set shape to handle dependent floaters...
-			resizing_view->handleReshape(scaled_rect, false);
-			
+			resizing_view->setShape(scaled_rect, true);
 			
 			// update last valid mouse cursor position based on resized view's actual size
 			LLRect new_rect = resizing_view->getRect();

File indra/llui/lltextbase.cpp

 }
 
 // Finds the text segment (if any) at the give local screen position
-LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y )
+LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line)
 {
 	// Find the cursor position at the requested local screen position
-	S32 offset = getDocIndexFromLocalCoord( x, y, FALSE );
+	S32 offset = getDocIndexFromLocalCoord( x, y, FALSE, hit_past_end_of_line);
 	segment_set_t::iterator seg_iter = getSegIterContaining(offset);
 	if (seg_iter != mSegments.end())
 	{
 // will be put to its right.  If round is false, the cursor will always be put to the
 // character's left.
 
-S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const
+S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, bool hit_past_end_of_line) const
 {
 	// Figure out which line we're nearest to.
 	LLRect visible_region = getVisibleDocumentRect();
 		S32 text_width, text_height;
 		segmentp->getDimensions(line_seg_offset, segment_line_length, text_width, text_height);
 		if (local_x < start_x + text_width						// cursor to left of right edge of text
-			|| segmentp->getEnd() >= line_iter->mDocIndexEnd - 1)	// or this segment wraps to next line
+			|| (hit_past_end_of_line && (segmentp->getEnd() >= line_iter->mDocIndexEnd - 1)))	// or this segment wraps to next line
 		{
 			// Figure out which character we're nearest to.
 			S32 offset;
 {
 	if (getStyle() && getStyle()->isLink())
 	{
-		LLUI::getWindow()->setCursor(UI_CURSOR_HAND);
-		return TRUE;
+		// Only process the click if it's actually in this segment, not to the right of the end-of-line.
+		if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+		{
+			LLUI::getWindow()->setCursor(UI_CURSOR_HAND);
+			return TRUE;
+		}
 	}
 	return FALSE;
 }
 {
 	if (getStyle() && getStyle()->isLink())
 	{
-		mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF());
-		return TRUE;
+		// Only process the click if it's actually in this segment, not to the right of the end-of-line.
+		if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+		{
+			mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF());
+			return TRUE;
+		}
 	}
 	return FALSE;
 }
 {
 	if (getStyle() && getStyle()->isLink())
 	{
-		// eat mouse down event on hyperlinks, so we get the mouse up
-		return TRUE;
+		// Only process the click if it's actually in this segment, not to the right of the end-of-line.
+		if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+		{
+			// eat mouse down event on hyperlinks, so we get the mouse up
+			return TRUE;
+		}
 	}
 
 	return FALSE;
 {
 	if (getStyle() && getStyle()->isLink())
 	{
-		LLUrlAction::clickAction(getStyle()->getLinkHREF());
-		return TRUE;
+		// Only process the click if it's actually in this segment, not to the right of the end-of-line.
+		if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+		{
+			LLUrlAction::clickAction(getStyle()->getLinkHREF());
+			return TRUE;
+		}
 	}
 
 	return FALSE;

File indra/llui/lltextbase.h

 
 class LLContextMenu;
 class LLTextSegment;
+class LLNormalTextSegment;
 
 typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
 
 	protected LLEditMenuHandler
 {
 public:
+	friend class LLTextSegment;
+	friend class LLNormalTextSegment;
+
 	struct LineSpacingParams : public LLInitParam::Choice<LineSpacingParams>
 	{
 		Alternative<F32>	multiple;
 	S32						getVPad() { return mVPad; }
 	S32						getHPad() { return mHPad; }
 
-	S32						getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const;
+	S32						getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, bool hit_past_end_of_line = true) const;
 	LLRect					getLocalRectFromDocIndex(S32 pos) const;
 	LLRect					getDocRectFromDocIndex(S32 pos) const;
 
 	// manage segments 
 	void                			getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const;
 	void                			getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp );
-	LLTextSegmentPtr    			getSegmentAtLocalPos( S32 x, S32 y );
+	LLTextSegmentPtr    			getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line = true);
 	segment_set_t::iterator			getSegIterContaining(S32 index);
 	segment_set_t::const_iterator	getSegIterContaining(S32 index) const;
 	void                			clearSegments();

File indra/newview/app_settings/settings.xml

     <key>Type</key>
     <string>Boolean</string>
     <key>Value</key>
-    <integer>0</integer>
+    <integer>1</integer>
   </map>
   <key>MediaShowWithinParcel</key>
   <map>
       <key>Value</key>
       <integer>1</integer>
     </map>
-    <key>VoiceDefaultInternalLevel</key>
-    <map>
-      <key>Comment</key>
-      <string>Internal level of voice set by default. Is equivalent to 0.5 (from 0.0-1.0 range) external voice level (internal = 400 * external^2).</string>
-      <key>Persist</key>
-      <integer>1</integer>
-      <key>Type</key>
-      <string>S32</string>
-      <key>Value</key>
-      <integer>100</integer>
-    </map>
     <key>VoiceEarLocation</key>
     <map>
       <key>Comment</key>

File indra/newview/llagentwearables.cpp

 	if (mAvatarObject)
 	{
 		mAvatarObject->updateVisualParams();
+		mAvatarObject->invalidateAll();
 	}
 
 	// Start rendering & update the server

File indra/newview/llbottomtray.cpp

 	{
 		mBottomTrayLite->setVisible(visible);
 	}
-	else
+	else 
 	{
 		LLPanel::setVisible(visible);
 	}

File indra/newview/llfloaterpreference.cpp

 	mCommitCallbackRegistrar.add("Pref.VertexShaderEnable",     boost::bind(&LLFloaterPreference::onVertexShaderEnable, this));	
 	mCommitCallbackRegistrar.add("Pref.WindowedMod",            boost::bind(&LLFloaterPreference::onCommitWindowedMode, this));	
 	mCommitCallbackRegistrar.add("Pref.UpdateSliderText",       boost::bind(&LLFloaterPreference::onUpdateSliderText,this, _1,_2));	
-	mCommitCallbackRegistrar.add("Pref.ParcelMediaAutoPlayEnable",       boost::bind(&LLFloaterPreference::onCommitParcelMediaAutoPlayEnable, this));	
-	mCommitCallbackRegistrar.add("Pref.MediaEnabled",           boost::bind(&LLFloaterPreference::onCommitMediaEnabled, this));	
-	mCommitCallbackRegistrar.add("Pref.MusicEnabled",           boost::bind(&LLFloaterPreference::onCommitMusicEnabled, this));	
 	mCommitCallbackRegistrar.add("Pref.QualityPerformance",     boost::bind(&LLFloaterPreference::onChangeQuality, this, _2));	
 	mCommitCallbackRegistrar.add("Pref.applyUIColor",			boost::bind(&LLFloaterPreference::applyUIColor, this ,_1, _2));
 	mCommitCallbackRegistrar.add("Pref.getUIColor",				boost::bind(&LLFloaterPreference::getUIColor, this ,_1, _2));
 	}
 }
 
-void LLFloaterPreference::onCommitParcelMediaAutoPlayEnable()
-{
-	BOOL autoplay = getChild<LLCheckBoxCtrl>("autoplay_enabled")->get();
-		
-	gSavedSettings.setBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING, autoplay);
-
-	lldebugs << "autoplay now = " << int(autoplay) << llendl;
-}
-
-void LLFloaterPreference::onCommitMediaEnabled()
-{
-	LLCheckBoxCtrl *media_enabled_ctrl = getChild<LLCheckBoxCtrl>("media_enabled");
-	bool enabled = media_enabled_ctrl->get();
-	gSavedSettings.setBOOL("AudioStreamingMedia", enabled);
-}
-
-void LLFloaterPreference::onCommitMusicEnabled()
-{
-	LLCheckBoxCtrl *music_enabled_ctrl = getChild<LLCheckBoxCtrl>("music_enabled");
-	bool enabled = music_enabled_ctrl->get();
-	gSavedSettings.setBOOL("AudioStreamingMusic", enabled);
-}
-
 void LLFloaterPreference::refresh()
 {
 	LLPanel::refresh();

File indra/newview/llpanelnearbymedia.cpp

 #include "llcheckboxctrl.h"
 #include "llcombobox.h"
 #include "llresizebar.h"
+#include "llresizehandle.h"
 #include "llscrolllistctrl.h"
 #include "llscrolllistitem.h"
 #include "llscrolllistcell.h"
 	p.resizing_view = this;
 	addChild( LLUICtrlFactory::create<LLResizeBar>(p) );
 
+	p.rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0);
+	p.name = "resizebar_left";
+	p.min_size = getRect().getWidth();
+	p.side = LLResizeBar::LEFT;
+	addChild( LLUICtrlFactory::create<LLResizeBar>(p) );
+	
+	LLResizeHandle::Params resize_handle_p;
+	resize_handle_p.rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 );
+	resize_handle_p.mouse_opaque(false);
+	resize_handle_p.min_width(getRect().getWidth());
+	resize_handle_p.min_height(getRect().getHeight());
+	resize_handle_p.corner(LLResizeHandle::LEFT_BOTTOM);
+	addChild(LLUICtrlFactory::create<LLResizeHandle>(resize_handle_p));
+
 	mNearbyMediaPanel = getChild<LLUICtrl>("nearby_media_panel");
 	mMediaList = getChild<LLScrollListCtrl>("media_list");
 	mEnableAllCtrl = getChild<LLUICtrl>("all_nearby_media_enable_btn");
 	updateColumns();
 	
 	LLView* minimized_controls = getChildView("minimized_controls");
-	mMoreHeight = getRect().getHeight();
-	mLessHeight = getRect().getHeight() - minimized_controls->getRect().mBottom;
+	mMoreRect = getRect();
+	mLessRect = getRect();
+	mLessRect.mBottom = minimized_controls->getRect().mBottom;
+
 	getChild<LLUICtrl>("more_less_btn")->setValue(false);
 	onMoreLess();
 	
 	LLButton* more_less_btn = getChild<LLButton>("more_less_btn");
 	if (more_less_btn->getValue().asBoolean())
 	{
-		mMoreHeight = getRect().getHeight();
+		mMoreRect = getRect();
 	}
 
 }
 
 		// Clear all items so the list gets regenerated.
 		mMediaList->deleteAllItems();
+		mParcelAudioItem = NULL;
+		mParcelMediaItem = NULL;
 		all_items_deleted = true;
 		
 		updateColumns();
 	// enable resizing when expanded
 	getChildView("resizebar_bottom")->setEnabled(is_more);
 
-	S32 new_height = is_more ? mMoreHeight : mLessHeight;
-
-	LLRect new_rect = getRect();
-	new_rect.mBottom = new_rect.mTop - new_height;
+	LLRect new_rect = is_more ? mMoreRect : mLessRect;
+	new_rect.translate(getRect().mRight - new_rect.mRight, getRect().mTop - new_rect.mTop);
 
 	setShape(new_rect);
 }

File indra/newview/llpanelnearbymedia.h

 	std::string			mParcelMediaName;
 	std::string			mParcelAudioName;
 	
-	S32				mMoreHeight;
-	S32				mLessHeight;
-	LLFrameTimer			mHoverTimer;
-	LLScrollListItem*		mParcelMediaItem;
-	LLScrollListItem*		mParcelAudioItem;
+	LLRect				mMoreRect;
+	LLRect				mLessRect;
+	LLFrameTimer		mHoverTimer;
+	LLScrollListItem*	mParcelMediaItem;
+	LLScrollListItem*	mParcelAudioItem;
 };
 
 

File indra/newview/lltexlayer.cpp

 BOOL LLTexLayerSetBuffer::needsRender()
 {
 	const LLVOAvatarSelf* avatar = mTexLayerSet->getAvatar();
-	BOOL upload_now = mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal();
-	BOOL needs_update = gAgentQueryManager.hasNoPendingQueries() && (mNeedsUpdate || upload_now) && !avatar->mAppearanceAnimating;
+	BOOL upload_now = mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal() && gAgentQueryManager.hasNoPendingQueries();
+	BOOL needs_update = (mNeedsUpdate || upload_now) && !avatar->mAppearanceAnimating;
 	if (needs_update)
 	{
 		BOOL invalid_skirt = avatar->getBakedTE(mTexLayerSet) == LLVOAvatarDefines::TEX_SKIRT_BAKED && !avatar->isWearingWearableType(WT_SKIRT);

File indra/newview/llvieweraudio.cpp

 		{
 			// initialize wind volume (force_update) by using large volume_delta
 			// which is sufficient to completely turn off or turn on wind noise
-			volume_delta = max_wind_volume;
+			volume_delta = 1.f;
 		}
 
 		// mute wind when not flying

File indra/newview/llviewermedia.cpp

 {
 	if(mMediaSource)
 	{
+		// trim whitespace from front and back of URL - fixes EXT-5363
+		LLStringUtil::trim( mMediaURL );
+
 		// *HACK: we don't know if the URI coming in is properly escaped
 		// (the contract doesn't specify whether it is escaped or not.
 		// but LLQtWebKit expects it to be, so we do our best to encode

File indra/newview/llvoiceclient.cpp

 	 * Gets stored external (vivox) volume level for specified speaker and
 	 * transforms it into internal (viewer) level.
 	 *
-	 * If specified user is not found default level will be returned. It is equivalent of 
-	 * external level 0.5 from the 0.0..1.0 range.
+	 * If specified user is not found -1 will be returned.
 	 * Internal level is calculated as: internal = 400 * external^2
-	 * Maps 0.0 to 1.0 to internal values 0-400 with default 0.5 == 100
+	 * Maps 0.0 to 1.0 to internal values 0-400
 	 *
 	 * @param[in] speaker_id - LLUUID of user to get his volume level
 	 */
 
 S32 LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id)
 {
-	// default internal level of user voice.
-	const static LLUICachedControl<S32> DEFAULT_INTERNAL_VOLUME_LEVEL("VoiceDefaultInternalLevel", 100);
-	S32 ret_val = DEFAULT_INTERNAL_VOLUME_LEVEL;
+	// Return value of -1 indicates no level is stored for this speaker
+	S32 ret_val = -1;
 	speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id);
 	
 	if (it != mSpeakersData.end())
 		mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result));
 
 		result->mUserVolume = LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID);
+		if (result->mUserVolume != -1)
+		{
+			result->mVolumeDirty = true;
+			mVolumeDirty = true;
+		}
 
 		LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
 	}