diff options
57 files changed, 1505 insertions, 423 deletions
| diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 78a6ab1eaa..ab9cd11769 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1562,7 +1562,7 @@ std::string LLTextBase::getText() const  	return getViewModel()->getValue().asString();  } -void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) +void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params)  {  	LLStyle::Params style_params(input_params);  	style_params.fillFrom(getDefaultStyleParams()); @@ -1598,8 +1598,7 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c  					part = (S32)LLTextParser::MIDDLE;  				}  				std::string subtext=text.substr(0,start); -				appendAndHighlightText(subtext, prepend_newline, part, style_params);  -				prepend_newline = false; +				appendAndHighlightTextImpl(subtext, part, style_params);   			}  			// output an optional icon before the Url @@ -1613,19 +1612,18 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c  					// Text will be replaced during rendering with the icon,  					// but string cannot be empty or the segment won't be  					// added (or drawn). -					appendAndHighlightText(" ", prepend_newline, part, icon); -					prepend_newline = false; +					appendImageSegment(part, icon);  				}  			}  			// output the styled Url (unless we've been asked to suppress hyperlinking)  			if (match.isLinkDisabled())  			{ -				appendAndHighlightText(match.getLabel(), prepend_newline, part, style_params); +				appendAndHighlightTextImpl(match.getLabel(), part, style_params);  			}  			else  			{ -				appendAndHighlightText(match.getLabel(), prepend_newline, part, link_params); +				appendAndHighlightTextImpl(match.getLabel(), part, link_params);  				// set the tooltip for the Url label  				if (! match.getTooltip().empty()) @@ -1638,8 +1636,6 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c  						}  				}  			} -			prepend_newline = false; -  			// move on to the rest of the text after the Url  			if (end < (S32)text.length())   			{ @@ -1652,13 +1648,41 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c  				break;  			}  		} -		if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END; -		if (end < (S32)text.length()) appendAndHighlightText(text, prepend_newline, part, style_params);		 +		if (part != (S32)LLTextParser::WHOLE)  +			part=(S32)LLTextParser::END; +		if (end < (S32)text.length())  +			appendAndHighlightTextImpl(text, part, style_params);		  	}  	else  	{ -		appendAndHighlightText(new_text, prepend_newline, part, style_params); +		appendAndHighlightTextImpl(new_text, part, style_params); +	} +} + +void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) +{ +	if (new_text.empty())  +		return; + +	if(prepend_newline) +		appendLineBreakSegment(input_params); +	std::string::size_type start = 0; +	std::string::size_type pos = new_text.find("\n",start); +	 +	while(pos!=-1) +	{ +		if(pos!=start) +		{ +			std::string str = std::string(new_text,start,pos-start); +			appendTextImpl(str,input_params); +		} +		appendLineBreakSegment(input_params); +		start = pos+1; +		pos = new_text.find("\n",start);  	} + +	std::string str = std::string(new_text,start,new_text.length()-start); +	appendTextImpl(str,input_params);  }  void LLTextBase::needsReflow(S32 index) @@ -1667,10 +1691,28 @@ void LLTextBase::needsReflow(S32 index)  	mReflowIndex = llmin(mReflowIndex, index);  } -void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& style_params) +void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params)  { -	if (new_text.empty()) return;                                                                                  +	segment_vec_t segments; +	LLStyleConstSP sp(new LLStyle(style_params)); +	segments.push_back(new LLLineBreakTextSegment(sp, getLength())); +	insertStringNoUndo(getLength(), utf8str_to_wstring("\n"), &segments); +} + +void LLTextBase::appendImageSegment(S32 highlight_part, const LLStyle::Params& style_params) +{ +	segment_vec_t segments; +	LLStyleConstSP sp(new LLStyle(style_params)); +	segments.push_back(new LLImageTextSegment(sp, getLength(),*this)); + +	insertStringNoUndo(getLength(), utf8str_to_wstring(" "), &segments); +} + + + +void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params) +{  	// Save old state  	S32 selection_start = mSelectionStart;  	S32 selection_end = mSelectionEnd; @@ -1698,14 +1740,8 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen  			highlight_params.color = lcolor;  			LLWString wide_text; -			if (prepend_newline && (i == 0 || pieces.size() <= 1 ))  -			{ -				wide_text = utf8str_to_wstring(std::string("\n") + pieces[i]["text"].asString()); -			} -			else -			{ -				wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); -			} +			wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); +  			S32 cur_length = getLength();  			LLStyleConstSP sp(new LLStyle(highlight_params));  			LLTextSegmentPtr segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this); @@ -1717,17 +1753,7 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen  	else  	{  		LLWString wide_text; - -		// Add carriage return if not first line -		if (getLength() != 0 -			&& prepend_newline) -		{ -			wide_text = utf8str_to_wstring(std::string("\n") + new_text); -		} -		else -		{ -			wide_text = utf8str_to_wstring(new_text); -		} +		wide_text = utf8str_to_wstring(new_text);  		segment_vec_t segments;  		S32 segment_start = old_length; @@ -1755,11 +1781,32 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen  	{  		setCursorPos(cursor_pos);  	} +} + +void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& style_params) +{ +	if (new_text.empty()) return;  + +	if(prepend_newline) +		appendLineBreakSegment(style_params); + +	std::string::size_type start = 0; +	std::string::size_type pos = new_text.find("\n",start); +	 +	while(pos!=-1) +	{ +		if(pos!=start) +		{ +			std::string str = std::string(new_text,start,pos-start); +			appendAndHighlightTextImpl(str,highlight_part, style_params); +		} +		appendLineBreakSegment(style_params); +		start = pos+1; +		pos = new_text.find("\n",start); +	} -	//if( !allow_undo ) -	//{ -	//	blockUndo(); -	//} +	std::string str = std::string(new_text,start,new_text.length()-start); +	appendAndHighlightTextImpl(str,highlight_part, style_params);  } @@ -1852,14 +1899,19 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,  		S32 text_width, text_height;  		bool newline = segmentp->getDimensions(line_seg_offset, segment_line_length, text_width, text_height); +		if(newline) +		{ +			pos = segment_line_start + segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); +			break; +		} +  		// if we've reached a line of text *below* the mouse cursor, doc index is first character on that line  		if (hit_past_end_of_line && local_y - mVisibleTextRect.mBottom + visible_region.mBottom > line_iter->mRect.mTop)  		{  			pos = segment_line_start;  			break;  		} -		if (local_x < start_x + text_width			// cursor to left of right edge of text -			|| newline)								// or this line ends with a newline, set doc pos to newline char +		if (local_x < start_x + text_width)			// cursor to left of right edge of text  		{  			// Figure out which character we're nearest to.  			S32 offset; @@ -1883,13 +1935,13 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,  			pos = segment_line_start + offset;  			break;  		} -		else if (hit_past_end_of_line && segmentp->getEnd() >= line_iter->mDocIndexEnd - 1)	 +		else if (hit_past_end_of_line && segmentp->getEnd() > line_iter->mDocIndexEnd - 1)	  		{ -			// segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd) -			pos = llmin(getLength(), line_iter->mDocIndexEnd); +			// segment wraps to next line, so just set doc pos to the end of the line + 			// segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd) + 			pos = llmin(getLength(), line_iter->mDocIndexEnd);  			break;  		} -  		start_x += text_width;  	} @@ -2346,25 +2398,6 @@ F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selec  {  	if( end - start > 0 )  	{ -		if ( mStyle->isImage() && (start >= 0) && (end <= mEnd - mStart)) -		{ -			// ...for images, only render the image, not the underlying text, -			// which is only a placeholder space -			LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha; -			LLUIImagePtr image = mStyle->getImage(); -			S32 style_image_height = image->getHeight(); -			S32 style_image_width = image->getWidth(); -			// Text is drawn from the top of the draw_rect downward -			S32 text_center = draw_rect.mTop - (mFontHeight / 2); -			// Align image to center of text -			S32 image_bottom = text_center - (style_image_height / 2); -			image->draw(draw_rect.mLeft, image_bottom,  -				style_image_width, style_image_height, color); -			 -			const S32 IMAGE_HPAD = 3; -			return draw_rect.mLeft + style_image_width + IMAGE_HPAD; -		} -  		return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect);  	}  	return draw_rect.mLeft; @@ -2377,11 +2410,6 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele  	const LLWString &text = mEditor.getWText(); -	if ( text[seg_end-1] == '\n' ) -	{ -		--seg_end; -	} -  	F32 right_x = rect.mLeft;  	if (!mStyle->isVisible())  	{ @@ -2540,33 +2568,14 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt  {  	height = 0;  	width = 0; -	bool force_newline = false;  	if (num_chars > 0)  	{  		height = mFontHeight;  		const LLWString &text = mEditor.getWText();  		// if last character is a newline, then return true, forcing line break -		llwchar last_char = text[mStart + first_char + num_chars - 1]; -		if (last_char == '\n') -		{ -			force_newline = true; -			// don't count newline in font width -			width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars - 1); -		} -		else -		{ -			width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); -		} -	} - -	LLUIImagePtr image = mStyle->getImage(); -	if( image.notNull()) -	{ -		width += image->getWidth(); -		height = llmax(height, image->getHeight()); +		width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars);  	} - -	return force_newline; +	return false;  }  S32	LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const @@ -2589,15 +2598,7 @@ S32	LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin  		num_pixels = llmax(0, num_pixels - image->getWidth());  	} -	// search for newline and if found, truncate there -	S32 last_char = mStart + segment_offset; -	for (; last_char != mEnd; ++last_char) -	{ -		if (text[last_char] == '\n')  -		{ -			break; -		} -	} +	S32 last_char = mEnd;  	// set max characters to length of segment, or to first newline  	max_chars = llmin(max_chars, last_char - (mStart + segment_offset)); @@ -2625,8 +2626,7 @@ S32	LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin  	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 < mEnd  -		&& (last_char_in_run >= mEditor.getLength()  -			|| text[last_char_in_run] == '\n')) +		&& (last_char_in_run >= mEditor.getLength() ))  	{  		num_chars++;  	} @@ -2721,3 +2721,87 @@ void LLInlineViewSegment::linkToDocument(LLTextBase* editor)  {  	editor->addDocumentChild(mView);  } + +LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1) +{ +	mFontHeight = llceil(style->getFont()->getLineHeight()); +} +LLLineBreakTextSegment::~LLLineBreakTextSegment() +{ +} +bool LLLineBreakTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const +{ +	width = 0; +	height = mFontHeight; + +	return true; +} +S32	LLLineBreakTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const +{ +	return 1; +} +F32	LLLineBreakTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) +{ +	return  draw_rect.mLeft; +} + +LLImageTextSegment::LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor) +	:LLTextSegment(pos,pos+1) +	,mStyle( style ) +	,mEditor(editor) +{ +} + +LLImageTextSegment::~LLImageTextSegment() +{ +} + +static const S32 IMAGE_HPAD = 3; + +bool LLImageTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const +{ +	width = 0; +	height = llceil(mStyle->getFont()->getLineHeight());; + +	LLUIImagePtr image = mStyle->getImage(); +	if( image.notNull()) +	{ +		width += image->getWidth() + IMAGE_HPAD; +		height = llmax(height, image->getHeight() + IMAGE_HPAD ); +	} +	return false; +} + +S32	 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const +{ +	LLUIImagePtr image = mStyle->getImage(); +	S32 image_width = image->getWidth(); +	if(num_pixels>image_width + IMAGE_HPAD) +	{ +		return 1; +	} + +	return 0; +} +F32	LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) +{ +	if ( (start >= 0) && (end <= mEnd - mStart)) +	{ +		LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha; +		LLUIImagePtr image = mStyle->getImage(); +		S32 style_image_height = image->getHeight(); +		S32 style_image_width = image->getWidth(); +		// Text is drawn from the top of the draw_rect downward +		 +		S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2); +		// Align image to center of draw rect +		S32 image_bottom = text_center - (style_image_height / 2); +		image->draw(draw_rect.mLeft, image_bottom,  +			style_image_width, style_image_height, color); +		 +		const S32 IMAGE_HPAD = 3; +		return draw_rect.mLeft + style_image_width + IMAGE_HPAD; +	} +	return 0.0; +} + diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 8ed0680df9..176308c61a 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -316,6 +316,13 @@ protected:  	void							needsScroll() { mScrollNeeded = TRUE; }  	void							replaceUrlLabel(const std::string &url, const std::string &label); +	void							appendLineBreakSegment(const LLStyle::Params& style_params); +	void							appendImageSegment(S32 highlight_part, const LLStyle::Params& style_params); +	 +	void							appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params = LLStyle::Params()); +	void							appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params); +	 +  protected:  	// text segmentation and flow  	segment_set_t       		mSegments; @@ -507,5 +514,32 @@ private:  	bool	mForceNewLine;  }; +class LLLineBreakTextSegment : public LLTextSegment +{ +public: + +	LLLineBreakTextSegment(LLStyleConstSP style,S32 pos); +	~LLLineBreakTextSegment(); +	bool		getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; +	S32			getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; +	F32			draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); + +private: +	S32			mFontHeight; +}; + +class LLImageTextSegment : public LLTextSegment +{ +public: +	LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor); +	~LLImageTextSegment(); +	bool		getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; +	S32			getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; +	F32			draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); + +private: +	class LLTextBase&	mEditor; +	LLStyleConstSP	mStyle; +};  #endif diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 4fd62045e8..c9474d66b7 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1083,6 +1083,28 @@ void LLTextEditor::addChar(llwchar wc)  	setCursorPos(mCursorPos + addChar( mCursorPos, wc ));  } +void LLTextEditor::addLineBreakChar() +{ +	if( !getEnabled() ) +	{ +		return; +	} +	if( hasSelection() ) +	{ +		deleteSelection(TRUE); +	} +	else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) +	{ +		removeChar(mCursorPos); +	} + +	LLStyleConstSP sp(new LLStyle(LLStyle::Params())); +	LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos); + +	S32 pos = execute(new TextCmdAddChar(mCursorPos, FALSE, '\n', segment)); +	 +	setCursorPos(mCursorPos + pos); +}  BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) @@ -1404,7 +1426,27 @@ void LLTextEditor::pasteHelper(bool is_primary)  	}  	// Insert the new text into the existing text. -	setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE, LLTextSegmentPtr())); + +	//paste text with linebreaks. +	std::basic_string<llwchar>::size_type start = 0; +	std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start); +	 +	while(pos!=-1) +	{ +		if(pos!=start) +		{ +			std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,pos-start); +			setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr())); +		} +		addLineBreakChar(); +		 +		start = pos+1; +		pos = clean_string.find('\n',start); +	} + +	std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,clean_string.length()-start); +	setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr())); +  	deselect();  	onKeyStroke(); @@ -2169,7 +2211,10 @@ void LLTextEditor::autoIndent()  	}  	// Insert that number of spaces on the new line -	addChar( '\n' ); + +	//appendLineBreakSegment(LLStyle::Params());//addChar( '\n' ); +	addLineBreakChar(); +  	for( i = 0; i < space_count; i++ )  	{  		addChar( ' ' ); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 9b3ab9414c..7b68974fd8 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -240,6 +240,7 @@ protected:  	// Undoable operations  	void			addChar(llwchar c); // at mCursorPos  	S32				addChar(S32 pos, llwchar wc); +	void			addLineBreakChar();  	S32				overwriteChar(S32 pos, llwchar wc);  	void			removeChar();  	S32 			removeChar(S32 pos); diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 71f030677a..3c21fe8d61 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -94,6 +94,8 @@ public:  	/// is this a match for a URL that should not be hyperlinked?  	bool isLinkDisabled() const { return mDisabledLink; } +	virtual LLUUID	getID(const std::string &string) const { return LLUUID::null; } +  protected:  	std::string getIDStringFromUrl(const std::string &url) const;  	std::string escapeUrl(const std::string &url) const; diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp index 72a199c220..7c96665ce4 100644 --- a/indra/llui/llurlmatch.cpp +++ b/indra/llui/llurlmatch.cpp @@ -51,7 +51,7 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,  						   const std::string &label, const std::string &tooltip,  						   const std::string &icon, const LLUIColor& color,  						   const std::string &menu, const std::string &location, -						   bool disabled_link) +						   bool disabled_link, const LLUUID& id)  {  	mStart = start;  	mEnd = end; @@ -63,4 +63,5 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,  	mMenuName = menu;  	mLocation = location;  	mDisabledLink = disabled_link; +	mID = id;  } diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h index e86762548b..78dd2c528f 100644 --- a/indra/llui/llurlmatch.h +++ b/indra/llui/llurlmatch.h @@ -90,7 +90,10 @@ public:  	void setValues(U32 start, U32 end, const std::string &url, const std::string &label,  	               const std::string &tooltip, const std::string &icon,  				   const LLUIColor& color, const std::string &menu,  -				   const std::string &location, bool disabled_link); +				   const std::string &location, bool disabled_link +				   , const LLUUID& id ); + +	const LLUUID& getID() const { return mID;}  private:  	U32         mStart; @@ -101,6 +104,8 @@ private:  	std::string mIcon;  	std::string mMenuName;  	std::string mLocation; + +	LLUUID		mID;  	LLUIColor	mColor;  	bool        mDisabledLink;  }; diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 4341286eb4..1f86f72faa 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -183,7 +183,8 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL  						match_entry->getColor(),  						match_entry->getMenuName(),  						match_entry->getLocation(url), -						match_entry->isLinkDisabled()); +						match_entry->isLinkDisabled(), +						match_entry->getID(url));  		return true;  	} @@ -217,7 +218,8 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr  						match.getColor(),  						match.getMenuName(),  						match.getLocation(), -						match.isLinkDisabled()); +						match.isLinkDisabled(), +						match.getID());  		return true;  	}  	return false; diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index 24a32de268..06b850d233 100644 --- a/indra/llui/tests/llurlmatch_test.cpp +++ b/indra/llui/tests/llurlmatch_test.cpp @@ -54,7 +54,7 @@ namespace tut  		LLUrlMatch match;  		ensure("empty()", match.empty()); -		match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "", false); +		match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure("! empty()", ! match.empty());  	} @@ -67,7 +67,7 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getStart() == 0", match.getStart(), 0); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getStart() == 10", match.getStart(), 10);  	} @@ -80,7 +80,7 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getEnd() == 0", match.getEnd(), 0); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getEnd() == 20", match.getEnd(), 20);  	} @@ -93,10 +93,10 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getUrl() == ''", match.getUrl(), ""); -		match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getUrl() == '' (2)", match.getUrl(), "");  	} @@ -109,10 +109,10 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getLabel() == ''", match.getLabel(), ""); -		match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getLabel() == '' (2)", match.getLabel(), "");  	} @@ -125,10 +125,10 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getTooltip() == ''", match.getTooltip(), ""); -		match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getTooltip() == '' (2)", match.getTooltip(), "");  	} @@ -141,10 +141,10 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getIcon() == ''", match.getIcon(), ""); -		match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure_equals("getIcon() == '' (2)", match.getIcon(), "");  	} @@ -157,10 +157,10 @@ namespace tut  		LLUrlMatch match;  		ensure("getMenuName() empty", match.getMenuName().empty()); -		match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "", false); +		match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "", false,LLUUID::null);  		ensure_equals("getMenuName() == \"xui_file.xml\"", match.getMenuName(), "xui_file.xml"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure("getMenuName() empty (2)", match.getMenuName().empty());  	} @@ -173,10 +173,10 @@ namespace tut  		LLUrlMatch match;  		ensure("getLocation() empty", match.getLocation().empty()); -		match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris", false); +		match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris", false,LLUUID::null);  		ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false); +		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);  		ensure("getLocation() empty (2)", match.getLocation().empty());  	}  } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9ba44e787b..b00104c427 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -466,6 +466,7 @@ set(viewer_SOURCE_FILES      llviewchildren.cpp      llviewerassetstorage.cpp      llviewerassettype.cpp +    llviewerattachmenu.cpp      llvieweraudio.cpp      llviewercamera.cpp      llviewerchat.cpp @@ -984,6 +985,7 @@ set(viewer_HEADER_FILES      llviewchildren.h      llviewerassetstorage.h      llviewerassettype.h +    llviewerattachmenu.h      llvieweraudio.h      llviewercamera.h      llviewerchat.h diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index e5796f8e63..d2a01aba16 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -1974,6 +1974,32 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos  }  // static +void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id) +{ +	LLWearable* wearable = LLWearableList::instance().createNewWearable(type); +	LLAssetType::EType asset_type = wearable->getAssetType(); +	LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; +	LLPointer<LLInventoryCallback> cb = wear ? new WearOnAvatarCallback : NULL; +	LLUUID folder_id; + +	if (parent_id.notNull()) +	{ +		folder_id = parent_id; +	} +	else +	{ +		LLFolderType::EType folder_type = LLFolderType::assetTypeToFolderType(asset_type); +		folder_id = gInventory.findCategoryUUIDForType(folder_type); +	} + +	create_inventory_item(gAgent.getID(), gAgent.getSessionID(), +						  folder_id, wearable->getTransactionID(), wearable->getName(), +						  wearable->getDescription(), asset_type, inv_type, wearable->getType(), +						  wearable->getPermissions().getMaskNextOwner(), +						  cb); +} + +// static  void LLAgentWearables::editWearable(const LLUUID& item_id)  {  	LLViewerInventoryItem* item; diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 5d5c5ae371..6d379746ba 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -144,6 +144,7 @@ protected:  	//--------------------------------------------------------------------  public: +	static void		createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null);  	static void		editWearable(const LLUUID& item_id);  	bool			moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index f27e632180..75ffb9f329 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -704,6 +704,40 @@ void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit)  	wearInventoryCategory(cat, false, false);  } +// Open outfit renaming dialog. +void LLAppearanceMgr::renameOutfit(const LLUUID& outfit_id) +{ +	LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id); +	if (!cat) +	{ +		return; +	} + +	LLSD args; +	args["NAME"] = cat->getName(); + +	LLSD payload; +	payload["cat_id"] = outfit_id; + +	LLNotificationsUtil::add("RenameOutfit", args, payload, boost::bind(onOutfitRename, _1, _2)); +} + +// User typed new outfit name. +// static +void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& response) +{ +	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +	if (option != 0) return; // canceled + +	std::string outfit_name = response["new_name"].asString(); +	LLStringUtil::trim(outfit_name); +	if (!outfit_name.empty()) +	{ +		LLUUID cat_id = notification["payload"]["cat_id"].asUUID(); +		rename_category(&gInventory, cat_id, outfit_name); +	} +} +  void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id)  {  	LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); @@ -714,7 +748,7 @@ void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id)  {  	LLInventoryModel::cat_array_t cats;  	LLInventoryModel::item_array_t items; -	LLFindWearables collector; +	LLFindWorn collector;  	gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector); @@ -842,6 +876,41 @@ BOOL LLAppearanceMgr::getCanMakeFolderIntoOutfit(const LLUUID& folder_id)  	return ((required_wearables & folder_wearables) == required_wearables);  } +bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id) +{ +	// Disallow removing the base outfit. +	if (outfit_cat_id == getBaseOutfitUUID()) +	{ +		return false; +	} + +	// Check if the outfit folder itself is removable. +	if (!get_is_category_removable(&gInventory, outfit_cat_id)) +	{ +		return false; +	} + +	// Check if the folder contains worn items. +	LLInventoryModel::cat_array_t cats; +	LLInventoryModel::item_array_t items; +	LLFindWorn filter_worn; +	gInventory.collectDescendentsIf(outfit_cat_id, cats, items, false, filter_worn); +	if (!items.empty()) +	{ +		return false; +	} + +	// Check for the folder's non-removable descendants. +	LLFindNonRemovableObjects filter_non_removable; +	LLInventoryModel::item_array_t::const_iterator it; +	gInventory.collectDescendentsIf(outfit_cat_id, cats, items, false, filter_non_removable); +	if (!cats.empty() || !items.empty()) +	{ +		return false; +	} + +	return true; +}  void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category)  { diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index dbde055c3a..f1beef5857 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -59,6 +59,7 @@ public:  	void wearOutfitByName(const std::string& name);  	void changeOutfit(bool proceed, const LLUUID& category, bool append);  	void replaceCurrentOutfit(const LLUUID& new_outfit); +	void renameOutfit(const LLUUID& outfit_id);  	void takeOffOutfit(const LLUUID& cat_id);  	void addCategoryToCurrentOutfit(const LLUUID& cat_id); @@ -69,6 +70,9 @@ public:  	// Return whether this folder contains minimal contents suitable for making a full outfit.  	BOOL getCanMakeFolderIntoOutfit(const LLUUID& folder_id); +	// Determine whether a given outfit can be removed. +	bool getCanRemoveOutfit(const LLUUID& outfit_cat_id); +  	// Copy all items in a category.  	void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id,  									 LLPointer<LLInventoryCallback> cb); @@ -180,6 +184,8 @@ private:  	void purgeCategory(const LLUUID& category, bool keep_outfit_links);  	void purgeBaseOutfitLink(const LLUUID& category); +	static void onOutfitRename(const LLSD& notification, const LLSD& response); +  	std::set<LLUUID> mRegisteredAttachments;  	bool mAttachmentInvLinkEnabled;  	bool mOutfitIsDirty; diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index 6acd16326a..79ce2f8f6b 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -45,6 +45,7 @@  #include "llwearableitemslist.h"  #include "llpaneloutfitedit.h"  #include "llsidetray.h" +#include "lltrans.h"  static LLRegisterPanelClassWrapper<LLCOFWearables> t_cof_wearables("cof_wearables"); @@ -52,6 +53,39 @@ const LLSD REARRANGE = LLSD().with("rearrange", LLSD());  static const LLWearableItemNameComparator WEARABLE_NAME_COMPARATOR; +////////////////////////////////////////////////////////////////////////// + +class CofContextMenu : public LLListContextMenu +{ +protected: +	static void updateCreateWearableLabel(LLMenuGL* menu, const LLUUID& item_id) +	{ +		LLMenuItemGL* menu_item = menu->getChild<LLMenuItemGL>("create_new"); + +		// Hide the "Create new <WEARABLE_TYPE>" if it's irrelevant. +		LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); +		if (!item || !item->isWearableType()) +		{ +			menu_item->setVisible(FALSE); +			return; +		} + +		// Set proper label for the "Create new <WEARABLE_TYPE>" menu item. +		LLStringUtil::format_map_t args; +		LLWearableType::EType w_type = item->getWearableType(); +		args["[WEARABLE_TYPE]"] = LLWearableType::getTypeDefaultNewName(w_type); +		std::string new_label = LLTrans::getString("CreateNewWearable", args); +		menu_item->setLabel(new_label); +	} + +	static void createNew(const LLUUID& item_id) +	{ +		LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); +		if (!item || !item->isWearableType()) return; + +		LLAgentWearables::createWearable(item->getWearableType(), true); +	} +};  ////////////////////////////////////////////////////////////////////////// @@ -72,7 +106,7 @@ protected:  ////////////////////////////////////////////////////////////////////////// -class CofClothingContextMenu : public LLListContextMenu +class CofClothingContextMenu : public CofContextMenu  {  protected: @@ -87,10 +121,17 @@ protected:  		registrar.add("Clothing.MoveUp", boost::bind(moveWearable, selected_id, false));  		registrar.add("Clothing.MoveDown", boost::bind(moveWearable, selected_id, true));  		registrar.add("Clothing.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); +		registrar.add("Clothing.Create", boost::bind(createNew, selected_id));  		enable_registrar.add("Clothing.OnEnable", boost::bind(&CofClothingContextMenu::onEnable, this, _2)); -		return createFromFile("menu_cof_clothing.xml"); +		LLContextMenu* menu = createFromFile("menu_cof_clothing.xml"); +		llassert(menu); +		if (menu) +		{ +			updateCreateWearableLabel(menu, selected_id); +		} +		return menu;  	}  	bool onEnable(const LLSD& data) @@ -106,6 +147,10 @@ protected:  		{  			return gAgentWearables.canMoveWearable(selected_id, true);  		} +		else if ("take_off" == param) +		{ +			return get_is_item_worn(selected_id); +		}  		else if ("edit" == param)  		{  			return gAgentWearables.isWearableModifiable(selected_id); @@ -120,12 +165,11 @@ protected:  		LLViewerInventoryItem* item = gInventory.getItem(item_id);  		return LLAppearanceMgr::instance().moveWearable(item, closer_to_body);  	} -  };  ////////////////////////////////////////////////////////////////////////// -class CofBodyPartContextMenu : public LLListContextMenu +class CofBodyPartContextMenu : public CofContextMenu  {  protected: @@ -140,10 +184,17 @@ protected:  		LLPanelOutfitEdit* panel_oe = dynamic_cast<LLPanelOutfitEdit*>(LLSideTray::getInstance()->getPanel("panel_outfit_edit"));  		registrar.add("BodyPart.Replace", boost::bind(&LLPanelOutfitEdit::onReplaceBodyPartMenuItemClicked, panel_oe, selected_id));  		registrar.add("BodyPart.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); +		registrar.add("BodyPart.Create", boost::bind(createNew, selected_id));  		enable_registrar.add("BodyPart.OnEnable", boost::bind(&CofBodyPartContextMenu::onEnable, this, _2)); -		return createFromFile("menu_cof_body_part.xml"); +		LLContextMenu* menu = createFromFile("menu_cof_body_part.xml"); +		llassert(menu); +		if (menu) +		{ +			updateCreateWearableLabel(menu, selected_id); +		} +		return menu;  	}  	bool onEnable(const LLSD& data) diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index abdb55ec17..7c5586ec96 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -572,7 +572,7 @@ void LLPanelLandGeneral::refresh()  		if (regionp)  		{  			insert_maturity_into_textbox(mContentRating, gFloaterView->getParentFloater(this), MATURITY); -			mLandType->setText(regionp->getSimProductName()); +			mLandType->setText(LLTrans::getString(regionp->getSimProductName()));  		}  		// estate owner/manager cannot edit other parts of the parcel diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 1f87b14ddd..b7495f7dbe 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -164,38 +164,7 @@ time_t LLInvFVBridge::getCreationDate() const  // Can be destroyed (or moved to trash)  BOOL LLInvFVBridge::isItemRemovable() const  { -	const LLInventoryModel* model = getInventoryModel(); -	if(!model)  -	{ -		return FALSE; -	} - -	// Can't delete an item that's in the library. -	if(!model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID())) -	{ -		return FALSE; -	} - -	// Disable delete from COF folder; have users explicitly choose "detach/take off", -	// unless the item is not worn but in the COF (i.e. is bugged). -	if (LLAppearanceMgr::instance().getIsProtectedCOFItem(mUUID)) -	{ -		if (get_is_item_worn(mUUID)) -		{ -			return FALSE; -		} -	} - -	const LLInventoryObject *obj = model->getItem(mUUID); -	if (obj && obj->getIsLinkType()) -	{ -		return TRUE; -	} -	if (get_is_item_worn(mUUID)) -	{ -		return FALSE; -	} -	return TRUE; +	return get_is_item_removable(getInventoryModel(), mUUID);  }  // Can be moved to another folder @@ -833,24 +802,7 @@ void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model,  										 const LLUUID& new_parent_id,  										 BOOL restamp)  { -	// Can't move a folder into a child of itself. -	if (model->isObjectDescendentOf(new_parent_id, cat->getUUID())) -	{ -		return; -	} - -	LLInventoryModel::update_list_t update; -	LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1); -	update.push_back(old_folder); -	LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); -	update.push_back(new_folder); -	model->accountForUpdate(update); -	 -	LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat); -	new_cat->setParent(new_parent_id); -	new_cat->updateParentOnServer(restamp); -	model->updateCategory(new_cat); -	model->notifyObservers(); +	change_category_parent(model, cat, new_parent_id, restamp);  }  LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, @@ -1538,26 +1490,7 @@ public:  // Can be destroyed (or moved to trash)  BOOL LLFolderBridge::isItemRemovable() const  { -	LLInventoryModel* model = getInventoryModel(); -	if(!model) -	{ -		return FALSE; -	} - -	if(!model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID())) -	{ -		return FALSE; -	} - -	if (!isAgentAvatarValid()) return FALSE; - -	LLInventoryCategory* category = model->getCategory(mUUID); -	if(!category) -	{ -		return FALSE; -	} - -	if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) +	if (!get_is_category_removable(getInventoryModel(), mUUID))  	{  		return FALSE;  	} @@ -1573,6 +1506,7 @@ BOOL LLFolderBridge::isItemRemovable() const  			return FALSE;  		}  	} +  	return TRUE;  } @@ -2379,21 +2313,8 @@ LLUIImagePtr LLFolderBridge::getOpenIcon() const  BOOL LLFolderBridge::renameItem(const std::string& new_name)  { -	if(!isItemRenameable()) -		return FALSE; -	LLInventoryModel* model = getInventoryModel(); -	if(!model) -		return FALSE; -	LLViewerInventoryCategory* cat = getCategory(); -	if(cat && (cat->getName() != new_name)) -	{ -		LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat); -		new_cat->rename(new_name); -		new_cat->updateServer(FALSE); -		model->updateCategory(new_cat); +	rename_category(getInventoryModel(), mUUID, new_name); -		model->notifyObservers(); -	}  	// return FALSE because we either notified observers (& therefore  	// rebuilt) or we didn't update.  	return FALSE; @@ -2447,36 +2368,7 @@ bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& re  	{  		// move it to the trash  		LLPreview::hide(mUUID); -		LLInventoryModel* model = getInventoryModel(); -		if(!model) return FALSE; -		 -		const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); -		 -		// Look for any gestures and deactivate them -		LLInventoryModel::cat_array_t	descendent_categories; -		LLInventoryModel::item_array_t	descendent_items; -		gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE ); -		 -		for (LLInventoryModel::item_array_t::const_iterator iter = descendent_items.begin(); -			 iter != descendent_items.end(); -			 ++iter) -		{ -			const LLViewerInventoryItem* item = (*iter); -			const LLUUID& item_id = item->getUUID(); -			if (item->getType() == LLAssetType::AT_GESTURE -				&& LLGestureMgr::instance().isGestureActive(item_id)) -			{ -				LLGestureMgr::instance().deactivateGesture(item_id); -			} -		} -		 -		// go ahead and do the normal remove if no 'last calling -		// cards' are being removed. -		LLViewerInventoryCategory* cat = getCategory(); -		if(cat) -		{ -			LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE); -		} +		remove_category(getInventoryModel(), mUUID);  		return TRUE;  	}  	return FALSE; @@ -2672,22 +2564,6 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv  	return ((item_array.count() > 0) ? TRUE : FALSE );  } -class LLFindWorn : public LLInventoryCollectFunctor -{ -public: -	LLFindWorn() {} -	virtual ~LLFindWorn() {} -	virtual bool operator()(LLInventoryCategory* cat, -							LLInventoryItem* item) -	{ -		if (item && get_is_item_worn(item->getUUID())) -		{ -			return TRUE; -		} -		return FALSE; -	} -}; -  BOOL LLFolderBridge::areAnyContentsWorn(LLInventoryModel* model) const  {  	LLInventoryModel::cat_array_t cat_array; @@ -3006,22 +2882,7 @@ void LLFolderBridge::createWearable(LLFolderBridge* bridge, LLWearableType::ETyp  {  	if(!bridge) return;  	LLUUID parent_id = bridge->getUUID(); -	createWearable(parent_id, type); -} - -// Separate function so can be called by global menu as well as right-click -// menu. -// static -void LLFolderBridge::createWearable(const LLUUID &parent_id, LLWearableType::EType type) -{ -	LLWearable* wearable = LLWearableList::instance().createNewWearable(type); -	LLAssetType::EType asset_type = wearable->getAssetType(); -	LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; -	create_inventory_item(gAgent.getID(), gAgent.getSessionID(), -						  parent_id, wearable->getTransactionID(), wearable->getName(), -						  wearable->getDescription(), asset_type, inv_type, wearable->getType(), -						  wearable->getPermissions().getMaskNextOwner(), -						  LLPointer<LLInventoryCallback>(NULL)); +	LLAgentWearables::createWearable(type, false, parent_id);  }  void LLFolderBridge::modifyOutfit(BOOL append) @@ -4898,13 +4759,7 @@ void LLWearableBridge::onEditOnAvatar(void* user_data)  void LLWearableBridge::editOnAvatar()  { -	LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID); -	if( wearable ) -	{ -		LLPanel * panel = LLSideTray::getInstance()->getPanel("sidepanel_appearance"); - -		LLSidepanelAppearance::editWearable(wearable, panel); -	} +	LLAgentWearables::editWearable(mUUID);  }  // static diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index c5efefac7e..59c1f3d6fb 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -270,7 +270,6 @@ public:  	virtual BOOL copyToClipboard() const;  	static void createWearable(LLFolderBridge* bridge, LLWearableType::EType type); -	static void createWearable(const LLUUID &parent_folder_id, LLWearableType::EType type);  	LLViewerInventoryCategory* getCategory() const; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 0cc4b0e389..817da34c61 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -130,6 +130,90 @@ void change_item_parent(LLInventoryModel* model,  	}  } +void change_category_parent(LLInventoryModel* model, +	LLViewerInventoryCategory* cat, +	const LLUUID& new_parent_id, +	BOOL restamp) +{ +	if (!model || !cat) +	{ +		return; +	} + +	// Can't move a folder into a child of itself. +	if (model->isObjectDescendentOf(new_parent_id, cat->getUUID())) +	{ +		return; +	} + +	LLInventoryModel::update_list_t update; +	LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1); +	update.push_back(old_folder); +	LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); +	update.push_back(new_folder); +	model->accountForUpdate(update); + +	LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat); +	new_cat->setParent(new_parent_id); +	new_cat->updateParentOnServer(restamp); +	model->updateCategory(new_cat); +	model->notifyObservers(); +} + +void remove_category(LLInventoryModel* model, const LLUUID& cat_id) +{ +	if (!model || !get_is_category_removable(model, cat_id)) +	{ +		return; +	} + +	// Look for any gestures and deactivate them +	LLInventoryModel::cat_array_t	descendent_categories; +	LLInventoryModel::item_array_t	descendent_items; +	gInventory.collectDescendents(cat_id, descendent_categories, descendent_items, FALSE); + +	for (LLInventoryModel::item_array_t::const_iterator iter = descendent_items.begin(); +		 iter != descendent_items.end(); +		 ++iter) +	{ +		const LLViewerInventoryItem* item = (*iter); +		const LLUUID& item_id = item->getUUID(); +		if (item->getType() == LLAssetType::AT_GESTURE +			&& LLGestureMgr::instance().isGestureActive(item_id)) +		{ +			LLGestureMgr::instance().deactivateGesture(item_id); +		} +	} + +	// go ahead and do the normal remove if no 'last calling +	// cards' are being removed. +	LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); +	if (cat) +	{ +		const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); +		change_category_parent(model, cat, trash_id, TRUE); +	} +} + +void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name) +{ +	LLViewerInventoryCategory* cat; + +	if (!model || +		!get_is_category_renameable(model, cat_id) || +		(cat = model->getCategory(cat_id)) == NULL || +		cat->getName() == new_name) +	{ +		return; +	} + +	LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat); +	new_cat->rename(new_name); +	new_cat->updateServer(FALSE); +	model->updateCategory(new_cat); + +	model->notifyObservers(); +}  BOOL get_is_item_worn(const LLUUID& id)  { @@ -160,6 +244,83 @@ BOOL get_is_item_worn(const LLUUID& id)  	return FALSE;  } +BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) +{ +	if (!model) +	{ +		return FALSE; +	} + +	// Can't delete an item that's in the library. +	if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) +	{ +		return FALSE; +	} + +	// Disable delete from COF folder; have users explicitly choose "detach/take off", +	// unless the item is not worn but in the COF (i.e. is bugged). +	if (LLAppearanceMgr::instance().getIsProtectedCOFItem(id)) +	{ +		if (get_is_item_worn(id)) +		{ +			return FALSE; +		} +	} + +	const LLInventoryObject *obj = model->getItem(id); +	if (obj && obj->getIsLinkType()) +	{ +		return TRUE; +	} +	if (get_is_item_worn(id)) +	{ +		return FALSE; +	} +	return TRUE; +} + +BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) +{ +	// This function doesn't check the folder's children. + +	if (!model) +	{ +		return FALSE; +	} + +	if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) +	{ +		return FALSE; +	} + +	if (!isAgentAvatarValid()) return FALSE; + +	LLInventoryCategory* category = model->getCategory(id); +	if (!category) +	{ +		return FALSE; +	} + +	if (LLFolderType::lookupIsProtectedType(category->getPreferredType())) +	{ +		return FALSE; +	} + +	return TRUE; +} + +BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) +{ +	LLViewerInventoryCategory* cat = model->getCategory(id); + +	if (cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()) && +		cat->getOwnerID() == gAgent.getID()) +	{ +		return TRUE; +	} +	return FALSE; +} +  void show_item_profile(const LLUUID& item_uuid)  {  	LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); @@ -376,6 +537,26 @@ void LLFindWearablesOfType::setType(LLWearableType::EType type)  	mWearableType = type;  } +bool LLFindWorn::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ +	return item && get_is_item_worn(item->getUUID()); +} + +bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ +	if (item) +	{ +		return !get_is_item_removable(&gInventory, item->getUUID()); +	} +	if (cat) +	{ +		return !get_is_category_removable(&gInventory, cat->getUUID()); +	} + +	llwarns << "Not a category and not an item?" << llendl; +	return false; +} +  ///----------------------------------------------------------------------------  /// LLAssetIDMatches   ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index bb365573d7..33b52cfd5e 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -46,6 +46,12 @@  // Is this item or its baseitem is worn, attached, etc...  BOOL get_is_item_worn(const LLUUID& id); +BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id); + +BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id); + +BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id); +  void show_item_profile(const LLUUID& item_uuid);  void show_item_original(const LLUUID& item_uuid); @@ -55,6 +61,15 @@ void change_item_parent(LLInventoryModel* model,  									 const LLUUID& new_parent_id,  									 BOOL restamp); +void change_category_parent(LLInventoryModel* model, +	LLViewerInventoryCategory* cat, +	const LLUUID& new_parent_id, +	BOOL restamp); + +void remove_category(LLInventoryModel* model, const LLUUID& cat_id); + +void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); +  // Generates a string containing the path to the item specified by item_id.  void append_path(const LLUUID& id, std::string& path); @@ -309,6 +324,22 @@ private:  	LLWearableType::EType mWearableType;  }; +// Find worn items. +class LLFindWorn : public LLInventoryCollectFunctor +{ +public: +	LLFindWorn() {} +	virtual ~LLFindWorn() {} +	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); +}; + +// Collect non-removable folders and items. +class LLFindNonRemovableObjects : public LLInventoryCollectFunctor +{ +public: +	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); +}; +  /**                    Inventory Collector Functions   **                                                                            **   *******************************************************************************/ diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 4766c1c227..bb3f34dde2 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -49,6 +49,7 @@  #include "llsidepanelinventory.h"  #include "llsidetray.h"  #include "llscrollcontainer.h" +#include "llviewerattachmenu.h"  #include "llviewerfoldertype.h"  #include "llvoavatarself.h" @@ -877,48 +878,19 @@ bool LLInventoryPanel::beginIMSession()  bool LLInventoryPanel::attachObject(const LLSD& userdata)  { +	// Copy selected item UUIDs to a vector.  	std::set<LLUUID> selected_items = mFolderRoot->getSelectionList(); - -	std::string joint_name = userdata.asString(); -	LLViewerJointAttachment* attachmentp = NULL; -	for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();  -		 iter != gAgentAvatarp->mAttachmentPoints.end(); ) -	{ -		LLVOAvatar::attachment_map_t::iterator curiter = iter++; -		LLViewerJointAttachment* attachment = curiter->second; -		if (attachment->getName() == joint_name) -		{ -			attachmentp = attachment; -			break; -		} -	} -	if (attachmentp == NULL) -	{ -		return true; -	} - +	uuid_vec_t items;  	for (std::set<LLUUID>::const_iterator set_iter = selected_items.begin();   		 set_iter != selected_items.end();   		 ++set_iter)  	{ -		const LLUUID &id = *set_iter; -		LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(id); -		if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())) -		{ -			rez_attachment(item, attachmentp); -		} -		else if(item && item->isFinished()) -		{ -			// must be in library. copy it to our inventory and put it on. -			LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(attachmentp); -			copy_inventory_item(gAgent.getID(), -								item->getPermissions().getOwner(), -								item->getUUID(), -								LLUUID::null, -								std::string(), -								cb); -		} +		items.push_back(*set_iter);  	} + +	// Attach selected items. +	LLViewerAttachMenu::attachObjects(items, userdata.asString()); +  	gFocusMgr.setKeyboardFocus(NULL);  	return true; diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 7bf41b0f92..77db280487 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -43,6 +43,8 @@  #include "llinventoryfunctions.h"  #include "llinventorymodel.h"  #include "lllistcontextmenu.h" +#include "llnotificationsutil.h" +#include "llsidetray.h"  #include "lltransutil.h"  #include "llviewermenu.h"  #include "llvoavatar.h" @@ -68,12 +70,63 @@ protected:  			boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id));  		registrar.add("Outfit.TakeOff",  				boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); -		// *TODO: implement this -		// registrar.add("Outfit.Rename", boost::bind()); -		// registrar.add("Outfit.Delete", boost::bind()); +		registrar.add("Outfit.Edit", boost::bind(editOutfit)); +		registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); +		registrar.add("Outfit.Delete", boost::bind(deleteOutfit, selected_id)); + +		enable_registrar.add("Outfit.OnEnable", boost::bind(&OutfitContextMenu::onEnable, this, _2));  		return createFromFile("menu_outfit_tab.xml");  	} + +	bool onEnable(const LLSD& data) +	{ +		std::string param = data.asString(); +		LLUUID outfit_cat_id = mUUIDs.back(); +		bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == outfit_cat_id; + +		if ("wear_replace" == param) +		{ +			return !is_worn; +		} +		else if ("wear_add" == param) +		{ +			return !is_worn; +		} +		else if ("take_off" == param) +		{ +			return is_worn; +		} +		else if ("edit" == param) +		{ +			return is_worn; +		} +		else if ("rename" == param) +		{ +			return get_is_category_renameable(&gInventory, outfit_cat_id); +		} +		else if ("delete" == param) +		{ +			return LLAppearanceMgr::instance().getCanRemoveOutfit(outfit_cat_id); +		} + +		return true; +	} + +	static void editOutfit() +	{ +		LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "edit_outfit")); +	} + +	static void renameOutfit(const LLUUID& outfit_cat_id) +	{ +		LLAppearanceMgr::instance().renameOutfit(outfit_cat_id); +	} + +	static void deleteOutfit(const LLUUID& outfit_cat_id) +	{ +		remove_category(&gInventory, outfit_cat_id); +	}  };  ////////////////////////////////////////////////////////////////////////// @@ -304,6 +357,10 @@ void LLOutfitsList::performAction(std::string action)  	{  		LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, TRUE );  	} +	else if ("rename_outfit" == action) +	{ +		LLAppearanceMgr::instance().renameOutfit(mSelectedOutfitUUID); +	}  }  void LLOutfitsList::setFilterSubString(const std::string& string) @@ -578,4 +635,5 @@ bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y)  	S32 header_bottom = tab->getLocalRect().getHeight() - tab->getHeaderHeight();  	return y >= header_bottom;  } +  // EOF diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h index 6dbdf00067..44f6ec908b 100644 --- a/indra/newview/lloutfitslist.h +++ b/indra/newview/lloutfitslist.h @@ -64,12 +64,12 @@ public:  	void refreshList(const LLUUID& category_id); -	void onSelectionChange(LLUICtrl* ctrl); -  	void performAction(std::string action);  	void setFilterSubString(const std::string& string); +	const LLUUID& getSelectedOutfitUUID() const { return mSelectedOutfitUUID; } +  private:  	/**  	 * Reads xml with accordion tab and Flat list from xml file. @@ -108,6 +108,10 @@ private:  	void onAccordionTabDoubleClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id);  	void onWearableItemsListRightClick(LLUICtrl* ctrl, S32 x, S32 y); +	void onSelectionChange(LLUICtrl* ctrl); + +	static void onOutfitRename(const LLSD& notification, const LLSD& response); +  	LLInventoryCategoriesObserver* 	mCategoriesObserver;  	LLAccordionCtrl*				mAccordion; diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 0ca5938c97..dee9572357 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -79,6 +79,70 @@ const U64 ALL_ITEMS_MASK = WEARABLE_MASK | ATTACHMENT_MASK;  static const std::string REVERT_BTN("revert_btn"); +class LLPanelOutfitEditGearMenu +{ +public: +	static LLMenuGL* create() +	{ +		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + +		registrar.add("Wearable.Create", boost::bind(onCreate, _2)); + +		LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( +			"menu_cof_gear.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); +		llassert(menu); +		if (menu) +		{ +			populateCreateWearableSubmenus(menu); +			menu->buildDrawLabels(); +		} + +		return menu; +	} + +private: +	static void onCreate(const LLSD& param) +	{ +		LLWearableType::EType type = LLWearableType::typeNameToType(param.asString()); +		if (type == LLWearableType::WT_NONE) +		{ +			llwarns << "Invalid wearable type" << llendl; +			return; +		} + +		LLAgentWearables::createWearable(type, true); +	} + +	// Populate the menu with items like "New Skin", "New Pants", etc. +	static void populateCreateWearableSubmenus(LLMenuGL* menu) +	{ +		LLView* menu_clothes	= gMenuHolder->findChildView("COF.Gear.New_Clothes", FALSE); +		LLView* menu_bp			= gMenuHolder->findChildView("COF.Geear.New_Body_Parts", FALSE); + +		if (!menu_clothes || !menu_bp) +		{ +			llassert(menu_clothes && menu_bp); +			return; +		} + +		for (U8 i = LLWearableType::WT_SHAPE; i != (U8) LLWearableType::WT_COUNT; ++i) +		{ +			LLWearableType::EType type = (LLWearableType::EType) i; +			const std::string& type_name = LLWearableType::getTypeName(type); + +			LLMenuItemCallGL::Params p; +			p.name = type_name; +			p.label = LLWearableType::getTypeDefaultNewName(type); +			p.on_click.function_name = "Wearable.Create"; +			p.on_click.parameter = LLSD(type_name); + +			LLView* parent = LLWearableType::getAssetType(type) == LLAssetType::AT_CLOTHING ? +				menu_clothes : menu_bp; +			LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent); +		} +	} +}; +  class LLCOFObserver : public LLInventoryObserver  {  public: @@ -721,29 +785,13 @@ void LLPanelOutfitEdit::onGearButtonClick(LLUICtrl* clicked_button)  {  	if(!mGearMenu)  	{ -		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - -		registrar.add("Gear.OnClick", boost::bind(&LLPanelOutfitEdit::onGearMenuItemClick, this, _2)); - -		mGearMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( -			"menu_cof_gear.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); -		mGearMenu->buildDrawLabels(); -		mGearMenu->updateParent(LLMenuGL::sMenuContainer); +		mGearMenu = LLPanelOutfitEditGearMenu::create();  	}  	S32 menu_y = mGearMenu->getRect().getHeight() + clicked_button->getRect().getHeight();  	LLMenuGL::showPopup(clicked_button, mGearMenu, 0, menu_y);  } -void LLPanelOutfitEdit::onGearMenuItemClick(const LLSD& data) -{ -	std::string param = data.asString(); -	if("add" == param) -	{ -		// TODO -	} -} -  void LLPanelOutfitEdit::showFilteredWearableItemsList(LLWearableType::EType type)  {  	mWearableListTypeCollector->setType(type); diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h index 6ce581f5f7..802386c573 100644 --- a/indra/newview/llpaneloutfitedit.h +++ b/indra/newview/llpaneloutfitedit.h @@ -132,7 +132,6 @@ public:  private:  	void onGearButtonClick(LLUICtrl* clicked_button); -	void onGearMenuItemClick(const LLSD& data);  	void showFilteredWearableItemsList(LLWearableType::EType type); diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 9babddc033..5f67f3d989 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -42,6 +42,7 @@  #include "llfloaterworldmap.h"  #include "llfloaterinventory.h"  #include "llfoldervieweventlistener.h" +#include "llinventorybridge.h"  #include "llinventoryfunctions.h"  #include "llinventorymodelbackgroundfetch.h"  #include "llinventorypanel.h" @@ -70,11 +71,134 @@ static const std::string COF_TAB_NAME = "cof_tab";  static LLRegisterPanelClassWrapper<LLPanelOutfitsInventory> t_inventory("panel_outfits_inventory"); +class LLOutfitListGearMenu +{ +public: +	LLOutfitListGearMenu(LLOutfitsList* olist) +	:	mOutfitList(olist), +		mMenu(NULL) +	{ +		llassert_always(mOutfitList); + +		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; +		LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + +		registrar.add("Gear.Wear", boost::bind(&LLOutfitListGearMenu::onWear, this)); +		registrar.add("Gear.TakeOff", boost::bind(&LLOutfitListGearMenu::onTakeOff, this)); +		registrar.add("Gear.Rename", boost::bind(&LLOutfitListGearMenu::onRename, this)); +		registrar.add("Gear.Delete", boost::bind(&LLOutfitListGearMenu::onDelete, this)); +		registrar.add("Gear.Create", boost::bind(&LLOutfitListGearMenu::onCreate, this, _2)); + +		enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenu::onEnable, this, _2)); + +		mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( +			"menu_outfit_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +		llassert(mMenu); +	} + +	LLMenuGL* getMenu() { return mMenu; } + +private: +	const LLUUID& getSelectedOutfitID() +	{ +		return mOutfitList->getSelectedOutfitUUID(); +	} + +	LLViewerInventoryCategory* getSelectedOutfit() +	{ +		const LLUUID& selected_outfit_id = getSelectedOutfitID(); +		if (selected_outfit_id.isNull()) +		{ +			return NULL; +		} + +		LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id); +		return cat; +	} + +	void onWear() +	{ +		LLViewerInventoryCategory* selected_outfit = getSelectedOutfit(); +		if (selected_outfit) +		{ +			LLAppearanceMgr::instance().wearInventoryCategory( +				selected_outfit, /*copy=*/ FALSE, /*append=*/ FALSE); +		} +	} + +	void onTakeOff() +	{ +		const LLUUID& selected_outfit_id = getSelectedOutfitID(); +		if (selected_outfit_id.notNull()) +		{ +			LLAppearanceMgr::instance().takeOffOutfit(selected_outfit_id); +		} +	} + +	void onRename() +	{ +		const LLUUID& selected_outfit_id = getSelectedOutfitID(); +		if (selected_outfit_id.notNull()) +		{ +			LLAppearanceMgr::instance().renameOutfit(selected_outfit_id); +		} +	} + +	void onDelete() +	{ +		const LLUUID& selected_outfit_id = getSelectedOutfitID(); +		if (selected_outfit_id.notNull()) +		{ +			remove_category(&gInventory, selected_outfit_id); +		} +	} + +	void onCreate(const LLSD& data) +	{ +		LLWearableType::EType type = LLWearableType::typeNameToType(data.asString()); +		if (type == LLWearableType::WT_NONE) +		{ +			llwarns << "Invalid wearable type" << llendl; +			return; +		} + +		LLAgentWearables::createWearable(type, true); +	} + +	bool onEnable(LLSD::String param) +	{ +		const LLUUID& selected_outfit_id = getSelectedOutfitID(); +		bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == selected_outfit_id; + +		if ("wear" == param) +		{ +			return !is_worn; +		} +		else if ("take_off" == param) +		{ +			return is_worn; +		} +		else if ("rename" == param) +		{ +			return get_is_category_renameable(&gInventory, selected_outfit_id); +		} +		else if ("delete" == param) +		{ +			return LLAppearanceMgr::instance().getCanRemoveOutfit(selected_outfit_id); +		} + +		return true; +	} + +	LLOutfitsList*	mOutfitList; +	LLMenuGL*		mMenu; +};  LLPanelOutfitsInventory::LLPanelOutfitsInventory() :  	mMyOutfitsPanel(NULL),  	mCurrentOutfitPanel(NULL),  	mParent(NULL), +	mGearMenu(NULL),  	mInitialized(false)  {  	mSavedFolderState = new LLSaveFolderState(); @@ -84,6 +208,7 @@ LLPanelOutfitsInventory::LLPanelOutfitsInventory() :  LLPanelOutfitsInventory::~LLPanelOutfitsInventory()  { +	delete mGearMenu;  	delete mSavedFolderState;  } @@ -374,7 +499,7 @@ void LLPanelOutfitsInventory::initListCommandsHandlers()  {  	mListCommands = getChild<LLPanel>("bottom_panel"); -	mListCommands->childSetAction("options_gear_btn", boost::bind(&LLPanelOutfitsInventory::onGearButtonClick, this)); +	mListCommands->childSetAction("options_gear_btn", boost::bind(&LLPanelOutfitsInventory::showGearMenu, this));  	mListCommands->childSetAction("trash_btn", boost::bind(&LLPanelOutfitsInventory::onTrashButtonClick, this));  	mListCommands->childSetAction("wear_btn", boost::bind(&LLPanelOutfitsInventory::onWearButtonClick, this)); @@ -385,8 +510,7 @@ void LLPanelOutfitsInventory::initListCommandsHandlers()  				   ,       _7 // EAcceptance* accept  				   )); -	mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_outfit_gear.xml", -		gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +	mGearMenu = new LLOutfitListGearMenu(mMyOutfitsPanel);  }  void LLPanelOutfitsInventory::updateListCommands() @@ -401,18 +525,14 @@ void LLPanelOutfitsInventory::updateListCommands()  	mSaveComboBtn->setSaveBtnEnabled(make_outfit_enabled);  } -void LLPanelOutfitsInventory::onGearButtonClick() -{ -	showActionMenu(mMenuGearDefault,"options_gear_btn"); -} - -void LLPanelOutfitsInventory::showActionMenu(LLMenuGL* menu, std::string spawning_view_name) +void LLPanelOutfitsInventory::showGearMenu()  { +	LLMenuGL* menu = mGearMenu ? mGearMenu->getMenu() : NULL;  	if (menu)  	{  		menu->buildDrawLabels();  		menu->updateParent(LLMenuGL::sMenuContainer); -		LLView* spawning_view = getChild<LLView> (spawning_view_name); +		LLView* spawning_view = getChild<LLView>("options_gear_btn");  		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); diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h index 7bdd37c16c..aff7839bcc 100644 --- a/indra/newview/llpaneloutfitsinventory.h +++ b/indra/newview/llpaneloutfitsinventory.h @@ -47,6 +47,7 @@ class LLMenuGL;  class LLSidepanelAppearance;  class LLTabContainer;  class LLSaveOutfitComboBtn; +class LLOutfitListGearMenu;  class LLPanelOutfitsInventory : public LLPanel  { @@ -116,9 +117,8 @@ private:  protected:  	void initListCommandsHandlers();  	void updateListCommands(); -	void onGearButtonClick();  	void onWearButtonClick(); -	void showActionMenu(LLMenuGL* menu, std::string spawning_view_name); +	void showGearMenu();  	void onTrashButtonClick();  	void onClipboardAction(const LLSD& userdata);  	BOOL isActionEnabled(const LLSD& command_name); @@ -129,7 +129,7 @@ protected:  	void onWearablesLoaded();  private:  	LLPanel*					mListCommands; -	LLMenuGL*					mMenuGearDefault; +	LLOutfitListGearMenu*		mGearMenu;  	LLMenuGL*					mMenuAdd;  	// List Commands                                                              //  	//////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llviewerattachmenu.cpp b/indra/newview/llviewerattachmenu.cpp new file mode 100644 index 0000000000..f7f5ec72fd --- /dev/null +++ b/indra/newview/llviewerattachmenu.cpp @@ -0,0 +1,139 @@ +/**  + * @file llviewerattachmenu.cpp + * @brief "Attach to" / "Attach to HUD" submenus. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + *  + * Copyright (c) 2010, 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 "llviewerattachmenu.h" + +// project includes +#include "llagent.h" +#include "llinventorybridge.h" // for rez_attachment() +#include "llinventorymodel.h" +#include "llviewerinventory.h" +#include "llviewermenu.h" // for gMenuHolder +#include "llvoavatarself.h" + +// linden libraries +#include "llmenugl.h" +#include "lltrans.h" + +// static +void LLViewerAttachMenu::populateMenus(const std::string& attach_to_menu_name, const std::string& attach_to_hud_menu_name) +{ +	// *TODO: share this code with other similar menus +	// (inventory panel context menu, in-world object menu). + +	if (attach_to_menu_name.empty() || attach_to_hud_menu_name.empty() || !isAgentAvatarValid()) return; + +	LLContextMenu* attach_menu = gMenuHolder->getChild<LLContextMenu>(attach_to_menu_name); +	LLContextMenu* attach_hud_menu = gMenuHolder->getChild<LLContextMenu>(attach_to_hud_menu_name); + +	if (!attach_menu || attach_menu->getChildCount() != 0 || +		!attach_hud_menu || attach_hud_menu->getChildCount() != 0) +	{ +		return; +	} + +	// Populate "Attach to..." / "Attach to HUD..." submenus. +	for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); +		 iter != gAgentAvatarp->mAttachmentPoints.end(); ) +	{ +		LLVOAvatar::attachment_map_t::iterator curiter = iter++; +		LLViewerJointAttachment* attachment = curiter->second; +		LLMenuItemCallGL::Params p; +		std::string submenu_name = attachment->getName(); +		std::string translated_submenu_name; + +		if (LLTrans::findString(translated_submenu_name, submenu_name)) +		{ +			p.name = (" ") + translated_submenu_name + " "; +		} +		else +		{ +			p.name = submenu_name; +		} + +		LLSD cbparams; +		cbparams["index"] = curiter->first; +		cbparams["label"] = attachment->getName(); +		p.on_click.function_name = "Object.Attach"; +		p.on_click.parameter = LLSD(attachment->getName()); +		p.on_enable.function_name = "Attachment.Label"; +		p.on_enable.parameter = cbparams; + +		LLMenuItemCallGL* item = LLUICtrlFactory::create<LLMenuItemCallGL>(p); +		LLView* parent_menu = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu; +		parent_menu->addChild(item); +	} +} + +// static +void LLViewerAttachMenu::attachObjects(const uuid_vec_t& items, const std::string& joint_name) +{ +	LLViewerJointAttachment* attachmentp = NULL; +	for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();  +		 iter != gAgentAvatarp->mAttachmentPoints.end(); ) +	{ +		LLVOAvatar::attachment_map_t::iterator curiter = iter++; +		LLViewerJointAttachment* attachment = curiter->second; +		if (attachment->getName() == joint_name) +		{ +			attachmentp = attachment; +			break; +		} +	} +	if (attachmentp == NULL) +	{ +		return; +	} + +	for (uuid_vec_t::const_iterator it = items.begin(); it != items.end(); ++it) +	{ +		const LLUUID &id = *it; +		LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getLinkedItem(id); +		if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())) +		{ +			rez_attachment(item, attachmentp); +		} +		else if(item && item->isFinished()) +		{ +			// must be in library. copy it to our inventory and put it on. +			LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(attachmentp); +			copy_inventory_item(gAgent.getID(), +								item->getPermissions().getOwner(), +								item->getUUID(), +								LLUUID::null, +								std::string(), +								cb); +		} +	} +} diff --git a/indra/newview/llviewerattachmenu.h b/indra/newview/llviewerattachmenu.h new file mode 100644 index 0000000000..d1db9914f3 --- /dev/null +++ b/indra/newview/llviewerattachmenu.h @@ -0,0 +1,43 @@ +/**  + * @file llviewerattachmenu.h + * @brief "Attach to" / "Attach to HUD" submenus. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + *  + * Copyright (c) 2010, 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_LLVIEWERATTACHMENU_H +#define LL_LLVIEWERATTACHMENU_H + +class LLViewerAttachMenu +{ +public: +	static void populateMenus(const std::string& attach_to_menu_name, const std::string& attach_to_hud_menu_name); +	static void attachObjects(const uuid_vec_t& items, const std::string& joint_name); +}; + +#endif // LL_LLVIEWERATTACHMENU_H diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 4cb5e86fc2..d745356dcd 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -39,6 +39,7 @@  #include "llagent.h"  #include "llagentcamera.h" +#include "llagentwearables.h"  #include "llviewerfoldertype.h"  #include "llfolderview.h"  #include "llviewercontrol.h" @@ -881,6 +882,14 @@ void WearOnAvatarCallback::fire(const LLUUID& inv_item)  void ModifiedCOFCallback::fire(const LLUUID& inv_item)  {  	LLAppearanceMgr::instance().updateAppearanceFromCOF(); + +	if (LLSideTray::getInstance()->isPanelActive("sidepanel_appearance")) +	{ +		// *HACK: Edit the wearable that has just been worn +		//        only if the Appearance SP is currently opened. +		LLAgentWearables::editWearable(inv_item); +	} +  	// TODO: camera mode may not be changed if a debug setting is tweaked  	if( gAgentCamera.cameraCustomizeAvatar() )  	{ @@ -1241,10 +1250,8 @@ void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, cons  		LLWearableType::EType wearable_type = LLWearableType::typeNameToType(type_name);  		if (wearable_type >= LLWearableType::WT_SHAPE && wearable_type < LLWearableType::WT_COUNT)  		{ -			LLAssetType::EType asset_type = LLWearableType::getAssetType(wearable_type); -			LLFolderType::EType folder_type = LLFolderType::assetTypeToFolderType(asset_type); -			const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(folder_type); -			LLFolderBridge::createWearable(parent_id, wearable_type); +			const LLUUID parent_id = bridge ? bridge->getUUID() : LLUUID::null; +			LLAgentWearables::createWearable(wearable_type, false, parent_id);  		}  		else  		{ diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index cfb48a22bb..c35b45d446 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -41,6 +41,7 @@  #include "llinventorymodel.h"  #include "llmenugl.h" // for LLContextMenu  #include "lltransutil.h" +#include "llviewerattachmenu.h"  class LLFindOutfitItems : public LLInventoryCollectFunctor  { @@ -511,7 +512,9 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu()  	// Register handlers common for all wearable types.  	registrar.add("Wearable.Wear", boost::bind(handleMultiple, wear, ids));  	registrar.add("Wearable.Edit", boost::bind(handleMultiple, LLAgentWearables::editWearable, ids)); +	registrar.add("Wearable.CreateNew", boost::bind(createNewWearable, selected_id));  	registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id)); +	registrar.add("Wearable.TakeOffDetach", boost::bind(handleMultiple, take_off, ids));  	// Register handlers for clothing.  	registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, ids)); @@ -521,12 +524,16 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu()  	// Register handlers for attachments.  	registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, ids));  	registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id)); +	registrar.add("Object.Attach", boost::bind(LLViewerAttachMenu::attachObjects, ids, _2));  	// Create the menu.  	LLContextMenu* menu = createFromFile("menu_wearable_list_item.xml");  	// Determine which items should be visible/enabled.  	updateItemsVisibility(menu); + +	// Update labels for the items requiring that. +	updateItemsLabels(menu);  	return menu;  } @@ -540,10 +547,10 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu  	const uuid_vec_t& ids = mUUIDs;	// selected items IDs  	U32 mask = 0;					// mask of selected items' types -	U32 nitems = ids.size();		// number of selected items -	U32 nworn = 0;					// number of worn items among the selected ones -	U32 nwornlinks = 0;				// number of worn links among the selected items -	U32 neditable = 0;				// number of editable items among the selected ones +	U32 n_items = ids.size();		// number of selected items +	U32 n_worn = 0;					// number of worn items among the selected ones +	U32 n_links = 0;				// number of links among the selected items +	U32 n_editable = 0;				// number of editable items among the selected ones  	for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it)  	{ @@ -565,38 +572,82 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu  		if (is_worn)  		{ -			++nworn; - -			if (is_link) -			{ -				++nwornlinks; -			} +			++n_worn;  		}  		if (is_editable)  		{ -			++neditable; +			++n_editable; +		} +		if (is_link) +		{ +			++n_links;  		}  	} // for  	// *TODO: eliminate multiple traversals over the menu items -	// *TODO: try disabling items rather than hiding them -	// *FIX:  we may hide *all* items and thus get an ugly empty menu -	setMenuItemVisible(menu, "wear",			nworn == 0); -	setMenuItemVisible(menu, "edit",			mask & (MASK_CLOTHING|MASK_BODYPART) && nitems == 1 && neditable == 1); -	setMenuItemVisible(menu, "show_original",	nitems == 1 && nwornlinks == nitems); -	setMenuItemVisible(menu, "take_off",		mask == MASK_CLOTHING && nworn == nitems); // selected only worn clothes -	setMenuItemVisible(menu, "detach",			mask == MASK_ATTACHMENT && nworn == nitems); -	setMenuItemVisible(menu, "object_profile",	mask == MASK_ATTACHMENT && nitems == 1); +	setMenuItemVisible(menu, "wear",				n_worn == 0); +	setMenuItemVisible(menu, "edit",				mask & (MASK_CLOTHING|MASK_BODYPART) && n_items == 1); +	setMenuItemEnabled(menu, "edit",				n_editable == 1 && n_worn == 1); +	setMenuItemVisible(menu, "create_new",			mask & (MASK_CLOTHING|MASK_BODYPART) && n_items == 1); +	setMenuItemEnabled(menu, "show_original",		n_items == 1 && n_links == n_items); +	setMenuItemVisible(menu, "take_off",			mask == MASK_CLOTHING && n_worn == n_items); +	setMenuItemVisible(menu, "detach",				mask == MASK_ATTACHMENT && n_worn == n_items); +	setMenuItemVisible(menu, "take_off_or_detach",	mask == (MASK_ATTACHMENT|MASK_CLOTHING)); +	setMenuItemEnabled(menu, "take_off_or_detach",	n_worn == n_items); +	setMenuItemVisible(menu, "object_profile",		mask & (MASK_ATTACHMENT|MASK_CLOTHING)); +	setMenuItemEnabled(menu, "object_profile",		n_items == 1); + +	// Populate or hide the "Attach to..." / "Attach to HUD..." submenus. +	if (mask == MASK_ATTACHMENT && n_worn == 0) +	{ +		LLViewerAttachMenu::populateMenus("wearable_attach_to", "wearable_attach_to_hud"); +	} +	else +	{ +		setMenuItemVisible(menu, "wearable_attach_to",			false); +		setMenuItemVisible(menu, "wearable_attach_to_hud",		false); +	} + +	if (mask & MASK_UNKNOWN) +	{ +		llwarns << "Non-wearable items passed." << llendl; +	} +} + +void LLWearableItemsList::ContextMenu::updateItemsLabels(LLContextMenu* menu) +{ +	llassert(menu); +	if (!menu) return; + +	// Set proper label for the "Create new <WEARABLE_TYPE>" menu item. +	LLViewerInventoryItem* item = gInventory.getLinkedItem(mUUIDs.back()); +	if (!item || !item->isWearableType()) return; + +	LLStringUtil::format_map_t args; +	LLWearableType::EType w_type = item->getWearableType(); +	args["[WEARABLE_TYPE]"] = LLWearableType::getTypeDefaultNewName(w_type); +	std::string new_label = LLTrans::getString("CreateNewWearable", args); + +	LLMenuItemGL* menu_item = menu->getChild<LLMenuItemGL>("create_new"); +	menu_item->setLabel(new_label);  }  // We need this method to convert non-zero BOOL values to exactly 1 (TRUE).  // Otherwise code relying on a BOOL value being TRUE may fail  // (I experienced a weird assert in LLView::drawChildren() because of that. +// static  void LLWearableItemsList::ContextMenu::setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val)  {  	menu->setItemVisible(name, val);  } +// static +void LLWearableItemsList::ContextMenu::setMenuItemEnabled(LLContextMenu* menu, const std::string& name, bool val) +{ +	menu->setItemEnabled(name, val); +} + +// static  void LLWearableItemsList::ContextMenu::updateMask(U32& mask, LLAssetType::EType at)  {  	if (at == LLAssetType::AT_CLOTHING) @@ -613,8 +664,17 @@ void LLWearableItemsList::ContextMenu::updateMask(U32& mask, LLAssetType::EType  	}  	else  	{ -		llwarns << "Unsupported asset type: " << at << llendl; +		mask |= MASK_UNKNOWN;  	}  } +// static +void LLWearableItemsList::ContextMenu::createNewWearable(const LLUUID& item_id) +{ +	LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); +	if (!item || !item->isWearableType()) return; + +	LLAgentWearables::createWearable(item->getWearableType(), true); +} +  // EOF diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index 995a8976f3..0ed480a92a 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -315,12 +315,16 @@ public:  			MASK_CLOTHING		= 0x01,  			MASK_BODYPART		= 0x02,  			MASK_ATTACHMENT		= 0x04, +			MASK_UNKNOWN		= 0x08,  		};  		/* virtual */ LLContextMenu* createMenu();  		void updateItemsVisibility(LLContextMenu* menu); -		void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val); -		void updateMask(U32& mask, LLAssetType::EType at); +		void updateItemsLabels(LLContextMenu* menu); +		static void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val); +		static void setMenuItemEnabled(LLContextMenu* menu, const std::string& name, bool val); +		static void updateMask(U32& mask, LLAssetType::EType at); +		static void createNewWearable(const LLUUID& item_id);  	};  	struct Params : public LLInitParam::Block<Params, LLInventoryItemsList::Params> 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 5f28fa6495..54ef6d65a7 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -756,7 +756,7 @@ Leyla Linden               </text>               mouse_opaque="false"               name="resellable_clause"               word_wrap="true" -             width="330"> +             width="360">                  Land in this region may not be resold.              </text>              <text diff --git a/indra/newview/skins/default/xui/en/menu_cof_attachment.xml b/indra/newview/skins/default/xui/en/menu_cof_attachment.xml index b422d87938..c402100fb1 100644 --- a/indra/newview/skins/default/xui/en/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/en/menu_cof_attachment.xml @@ -10,12 +10,4 @@           function="Attachment.Detach"           parameter="detach"/>      </menu_item_call> -    <context_menu -     label="Attach to" -     layout="topleft" -     name="attach_to" /> -    <context_menu -     label="Attach to HUD" -     layout="topleft" -     name="attach_to_hud" />  </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_cof_body_part.xml b/indra/newview/skins/default/xui/en/menu_cof_body_part.xml index 01008ef203..f0e8461360 100644 --- a/indra/newview/skins/default/xui/en/menu_cof_body_part.xml +++ b/indra/newview/skins/default/xui/en/menu_cof_body_part.xml @@ -19,4 +19,15 @@           function="BodyPart.OnEnable"           parameter="edit" />      </menu_item_call> +    <menu_item_call +     label="Create New" +     layout="topleft" +     name="create_new" +     translate="false"> +        <on_click +         function="BodyPart.Create"/> +        <on_enable +         function="BodyPart.OnEnable" +         parameter="create" /> +    </menu_item_call>  </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_cof_clothing.xml b/indra/newview/skins/default/xui/en/menu_cof_clothing.xml index f9cb29f0d7..12ee9b045b 100644 --- a/indra/newview/skins/default/xui/en/menu_cof_clothing.xml +++ b/indra/newview/skins/default/xui/en/menu_cof_clothing.xml @@ -8,6 +8,9 @@       name="take_off">          <on_click           function="Clothing.TakeOff" /> +        <on_enable +         function="Clothing.OnEnable" +         parameter="take_off" />      </menu_item_call>      <menu_item_call       label="Move Up a Layer" @@ -39,4 +42,15 @@           function="Clothing.OnEnable"           parameter="edit" />      </menu_item_call> +    <menu_item_call +     label="Create New" +     layout="topleft" +     name="create_new" +     translate="false"> +        <on_click +         function="Clothing.Create"/> +        <on_enable +         function="Clothing.OnEnable" +         parameter="create" /> +    </menu_item_call>  </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_cof_gear.xml b/indra/newview/skins/default/xui/en/menu_cof_gear.xml index 982d4f2015..c2a11a64ec 100644 --- a/indra/newview/skins/default/xui/en/menu_cof_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_cof_gear.xml @@ -2,15 +2,12 @@  <menu   layout="topleft"   name="Gear COF"> -    <menu_item_call -     label="Add To Outfit" +    <menu +     label="New Clothes"       layout="topleft" -     name="add"> -        <on_click -         function="Gear.OnClick" -         parameter="add"/> -        <on_enable -         function="Gear.OnEnable" -         parameter="add" /> -    </menu_item_call> +     name="COF.Gear.New_Clothes" /> +    <menu +     label="New Body Parts" +     layout="topleft" +     name="COF.Geear.New_Body_Parts" />  </menu> diff --git a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml index dfc72b557c..b5eda8e999 100644 --- a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml @@ -7,31 +7,174 @@       layout="topleft"       name="wear">          <on_click -         function="Gear.OnClick" -         parameter="wear"/> +         function="Gear.Wear" />          <on_enable           function="Gear.OnEnable"           parameter="wear" />      </menu_item_call>      <menu_item_call -     label="Take Off - Remove Current Outfit" +     label="Take Off - Remove from Current Outfit"       layout="topleft"       name="take_off">          <on_click -         function="Gear.OnClick" -         parameter="take_off"/> +         function="Gear.TakeOff" />          <on_enable           function="Gear.OnEnable"           parameter="take_off" />      </menu_item_call> + +            <menu_item_separator /> +            <!-- copied (with minor modifications) from menu_inventory_add.xml --> +            <!--  *TODO: generate dynamically? --> +            <menu +             height="175" +             label="New Clothes" +             layout="topleft" +             left_delta="0" +             mouse_opaque="false" +             name="New Clothes" +             top_pad="514" +             width="125"> +                <menu_item_call +                 label="New Shirt" +                 layout="topleft" +                 name="New Shirt"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="shirt" /> +                </menu_item_call> +                <menu_item_call +                 label="New Pants" +                 layout="topleft" +                 name="New Pants"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="pants" /> +                </menu_item_call> +                <menu_item_call +                 label="New Shoes" +                 layout="topleft" +                 name="New Shoes"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="shoes" /> +                </menu_item_call> +                <menu_item_call +                 label="New Socks" +                 layout="topleft" +                 name="New Socks"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="socks" /> +                </menu_item_call> +                <menu_item_call +                 label="New Jacket" +                 layout="topleft" +                 name="New Jacket"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="jacket" /> +                </menu_item_call> +                <menu_item_call +                 label="New Skirt" +                 layout="topleft" +                 name="New Skirt"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="skirt" /> +                </menu_item_call> +                <menu_item_call +                 label="New Gloves" +                 layout="topleft" +                 name="New Gloves"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="gloves" /> +                </menu_item_call> +                <menu_item_call +                 label="New Undershirt" +                 layout="topleft" +                 name="New Undershirt"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="undershirt" /> +                </menu_item_call> +                <menu_item_call +                 label="New Underpants" +                 layout="topleft" +                 name="New Underpants"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="underpants" /> +                </menu_item_call> +                <menu_item_call +                 label="New Alpha" +                 layout="topleft" +                 name="New Alpha"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="alpha" /> +                </menu_item_call> +                <menu_item_call +                 label="New Tattoo" +                 layout="topleft" +                 name="New Tattoo"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="tattoo" /> +                </menu_item_call> +            </menu> +            <menu +             height="85" +             label="New Body Parts" +             layout="topleft" +             left_delta="0" +             mouse_opaque="false" +             name="New Body Parts" +             top_pad="514" +             width="118"> +                <menu_item_call +                 label="New Shape" +                 layout="topleft" +                 name="New Shape"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="shape" /> +                </menu_item_call> +                <menu_item_call +                 label="New Skin" +                 layout="topleft" +                 name="New Skin"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="skin" /> +                </menu_item_call> +                <menu_item_call +                 label="New Hair" +                 layout="topleft" +                 name="New Hair"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="hair" /> +                </menu_item_call> +                <menu_item_call +                 label="New Eyes" +                 layout="topleft" +                 name="New Eyes"> +                    <menu_item_call.on_click +                     function="Gear.Create" +                     parameter="eyes" /> +                </menu_item_call> +            </menu> +            <!-- copied from menu_inventory_add.xml --> +      <menu_item_separator />      <menu_item_call       label="Rename"       layout="topleft"       name="rename">          <on_click -         function="Gear.OnClick" -         parameter="rename"/> +         function="Gear.Rename" />          <on_enable           function="Gear.OnEnable"           parameter="rename" /> @@ -41,10 +184,9 @@       layout="topleft"       name="delete_outfit">          <on_click -         function="Gear.OnClick" -         parameter="delete_outfit"/> +         function="Gear.Delete" />          <on_enable           function="Gear.OnEnable" -         parameter="delete_outfit" /> +         parameter="delete" />      </menu_item_call>  </menu> diff --git a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml index 8f3e62157a..67559638d9 100644 --- a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml +++ b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml @@ -8,6 +8,9 @@       name="wear_replace">          <on_click           function="Outfit.WearReplace" /> +        <on_enable +         function="Outfit.OnEnable" +         parameter="wear_replace" />      </menu_item_call>      <menu_item_call       label="Wear - Add to Current Outfit" @@ -15,13 +18,29 @@       name="wear_add">          <on_click           function="Outfit.WearAdd" /> +        <on_enable +         function="Outfit.OnEnable" +         parameter="wear_add" />      </menu_item_call>      <menu_item_call -     label="Take Off - Remove Current Outfit" +     label="Take Off - Remove from Current Outfit"       layout="topleft"       name="take_off">          <on_click           function="Outfit.TakeOff" /> +        <on_enable +         function="Outfit.OnEnable" +         parameter="take_off" /> +    </menu_item_call> +    <menu_item_call +     label="Edit Outfit" +     layout="topleft" +     name="edit"> +        <on_click +         function="Outfit.Edit" /> +        <on_enable +         function="Outfit.OnEnable" +         parameter="edit" />      </menu_item_call>      <menu_item_separator />      <menu_item_call @@ -30,6 +49,9 @@       name="rename">          <on_click           function="Outfit.Rename" /> +        <on_enable +         function="Outfit.OnEnable" +         parameter="rename" />      </menu_item_call>      <menu_item_call       label="Delete Outfit" @@ -37,5 +59,8 @@       name="delete">          <on_click           function="Outfit.Delete" /> +        <on_enable +         function="Outfit.OnEnable" +         parameter="delete" />      </menu_item_call>  </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml index 7ea7eaade5..e645702f93 100644 --- a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml @@ -9,22 +9,27 @@           function="Wearable.Wear" />      </menu_item_call>      <menu_item_call +     label="Take Off / Detach" +     layout="topleft" +     name="take_off_or_detach"> +        <on_click +         function="Wearable.TakeOffDetach" /> +    </menu_item_call> +    <menu_item_call       label="Detach"       layout="topleft"       name="detach">          <on_click           function="Attachment.Detach" />      </menu_item_call> -<!-- *TODO: implement the submenus -    <menu -     label="Attach to" +    <context_menu +     label="Attach to  ▶"       layout="topleft" -     name="attach_to" /> -    <menu -     label="Attach to HUD" +     name="wearable_attach_to" /> +    <context_menu +     label="Attach to HUD  ▶"       layout="topleft" -     name="attach_to_hud" /> ---> +     name="wearable_attach_to_hud" />      <menu_item_call       label="Object Profile"       layout="topleft" @@ -37,16 +42,14 @@       layout="topleft"       name="take_off">          <on_click -         function="Clothing.TakeOff" -         parameter="take_off"/> +         function="Clothing.TakeOff" />      </menu_item_call>      <menu_item_call       label="Edit"       layout="topleft"       name="edit">          <on_click -         function="Wearable.Edit" -         parameter="edit"/> +         function="Wearable.Edit" />      </menu_item_call>      <menu_item_call       label="Show Original" @@ -55,4 +58,12 @@          <on_click           function="Wearable.ShowOriginal" />      </menu_item_call> +    <menu_item_call +     label="Create New" +     layout="topleft" +     name="create_new" +     translate="false"> +        <on_click +         function="Wearable.CreateNew" /> +    </menu_item_call>  </context_menu> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 2590924b58..5ead756d20 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2070,6 +2070,28 @@ Would you be my friend?    <notification     icon="alertmodal.tga" +   label="Rename Outfit" +   name="RenameOutfit" +   type="alertmodal"> +    New outfit name: +    <form name="form"> +      <input name="new_name" type="text"> +        [NAME] +      </input> +      <button +       default="true" +       index="0" +       name="Offer" +       text="OK"/> +      <button +       index="1" +       name="Cancel" +       text="Cancel"/> +    </form> +  </notification> + +  <notification +   icon="alertmodal.tga"     name="RemoveFromFriends"     type="alertmodal">  Do you want to remove [FIRST_NAME] [LAST_NAME] from your Friends List? diff --git a/indra/newview/skins/default/xui/en/panel_cof_wearables.xml b/indra/newview/skins/default/xui/en/panel_cof_wearables.xml index 68c364680e..5f34c24bca 100644 --- a/indra/newview/skins/default/xui/en/panel_cof_wearables.xml +++ b/indra/newview/skins/default/xui/en/panel_cof_wearables.xml @@ -9,6 +9,7 @@   name="cof_wearables"   width="311">      <accordion +     fit_parent="true"       follows="all"       height="200"       layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_eyes.xml b/indra/newview/skins/default/xui/en/panel_edit_eyes.xml index 4149a0b06f..f173a2f3cb 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_eyes.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_eyes.xml @@ -51,6 +51,7 @@           top_pad="10"           width="313">       <accordion +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_gloves.xml b/indra/newview/skins/default/xui/en/panel_edit_gloves.xml index 94fd2f9080..a490f27b9f 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_gloves.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_gloves.xml @@ -65,6 +65,7 @@           top_pad="10"           width="313">       <accordion +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_hair.xml b/indra/newview/skins/default/xui/en/panel_edit_hair.xml index 9b60e83387..6bb5d2fa9b 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_hair.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_hair.xml @@ -51,6 +51,7 @@           top_pad="10"           width="313">     <accordion  +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_jacket.xml b/indra/newview/skins/default/xui/en/panel_edit_jacket.xml index 248ae9fe04..929cdffb3d 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_jacket.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_jacket.xml @@ -80,6 +80,7 @@           top_pad="10"           width="313">       <accordion +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_pants.xml b/indra/newview/skins/default/xui/en/panel_edit_pants.xml index 3ed1df2399..f22cf983aa 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_pants.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_pants.xml @@ -65,6 +65,7 @@           top_pad="10"           width="313">     <accordion  +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_shirt.xml b/indra/newview/skins/default/xui/en/panel_edit_shirt.xml index e088aa05ac..85823073b5 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_shirt.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_shirt.xml @@ -65,6 +65,7 @@           top_pad="10"           width="313">  	 <accordion +		fit_parent="true"  		follows="all"  		height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_shoes.xml b/indra/newview/skins/default/xui/en/panel_edit_shoes.xml index e079047a86..b26fde68f1 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_shoes.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_shoes.xml @@ -65,6 +65,7 @@           top_pad="10"           width="313">     <accordion  +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_skin.xml b/indra/newview/skins/default/xui/en/panel_edit_skin.xml index 9158685c40..45591ba2ad 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_skin.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_skin.xml @@ -85,6 +85,7 @@           width="313">      	 <accordion          layout="topleft" +		fit_parent="true"  		follows="all"  		height ="300"  		left="0" diff --git a/indra/newview/skins/default/xui/en/panel_edit_skirt.xml b/indra/newview/skins/default/xui/en/panel_edit_skirt.xml index 87f3270b31..bb8e0dca07 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_skirt.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_skirt.xml @@ -65,6 +65,7 @@           top_pad="10"           width="313">       <accordion +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_socks.xml b/indra/newview/skins/default/xui/en/panel_edit_socks.xml index 5bd99969a2..d813d94d93 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_socks.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_socks.xml @@ -65,6 +65,7 @@           top_pad="10"           width="313">       <accordion +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_underpants.xml b/indra/newview/skins/default/xui/en/panel_edit_underpants.xml index bbe5230341..19225e9757 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_underpants.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_underpants.xml @@ -65,6 +65,7 @@           top_pad="10"           width="313">       <accordion +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_edit_undershirt.xml b/indra/newview/skins/default/xui/en/panel_edit_undershirt.xml index a79c1b9eaa..720a55dcc2 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_undershirt.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_undershirt.xml @@ -65,6 +65,7 @@           top_pad="10"           width="313">       <accordion +        fit_parent="true"          follows="all"          height ="300"          layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_place_profile.xml b/indra/newview/skins/default/xui/en/panel_place_profile.xml index fcc998c08a..57ac79686d 100644 --- a/indra/newview/skins/default/xui/en/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml @@ -320,6 +320,7 @@               value="unknown"               width="268" />              <accordion +             fit_parent="true"               follows="all"               height="223"               layout="topleft" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 8dd5d64e71..9d7079a495 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -318,6 +318,10 @@  	<!-- For use when we do not have land type back from the server -->  	<string name="land_type_unknown">(unknown)</string> +	<!-- For land type back from the simulator --> +	<string name="Estate / Full Region">Estate / Full Region</string> +	<string name="Mainland / Full Region">Mainland / Full Region</string> +  	<!-- File load/save dialogs -->  	<string name="all_files">All Files</string>  	<string name="sound_files">Sounds</string> @@ -1826,6 +1830,7 @@ Clears (deletes) the media and all params from the given face.    <!-- Wearable List-->    <string name="NewWearable">New [WEARABLE_ITEM]</string> +  <string name="CreateNewWearable">Create [WEARABLE_TYPE]</string>  	<!-- LLGroupNotify -->  	<!-- used in the construction of a Group Notice blue dialog box, buttons, tooltip etc. Seems to be no longer utilized by code in Viewer 2.0 --> diff --git a/indra/newview/skins/default/xui/pl/floater_about_land.xml b/indra/newview/skins/default/xui/pl/floater_about_land.xml index e4908deb07..163868f200 100644 --- a/indra/newview/skins/default/xui/pl/floater_about_land.xml +++ b/indra/newview/skins/default/xui/pl/floater_about_land.xml @@ -196,13 +196,13 @@ Idź do Świat > O Posiadłości albo wybierz inną posiadłość żeby pokaz  			<text name="resellable_lbl">  				Odsprzedaj:  			</text> -			<text left="115" name="resellable_clause" width="338"> +			<text left="115" name="resellable_clause">  				Posiadłość zakupiona w tym Regionie nie może być odsprzedana.  			</text>  			<text name="changeable_lbl">  				Podziel:  			</text> -			<text left="115" name="changeable_clause" width="338"> +			<text left="115" name="changeable_clause">  				Posiadłość zakupiona w tym Regionie nie może być  łączona/dzielona.  			</text> | 
