diff options
60 files changed, 2543 insertions, 247 deletions
| diff --git a/doc/contributions.txt b/doc/contributions.txt index 62210f08f4..2361123343 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -18,6 +18,7 @@ Alissa Sabre  	VWR-171  	VWR-177  	VWR-213 +	VWR-250  	VWR-251  	VWR-286  	VWR-414 @@ -55,6 +56,7 @@ Catherine Pfeffer  Dale Glass  	VWR-120  	VWR-560 +	VWR-2502  	VWR-1358  	VWR-2041  Drewan Keats @@ -91,6 +93,8 @@ Feep Larsson  	VWR-1314  Fremont Cunningham  	VWR-1147 +Gudmund Shepherd +	VWR-1873  Gigs Taggart  	SVC-493  	VWR-38 @@ -106,6 +110,7 @@ Gigs Taggart  	VWR-1434  	VWR-1987  	VWR-2065 +	VWR-2502  Ginko Bayliss  	VWR-4  Grazer Kline @@ -216,7 +221,9 @@ Nicholaz Beresford  	VWR-1872  	VWR-1968  	VWR-2046 +	VWR-2142  	VWR-2152 +	VWR-2614  Nounouch Hapmouche  	VWR-238  Paul Churchill diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index 2408fab96f..a73fc6c293 100644 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -28,6 +28,7 @@ $/LicenseInfo$  """  import commands +import errno  import filecmp  import fnmatch  import getopt diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 6dab598341..9895a684b2 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -239,6 +239,84 @@ LLWString utf16str_to_wstring(const llutf16string &utf16str)  	return utf16str_to_wstring(utf16str, len);  } +// Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string. +S32 utf16str_wstring_length(const llutf16string &utf16str, const S32 utf16_len) +{ +	S32 surrogate_pairs = 0; +	// ... craziness to make gcc happy (llutf16string.c_str() is tweaked on linux): +	const U16 *const utf16_chars = &(*(utf16str.begin())); +	S32 i = 0; +	while (i < utf16_len) +	{ +		const U16 c = utf16_chars[i++]; +		if (c >= 0xD800 && c <= 0xDBFF)		// See http://en.wikipedia.org/wiki/UTF-16 +		{   // Have first byte of a surrogate pair +			if (i >= utf16_len) +			{ +				break; +			} +			const U16 d = utf16_chars[i]; +			if (d >= 0xDC00 && d <= 0xDFFF) +			{   // Have valid second byte of a surrogate pair +				surrogate_pairs++; +				i++; +			} +		} +	} +	return utf16_len - surrogate_pairs; +} + +// Length in utf16string (UTF-16) of wlen wchars beginning at woffset. +S32 wstring_utf16_length(const LLWString &wstr, const S32 woffset, const S32 wlen) +{ +	const S32 end = llmin((S32)wstr.length(), woffset + wlen); +	if (end < woffset) +	{ +		return 0; +	} +	else +	{ +		S32 length = end - woffset; +		for (S32 i = woffset; i < end; i++) +		{ +			if (wstr[i] >= 0x10000) +			{ +				length++; +			} +		} +		return length; +	} +} + +// Given a wstring and an offset in it, returns the length as wstring (i.e., +// number of llwchars) of the longest substring that starts at the offset +// and whose equivalent utf-16 string does not exceeds the given utf16_length. +S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, const S32 woffset, const S32 utf16_length, BOOL *unaligned) +{ +	const S32 end = wstr.length(); +	BOOL u = FALSE; +	S32 n = woffset + utf16_length; +	S32 i = woffset; +	while (i < end) +	{ +		if (wstr[i] >= 0x10000) +		{ +			--n; +		} +		if (i >= n) +		{ +			u = (i > n); +			break; +		} +		i++; +	} +	if (unaligned) +	{ +		*unaligned = u; +	} +	return i - woffset; +} +  S32 wchar_utf8_length(const llwchar wc)  {  	if (wc < 0x80) diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 70f7d5483e..ec4ff335c9 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -433,6 +433,15 @@ S32 wchar_utf8_length(const llwchar wc);  std::string utf8str_tolower(const std::string& utf8str); +// Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string. +S32 utf16str_wstring_length(const llutf16string &utf16str, S32 len); + +// Length in utf16string (UTF-16) of wlen wchars beginning at woffset. +S32 wstring_utf16_length(const LLWString & wstr, S32 woffset, S32 wlen); + +// Length in wstring (i.e., llwchar count) of a part of a wstring specified by utf16 length (i.e., utf16 units.) +S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, S32 woffset, S32 utf16_length, BOOL *unaligned = NULL); +  /**   * @brief Properly truncate a utf8 string to a maximum byte count.   *  diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 4dea08505d..7c8fe86d2b 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -86,7 +86,7 @@ const S32 SCULPT_REZ_2 = 8;  const S32 SCULPT_REZ_3 = 16;  const S32 SCULPT_REZ_4 = 32; -const F32 SCULPT_MIN_AREA = 0.005f; +const F32 SCULPT_MIN_AREA = 0.002f;  BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)  {     @@ -1834,9 +1834,9 @@ inline LLVector3 sculpt_rgb_to_vector(U8 r, U8 g, U8 b)  {  	// maps RGB values to vector values [0..255] -> [-0.5..0.5]  	LLVector3 value; -	value.mV[VX] = r / 256.f - 0.5f; -	value.mV[VY] = g / 256.f - 0.5f; -	value.mV[VZ] = b / 256.f - 0.5f; +	value.mV[VX] = r / 255.f - 0.5f; +	value.mV[VY] = g / 255.f - 0.5f; +	value.mV[VZ] = b / 255.f - 0.5f;  	return value;  } diff --git a/indra/llmessage/lltransfermanager.cpp b/indra/llmessage/lltransfermanager.cpp index e80bc8cce8..ff4f8a2e66 100644 --- a/indra/llmessage/lltransfermanager.cpp +++ b/indra/llmessage/lltransfermanager.cpp @@ -106,10 +106,15 @@ void LLTransferManager::cleanup()  void LLTransferManager::updateTransfers()  { -	host_tc_map::iterator iter; -	for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++) +	host_tc_map::iterator iter,cur; + +	iter = mTransferConnections.begin(); + +	while (iter !=mTransferConnections.end())  	{ -		iter->second->updateTransfers(); +		cur = iter; +		iter++; +		cur->second->updateTransfers();  	}  } @@ -571,7 +576,6 @@ void LLTransferManager::processTransferAbort(LLMessageSystem *msgp, void **)  	msgp->getUUID("TransferInfo", "TransferID", transfer_id);  	msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type); -  	// See if it's a target that we're trying to abort  	// Find the transfer associated with this packet.  	LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type); @@ -651,10 +655,14 @@ LLTransferConnection::~LLTransferConnection()  void LLTransferConnection::updateTransfers()  {  	// Do stuff for source transfers (basically, send data out). -	tsc_iter iter; -	for (iter = mTransferSourceChannels.begin(); iter != mTransferSourceChannels.end(); iter++) +	tsc_iter iter, cur; +	iter = mTransferSourceChannels.begin(); + +	while (iter !=mTransferSourceChannels.end())  	{ -		(*iter)->updateTransfers(); +		cur = iter; +		iter++; +		(*cur)->updateTransfers();  	}  	// Do stuff for target transfers @@ -768,14 +776,16 @@ void LLTransferSourceChannel::updateTransfers()  		return;  	} -	LLPriQueueMap<LLTransferSource *>::pqm_iter iter; - +	LLPriQueueMap<LLTransferSource *>::pqm_iter iter, next;  	BOOL done = FALSE;  	for (iter = mTransferSources.mMap.begin(); (iter != mTransferSources.mMap.end()) && !done;)  	{  		//llinfos << "LLTransferSourceChannel::updateTransfers()" << llendl; -		// Do stuff. +		// Do stuff.  +		next = iter; +		next++; +  		LLTransferSource *tsp = iter->second;  		U8 *datap = NULL;  		S32 data_size = 0; @@ -793,11 +803,12 @@ void LLTransferSourceChannel::updateTransfers()  			// We don't have any data, but we're not done, just go on.  			// This will presumably be used for streaming or async transfers that  			// are stalled waiting for data from another source. -			iter++; +			iter=next;  			continue;  		}  		LLUUID *cb_uuid = new LLUUID(tsp->getID()); +		LLUUID transaction_id = tsp->getID();  		// Send the data now, even if it's an error.  		// The status code will tell the other end what to do. @@ -822,7 +833,17 @@ void LLTransferSourceChannel::updateTransfers()  			delete[] datap;  			datap = NULL;  		} -		 + +		if (findTransferSource(transaction_id) == NULL) +		{ +			//Warning!  In the case of an aborted transfer, the sendReliable call above calls  +			//AbortTransfer which in turn calls deleteTransfer which means that somewhere way  +			//down the chain our current iter can get invalidated resulting in an infrequent +			//sim crash.  This check gets us to a valid transfer source in this event. +			iter=next; +			continue; +		} +  		// Update the packet counter  		tsp->setLastPacketID(packet_id); @@ -839,7 +860,8 @@ void LLTransferSourceChannel::updateTransfers()  			tsp->completionCallback(status);  			delete tsp; -			mTransferSources.mMap.erase(iter++); +			mTransferSources.mMap.erase(iter); +			iter = next;  			break;  		default:  			llerrs << "Unknown transfer error code!" << llendl; @@ -876,23 +898,20 @@ LLTransferSource *LLTransferSourceChannel::findTransferSource(const LLUUID &tran  BOOL LLTransferSourceChannel::deleteTransfer(LLTransferSource *tsp)  { +  	LLPriQueueMap<LLTransferSource *>::pqm_iter iter;  	for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)  	{  		if (iter->second == tsp)  		{ -			break; +			delete tsp; +			mTransferSources.mMap.erase(iter); +			return TRUE;  		}  	} -	if (iter == mTransferSources.mMap.end()) -	{ -		llerrs << "Unable to find transfer source to delete!" << llendl; -		return FALSE; -	} -	mTransferSources.mMap.erase(iter); -	delete tsp; -	return TRUE; +	llerrs << "Unable to find transfer source to delete!" << llendl; +	return FALSE;  } @@ -1000,18 +1019,14 @@ BOOL LLTransferTargetChannel::deleteTransfer(LLTransferTarget *ttp)  	{  		if (*iter == ttp)  		{ -			break; +			delete ttp; +			mTransferTargets.erase(iter); +			return TRUE;  		}  	} -	if (iter == mTransferTargets.end()) -	{ -		llerrs << "Unable to find transfer target to delete!" << llendl; -		return FALSE; -	} -	mTransferTargets.erase(iter); -	delete ttp; -	return TRUE; +	llerrs << "Unable to find transfer target to delete!" << llendl; +	return FALSE;  } @@ -1072,7 +1087,7 @@ void LLTransferSource::sendTransferStatus(LLTSCode status)  void LLTransferSource::abortTransfer()  {  	// Send a message down, call the completion callback -	llinfos << "Aborting transfer " << getID() << " to " << mChannelp->getHost() << llendl; +	llinfos << "LLTransferSource::Aborting transfer " << getID() << " to " << mChannelp->getHost() << llendl;  	gMessageSystem->newMessage("TransferAbort");  	gMessageSystem->nextBlock("TransferInfo");  	gMessageSystem->addUUID("TransferID", getID()); @@ -1204,7 +1219,7 @@ LLTransferTarget::~LLTransferTarget()  void LLTransferTarget::abortTransfer()  {  	// Send a message up, call the completion callback -	llinfos << "Aborting transfer " << getID() << " from " << mChannelp->getHost() << llendl; +	llinfos << "LLTransferTarget::Aborting transfer " << getID() << " from " << mChannelp->getHost() << llendl;  	gMessageSystem->newMessage("TransferAbort");  	gMessageSystem->nextBlock("TransferInfo");  	gMessageSystem->addUUID("TransferID", getID()); diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index b41879380f..77bca8f803 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -41,6 +41,7 @@  #include "llvolumemgr.h"  #include "llstring.h"  #include "lldatapacker.h" +#include "llsdutil.h"  /**   * exported constants @@ -1795,6 +1796,47 @@ void LLLightParams::copy(const LLNetworkData& data)  	mFalloff = param->mFalloff;  } +LLSD LLLightParams::asLLSD() const +{ +	LLSD sd; +	 +	sd["color"] = ll_sd_from_color4(getColor()); +	sd["radius"] = getRadius(); +	sd["falloff"] = getFalloff(); +	sd["cutoff"] = getCutoff(); +		 +	return sd; +} + +bool LLLightParams::fromLLSD(LLSD& sd) +{ +	const char *w; +	w = "color"; +	if (sd.has(w)) +	{ +		setColor( ll_color4_from_sd(sd["color"]) ); +	} else goto fail; +	w = "radius"; +	if (sd.has(w)) +	{ +		setRadius( (F32)sd[w].asReal() ); +	} else goto fail; +	w = "falloff"; +	if (sd.has(w)) +	{ +		setFalloff( (F32)sd[w].asReal() ); +	} else goto fail; +	w = "cutoff"; +	if (sd.has(w)) +	{ +		setCutoff( (F32)sd[w].asReal() ); +	} else goto fail; +	 +	return true; + fail: +	return false; +} +  //============================================================================  LLFlexibleObjectData::LLFlexibleObjectData() @@ -1876,6 +1918,59 @@ void LLFlexibleObjectData::copy(const LLNetworkData& data)  	//mRenderingCollisionSphere = flex_data->mRenderingCollisionSphere;  } +LLSD LLFlexibleObjectData::asLLSD() const +{ +	LLSD sd; + +	sd["air_friction"] = getAirFriction(); +	sd["gravity"] = getGravity(); +	sd["simulate_lod"] = getSimulateLOD(); +	sd["tension"] = getTension(); +	sd["user_force"] = getUserForce().getValue(); +	sd["wind_sensitivity"] = getWindSensitivity(); +	 +	return sd; +} + +bool LLFlexibleObjectData::fromLLSD(LLSD& sd) +{ +	const char *w; +	w = "air_friction"; +	if (sd.has(w)) +	{ +		setAirFriction( (F32)sd[w].asReal() ); +	} else goto fail; +	w = "gravity"; +	if (sd.has(w)) +	{ +		setGravity( (F32)sd[w].asReal() ); +	} else goto fail; +	w = "simulate_lod"; +	if (sd.has(w)) +	{ +		setSimulateLOD( sd[w].asInteger() ); +	} else goto fail; +	w = "tension"; +	if (sd.has(w)) +	{ +		setTension( (F32)sd[w].asReal() ); +	} else goto fail; +	w = "user_force"; +	if (sd.has(w)) +	{ +		LLVector3 user_force = ll_vector3_from_sd(sd[w], 0); +		setUserForce( user_force ); +	} else goto fail; +	w = "wind_sensitivity"; +	if (sd.has(w)) +	{ +		setWindSensitivity( (F32)sd[w].asReal() ); +	} else goto fail; +	 +	return true; + fail: +	return false; +}  //============================================================================ @@ -1927,3 +2022,34 @@ void LLSculptParams::copy(const LLNetworkData& data)  	mSculptType = param->mSculptType;  } + + +LLSD LLSculptParams::asLLSD() const +{ +	LLSD sd; +	 +	sd["texture"] = mSculptTexture; +	sd["type"] = mSculptType; +		 +	return sd; +} + +bool LLSculptParams::fromLLSD(LLSD& sd) +{ +	const char *w; +	w = "texture"; +	if (sd.has(w)) +	{ +		setSculptTexture( sd[w] ); +	} else goto fail; +	w = "type"; +	if (sd.has(w)) +	{ +		setSculptType( (U8)sd[w].asInteger() ); +	} else goto fail; +	 +	return true; + fail: +	return false; +} + diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index 53b17bc2ef..eef58341e7 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -141,7 +141,13 @@ public:  	/*virtual*/ BOOL unpack(LLDataPacker &dp);  	/*virtual*/ bool operator==(const LLNetworkData& data) const;  	/*virtual*/ void copy(const LLNetworkData& data); +	// LLSD implementations here are provided by Eddy Stryker. +	// NOTE: there are currently unused in protocols +	LLSD asLLSD() const; +	operator LLSD() const { return asLLSD(); } +	bool fromLLSD(LLSD& sd); +	  	void setColor(const LLColor4& color)	{ mColor = color; mColor.clamp(); }  	void setRadius(F32 radius)				{ mRadius = llclamp(radius, LIGHT_MIN_RADIUS, LIGHT_MAX_RADIUS); }  	void setFalloff(F32 falloff)			{ mFalloff = llclamp(falloff, LIGHT_MIN_FALLOFF, LIGHT_MAX_FALLOFF); } @@ -229,6 +235,9 @@ public:  	BOOL unpack(LLDataPacker &dp);  	bool operator==(const LLNetworkData& data) const;  	void copy(const LLNetworkData& data); +	LLSD asLLSD() const; +	operator LLSD() const { return asLLSD(); } +	bool fromLLSD(LLSD& sd);  };// end of attributes structure @@ -245,6 +254,9 @@ public:  	/*virtual*/ BOOL unpack(LLDataPacker &dp);  	/*virtual*/ bool operator==(const LLNetworkData& data) const;  	/*virtual*/ void copy(const LLNetworkData& data); +	LLSD asLLSD() const; +	operator LLSD() const { return asLLSD(); } +	bool fromLLSD(LLSD& sd);  	void setSculptTexture(const LLUUID& id) { mSculptTexture = id; }  	LLUUID getSculptTexture()               { return mSculptTexture; } diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 420970a38a..3c7cd17b92 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -72,6 +72,15 @@ const S32   SCROLL_INCREMENT_DEL = 4;	// make space for baskspacing  const F32   AUTO_SCROLL_TIME = 0.05f;  const F32	LABEL_HPAD = 5.f; +const F32	PREEDIT_MARKER_BRIGHTNESS = 0.4f; +const S32	PREEDIT_MARKER_GAP = 1; +const S32	PREEDIT_MARKER_POSITION = 2; +const S32	PREEDIT_MARKER_THICKNESS = 1; +const F32	PREEDIT_STANDOUT_BRIGHTNESS = 0.6f; +const S32	PREEDIT_STANDOUT_GAP = 1; +const S32	PREEDIT_STANDOUT_POSITION = 2; +const S32	PREEDIT_STANDOUT_THICKNESS = 2; +  // This is a friend class of and is only used by LLLineEditor  class LLLineEditorRollback  { @@ -127,7 +136,6 @@ LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect,  						   S32 border_thickness)  	:  		LLUICtrl( name, rect, TRUE, commit_callback, userdata, FOLLOWS_TOP | FOLLOWS_LEFT ), -		mMaxLengthChars(max_length_bytes),  		mMaxLengthBytes(max_length_bytes),  		mCursorPos( 0 ),  		mScrollHPos( 0 ), @@ -223,12 +231,19 @@ LLString LLLineEditor::getWidgetTag() const  	return LL_LINE_EDITOR_TAG;  } -void LLLineEditor::onFocusLost() +void LLLineEditor::onFocusReceived()  { -	// Need to notify early when loosing focus. -	getWindow()->allowLanguageTextInput(FALSE); +	LLUICtrl::onFocusReceived(); +	updateAllowingLanguageInput(); +} -	LLUICtrl::onFocusLost(); +void LLLineEditor::onFocusLost() +{ +	// The call to updateAllowLanguageInput() +	// when loosing the keyboard focus *may* +	// indirectly invoke handleUnicodeCharHere(),  +	// so it must be called before onCommit. +	updateAllowingLanguageInput();  	if( mCommitOnFocusLost && mText.getString() != mPrevText)   	{ @@ -241,6 +256,8 @@ void LLLineEditor::onFocusLost()  	}  	getWindow()->showCursorFromMouseMove(); + +	LLUICtrl::onFocusLost();  }  void LLLineEditor::onCommit() @@ -301,6 +318,7 @@ void LLLineEditor::setEnabled(BOOL enabled)  {  	mReadOnly = !enabled;  	setTabStop(!mReadOnly); +	updateAllowingLanguageInput();  } @@ -308,7 +326,6 @@ void LLLineEditor::setMaxTextLength(S32 max_text_length)  {  	S32 max_len = llmax(0, max_text_length);  	mMaxLengthBytes = max_len; -	mMaxLengthChars = max_len;  }   void LLLineEditor::setBorderWidth(S32 left, S32 right) @@ -337,13 +354,13 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)  	BOOL allSelected = (len > 0) && (( mSelectionStart == 0 && mSelectionEnd == len )   		|| ( mSelectionStart == len && mSelectionEnd == 0 )); +	// Do safe truncation so we don't split multi-byte characters  	LLString truncated_utf8 = new_text;  	if (truncated_utf8.size() > (U32)mMaxLengthBytes) -	{ -		utf8str_truncate(truncated_utf8, mMaxLengthBytes); +	{	 +		truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes);  	}  	mText.assign(truncated_utf8); -	mText.truncate(mMaxLengthChars);  	if (allSelected)  	{ @@ -735,17 +752,12 @@ void LLLineEditor::addChar(const llwchar uni_char)  		mText.erase(getCursor(), 1);  	} -	S32 length_chars = mText.length(); -	S32 cur_bytes = mText.getString().size();; +	S32 cur_bytes = mText.getString().size();  	S32 new_bytes = wchar_utf8_length(new_c);  	BOOL allow_char = TRUE; -	// Inserting character -	if (length_chars == mMaxLengthChars) -	{ -		allow_char = FALSE; -	} +	// Check byte length limit  	if ((new_bytes + cur_bytes) > mMaxLengthBytes)  	{  		allow_char = FALSE; @@ -794,6 +806,12 @@ void LLLineEditor::setSelection(S32 start, S32 end)  	setCursor(start);  } +void LLLineEditor::setDrawAsterixes(BOOL b) +{ +	mDrawAsterixes = b; +	updateAllowingLanguageInput(); +} +  S32 LLLineEditor::prevWordPos(S32 cursorPos) const  {  	const LLWString& wtext = mText.getWString(); @@ -1022,13 +1040,11 @@ void LLLineEditor::paste()  			// Insert the string -			//check to see that the size isn't going to be larger than the -			//max number of characters or bytes +			// Check to see that the size isn't going to be larger than the max number of bytes  			U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText); -			size_t available_chars = mMaxLengthChars - mText.length();  			if ( available_bytes < (U32) wstring_utf8_length(clean_string) ) -			{ +			{	// Doesn't all fit  				llwchar current_symbol = clean_string[0];  				U32 wchars_that_fit = 0;  				U32 total_bytes = wchar_utf8_length(current_symbol); @@ -1043,20 +1059,13 @@ void LLLineEditor::paste()  					current_symbol = clean_string[++wchars_that_fit];  					total_bytes += wchar_utf8_length(current_symbol);  				} - +				// Truncate the clean string at the limit of what will fit  				clean_string = clean_string.substr(0, wchars_that_fit);  				reportBadKeystroke();  			} -			else if (available_chars < clean_string.length()) -			{ -				// We can't insert all the characters.  Insert as many as possible -				// but make a noise to alert the user. JC -				clean_string = clean_string.substr(0, available_chars); -				reportBadKeystroke(); -			}  			mText.insert(getCursor(), clean_string); -			setCursor(llmin(mMaxLengthChars, getCursor() + (S32)clean_string.length())); +			setCursor( getCursor() + (S32)clean_string.length() );  			deselect();  			// Validate new string and rollback the if needed. @@ -1523,6 +1532,41 @@ void LLLineEditor::draw()  	}  	LLColor4 label_color = mTentativeFgColor; +	if (hasPreeditString()) +	{ +		// Draw preedit markers.  This needs to be before drawing letters. +		for (U32 i = 0; i < mPreeditStandouts.size(); i++) +		{ +			const S32 preedit_left = mPreeditPositions[i]; +			const S32 preedit_right = mPreeditPositions[i + 1]; +			if (preedit_right > mScrollHPos) +			{ +				S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor()); +				S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight); +				if (preedit_pixels_left >= background.mRight) +				{ +					break; +				} +				if (mPreeditStandouts[i]) +				{ +					gl_rect_2d(preedit_pixels_left + PREEDIT_STANDOUT_GAP, +						background.mBottom + PREEDIT_STANDOUT_POSITION, +						preedit_pixels_right - PREEDIT_STANDOUT_GAP - 1, +						background.mBottom + PREEDIT_STANDOUT_POSITION - PREEDIT_STANDOUT_THICKNESS, +						(text_color * PREEDIT_STANDOUT_BRIGHTNESS + bg_color * (1 - PREEDIT_STANDOUT_BRIGHTNESS)).setAlpha(1.0f)); +				} +				else +				{ +					gl_rect_2d(preedit_pixels_left + PREEDIT_MARKER_GAP, +						background.mBottom + PREEDIT_MARKER_POSITION, +						preedit_pixels_right - PREEDIT_MARKER_GAP - 1, +						background.mBottom + PREEDIT_MARKER_POSITION - PREEDIT_MARKER_THICKNESS, +						(text_color * PREEDIT_MARKER_BRIGHTNESS + bg_color * (1 - PREEDIT_MARKER_BRIGHTNESS)).setAlpha(1.0f)); +				} +			} +		} +	} +  	S32 rendered_text = 0;  	F32 rendered_pixels_right = (F32)mMinHPixels;  	F32 text_bottom = (F32)background.mBottom + (F32)UI_LINEEDITOR_V_PAD; @@ -1677,7 +1721,7 @@ void LLLineEditor::draw()  // Returns the local screen space X coordinate associated with the text cursor position. -S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) +S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const  {  	S32 dpos = getCursor() - mScrollHPos + cursor_offset;  	S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mMinHPixels; @@ -1715,7 +1759,7 @@ void LLLineEditor::setFocus( BOOL new_state )  	if (!new_state)  	{ -		getWindow()->allowLanguageTextInput(FALSE); +		getWindow()->allowLanguageTextInput(this, FALSE);  	} @@ -1757,7 +1801,7 @@ void LLLineEditor::setFocus( BOOL new_state )  		// fine on 1.15.0.2, since all prevalidate func reject any  		// non-ASCII characters.  I'm not sure on future versions,  		// however. -		getWindow()->allowLanguageTextInput(mPrevalidateFunc == NULL); +		getWindow()->allowLanguageTextInput(this, mPrevalidateFunc == NULL);  	}  } @@ -1776,6 +1820,12 @@ void LLLineEditor::setRect(const LLRect& rect)  	}  } +void LLLineEditor::setPrevalidate(BOOL (*func)(const LLWString &)) +{ +	mPrevalidateFunc = func; +	updateAllowingLanguageInput(); +} +  // Limits what characters can be used to [1234567890.-] with [-] only valid in the first position.  // Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for  // the simple reasons that intermediate states may be invalid even if the final result is valid. @@ -2336,6 +2386,239 @@ BOOL LLLineEditor::setLabelArg( const LLString& key, const LLStringExplicit& tex  	return TRUE;  } + +void LLLineEditor::updateAllowingLanguageInput() +{ +	// Allow Language Text Input only when this LineEditor has +	// no prevalidate function attached (as long as other criteria +	// common to LLTextEditor).  This criterion works +	// fine on 1.15.0.2, since all prevalidate func reject any +	// non-ASCII characters.  I'm not sure on future versions, +	// however... +	if (hasFocus() && !mReadOnly && !mDrawAsterixes && mPrevalidateFunc == NULL) +	{ +		getWindow()->allowLanguageTextInput(this, TRUE); +	} +	else +	{ +		getWindow()->allowLanguageTextInput(this, FALSE); +	} +} + +BOOL LLLineEditor::hasPreeditString() const +{ +	return (mPreeditPositions.size() > 1); +} + +void LLLineEditor::resetPreedit() +{ +	if (hasPreeditString()) +	{ +		const S32 preedit_pos = mPreeditPositions.front(); +		mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos); +		mText.insert(preedit_pos, mPreeditOverwrittenWString); +		setCursor(preedit_pos); +		 +		mPreeditWString.clear(); +		mPreeditOverwrittenWString.clear(); +		mPreeditPositions.clear(); + +		mKeystrokeTimer.reset(); +		if (mKeystrokeCallback) +		{ +			mKeystrokeCallback(this, mCallbackUserData); +		} +	} +} + +void LLLineEditor::updatePreedit(const LLWString &preedit_string, +		const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) +{ +	// Just in case. +	if (mReadOnly) +	{ +		return; +	} + +	if (hasSelection()) +	{ +		if (hasPreeditString()) +		{ +			llwarns << "Preedit and selection!" << llendl; +			deselect(); +		} +		else +		{ +			deleteSelection(); +		} +	} + +	S32 insert_preedit_at = getCursor(); +	if (hasPreeditString()) +	{ +		insert_preedit_at = mPreeditPositions.front(); +		//mText.replace(insert_preedit_at, mPreeditPositions.back() - insert_preedit_at, mPreeditOverwrittenWString); +		mText.erase(insert_preedit_at, mPreeditPositions.back() - insert_preedit_at); +		mText.insert(insert_preedit_at, mPreeditOverwrittenWString); +	} + +	mPreeditWString = preedit_string; +	mPreeditPositions.resize(preedit_segment_lengths.size() + 1); +	S32 position = insert_preedit_at; +	for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++) +	{ +		mPreeditPositions[i] = position; +		position += preedit_segment_lengths[i]; +	} +	mPreeditPositions.back() = position; +	if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) +	{ +		mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) ); +		mText.erase(insert_preedit_at, mPreeditWString.length()); +	} +	else +	{ +		mPreeditOverwrittenWString.clear(); +	} +	mText.insert(insert_preedit_at, mPreeditWString); + +	mPreeditStandouts = preedit_standouts; + +	setCursor(position); +	setCursor(mPreeditPositions.front() + caret_position); + +	// Update of the preedit should be caused by some key strokes. +	mKeystrokeTimer.reset(); +	if( mKeystrokeCallback ) +	{ +		mKeystrokeCallback( this, mCallbackUserData ); +	} +} + +BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const +{ +	if (control) +	{ +		LLRect control_rect_screen; +		localRectToScreen(mRect, &control_rect_screen); +		LLUI::screenRectToGL(control_rect_screen, control); +	} + +	S32 preedit_left_column, preedit_right_column; +	if (hasPreeditString()) +	{ +		preedit_left_column = mPreeditPositions.front(); +		preedit_right_column = mPreeditPositions.back(); +	} +	else +	{ +		preedit_left_column = preedit_right_column = getCursor(); +	} +	if (preedit_right_column < mScrollHPos) +	{ +		// This should not occure... +		return FALSE; +	} + +	const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor()); +	if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column) +	{ +		return FALSE; +	} + +	if (coord) +	{ +		S32 query_local = findPixelNearestPos(query - getCursor()); +		S32 query_screen_x, query_screen_y; +		localPointToScreen(query_local, mRect.getHeight() / 2, &query_screen_x, &query_screen_y); +		LLUI::screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); +	} + +	if (bounds) +	{ +		S32 preedit_left_local = findPixelNearestPos(llmax(preedit_left_column, mScrollHPos) - getCursor()); +		S32 preedit_right_local = llmin(findPixelNearestPos(preedit_right_column - getCursor()), mRect.getWidth() - mBorderThickness); +		if (preedit_left_local > preedit_right_local) +		{ +			// Is this condition possible? +			preedit_right_local = preedit_left_local; +		} + +		LLRect preedit_rect_local(preedit_left_local, mRect.getHeight(), preedit_right_local, 0); +		LLRect preedit_rect_screen; +		localRectToScreen(preedit_rect_local, &preedit_rect_screen); +		LLUI::screenRectToGL(preedit_rect_screen, bounds); +	} + +	return TRUE; +} + +void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const +{ +	if (hasPreeditString()) +	{ +		*position = mPreeditPositions.front(); +		*length = mPreeditPositions.back() - mPreeditPositions.front(); +	} +	else +	{ +		*position = mCursorPos; +		*length = 0; +	} +} + +void LLLineEditor::getSelectionRange(S32 *position, S32 *length) const +{ +	if (hasSelection()) +	{ +		*position = llmin(mSelectionStart, mSelectionEnd); +		*length = llabs(mSelectionStart - mSelectionEnd); +	} +	else +	{ +		*position = mCursorPos; +		*length = 0; +	} +} + +void LLLineEditor::markAsPreedit(S32 position, S32 length) +{ +	deselect(); +	setCursor(position); +	if (hasPreeditString()) +	{ +		llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl; +	} +	mPreeditWString.assign( LLWString( mText.getWString(), position, length ) ); +	if (length > 0) +	{ +		mPreeditPositions.resize(2); +		mPreeditPositions[0] = position; +		mPreeditPositions[1] = position + length; +		mPreeditStandouts.resize(1); +		mPreeditStandouts[0] = FALSE; +	} +	else +	{ +		mPreeditPositions.clear(); +		mPreeditStandouts.clear(); +	} +	if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) +	{ +		mPreeditOverwrittenWString = mPreeditWString; +	} +	else +	{ +		mPreeditOverwrittenWString.clear(); +	} +} + +S32 LLLineEditor::getPreeditFontSize() const +{ +	return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); +} + +  LLSearchEditor::LLSearchEditor(const LLString& name,   		const LLRect& rect,  		S32 max_length_bytes, diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index f1b9fbe33e..a019353856 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -53,6 +53,8 @@  #include "lluistring.h"  #include "llviewborder.h" +#include "llpreeditor.h" +  class LLFontGL;  class LLLineEditorRollback;  class LLButton; @@ -63,7 +65,7 @@ typedef BOOL (*LLLinePrevalidateFunc)(const LLWString &wstr);  // Classes  //  class LLLineEditor -: public LLUICtrl, public LLEditMenuHandler +: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor  {  	friend class LLLineEditorRollback; @@ -120,6 +122,7 @@ public:  	// view overrides  	virtual void	draw();  	virtual void	reshape(S32 width,S32 height,BOOL called_from_parent=TRUE); +	virtual void	onFocusReceived();  	virtual void	onFocusLost();  	virtual void	setEnabled(BOOL enabled); @@ -146,7 +149,7 @@ public:  	const LLWString& getWText() const	{ return mText.getWString(); }  	S32				getLength() const	{ return mText.length(); } -	S32				getCursor()		{ return mCursorPos; } +	S32				getCursor()	const	{ return mCursorPos; }  	void			setCursor( S32 pos );  	void			setCursorToEnd(); @@ -177,13 +180,13 @@ public:  	void			setIgnoreTab(BOOL b)			{ mIgnoreTab = b; }  	void			setPassDelete(BOOL b)			{ mPassDelete = b; } -	void			setDrawAsterixes(BOOL b)		{ mDrawAsterixes = b; } +	void			setDrawAsterixes(BOOL b);  	// get the cursor position of the beginning/end of the prev/next word in the text  	S32				prevWordPos(S32 cursorPos) const;  	S32				nextWordPos(S32 cursorPos) const; -	BOOL			hasSelection() { return (mSelectionStart != mSelectionEnd); } +	BOOL			hasSelection() const { return (mSelectionStart != mSelectionEnd); }  	void			startSelection();  	void			endSelection();  	void			extendSelection(S32 new_cursor_pos); @@ -199,7 +202,7 @@ public:  	static BOOL		isPartOfWord(llwchar c);  	// Prevalidation controls which keystrokes can affect the editor -	void			setPrevalidate( BOOL (*func)(const LLWString &) ) { mPrevalidateFunc = func; } +	void			setPrevalidate( BOOL (*func)(const LLWString &) );  	static BOOL		prevalidateFloat(const LLWString &str );  	static BOOL		prevalidateInt(const LLWString &str );  	static BOOL		prevalidatePositiveS32(const LLWString &str); @@ -221,7 +224,7 @@ protected:  	void			addChar(const llwchar c);  	void			setCursorAtLocalPos(S32 local_mouse_x); -	S32				findPixelNearestPos(S32 cursor_offset = 0); +	S32				findPixelNearestPos(S32 cursor_offset = 0) const;  	void			reportBadKeystroke();  	BOOL			handleSpecialKey(KEY key, MASK mask); @@ -230,6 +233,19 @@ protected:  	S32				handleCommitKey(KEY key, MASK mask);  protected: +	void			updateAllowingLanguageInput(); +	BOOL			hasPreeditString() const; +	// Implementation (overrides) of LLPreeditor +	virtual void	resetPreedit(); +	virtual void	updatePreedit(const LLWString &preedit_string, +						const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position); +	virtual void	markAsPreedit(S32 position, S32 length); +	virtual void	getPreeditRange(S32 *position, S32 *length) const; +	virtual void	getSelectionRange(S32 *position, S32 *length) const; +	virtual BOOL	getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; +	virtual S32		getPreeditFontSize() const; + +protected:  	LLUIString		mText;					// The string being edited.  	LLString		mPrevText;				// Saved string for 'ESC' revert  	LLUIString		mLabel;					// text label that is visible when no user text provided @@ -241,8 +257,7 @@ protected:  	LLViewBorder* mBorder;  	const LLFontGL*	mGLFont; -	S32			mMaxLengthChars;			// Max number of characters -	S32			mMaxLengthBytes;			// Max length of the UTF8 string. +	S32			mMaxLengthBytes;			// Max length of the UTF8 string in bytes  	S32			mCursorPos;					// I-beam is just after the mCursorPos-th character.  	S32			mScrollHPos;				// Horizontal offset from the start of mText.  Used for scrolling.  	LLFrameTimer mScrollTimer; @@ -288,6 +303,11 @@ protected:  	BOOL		mPassDelete;  	BOOL		mReadOnly; + +	LLWString	mPreeditWString; +	LLWString	mPreeditOverwrittenWString; +	std::vector<S32> mPreeditPositions; +	LLPreeditor::standouts_t mPreeditStandouts;  }; diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index d150f8954e..46f9f515d7 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -632,7 +632,7 @@ public:  };  LLMenuItemSeparatorGL::LLMenuItemSeparatorGL( const LLString &name ) : -	LLMenuItemGL( SEPARATOR_NAME, SEPARATOR_LABEL ) +	LLMenuItemGL( name, SEPARATOR_LABEL )  {  } @@ -2832,7 +2832,7 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa  	while(1)  	{  		// skip separators and disabled items -		if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getName() != SEPARATOR_NAME) +		if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getType() != SEPARATOR_NAME)  		{  			if (cur_item)  			{ diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index efd42455e5..8bd7b1509f 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -174,7 +174,7 @@ BOOL LLTextBox::handleHover(S32 x, S32 y, MASK mask)  		mHasHover = TRUE; // This should be set every frame during a hover.  		return TRUE;  	} -	return FALSE; +	return LLView::handleHover(x,y,mask);  }  void LLTextBox::setText(const LLStringExplicit& text) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index d08997c3ed..5c8b7c7281 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -79,6 +79,15 @@ const F32	CURSOR_FLASH_DELAY = 1.0f;  // in seconds  const S32	CURSOR_THICKNESS = 2;  const S32	SPACES_PER_TAB = 4; +const F32	PREEDIT_MARKER_BRIGHTNESS = 0.4f; +const S32	PREEDIT_MARKER_GAP = 1; +const S32	PREEDIT_MARKER_POSITION = 2; +const S32	PREEDIT_MARKER_THICKNESS = 1; +const F32	PREEDIT_STANDOUT_BRIGHTNESS = 0.6f; +const S32	PREEDIT_STANDOUT_GAP = 1; +const S32	PREEDIT_STANDOUT_POSITION = 2; +const S32	PREEDIT_STANDOUT_THICKNESS = 2; +  LLColor4 LLTextEditor::mLinkColor = LLColor4::blue;  void (* LLTextEditor::mURLcallback)(const char*)              = NULL;  bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&)   = NULL; @@ -274,14 +283,14 @@ private:  LLTextEditor::LLTextEditor(	  	const LLString& name,   	const LLRect& rect,  -	S32 max_length,  +	S32 max_length,						// In bytes  	const LLString &default_text,   	const LLFontGL* font,  	BOOL allow_embedded_items)  	:	  	LLUICtrl( name, rect, TRUE, NULL, NULL, FOLLOWS_TOP | FOLLOWS_LEFT ),  	mTextIsUpToDate(TRUE), -	mMaxTextLength( max_length ), +	mMaxTextByteLength( max_length ),  	mBaseDocIsPristine(TRUE),  	mPristineCmd( NULL ),  	mLastCmd( NULL ), @@ -510,13 +519,27 @@ BOOL LLTextEditor::isPartOfWord(llwchar c) { return (c == '_') || isalnum(c); } -void LLTextEditor::truncate() +BOOL LLTextEditor::truncate()  { -	if (mWText.size() > (size_t)mMaxTextLength) -	{ -		LLWString::truncate(mWText, mMaxTextLength); -		mTextIsUpToDate = FALSE; +	BOOL did_truncate = FALSE; + +	// First rough check - if we're less than 1/4th the size, we're OK +	if (mWText.size() >= (size_t) (mMaxTextByteLength / 4)) +	{	 +		// Have to check actual byte size +		S32 utf8_byte_size = wstring_utf8_length( mWText ); +		if ( utf8_byte_size > mMaxTextByteLength ) +		{ +			// Truncate safely in UTF-8 +			std::string temp_utf8_text = wstring_to_utf8str( mWText ); +			temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength ); +			mWText = utf8str_to_wstring( temp_utf8_text ); +			mTextIsUpToDate = FALSE; +			did_truncate = TRUE; +		}  	} + +	return did_truncate;  }  void LLTextEditor::setText(const LLStringExplicit &utf8str) @@ -750,12 +773,12 @@ S32 LLTextEditor::nextWordPos(S32 cursorPos) const  	return cursorPos;  } -S32 LLTextEditor::getLineCount() +S32 LLTextEditor::getLineCount() const  {  	return mLineStartList.size();  } -S32 LLTextEditor::getLineStart( S32 line ) +S32 LLTextEditor::getLineStart( S32 line ) const  {  	S32 num_lines = getLineCount();  	if (num_lines == 0) @@ -1604,7 +1627,7 @@ void LLTextEditor::removeChar()  // Add a single character to the text  S32 LLTextEditor::addChar(S32 pos, llwchar wc)  { -	if ((S32)mWText.length() == mMaxTextLength) +	if ( (wstring_utf8_length( mWText ) + wchar_utf8_length( wc ))  >= mMaxTextByteLength)  	{  		make_ui_sound("UISndBadKeystroke");  		return 0; @@ -2490,11 +2513,16 @@ void LLTextEditor::redo()  	}  } +void LLTextEditor::onFocusReceived() +{ +	LLUICtrl::onFocusReceived(); +	updateAllowingLanguageInput(); +}  // virtual, from LLView  void LLTextEditor::onFocusLost()  { -	getWindow()->allowLanguageTextInput(FALSE); +	updateAllowingLanguageInput();  	// Route menu back to the default   	if( gEditMenuHandler == this ) @@ -2521,6 +2549,7 @@ void LLTextEditor::setEnabled(BOOL enabled)  	{  		mReadOnly = read_only;  		updateSegments(); +		updateAllowingLanguageInput();  	}  } @@ -2825,6 +2854,100 @@ void LLTextEditor::drawCursor()  	}  } +void LLTextEditor::drawPreeditMarker() +{ +	if (!hasPreeditString()) +	{ +		return; +	} + +	const llwchar *text = mWText.c_str(); +	const S32 text_len = getLength(); +	const S32 num_lines = getLineCount(); + +	S32 cur_line = mScrollbar->getDocPos(); +	if (cur_line >= num_lines) +	{ +		return; +	} +		 +	const S32 line_height = llround( mGLFont->getLineHeight() ); + +	S32 line_start = getLineStart(cur_line); +	S32 line_y = mTextRect.mTop - line_height; +	while((mTextRect.mBottom <= line_y) && (num_lines > cur_line)) +	{ +		S32 next_start = -1; +		S32 line_end = text_len; + +		if ((cur_line + 1) < num_lines) +		{ +			next_start = getLineStart(cur_line + 1); +			line_end = next_start; +		} +		if ( text[line_end-1] == '\n' ) +		{ +			--line_end; +		} + +		// Does this line contain preedits? +		if (line_start >= mPreeditPositions.back()) +		{ +			// We have passed the preedits. +			break; +		} +		if (line_end > mPreeditPositions.front()) +		{ +			for (U32 i = 0; i < mPreeditStandouts.size(); i++) +			{ +				S32 left = mPreeditPositions[i]; +				S32 right = mPreeditPositions[i + 1]; +				if (right <= line_start || left >= line_end) +				{ +					continue; +				} + +				S32 preedit_left = mTextRect.mLeft; +				if (left > line_start) +				{ +					preedit_left += mGLFont->getWidth(text, line_start, left - line_start, mAllowEmbeddedItems); +				} +				S32 preedit_right = mTextRect.mLeft; +				if (right < line_end) +				{ +					preedit_right += mGLFont->getWidth(text, line_start, right - line_start, mAllowEmbeddedItems); +				} +				else +				{ +					preedit_right += mGLFont->getWidth(text, line_start, line_end - line_start, mAllowEmbeddedItems); +				} + +				if (mPreeditStandouts[i]) +				{ +					gl_rect_2d(preedit_left + PREEDIT_STANDOUT_GAP, +							line_y + PREEDIT_STANDOUT_POSITION, +							preedit_right - PREEDIT_STANDOUT_GAP - 1, +							line_y + PREEDIT_STANDOUT_POSITION - PREEDIT_STANDOUT_THICKNESS, +							(mCursorColor * PREEDIT_STANDOUT_BRIGHTNESS + mWriteableBgColor * (1 - PREEDIT_STANDOUT_BRIGHTNESS)).setAlpha(1.0f)); +				} +				else +				{ +					gl_rect_2d(preedit_left + PREEDIT_MARKER_GAP, +							line_y + PREEDIT_MARKER_POSITION, +							preedit_right - PREEDIT_MARKER_GAP - 1, +							line_y + PREEDIT_MARKER_POSITION - PREEDIT_MARKER_THICKNESS, +							(mCursorColor * PREEDIT_MARKER_BRIGHTNESS + mWriteableBgColor * (1 - PREEDIT_MARKER_BRIGHTNESS)).setAlpha(1.0f)); +				} +			} +		} + +		// move down one line +		line_y -= line_height; +		line_start = next_start; +		cur_line++; +	} +} +  void LLTextEditor::drawText()  { @@ -3025,6 +3148,7 @@ void LLTextEditor::draw()  			drawBackground();  			drawSelectionBackground(); +			drawPreeditMarker();  			drawText();  			drawCursor(); @@ -3067,10 +3191,10 @@ void LLTextEditor::setFocus( BOOL new_state )  	// Don't change anything if the focus state didn't change  	if (new_state == old_state) return; -	// Notify early if we are loosing focus. +	// Notify early if we are losing focus.  	if (!new_state)  	{ -		getWindow()->allowLanguageTextInput(FALSE); +		getWindow()->allowLanguageTextInput(this, FALSE);  	}  	LLUICtrl::setFocus( new_state ); @@ -3093,12 +3217,6 @@ void LLTextEditor::setFocus( BOOL new_state )  		endSelection();  	} - -	// Notify late if we are gaining focus. -	if (new_state && !mReadOnly) -	{ -		getWindow()->allowLanguageTextInput(TRUE); -	}  }  BOOL LLTextEditor::acceptsTextInput() const @@ -3540,22 +3658,20 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars)  S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr)  { -	S32 len = mWText.length(); -	S32 s_len = wstr.length(); -	S32 new_len = len + s_len; -	if( new_len > mMaxTextLength ) -	{ -		new_len = mMaxTextLength; +	S32 old_len = mWText.length();		// length() returns character length +	S32 insert_len = wstr.length(); +	mWText.insert(pos, wstr); +	mTextIsUpToDate = FALSE; + +	if ( truncate() ) +	{  		// The user's not getting everything he's hoping for  		make_ui_sound("UISndBadKeystroke"); +		insert_len = mWText.length() - old_len;  	} -	mWText.insert(pos, wstr); -	mTextIsUpToDate = FALSE; -	truncate(); - -	return new_len - len; +	return insert_len;  }  S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) @@ -3920,7 +4036,7 @@ BOOL LLTextEditor::importBuffer(const LLString& buffer )  		return FALSE;  	} -	if( text_len > mMaxTextLength ) +	if( text_len > mMaxTextByteLength )  	{  		llwarns << "Invalid Linden text length: " << text_len << llendl;  		return FALSE; @@ -4281,3 +4397,262 @@ BOOL LLTextEditor::findHTML(const LLString &line, S32 *begin, S32 *end)  	}  	return matched;  } + + + +void LLTextEditor::updateAllowingLanguageInput() +{ +	if (hasFocus() && !mReadOnly) +	{ +		getWindow()->allowLanguageTextInput(this, TRUE); +	} +	else +	{ +		getWindow()->allowLanguageTextInput(this, FALSE); +	} +} + +// Preedit is managed off the undo/redo command stack. + +BOOL LLTextEditor::hasPreeditString() const +{ +	return (mPreeditPositions.size() > 1); +} + +void LLTextEditor::resetPreedit() +{ +	if (hasPreeditString()) +	{ +		mCursorPos = mPreeditPositions.front(); +		removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos); +		insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString); + +		mPreeditWString.clear(); +		mPreeditOverwrittenWString.clear(); +		mPreeditPositions.clear(); + +		updateLineStartList(); +		setCursorPos(mCursorPos); +		// updateScrollFromCursor(); +	} +} + +void LLTextEditor::updatePreedit(const LLWString &preedit_string, +		const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) +{ +	// Just in case. +	if (mReadOnly) +	{ +		return; +	} + +	if (hasSelection()) +	{ +		if (hasPreeditString()) +		{ +			llwarns << "Preedit and selection!" << llendl; +			deselect(); +		} +		else +		{ +			deleteSelection(TRUE); +		} +	} + +	getWindow()->hideCursorUntilMouseMove(); + +	S32 insert_preedit_at = mCursorPos; +	if (hasPreeditString()) +	{ +		insert_preedit_at = mPreeditPositions.front(); +		removeStringNoUndo(insert_preedit_at, mPreeditPositions.back() - insert_preedit_at); +		insertStringNoUndo(insert_preedit_at, mPreeditOverwrittenWString); +	} + +	mPreeditWString = preedit_string; +	mPreeditPositions.resize(preedit_segment_lengths.size() + 1); +	S32 position = insert_preedit_at; +	for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++) +	{ +		mPreeditPositions[i] = position; +		position += preedit_segment_lengths[i]; +	} +	mPreeditPositions.back() = position; + +	if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) +	{ +		mPreeditOverwrittenWString = getWSubString(insert_preedit_at, mPreeditWString.length()); +		removeStringNoUndo(insert_preedit_at, mPreeditWString.length()); +	} +	else +	{ +		mPreeditOverwrittenWString.clear(); +	} +	insertStringNoUndo(insert_preedit_at, mPreeditWString); + +	mPreeditStandouts = preedit_standouts; + +	updateLineStartList(); +	setCursorPos(insert_preedit_at + caret_position); +	// updateScrollFromCursor(); + +	// Update of the preedit should be caused by some key strokes. +	mKeystrokeTimer.reset(); +} + +BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const +{ +	if (control) +	{ +		LLRect control_rect_screen; +		localRectToScreen(mTextRect, &control_rect_screen); +		LLUI::screenRectToGL(control_rect_screen, control); +	} + +	S32 preedit_left_position, preedit_right_position; +	if (hasPreeditString()) +	{ +		preedit_left_position = mPreeditPositions.front(); +		preedit_right_position = mPreeditPositions.back(); +	} +	else +	{ +		preedit_left_position = preedit_right_position = mCursorPos; +	} + +	const S32 query = (query_offset >= 0 ? preedit_left_position + query_offset : mCursorPos); +	if (query < preedit_left_position || query > preedit_right_position) +	{ +		return FALSE; +	} + +	const S32 first_visible_line = mScrollbar->getDocPos(); +	if (query < getLineStart(first_visible_line)) +	{ +		return FALSE; +	} + +	S32 current_line = first_visible_line; +	S32 current_line_start, current_line_end; +	for (;;) +	{ +		current_line_start = getLineStart(current_line); +		current_line_end = getLineStart(current_line + 1); +		if (query >= current_line_start && query < current_line_end) +		{ +			break; +		} +		if (current_line_start == current_line_end) +		{ +			// We have reached on the last line.  The query position must be here. +			break; +		} +		current_line++; +	} + +	const llwchar * const text = mWText.c_str(); +	const S32 line_height = llround(mGLFont->getLineHeight()); + +	if (coord) +	{ +		const S32 query_x = mTextRect.mLeft + mGLFont->getWidth(text, current_line_start, query - current_line_start, mAllowEmbeddedItems); +		const S32 query_y = mTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; +		S32 query_screen_x, query_screen_y; +		localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y); +		LLUI::screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); +	} + +	if (bounds) +	{ +		S32 preedit_left = mTextRect.mLeft; +		if (preedit_left_position > current_line_start) +		{ +			preedit_left += mGLFont->getWidth(text, current_line_start, preedit_left_position - current_line_start, mAllowEmbeddedItems); +		} + +		S32 preedit_right = mTextRect.mLeft; +		if (preedit_right_position < current_line_end) +		{ +			preedit_right += mGLFont->getWidth(text, current_line_start, preedit_right_position - current_line_start, mAllowEmbeddedItems); +		} +		else +		{ +			preedit_right += mGLFont->getWidth(text, current_line_start, current_line_end - current_line_start, mAllowEmbeddedItems); +		} + +		const S32 preedit_top = mTextRect.mTop - (current_line - first_visible_line) * line_height; +		const S32 preedit_bottom = preedit_top - line_height; + +		const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom); +		LLRect preedit_rect_screen; +		localRectToScreen(preedit_rect_local, &preedit_rect_screen); +		LLUI::screenRectToGL(preedit_rect_screen, bounds); +	} + +	return TRUE; +} + +void LLTextEditor::getSelectionRange(S32 *position, S32 *length) const +{ +	if (hasSelection()) +	{ +		*position = llmin(mSelectionStart, mSelectionEnd); +		*length = llabs(mSelectionStart - mSelectionEnd); +	} +	else +	{ +		*position = mCursorPos; +		*length = 0; +	} +} + +void LLTextEditor::getPreeditRange(S32 *position, S32 *length) const +{ +	if (hasPreeditString()) +	{ +		*position = mPreeditPositions.front(); +		*length = mPreeditPositions.back() - mPreeditPositions.front(); +	} +	else +	{ +		*position = mCursorPos; +		*length = 0; +	} +} + +void LLTextEditor::markAsPreedit(S32 position, S32 length) +{ +	deselect(); +	setCursorPos(position); +	if (hasPreeditString()) +	{ +		llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl; +	} +	mPreeditWString = LLWString( mWText, position, length ); +	if (length > 0) +	{ +		mPreeditPositions.resize(2); +		mPreeditPositions[0] = position; +		mPreeditPositions[1] = position + length; +		mPreeditStandouts.resize(1); +		mPreeditStandouts[0] = FALSE; +	} +	else +	{ +		mPreeditPositions.clear(); +		mPreeditStandouts.clear(); +	} +	if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) +	{ +		mPreeditOverwrittenWString = mPreeditWString; +	} +	else +	{ +		mPreeditOverwrittenWString.clear(); +	} +} + +S32 LLTextEditor::getPreeditFontSize() const +{ +	return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); +} diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index d38accca8f..7049de7b89 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -43,6 +43,8 @@  #include "lleditmenuhandler.h"  #include "lldarray.h" +#include "llpreeditor.h" +  class LLFontGL;  class LLScrollbar;  class LLViewBorder; @@ -64,7 +66,7 @@ const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1;  class LLTextSegment;  class LLTextCmd; -class LLTextEditor : public LLUICtrl, LLEditMenuHandler +class LLTextEditor : public LLUICtrl, LLEditMenuHandler, protected LLPreeditor  {  	friend class LLTextCmd;  public: @@ -104,6 +106,7 @@ public:  	// view overrides  	virtual void	reshape(S32 width, S32 height, BOOL called_from_parent);  	virtual void	draw(); +	virtual void	onFocusReceived();  	virtual void	onFocusLost();  	virtual void	setEnabled(BOOL enabled); @@ -234,7 +237,8 @@ public:  	void			setText(const LLStringExplicit &utf8str);  	void			setWText(const LLWString &wtext); -	S32				getMaxLength() const 			{ return mMaxTextLength; } +	// Returns byte length limit +	S32				getMaxLength() const 			{ return mMaxTextByteLength; }  	// Change cursor  	void			startOfLine(); @@ -259,6 +263,7 @@ protected:  	void			drawCursor();  	void			drawText();  	void			drawClippedSegment(const LLWString &wtext, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyle& color, F32* right_x); +	void			drawPreeditMarker();  	void			updateLineStartList(S32 startpos = 0);  	void			updateScrollFromCursor(); @@ -267,7 +272,7 @@ protected:  	void			pruneSegments();  	void 			assignEmbedded(const LLString &s); -	void 			truncate(); +	BOOL 			truncate();				// Returns true if truncation occurs  	static BOOL		isPartOfWord(llwchar c); @@ -291,7 +296,7 @@ protected:  	BOOL			handleControlKey(const KEY key, const MASK mask);  	BOOL			handleEditKey(const KEY key, const MASK mask); -	BOOL			hasSelection() { return (mSelectionStart !=mSelectionEnd); } +	BOOL			hasSelection() const		{ return (mSelectionStart !=mSelectionEnd); }  	BOOL			selectionContainsLineBreaks();  	void			startSelection();  	void			endSelection(); @@ -300,8 +305,8 @@ protected:  	S32				prevWordPos(S32 cursorPos) const;  	S32				nextWordPos(S32 cursorPos) const; -	S32 			getLineCount(); -	S32 			getLineStart( S32 line ); +	S32 			getLineCount() const; +	S32 			getLineStart( S32 line ) const;  	void			getLineAndOffset(S32 pos, S32* linep, S32* offsetp);  	S32				getPos(S32 line, S32 offset); @@ -338,6 +343,20 @@ protected:  	S32 			removeStringNoUndo(S32 pos, S32 length);  	S32				overwriteCharNoUndo(S32 pos, llwchar wc); +protected: +	void			updateAllowingLanguageInput(); +	BOOL			hasPreeditString() const; + +	// Overrides LLPreeditor +	virtual void	resetPreedit(); +	virtual void	updatePreedit(const LLWString &preedit_string, +						const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position); +	virtual void	markAsPreedit(S32 position, S32 length); +	virtual void	getPreeditRange(S32 *position, S32 *length) const; +	virtual void	getSelectionRange(S32 *position, S32 *length) const; +	virtual BOOL	getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; +	virtual S32		getPreeditFontSize() const; +  public:  	LLKeywords		mKeywords;  	static LLColor4 mLinkColor; @@ -349,7 +368,7 @@ protected:  	mutable LLString mUTF8Text;  	mutable BOOL	mTextIsUpToDate; -	S32				mMaxTextLength;		// Maximum length mText is allowed to be +	S32				mMaxTextByteLength;		// Maximum length mText is allowed to be in bytes  	const LLFontGL*	mGLFont; @@ -439,6 +458,11 @@ protected:  	BOOL			mParseHTML;  	LLString		mHTML; + +	LLWString			mPreeditWString; +	LLWString			mPreeditOverwrittenWString; +	std::vector<S32> 	mPreeditPositions; +	std::vector<BOOL> 	mPreeditStandouts;  };  class LLTextSegment diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 7af0d726cb..00a230dff3 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1696,6 +1696,34 @@ LLVector2 LLUI::getWindowSize()  }  //static +void LLUI::screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y) +{ +	*gl_x = llround((F32)screen_x * sGLScaleFactor.mV[VX]); +	*gl_y = llround((F32)screen_y * sGLScaleFactor.mV[VY]); +} + +//static +void LLUI::glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y) +{ +	*screen_x = llround((F32)gl_x / sGLScaleFactor.mV[VX]); +	*screen_y = llround((F32)gl_y / sGLScaleFactor.mV[VY]); +} + +//static +void LLUI::screenRectToGL(const LLRect& screen, LLRect *gl) +{ +	screenPointToGL(screen.mLeft, screen.mTop, &gl->mLeft, &gl->mTop); +	screenPointToGL(screen.mRight, screen.mBottom, &gl->mRight, &gl->mBottom); +} + +//static +void LLUI::glRectToScreen(const LLRect& gl, LLRect *screen) +{ +	glPointToScreen(gl.mLeft, gl.mTop, &screen->mLeft, &screen->mTop); +	glPointToScreen(gl.mRight, gl.mBottom, &screen->mRight, &screen->mBottom); +} + +//static  LLUUID			LLUI::findAssetUUIDByName(const LLString	&asset_name)  {  	if(asset_name == LLString::null) return LLUUID::null; diff --git a/indra/llui/llui.h b/indra/llui/llui.h index b78b046a8c..b98f4d5de2 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -174,6 +174,10 @@ public:  	static void setLineWidth(F32 width);  	static LLUUID findAssetUUIDByName(const LLString&	name);  	static LLVector2 getWindowSize(); +	static void screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y); +	static void glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y); +	static void screenRectToGL(const LLRect& screen, LLRect *gl); +	static void glRectToScreen(const LLRect& gl, LLRect *screen);  	static void setHtmlHelp(LLHtmlHelp* html_help);  private: diff --git a/indra/llwindow/llpreeditor.h b/indra/llwindow/llpreeditor.h new file mode 100644 index 0000000000..63e64ab781 --- /dev/null +++ b/indra/llwindow/llpreeditor.h @@ -0,0 +1,81 @@ +/**  + * @file llpreeditor.h + * @brief abstract class that defines interface for components to feedback preedits to users. + * + * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_PREEDITOR +#define LL_PREEDITOR + +class LLPreeditor +{ +public: + +	typedef std::vector<S32> segment_lengths_t; +	typedef std::vector<BOOL> standouts_t; +	 +	// We don't delete against LLPreeditor, but compilers complain without this... +	 +	virtual ~LLPreeditor() {}; + +	// Discard any preedit info. on this preeditor. +	 +	virtual void resetPreedit() = 0; + +	// Update the preedit feedback using specified details. +	// Existing preedit is discarded and replaced with the new one.  (I.e., updatePreedit is not cumulative.)  +	// All arguments are IN. +	// preedit_count is the number of elements in arrays preedit_list and preedit_standouts. +	// preedit list is an array of preedit texts (clauses.) +	// preedit_standouts indicates whether each preedit text should be shown as standout clause. +	// caret_position is the preedit-local position of text editing caret, in # of llwchar. +	 +	virtual void updatePreedit(const LLWString &preedit_string, +						const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) = 0; + +	// Turn the specified sub-contents into an active preedit. +	// Both position and length are IN and count with UTF-32 (llwchar) characters. +	// This method primarily facilitates reconversion. + +	virtual void markAsPreedit(S32 position, S32 length) = 0; + +	// Get the position and the length of the active preedit in the contents. +	// Both position and length are OUT and count with UTF-32 (llwchar) characters. +	// When this preeditor has no active preedit, position receives +	// the caret position, and length receives 0. + +	virtual void getPreeditRange(S32 *position, S32 *length) const = 0; + +	// Get the position and the length of the current selection in the contents. +	// Both position and length are OUT and count with UTF-32 (llwchar) characters. +	// When this preeditor has no selection, position receives +	// the caret position, and length receives 0. + +	virtual void getSelectionRange(S32 *position, S32 *length) const = 0; + +	// Get the locations where the preedit and related UI elements are displayed. +	// Locations are relative to the app window and measured in GL coordinate space (before scaling.) +	// query_position is IN argument, and other three are OUT. + +	virtual BOOL getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const = 0; + +	// Get the size (height) of the current font used in this preeditor. + +	virtual S32 getPreeditFontSize() const = 0; + +	// Get the contents of this preeditor as a LLWString.  If there is an active preedit, +	// the returned LLWString contains it. + +	virtual const LLWString & getWText() const = 0; + +	// Handle a UTF-32 char on this preeditor, i.e., add the character +	// to the contents. +	// This is a back door of the method of same name of LLWindowCallback. +	// called_from_parent should be set to FALSE if calling through LLPreeditor. + +	virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent) = 0; +}; + +#endif diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 4db2c8105f..9a3542d3fb 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -81,6 +81,8 @@ class LLSplashScreen;  class LLWindow; +class LLPreeditor; +  class LLWindowCallbacks  {  public: @@ -222,8 +224,10 @@ public:  	virtual void *getPlatformWindow() = 0;  	// control platform's Language Text Input mechanisms. -	virtual void allowLanguageTextInput( BOOL b ) {}; +	virtual void allowLanguageTextInput(LLPreeditor *preeditor, BOOL b) {}  	virtual void setLanguageTextInput( const LLCoordGL & pos ) {}; +	virtual void updateLanguageTextInputArea() {} +	virtual void interruptLanguageTextInput() {}  protected:  	LLWindow(BOOL fullscreen, U32 flags); diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 2724cb56e4..943b98e9d5 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -48,6 +48,7 @@  #include "indra_constants.h"  #include "llwindowmacosx-objc.h" +#include "llpreeditor.h"  extern BOOL gDebugWindowProc; @@ -172,8 +173,22 @@ static EventTypeSpec WindowHandlerEventList[] =  	{ kEventClassKeyboard, kEventRawKeyModifiersChanged },  	// Text input events -	{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } - +	{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, +	{ kEventClassTextInput, kEventTextInputUpdateActiveInputArea }, +	{ kEventClassTextInput, kEventTextInputOffsetToPos }, +	{ kEventClassTextInput, kEventTextInputPosToOffset }, +	{ kEventClassTextInput, kEventTextInputShowHideBottomWindow }, +	{ kEventClassTextInput, kEventTextInputGetSelectedText }, +	{ kEventClassTextInput, kEventTextInputFilterText }, + +	// TSM Document Access events (advanced input method support) +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }  };  static EventTypeSpec GlobalHandlerEventList[] = @@ -195,7 +210,22 @@ static EventTypeSpec GlobalHandlerEventList[] =  	{ kEventClassKeyboard, kEventRawKeyModifiersChanged },  	// Text input events -	{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } +	{ kEventClassTextInput, kEventTextInputUpdateActiveInputArea }, +	{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, +	{ kEventClassTextInput, kEventTextInputOffsetToPos }, +	{ kEventClassTextInput, kEventTextInputPosToOffset }, +	{ kEventClassTextInput, kEventTextInputShowHideBottomWindow }, +	{ kEventClassTextInput, kEventTextInputGetSelectedText }, +	{ kEventClassTextInput, kEventTextInputFilterText }, + +	// TSM Document Access events (advanced input method support) +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument }, +	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }  };  static EventTypeSpec CommandHandlerEventList[] = @@ -246,6 +276,7 @@ LLWindowMacOSX::LLWindowMacOSX(char *title, char *name, S32 x, S32 y, S32 width,  	mLanguageTextInputAllowed = FALSE;  	mTSMScriptCode = 0;  	mTSMLangCode = 0; +	mPreeditor = NULL;  	// For reasons that aren't clear to me, LLTimers seem to be created in the "started" state.  	// Since the started state of this one is used to track whether the NMRec has been installed, it wants to start out in the "stopped" state. @@ -497,15 +528,16 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits  			mTSMDocument = NULL;  		}  		static InterfaceTypeList types = { kUnicodeDocument }; -		OSErr err = NewTSMDocument(1, types, &mTSMDocument, 0); +		err = NewTSMDocument(1, types, &mTSMDocument, 0);  		if (err != noErr)  		{  			llwarns << "createContext: couldn't create a TSMDocument (" << err << ")" << llendl;  		}  		if (mTSMDocument)  		{ -			UseInputWindow(mTSMDocument, TRUE);  			ActivateTSMDocument(mTSMDocument); +			UseInputWindow(mTSMDocument, FALSE); +			allowLanguageTextInput(NULL, FALSE);  		}  	} @@ -1949,6 +1981,141 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e  		{  			switch (evtKind)  			{ +			case kEventTextInputUpdateActiveInputArea: +				{ +					EventParamType param_type; + +					long fix_len; +					UInt32 text_len; +					if (mPreeditor +						&& (result = GetEventParameter(event, kEventParamTextInputSendFixLen, +										typeLongInteger, ¶m_type, sizeof(fix_len), NULL, &fix_len)) == noErr +						&& typeLongInteger == param_type  +						&& (result = GetEventParameter(event, kEventParamTextInputSendText, +										typeUnicodeText, ¶m_type, 0, &text_len, NULL)) == noErr +						&& typeUnicodeText == param_type) +					{ +						// Handle an optional (but essential to facilitate TSMDA) ReplaceRange param. +						CFRange range; +						if (GetEventParameter(event, kEventParamTextInputSendReplaceRange, +								typeCFRange, ¶m_type, sizeof(range), NULL, &range) == noErr +							&& typeCFRange == param_type) +						{ +							// Although the spec. is unclear, replace range should +							// not present when there is an active preedit.  We just +							// ignore the case.  markAsPreedit will detect the case and warn it. +							const LLWString & text = mPreeditor->getWText(); +							const S32 location = wstring_wstring_length_from_utf16_length(text, 0, range.location); +							const S32 length = wstring_wstring_length_from_utf16_length(text, location, range.length); +							mPreeditor->markAsPreedit(location, length); +						} +						mPreeditor->resetPreedit(); +						 +						// Receive the text from input method. +						U16 *const text = new U16[text_len / sizeof(U16)]; +						GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText, NULL, text_len, NULL, text); +						if (fix_len < 0) +						{ +							// Do we still need this?  Seems obsolete... +							fix_len = text_len; +						} +						const LLWString fix_string +								= utf16str_to_wstring(llutf16string(text, fix_len / sizeof(U16))); +						const LLWString preedit_string +								= utf16str_to_wstring(llutf16string(text + fix_len / sizeof(U16), (text_len - fix_len) / sizeof(U16))); +						delete[] text; + +						// Handle fixed (comitted) string. +						if (fix_string.length() > 0) +						{ +							for (LLWString::const_iterator i = fix_string.begin(); i != fix_string.end(); i++) +							{ +								mPreeditor->handleUnicodeCharHere(*i, FALSE); +							} +						} + +						// Receive the segment info and caret position. +						LLPreeditor::segment_lengths_t preedit_segment_lengths; +						LLPreeditor::standouts_t preedit_standouts; +						S32 caret_position = preedit_string.length(); +						UInt32 text_range_array_size; +						if (GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, +								¶m_type, 0, &text_range_array_size, NULL) == noErr +							&& typeTextRangeArray == param_type +							&& text_range_array_size > sizeof(TextRangeArray)) +						{ +							// TextRangeArray is a variable-length struct. +							TextRangeArray * const text_range_array = (TextRangeArray *) new char[text_range_array_size]; +							GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, +									NULL, text_range_array_size, NULL, text_range_array); + +							// WARNING: We assume ranges are in ascending order,  +							// although the condition is undocumented.  It seems +							// OK to assume this.  I also assumed +							// the ranges are contiguous in previous versions, but I +							// have heard a rumore that older versions os ATOK may  +							// return ranges with some _gap_.  I don't know whether +							// it is true, but I'm preparing my code for the case. + +							const S32 ranges = text_range_array->fNumOfRanges; +							preedit_segment_lengths.reserve(ranges); +							preedit_standouts.reserve(ranges); + +							S32 last_bytes = 0; +							S32 last_utf32 = 0; +							for (S32 i = 0; i < ranges; i++) +							{ +								const TextRange &range = text_range_array->fRange[i]; +								if (range.fStart > last_bytes) +								{ +									const S32 length_utf16 = (range.fStart - last_bytes) / sizeof(U16); +									const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16); +									preedit_segment_lengths.push_back(length_utf32); +									preedit_standouts.push_back(FALSE); +									last_utf32 += length_utf32; +								} +								if (range.fEnd > range.fStart) +								{ +									const S32 length_utf16 = (range.fEnd - range.fStart) / sizeof(U16); +									const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16); +									preedit_segment_lengths.push_back(length_utf32); +									preedit_standouts.push_back( +										kTSMHiliteSelectedRawText == range.fHiliteStyle +										|| kTSMHiliteSelectedConvertedText ==  range.fHiliteStyle +										|| kTSMHiliteSelectedText == range.fHiliteStyle); +									last_utf32 += length_utf32; +								} +								if (kTSMHiliteCaretPosition == range.fHiliteStyle) +								{ +									caret_position = last_utf32; +								} +								last_bytes = range.fEnd; +							} +							if (preedit_string.length() > last_utf32) +							{ +								preedit_segment_lengths.push_back(preedit_string.length() - last_utf32); +								preedit_standouts.push_back(FALSE); +							} + +							delete[] (char *) text_range_array; +						} + +						// Handle preedit string. +						if (preedit_string.length() > 0) +						{ +							if (preedit_segment_lengths.size() == 0) +							{ +								preedit_segment_lengths.push_back(preedit_string.length()); +								preedit_standouts.push_back(FALSE); +							} +							mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths, preedit_standouts, caret_position); +						} + +						result = noErr; +					} +				} +				break; +				  			case kEventTextInputUnicodeForKeyEvent:  				{  					UInt32 modifiers = 0; @@ -2021,6 +2188,63 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e  					result = err;  				}  				break; +				 +			case kEventTextInputOffsetToPos: +				{ +					EventParamType param_type; +					long offset; +					if (mPreeditor +						&& GetEventParameter(event, kEventParamTextInputSendTextOffset, typeLongInteger, +								¶m_type, sizeof(offset), NULL, &offset) == noErr +						&& typeLongInteger == param_type) +					{ +						S32 preedit, preedit_length; +						mPreeditor->getPreeditRange(&preedit, &preedit_length); +						const LLWString & text = mPreeditor->getWText(); +						  +						LLCoordGL caret_coord; +						LLRect preedit_bounds; +						if (0 <= offset +							&& mPreeditor->getPreeditLocation(wstring_wstring_length_from_utf16_length(text, preedit, offset / sizeof(U16)), +															  &caret_coord, &preedit_bounds, NULL)) +						{ +							LLCoordGL caret_base_coord(caret_coord.mX, preedit_bounds.mBottom); +							LLCoordScreen caret_base_coord_screen; +							convertCoords(caret_base_coord, &caret_base_coord_screen); +							Point qd_point; +							qd_point.h = caret_base_coord_screen.mX; +							qd_point.v = caret_base_coord_screen.mY; +							SetEventParameter(event, kEventParamTextInputReplyPoint, typeQDPoint, sizeof(qd_point), &qd_point); +							 +							short line_height = (short) preedit_bounds.getHeight(); +							SetEventParameter(event, kEventParamTextInputReplyLineHeight, typeShortInteger, sizeof(line_height), &line_height); +							 +							result = noErr; +						} +						else +						{ +							result = errOffsetInvalid; +						} +					} +				} +				break; + +			case kEventTextInputGetSelectedText: +				{ +					if (mPreeditor) +					{ +						S32 selection, selection_length; +						mPreeditor->getSelectionRange(&selection, &selection_length); +						if (selection_length) +						{ +							const LLWString text = mPreeditor->getWText().substr(selection, selection_length); +							const llutf16string text_utf16 = wstring_to_utf16str(text); +							result = SetEventParameter(event, kEventParamTextInputReplyText, typeUnicodeText, +										text_utf16.length() * sizeof(U16), text_utf16.c_str()); +						} +					} +				} +				break;  			}  		}  		break; @@ -2194,6 +2418,13 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e  				switch (evtKind)  				{  				case kEventMouseDown: +					if (mLanguageTextInputAllowed) +					{ +						// We need to interrupt before handling mouse events, +						// so that the fixed string from IM are delivered to +						// the currently focused UI component. +						interruptLanguageTextInput(); +					}  					switch(button)  					{  					case kEventMouseButtonPrimary: @@ -2287,6 +2518,10 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e  			mCallbacks->handleFocus(this);  			break;  		case kEventWindowDeactivated: +			if (mTSMDocument) +			{ +				DeactivateTSMDocument(mTSMDocument); +			}  			mCallbacks->handleFocusLost(this);  			break;  		case kEventWindowBoundsChanging: @@ -2359,6 +2594,109 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e  			//					BringToFront(mWindow);  			//					result = noErr;  			break; +		 +		} +		break; + +	case kEventClassTSMDocumentAccess: +		if (mPreeditor) +		{ +			switch(evtKind) +			{		 + +			case kEventTSMDocumentAccessGetLength: +				{ +					// Return the number of UTF-16 units in the text, excluding those for preedit. + +					S32 preedit, preedit_length; +					mPreeditor->getPreeditRange(&preedit, &preedit_length); +					const LLWString & text = mPreeditor->getWText(); +					const CFIndex length = wstring_utf16_length(text, 0, preedit) +						+ wstring_utf16_length(text, preedit + preedit_length, text.length()); +					result = SetEventParameter(event, kEventParamTSMDocAccessCharacterCount, typeCFIndex, sizeof(length), &length); +				} +				break; + +			case kEventTSMDocumentAccessGetSelectedRange: +				{ +					// Return the selected range, excluding preedit. +					// In our preeditor, preedit and selection are exclusive, so, +					// when it has a preedit, there is no selection and the  +					// insertion point is on the preedit that corrupses into the +					// beginning of the preedit when the preedit was removed. + +					S32 preedit, preedit_length; +					mPreeditor->getPreeditRange(&preedit, &preedit_length); +					const LLWString & text = mPreeditor->getWText(); +					 +					CFRange range; +					if (preedit_length) +					{ +						range.location = wstring_utf16_length(text, 0, preedit); +						range.length = 0; +					} +					else +					{ +						S32 selection, selection_length; +						mPreeditor->getSelectionRange(&selection, &selection_length); +						range.location = wstring_utf16_length(text, 0, selection); +						range.length = wstring_utf16_length(text, selection, selection_length); +					} + +					result = SetEventParameter(event, kEventParamTSMDocAccessReplyCharacterRange, typeCFRange, sizeof(range), &range); +				} +				break; + +			case kEventTSMDocumentAccessGetCharacters: +				{ +					UniChar *target_pointer; +					CFRange range; +					EventParamType param_type; +					if ((result = GetEventParameter(event, kEventParamTSMDocAccessSendCharacterRange, +										typeCFRange, ¶m_type, sizeof(range), NULL, &range)) == noErr +						&& typeCFRange == param_type +						&& (result = GetEventParameter(event, kEventParamTSMDocAccessSendCharactersPtr, +										typePtr, ¶m_type, sizeof(target_pointer), NULL, &target_pointer)) == noErr +						&& typePtr == param_type) +					{ +						S32 preedit, preedit_length; +						mPreeditor->getPreeditRange(&preedit, &preedit_length); +						const LLWString & text = mPreeditor->getWText(); + +						// The GetCharacters event of TSMDA has a fundamental flaw; +						// An input method need to decide the starting offset and length +						// *before* it actually see the contents, so it is impossible +						// to guarantee the character-aligned access.  The event reply +						// has no way to indicate a condition something like "Request +						// was not fulfilled due to unaligned access.  Please retry." +						// Any error sent back to the input method stops use of TSMDA +						// entirely during the session... +						// We need to simulate very strictly the behaviour as if the +						// underlying *text engine* holds the contents in UTF-16. +						// I guess this is the reason why Apple repeats saying "all +						// text handling application should use UTF-16."  They are +						// trying to _fix_ the flaw by changing the appliations... +						// ... or, domination of UTF-16 in the industry may be a part +						// of the company vision, and Apple is trying to force third +						// party developers to obey their vision.  Remember that use +						// of 16 bits per _a_character_ was one of the very fundamental +						// Unicode design policy on its early days (during late 80s) +						// and the original Unicode design was by two Apple employees... + +						const llutf16string text_utf16 +							= wstring_to_utf16str(text, preedit) +							+ wstring_to_utf16str(text.substr(preedit + preedit_length)); + +						llassert_always(sizeof(U16) == sizeof(UniChar)); +						llassert(0 <= range.location && 0 <= range.length && range.location + range.length <= text_utf16.length()); +						memcpy(target_pointer, text_utf16.c_str() + range.location, range.length * sizeof(UniChar)); + +						// Note that result has already been set above. +					}					 +				} +				break; + +			}  		}  		break;  	} @@ -2995,10 +3333,34 @@ static long getDictLong (CFDictionaryRef refDict, CFStringRef key)  	return int_value; // otherwise return the long value  } -void LLWindowMacOSX::allowLanguageTextInput(BOOL b) +void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b)  {  	ScriptLanguageRecord script_language; +	if (preeditor != mPreeditor && !b) +	{ +		// This condition may occur by a call to +		// setEnabled(BOOL) against LLTextEditor or LLLineEditor +		// when the control is not focused. +		// We need to silently ignore the case so that +		// the language input status of the focused control +		// is not disturbed. +		return; +	} + +	// Take care of old and new preeditors. +	if (preeditor != mPreeditor || !b) +	{ +		// We need to interrupt before updating mPreeditor, +		// so that the fix string from input method goes to +		// the old preeditor. +		if (mLanguageTextInputAllowed) +		{ +			interruptLanguageTextInput(); +		} +		mPreeditor = (b ? preeditor : NULL); +	} +  	if (b == mLanguageTextInputAllowed)  	{  		return; @@ -3028,4 +3390,12 @@ void LLWindowMacOSX::allowLanguageTextInput(BOOL b)  	}  } +void LLWindowMacOSX::interruptLanguageTextInput() +{ +	if (mTSMDocument) +	{ +		FixTSMDocument(mTSMDocument); +	} +} +  #endif // LL_DARWIN diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index ee3019bd21..2a4cf97308 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -111,8 +111,10 @@ public:  	/*virtual*/ void *getPlatformWindow();  	/*virtual*/ void bringToFront() {}; -	/*virtual*/ void allowLanguageTextInput(BOOL b); -	 +	/*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, BOOL b); +	/*virtual*/ void updateLanguageTextInputArea(const LLCoordGL& caret, const LLRect& bounds); +	/*virtual*/ void interruptLanguageTextInput(); +  protected:  	LLWindowMacOSX(  		char *title, char *name, int x, int y, int width, int height, U32 flags, @@ -193,6 +195,7 @@ protected:  	BOOL		mLanguageTextInputAllowed;  	ScriptCode	mTSMScriptCode;  	LangCode	mTSMLangCode; +	LLPreeditor*	mPreeditor;  	friend class LLWindowManager;  }; diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index e5fd0f7360..4e4bed6485 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -57,6 +57,8 @@  #include "indra_constants.h" +#include "llpreeditor.h" +  // culled from winuser.h  #ifndef WM_MOUSEWHEEL /* Added to be compatible with later SDK's */  const S32	WM_MOUSEWHEEL = 0x020A; @@ -112,6 +114,7 @@ public:  public:  	// Wrappers for IMM API.  	static BOOL		isIME(HKL hkl);															 +	static HWND		getDefaultIMEWnd(HWND hwnd);  	static HIMC		getContext(HWND hwnd);													  	static BOOL		releaseContext(HWND hwnd, HIMC himc);  	static BOOL		getOpenStatus(HIMC himc);												 @@ -120,6 +123,11 @@ public:  	static BOOL		setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence);		  	static BOOL		getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form);					  	static BOOL		setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form);					 +	static LONG		getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length); +	static BOOL		setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength); +	static BOOL		setCompositionFont(HIMC himc, LPLOGFONTW logfont); +	static BOOL		setCandidateWindow(HIMC himc, LPCANDIDATEFORM candidate_form); +	static BOOL		notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value);  private:  	LLWinImm(); @@ -128,6 +136,7 @@ private:  private:  	// Pointers to IMM API.  	BOOL	 	(WINAPI *mImmIsIME)(HKL); +	HWND		(WINAPI *mImmGetDefaultIMEWnd)(HWND);  	HIMC		(WINAPI *mImmGetContext)(HWND);  	BOOL		(WINAPI *mImmReleaseContext)(HWND, HIMC);  	BOOL		(WINAPI *mImmGetOpenStatus)(HIMC); @@ -136,6 +145,11 @@ private:  	BOOL		(WINAPI *mImmSetConversionStatus)(HIMC, DWORD, DWORD);  	BOOL		(WINAPI *mImmGetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM);  	BOOL		(WINAPI *mImmSetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM); +	LONG		(WINAPI *mImmGetCompositionString)(HIMC, DWORD, LPVOID, DWORD); +	BOOL		(WINAPI *mImmSetCompositionString)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD); +	BOOL		(WINAPI *mImmSetCompositionFont)(HIMC, LPLOGFONTW); +	BOOL		(WINAPI *mImmSetCandidateWindow)(HIMC, LPCANDIDATEFORM); +	BOOL		(WINAPI *mImmNotifyIME)(HIMC, DWORD, DWORD, DWORD);  private:  	HMODULE		mHImmDll; @@ -155,6 +169,7 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)  	if (mHImmDll != NULL)  	{  		mImmIsIME               = (BOOL (WINAPI *)(HKL))                    GetProcAddress(mHImmDll, "ImmIsIME"); +		mImmGetDefaultIMEWnd	= (HWND (WINAPI *)(HWND))					GetProcAddress(mHImmDll, "ImmGetDefaultIMEWnd");  		mImmGetContext          = (HIMC (WINAPI *)(HWND))                   GetProcAddress(mHImmDll, "ImmGetContext");  		mImmReleaseContext      = (BOOL (WINAPI *)(HWND, HIMC))             GetProcAddress(mHImmDll, "ImmReleaseContext");  		mImmGetOpenStatus       = (BOOL (WINAPI *)(HIMC))                   GetProcAddress(mHImmDll, "ImmGetOpenStatus"); @@ -163,8 +178,14 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)  		mImmSetConversionStatus = (BOOL (WINAPI *)(HIMC, DWORD, DWORD))     GetProcAddress(mHImmDll, "ImmSetConversionStatus");  		mImmGetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM))   GetProcAddress(mHImmDll, "ImmGetCompositionWindow");  		mImmSetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM))   GetProcAddress(mHImmDll, "ImmSetCompositionWindow"); +		mImmGetCompositionString= (LONG (WINAPI *)(HIMC, DWORD, LPVOID, DWORD))					GetProcAddress(mHImmDll, "ImmGetCompositionStringW"); +		mImmSetCompositionString= (BOOL (WINAPI *)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD))	GetProcAddress(mHImmDll, "ImmSetCompositionStringW"); +		mImmSetCompositionFont  = (BOOL (WINAPI *)(HIMC, LPLOGFONTW))		GetProcAddress(mHImmDll, "ImmSetCompositionFontW"); +		mImmSetCandidateWindow  = (BOOL (WINAPI *)(HIMC, LPCANDIDATEFORM))  GetProcAddress(mHImmDll, "ImmSetCandidateWindow"); +		mImmNotifyIME			= (BOOL (WINAPI *)(HIMC, DWORD, DWORD, DWORD))	GetProcAddress(mHImmDll, "ImmNotifyIME");  		if (mImmIsIME == NULL || +			mImmGetDefaultIMEWnd == NULL ||  			mImmGetContext == NULL ||  			mImmReleaseContext == NULL ||  			mImmGetOpenStatus == NULL || @@ -172,7 +193,12 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)  			mImmGetConversionStatus == NULL ||  			mImmSetConversionStatus == NULL ||  			mImmGetCompostitionWindow == NULL || -			mImmSetCompostitionWindow == NULL) +			mImmSetCompostitionWindow == NULL || +			mImmGetCompositionString == NULL || +			mImmSetCompositionString == NULL || +			mImmSetCompositionFont == NULL || +			mImmSetCandidateWindow == NULL || +			mImmNotifyIME == NULL)  		{  			// If any of the above API entires are not found, we can't use IMM API.    			// So, turn off the IMM support.  We should log some warning message in  @@ -186,6 +212,7 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)  			// If we unload the library, make sure all the function pointers are cleared  			mImmIsIME = NULL; +			mImmGetDefaultIMEWnd = NULL;  			mImmGetContext = NULL;  			mImmReleaseContext = NULL;  			mImmGetOpenStatus = NULL; @@ -194,6 +221,11 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)  			mImmSetConversionStatus = NULL;  			mImmGetCompostitionWindow = NULL;  			mImmSetCompostitionWindow = NULL; +			mImmGetCompositionString = NULL; +			mImmSetCompositionString = NULL; +			mImmSetCompositionFont = NULL; +			mImmSetCandidateWindow = NULL; +			mImmNotifyIME = NULL;  		}  	}  } @@ -272,6 +304,50 @@ BOOL		LLWinImm::setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)  } +// static  +LONG		LLWinImm::getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length)					 +{  +	if ( sTheInstance.mImmGetCompositionString ) +		return sTheInstance.mImmGetCompositionString(himc, index, data, length);	 +	return FALSE; +} + + +// static  +BOOL		LLWinImm::setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength)					 +{  +	if ( sTheInstance.mImmSetCompositionString ) +		return sTheInstance.mImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength);	 +	return FALSE; +} + +// static  +BOOL		LLWinImm::setCompositionFont(HIMC himc, LPLOGFONTW pFont)					 +{  +	if ( sTheInstance.mImmSetCompositionFont ) +		return sTheInstance.mImmSetCompositionFont(himc, pFont);	 +	return FALSE; +} + +// static  +BOOL		LLWinImm::setCandidateWindow(HIMC himc, LPCANDIDATEFORM form)					 +{  +	if ( sTheInstance.mImmSetCandidateWindow ) +		return sTheInstance.mImmSetCandidateWindow(himc, form);	 +	return FALSE; +} + +// static  +BOOL		LLWinImm::notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value)					 +{  +	if ( sTheInstance.mImmNotifyIME ) +		return sTheInstance.mImmNotifyIME(himc, action, index, value);	 +	return FALSE; +} + + + +  // ----------------------------------------------------------------------------------------  LLWinImm::~LLWinImm()  { @@ -304,13 +380,14 @@ LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width,  	mNativeAspectRatio = 0.f;  	mMousePositionModified = FALSE;  	mInputProcessingPaused = FALSE; +	mPreeditor = NULL;  	// Initialize the keyboard  	gKeyboard = new LLKeyboardWin32();  	// Initialize (boot strap) the Language text input management,  	// based on the system's (user's) default settings. -	allowLanguageTextInput(FALSE); +	allowLanguageTextInput(mPreeditor, FALSE);  	GLuint			pixel_format;  	WNDCLASS		wc; @@ -938,6 +1015,10 @@ LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width,  	initCursors();  	setCursor( UI_CURSOR_ARROW ); +	// Initialize (boot strap) the Language text input management, +	// based on the system's (or user's) default settings. +	allowLanguageTextInput(NULL, FALSE); +  	// Direct Input  	HRESULT hr; @@ -2035,6 +2116,11 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_  				BOOL minimized = BOOL(HIWORD(w_param)); +				if (!activating && LLWinImm::isAvailable() && window_imp->mPreeditor) +				{ +					window_imp->interruptLanguageTextInput(); +				} +  				// JC - I'm not sure why, but if we don't report that we handled the   				// WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work   				// properly when we run fullscreen. @@ -2127,6 +2213,47 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_  			// pass on to windows  			break; +		case WM_IME_SETCONTEXT: +			if (LLWinImm::isAvailable() && window_imp->mPreeditor) +			{ +				l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW; +				// Invoke DefWinProc with the modified LPARAM. +			} +			break; + +		case WM_IME_STARTCOMPOSITION: +			if (LLWinImm::isAvailable() && window_imp->mPreeditor) +			{ +				window_imp->handleStartCompositionMessage(); +				return 0; +			} +			break; + +		case WM_IME_ENDCOMPOSITION: +			if (LLWinImm::isAvailable() && window_imp->mPreeditor) +			{ +				return 0; +			} +			break; + +		case WM_IME_COMPOSITION: +			if (LLWinImm::isAvailable() && window_imp->mPreeditor) +			{ +				window_imp->handleCompositionMessage(l_param); +				return 0; +			} +			break; + +		case WM_IME_REQUEST: +			if (LLWinImm::isAvailable() && window_imp->mPreeditor) +			{ +				LRESULT result = 0; +				if (window_imp->handleImeRequests(w_param, l_param, &result)) +				{ +					return result; +				} +			} +			break;  		case WM_CHAR:  			// Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need @@ -2154,6 +2281,11 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_  		case WM_LBUTTONDOWN:  			{ +				if (LLWinImm::isAvailable() && window_imp->mPreeditor) +				{ +					window_imp->interruptLanguageTextInput(); +				} +  				// Because we move the cursor position in the app, we need to query  				// to find out where the cursor at the time the event is handled.  				// If we don't do this, many clicks could get buffered up, and if the @@ -2236,6 +2368,11 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_  		case WM_RBUTTONDBLCLK:  		case WM_RBUTTONDOWN:  			{ +				if (LLWinImm::isAvailable() && window_imp->mPreeditor) +				{ +					window_imp->interruptLanguageTextInput(); +				} +  				// Because we move the cursor position in tllviewerhe app, we need to query  				// to find out where the cursor at the time the event is handled.  				// If we don't do this, many clicks could get buffered up, and if the @@ -2287,6 +2424,11 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_  		case WM_MBUTTONDOWN:  //		case WM_MBUTTONDBLCLK:  			{ +				if (LLWinImm::isAvailable() && window_imp->mPreeditor) +				{ +					window_imp->interruptLanguageTextInput(); +				} +  				// Because we move the cursor position in tllviewerhe app, we need to query  				// to find out where the cursor at the time the event is handled.  				// If we don't do this, many clicks could get buffered up, and if the @@ -3324,15 +3466,37 @@ void LLWindowWin32::focusClient()  	SetFocus ( mWindowHandle );  } -void LLWindowWin32::allowLanguageTextInput(BOOL b) +void LLWindowWin32::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b)  {  	if (b == sLanguageTextInputAllowed || !LLWinImm::isAvailable())  	{  		return;  	} + +	if (preeditor != mPreeditor && !b) +	{ +		// This condition may occur with a call to +		// setEnabled(BOOL) from LLTextEditor or LLLineEditor +		// when the control is not focused. +		// We need to silently ignore the case so that +		// the language input status of the focused control +		// is not disturbed. +		return; +	} + +	// Take care of old and new preeditors. +	if (preeditor != mPreeditor || !b) +	{ +		if (sLanguageTextInputAllowed) +		{ +			interruptLanguageTextInput(); +		} +		mPreeditor = (b ? preeditor : NULL); +	} +  	sLanguageTextInputAllowed = b; -	if (b) +	if ( sLanguageTextInputAllowed )  	{  		// Allowing: Restore the previous IME status, so that the user has a feeling that the previous   		// text input continues naturally.  Be careful, however, the IME status is meaningful only during the user keeps  @@ -3368,7 +3532,24 @@ void LLWindowWin32::allowLanguageTextInput(BOOL b)  			LLWinImm::releaseContext(mWindowHandle, himc);   		}  	} +} +void LLWindowWin32::fillCandidateForm(const LLCoordGL& caret, const LLRect& bounds,  +		CANDIDATEFORM *form) +{ +	LLCoordWindow caret_coord, top_left, bottom_right; +	convertCoords(caret, &caret_coord); +	convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left); +	convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right); + +	memset(form, 0, sizeof(CANDIDATEFORM)); +	form->dwStyle = CFS_EXCLUDE; +	form->ptCurrentPos.x = caret_coord.mX; +	form->ptCurrentPos.y = caret_coord.mY; +	form->rcArea.left   = top_left.mX; +	form->rcArea.top    = top_left.mY; +	form->rcArea.right  = bottom_right.mX; +	form->rcArea.bottom = bottom_right.mY;  } @@ -3416,4 +3597,455 @@ void LLWindowWin32::setLanguageTextInput( const LLCoordGL & position )  	}  } + +void LLWindowWin32::fillCharPosition(const LLCoordGL& caret, const LLRect& bounds, const LLRect& control, +		IMECHARPOSITION *char_position) +{ +	LLCoordScreen caret_coord, top_left, bottom_right; +	convertCoords(caret, &caret_coord); +	convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left); +	convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right); + +	char_position->pt.x = caret_coord.mX; +	char_position->pt.y = top_left.mY;	// Windows wants the coordinate of upper left corner of a character... +	char_position->cLineHeight = bottom_right.mY - top_left.mY; +	char_position->rcDocument.left   = top_left.mX; +	char_position->rcDocument.top    = top_left.mY; +	char_position->rcDocument.right  = bottom_right.mX; +	char_position->rcDocument.bottom = bottom_right.mY; +} + +void LLWindowWin32::fillCompositionLogfont(LOGFONT *logfont) +{ +	// Our font is a list of FreeType recognized font files that may +	// not have a corresponding ones in Windows' fonts.  Hence, we +	// can't simply tell Windows which font we are using.  We will +	// notify a _standard_ font for a current input locale instead. +	// We use a hard-coded knowledge about the Windows' standard +	// configuration to do so... + +	memset(logfont, 0, sizeof(LOGFONT)); + +	const WORD lang_id = LOWORD(GetKeyboardLayout(0)); +	switch (PRIMARYLANGID(lang_id)) +	{ +	case LANG_CHINESE: +		// We need to identify one of two Chinese fonts. +		switch (SUBLANGID(lang_id)) +		{ +		case SUBLANG_CHINESE_SIMPLIFIED: +		case SUBLANG_CHINESE_SINGAPORE: +			logfont->lfCharSet = GB2312_CHARSET; +			lstrcpy(logfont->lfFaceName, TEXT("SimHei")); +			break; +		case SUBLANG_CHINESE_TRADITIONAL: +		case SUBLANG_CHINESE_HONGKONG: +		case SUBLANG_CHINESE_MACAU: +		default: +			logfont->lfCharSet = CHINESEBIG5_CHARSET; +			lstrcpy(logfont->lfFaceName, TEXT("MingLiU")); +			break;			 +		} +		break; +	case LANG_JAPANESE: +		logfont->lfCharSet = SHIFTJIS_CHARSET; +		lstrcpy(logfont->lfFaceName, TEXT("MS Gothic")); +		break;		 +	case LANG_KOREAN: +		logfont->lfCharSet = HANGUL_CHARSET; +		lstrcpy(logfont->lfFaceName, TEXT("Gulim")); +		break; +	default: +		logfont->lfCharSet = ANSI_CHARSET; +		lstrcpy(logfont->lfFaceName, TEXT("Tahoma")); +		break; +	} +							 +	logfont->lfHeight = mPreeditor->getPreeditFontSize(); +	logfont->lfWeight = FW_NORMAL; +}	 + +U32 LLWindowWin32::fillReconvertString(const LLWString &text, +	S32 focus, S32 focus_length, RECONVERTSTRING *reconvert_string) +{ +	const llutf16string text_utf16 = wstring_to_utf16str(text); +	const DWORD required_size = sizeof(RECONVERTSTRING) + (text_utf16.length() + 1) * sizeof(WCHAR); +	if (reconvert_string && reconvert_string->dwSize >= required_size) +	{ +		const DWORD focus_utf16_at = wstring_utf16_length(text, 0, focus); +		const DWORD focus_utf16_length = wstring_utf16_length(text, focus, focus_length); + +		reconvert_string->dwVersion = 0; +		reconvert_string->dwStrLen = text_utf16.length(); +		reconvert_string->dwStrOffset = sizeof(RECONVERTSTRING); +		reconvert_string->dwCompStrLen = focus_utf16_length; +		reconvert_string->dwCompStrOffset = focus_utf16_at * sizeof(WCHAR); +		reconvert_string->dwTargetStrLen = 0; +		reconvert_string->dwTargetStrOffset = focus_utf16_at * sizeof(WCHAR); + +		const LPWSTR text = (LPWSTR)((BYTE *)reconvert_string + sizeof(RECONVERTSTRING)); +		memcpy(text, text_utf16.c_str(), (text_utf16.length() + 1) * sizeof(WCHAR)); +	} +	return required_size; +} + +void LLWindowWin32::updateLanguageTextInputArea() +{ +	if (!mPreeditor || !LLWinImm::isAvailable()) +	{ +		return; +	} + +	LLCoordGL caret_coord; +	LLRect preedit_bounds; +	if (mPreeditor->getPreeditLocation(-1, &caret_coord, &preedit_bounds, NULL)) +	{ +		mLanguageTextInputPointGL = caret_coord; +		mLanguageTextInputAreaGL = preedit_bounds; + +		CANDIDATEFORM candidate_form; +		fillCandidateForm(caret_coord, preedit_bounds, &candidate_form); + +		HIMC himc = LLWinImm::getContext(mWindowHandle); +		// Win32 document says there may be up to 4 candidate windows. +		// This magic number 4 appears only in the document, and +		// there are no constant/macro for the value... +		for (int i = 3; i >= 0; --i) +		{ +			candidate_form.dwIndex = i; +			LLWinImm::setCandidateWindow(himc, &candidate_form); +		} +		LLWinImm::releaseContext(mWindowHandle, himc); +	} +} + +void LLWindowWin32::interruptLanguageTextInput() +{ +	if (mPreeditor) +	{ +		if (LLWinImm::isAvailable()) +		{ +			HIMC himc = LLWinImm::getContext(mWindowHandle); +			LLWinImm::notifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); +			LLWinImm::releaseContext(mWindowHandle, himc); +		} +		mPreeditor->resetPreedit(); +	} +} + +void LLWindowWin32::handleStartCompositionMessage() +{ +	// Let IME know the font to use in feedback UI. +	LOGFONT logfont; +	fillCompositionLogfont(&logfont); +	HIMC himc = LLWinImm::getContext(mWindowHandle); +	LLWinImm::setCompositionFont(himc, &logfont); +	LLWinImm::releaseContext(mWindowHandle, himc); +} + +// Handle WM_IME_COMPOSITION message. + +void LLWindowWin32::handleCompositionMessage(const U32 indexes) +{ +	BOOL needs_update = FALSE; +	LLWString result_string; +	LLWString preedit_string; +	S32 preedit_string_utf16_length = 0; +	LLPreeditor::segment_lengths_t preedit_segment_lengths; +	LLPreeditor::standouts_t preedit_standouts; + +	// Step I: Receive details of preedits from IME. + +	HIMC himc = LLWinImm::getContext(mWindowHandle); + +	if (indexes & GCS_RESULTSTR) +	{ +		LONG size = LLWinImm::getCompositionString(himc, GCS_RESULTSTR, NULL, 0); +		if (size >= 0) +		{ +			const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1]; +			size = LLWinImm::getCompositionString(himc, GCS_RESULTSTR, data, size); +			if (size > 0) +			{ +				result_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR))); +			} +			delete[] data; +			needs_update = TRUE; +		} +	} +	 +	if (indexes & GCS_COMPSTR) +	{ +		LONG size = LLWinImm::getCompositionString(himc, GCS_COMPSTR, NULL, 0); +		if (size >= 0) +		{ +			const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1]; +			size = LLWinImm::getCompositionString(himc, GCS_COMPSTR, data, size); +			if (size > 0) +			{ +				preedit_string_utf16_length = size / sizeof(WCHAR); +				preedit_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR))); +			} +			delete[] data; +			needs_update = TRUE; +		} +	} + +	if ((indexes & GCS_COMPCLAUSE) && preedit_string.length() > 0) +	{ +		LONG size = LLWinImm::getCompositionString(himc, GCS_COMPCLAUSE, NULL, 0); +		if (size > 0) +		{ +			const LPDWORD data = new DWORD[size / sizeof(DWORD)]; +			size = LLWinImm::getCompositionString(himc, GCS_COMPCLAUSE, data, size); +			if (size >= sizeof(DWORD) * 2 +				&& data[0] == 0 && data[size / sizeof(DWORD) - 1] == preedit_string_utf16_length) +			{ +				preedit_segment_lengths.resize(size / sizeof(DWORD) - 1); +				S32 offset = 0; +				for (U32 i = 0; i < preedit_segment_lengths.size(); i++) +				{ +					const S32 length = wstring_wstring_length_from_utf16_length(preedit_string, offset, data[i + 1] - data[i]); +					preedit_segment_lengths[i] = length; +					offset += length; +				} +			} +			delete[] data; +		} +	} + +	if ((indexes & GCS_COMPATTR) && preedit_segment_lengths.size() > 1) +	{ +		LONG size = LLWinImm::getCompositionString(himc, GCS_COMPATTR, NULL, 0); +		if (size > 0) +		{ +			const LPBYTE data = new BYTE[size / sizeof(BYTE)]; +			size = LLWinImm::getCompositionString(himc, GCS_COMPATTR, data, size); +			if (size == preedit_string_utf16_length) +			{ +				preedit_standouts.assign(preedit_segment_lengths.size(), FALSE); +				S32 offset = 0; +				for (U32 i = 0; i < preedit_segment_lengths.size(); i++) +				{ +					if (ATTR_TARGET_CONVERTED == data[offset] || ATTR_TARGET_NOTCONVERTED == data[offset]) +					{ +						preedit_standouts[i] = TRUE; +					} +					offset += wstring_utf16_length(preedit_string, offset, preedit_segment_lengths[i]); +				} +			} +			delete[] data; +		} +	} + +	S32 caret_position = preedit_string.length(); +	if (indexes & GCS_CURSORPOS) +	{ +		const S32 caret_position_utf16 = LLWinImm::getCompositionString(himc, GCS_CURSORPOS, NULL, 0); +		if (caret_position_utf16 >= 0 && caret_position <= preedit_string_utf16_length) +		{ +			caret_position = wstring_wstring_length_from_utf16_length(preedit_string, 0, caret_position_utf16); +		} +	} + +	if (indexes == 0) +	{ +		// I'm not sure this condition really happens, but +		// Windows SDK document says it is an indication +		// of "reset everything." +		needs_update = TRUE; +	} + +	LLWinImm::releaseContext(mWindowHandle, himc); + +	// Step II: Update the active preeditor. + +	if (needs_update) +	{ +		mPreeditor->resetPreedit(); + +		if (result_string.length() > 0) +		{ +			for (LLWString::const_iterator i = result_string.begin(); i != result_string.end(); i++) +			{ +				mPreeditor->handleUnicodeCharHere(*i, FALSE); +			} +		} + +		if (preedit_string.length() > 0) +		{ +			if (preedit_segment_lengths.size() == 0) +			{ +				preedit_segment_lengths.assign(1, preedit_string.length()); +			} +			if (preedit_standouts.size() == 0) +			{ +				preedit_standouts.assign(preedit_segment_lengths.size(), FALSE); +			} +			mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths, preedit_standouts, caret_position); +		} + +		// Some IME doesn't query char position after WM_IME_COMPOSITION, +		// so we need to update them actively. +		updateLanguageTextInputArea(); +	} +} + +// Given a text and a focus range, find_context finds and returns a +// surrounding context of the focused subtext.  A variable pointed +// to by offset receives the offset in llwchars of the beginning of +// the returned context string in the given wtext. + +static LLWString find_context(const LLWString & wtext, S32 focus, S32 focus_length, S32 *offset) +{ +	static const S32 CONTEXT_EXCESS = 30;	// This value is by experiences. + +	const S32 e = llmin((S32) wtext.length(), focus + focus_length + CONTEXT_EXCESS); +	S32 end = focus + focus_length; +	while (end < e && '\n' != wtext[end]) +	{ +		end++; +	} + +	const S32 s = llmax(0, focus - CONTEXT_EXCESS); +	S32 start = focus; +	while (start > s && '\n' != wtext[start - 1]) +	{ +		--start; +	} + +	*offset = start; +	return wtext.substr(start, end - start); +} + +// Handle WM_IME_REQUEST message. +// If it handled the message, returns TRUE.  Otherwise, FALSE. +// When it handled the message, the value to be returned from +// the Window Procedure is set to *result. + +BOOL LLWindowWin32::handleImeRequests(U32 request, U32 param, LRESULT *result) +{ +	if ( mPreeditor ) +	{ +		switch (request) +		{ +			case IMR_CANDIDATEWINDOW:		// http://msdn2.microsoft.com/en-us/library/ms776080.aspx +			{ +				LLCoordGL caret_coord; +				LLRect preedit_bounds; +				mPreeditor->getPreeditLocation(-1, &caret_coord, &preedit_bounds, NULL); +				 +				CANDIDATEFORM *const form = (CANDIDATEFORM *)param; +				DWORD const dwIndex = form->dwIndex; +				fillCandidateForm(caret_coord, preedit_bounds, form); +				form->dwIndex = dwIndex; + +				*result = 1; +				return TRUE; +			} +			case IMR_QUERYCHARPOSITION: +			{ +				IMECHARPOSITION *const char_position = (IMECHARPOSITION *)param; + +				// char_position->dwCharPos counts in number of +				// WCHARs, i.e., UTF-16 encoding units, so we can't simply pass the +				// number to getPreeditLocation.   + +				const LLWString & wtext = mPreeditor->getWText(); +				S32 preedit, preedit_length; +				mPreeditor->getPreeditRange(&preedit, &preedit_length); +				LLCoordGL caret_coord; +				LLRect preedit_bounds, text_control; +				const S32 position = wstring_wstring_length_from_utf16_length(wtext, preedit, char_position->dwCharPos); + +				if (!mPreeditor->getPreeditLocation(position, &caret_coord, &preedit_bounds, &text_control)) +				{ +					llwarns << "*** IMR_QUERYCHARPOSITON called but getPreeditLocation failed." << llendl; +					return FALSE; +				} +				fillCharPosition(caret_coord, preedit_bounds, text_control, char_position); + +				*result = 1; +				return TRUE; +			} +			case IMR_COMPOSITIONFONT: +			{ +				fillCompositionLogfont((LOGFONT *)param); + +				*result = 1; +				return TRUE; +			} +			case IMR_RECONVERTSTRING: +			{ +				mPreeditor->resetPreedit(); +				const LLWString & wtext = mPreeditor->getWText(); +				S32 select, select_length; +				mPreeditor->getSelectionRange(&select, &select_length); + +				S32 context_offset; +				const LLWString context = find_context(wtext, select, select_length, &context_offset); + +				RECONVERTSTRING * const reconvert_string = (RECONVERTSTRING *)param; +				const U32 size = fillReconvertString(context, select - context_offset, select_length, reconvert_string); +				if (reconvert_string) +				{ +					if (select_length == 0) +					{ +						// Let the IME to decide the reconversion range, and +						// adjust the reconvert_string structure accordingly. +						HIMC himc = LLWinImm::getContext(mWindowHandle); +						const BOOL adjusted = LLWinImm::setCompositionString(himc, +									SCS_QUERYRECONVERTSTRING, reconvert_string, size, NULL, 0); +						LLWinImm::releaseContext(mWindowHandle, himc); +						if (adjusted) +						{ +							const llutf16string & text_utf16 = wstring_to_utf16str(context); +							const S32 new_preedit_start = reconvert_string->dwCompStrOffset / sizeof(WCHAR); +							const S32 new_preedit_end = new_preedit_start + reconvert_string->dwCompStrLen; +							select = utf16str_wstring_length(text_utf16, new_preedit_start); +							select_length = utf16str_wstring_length(text_utf16, new_preedit_end) - select; +							select += context_offset; +						} +					} +					mPreeditor->markAsPreedit(select, select_length); +				} + +				*result = size; +				return TRUE; +			} +			case IMR_CONFIRMRECONVERTSTRING: +			{ +				*result = FALSE; +				return TRUE; +			} +			case IMR_DOCUMENTFEED: +			{ +				const LLWString & wtext = mPreeditor->getWText(); +				S32 preedit, preedit_length; +				mPreeditor->getPreeditRange(&preedit, &preedit_length); +				 +				S32 context_offset; +				LLWString context = find_context(wtext, preedit, preedit_length, &context_offset); +				preedit -= context_offset; +				if (preedit_length) +				{ +					// IMR_DOCUMENTFEED may be called when we have an active preedit. +					// We should pass the context string *excluding* the preedit string. +					// Otherwise, some IME are confused. +					context.erase(preedit, preedit_length); +				} +				 +				RECONVERTSTRING *reconvert_string = (RECONVERTSTRING *)param; +				*result = fillReconvertString(context, preedit, 0, reconvert_string); +				return TRUE; +			} +			default: +				return FALSE; +		} +	} + +	return FALSE; +} + +  #endif // LL_WINDOWS diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 602e06600f..9ad99b0201 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -109,8 +109,10 @@ public:  	/*virtual*/ void bringToFront();  	/*virtual*/ void focusClient(); -	/*virtual*/ void allowLanguageTextInput(BOOL b); +	/*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, BOOL b);  	/*virtual*/ void setLanguageTextInput( const LLCoordGL & pos ); +	/*virtual*/ void updateLanguageTextInputArea(); +	/*virtual*/ void interruptLanguageTextInput();  protected:  	LLWindowWin32( @@ -139,6 +141,14 @@ protected:  	BOOL	shouldPostQuit() { return mPostQuit; } +	void	fillCompositionForm(const LLRect& bounds, COMPOSITIONFORM *form); +	void	fillCandidateForm(const LLCoordGL& caret, const LLRect& bounds, CANDIDATEFORM *form); +	void	fillCharPosition(const LLCoordGL& caret, const LLRect& bounds, const LLRect& control, IMECHARPOSITION *char_position); +	void	fillCompositionLogfont(LOGFONT *logfont); +	U32		fillReconvertString(const LLWString &text, S32 focus, S32 focus_length, RECONVERTSTRING *reconvert_string); +	void	handleStartCompositionMessage(); +	void	handleCompositionMessage(U32 indexes); +	BOOL	handleImeRequests(U32 request, U32 param, LRESULT *result);  protected:  	// @@ -189,6 +199,10 @@ protected:  	static DWORD	sWinIMEConversionMode;  	static DWORD	sWinIMESentenceMode;  	static LLCoordWindow sWinIMEWindowPosition; +	LLCoordGL		mLanguageTextInputPointGL; +	LLRect			mLanguageTextInputAreaGL; + +	LLPreeditor		*mPreeditor;  	friend class LLWindowManager;  }; diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp index d5e806f6cb..a984b597e4 100644 --- a/indra/mac_updater/mac_updater.cpp +++ b/indra/mac_updater/mac_updater.cpp @@ -496,14 +496,13 @@ bool isDirWritable(FSRef &dir)  	// This is kinda lame, but will pretty much always give the right answer.  	OSStatus err = noErr; -	char temp[PATH_MAX];		/* Flawfinder: ignore */ +	char temp[PATH_MAX] = "";		/* Flawfinder: ignore */  	err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp));  	if(err == noErr)  	{ -		temp[0] = '\0'; -		strncat(temp, "/.test_XXXXXX", sizeof(temp) - 1); +		strncat(temp, "/.test_XXXXXX", (sizeof(temp) - strlen(temp)) - 1);  		if(mkdtemp(temp) != NULL)  		{ @@ -557,8 +556,8 @@ static std::string HFSUniStr255_to_utf8str(const HFSUniStr255* src)  int restoreObject(const char* aside, const char* target, const char* path, const char* object)  { -	char source[PATH_MAX];		/* Flawfinder: ignore */ -	char dest[PATH_MAX];		/* Flawfinder: ignore */ +	char source[PATH_MAX] = "";		/* Flawfinder: ignore */ +	char dest[PATH_MAX] = "";		/* Flawfinder: ignore */  	snprintf(source, sizeof(source), "%s/%s/%s", aside, path, object);		  	snprintf(dest, sizeof(dest), "%s/%s", target, path);		  	FSRef sourceRef; @@ -592,7 +591,7 @@ int restoreObject(const char* aside, const char* target, const char* path, const  // Replace any mention of "Second Life" with the product name.  void filterFile(const char* filename)  { -	char temp[PATH_MAX];		/* Flawfinder: ignore */ +	char temp[PATH_MAX] = "";		/* Flawfinder: ignore */  	// First copy the target's version, so we can run it through sed.  	snprintf(temp, sizeof(temp), "cp '%s' '%s.tmp'", filename, filename);		  	system(temp);		/* Flawfinder: ignore */ @@ -724,13 +723,13 @@ void *updatethreadproc(void*)  {  	char tempDir[PATH_MAX] = "";		/* Flawfinder: ignore */  	FSRef tempDirRef; -	char temp[PATH_MAX];	/* Flawfinder: ignore */ +	char temp[PATH_MAX] = "";	/* Flawfinder: ignore */  	// *NOTE: This buffer length is used in a scanf() below.  	char deviceNode[1024] = "";	/* Flawfinder: ignore */  	FILE *downloadFile = NULL;  	OSStatus err;  	ProcessSerialNumber psn; -	char target[PATH_MAX];		/* Flawfinder: ignore */ +	char target[PATH_MAX] = "";		/* Flawfinder: ignore */  	FSRef targetRef;  	FSRef targetParentRef;  	FSVolumeRefNum targetVol; @@ -907,14 +906,14 @@ void *updatethreadproc(void*)  		if(err != noErr)  			throw 0; -		temp[0] = '\0'; -		strncat(temp, "/SecondLifeUpdate_XXXXXX", sizeof(temp) - 1); +		strncat(temp, "/SecondLifeUpdate_XXXXXX", (sizeof(temp) - strlen(temp)) - 1);  		if(mkdtemp(temp) == NULL)  		{  			throw 0;  		} -		strcpy(tempDir, temp);		/* Flawfinder: ignore */ +		strncpy(tempDir, temp, sizeof(tempDir)); +		temp[sizeof(tempDir) - 1] = '\0';  		llinfos << "tempDir is " << tempDir << llendl; diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 066990bafa..91d59a2c6b 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -49,6 +49,9 @@ ATI Radeon X1600				.*ATI.*Radeon X16.*						3  ATI Radeon X1700				.*ATI.*Radeon X17.*						3  ATI Radeon X1800				.*ATI.*Radeon X18.*						3  ATI Radeon X1900				.*ATI.*Radeon X19.*						3 +ATI Radeon X2400				.*ATI.*Radeon X24.*						3 +ATI Radeon X2600				.*ATI.*Radeon X26.*						3 +ATI Radeon X2900				.*ATI.*Radeon X29.*						3  ATI Radeon X300					.*ATI.*Radeon X3.*						2  ATI Radeon X400					.*ATI.*Radeon X4.*						2  ATI Radeon X500					.*ATI.*Radeon X5.*						2 diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 63d1986dec..ad934abfa7 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1530,6 +1530,9 @@ bool LLAppViewer::cleanup()  	delete gGlobalEconomy;  	gGlobalEconomy = NULL; +	delete gActiveChannelSpeakerMgr; +	gActiveChannelSpeakerMgr = NULL; +  	delete gLocalSpeakerMgr;  	gLocalSpeakerMgr = NULL; @@ -3015,7 +3018,7 @@ const std::vector<std::string>& LLAppViewer::getLoginURIs() const  	if (gLoginURIs.empty())  	{  		// not specified on the command line, use value from table -		gLoginURIs = LLSRV::rewriteURI(gGridInfo[gGridChoice].mLoginURI); +		gLoginURIs.push_back(gGridInfo[gGridChoice].mLoginURI);  	}  	return gLoginURIs;  } diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 1bcd1e1ab4..d957a3783a 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -372,10 +372,14 @@ void LLFeatureManager::loadGPUClass()  		char* ex = strtok(expr, ".*");  		char* rnd = (char*) renderer.c_str(); -		 +  		while (ex != NULL && rnd != NULL)  		{  			rnd = strstr(rnd, ex); +			if (rnd != NULL) +			{ +				rnd += strlen(ex); +			}  			ex = strtok(NULL, ".*");  		} diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 543dd94f3b..3b96a4ce5e 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -1045,7 +1045,8 @@ BOOL LLPanelLandObjects::postBuild()  	mSelectedObjects = LLUICtrlFactory::getTextBoxByName(this, "selected_objects_text");  	mCleanOtherObjectsTime = LLUICtrlFactory::getLineEditorByName(this, "clean other time"); -	mCleanOtherObjectsTime->setFocusLostCallback(onLostFocus);	 +	mCleanOtherObjectsTime->setFocusLostCallback(onLostFocus); +	mCleanOtherObjectsTime->setCommitCallback(onCommitClean);  	childSetPrevalidate("clean other time", LLLineEditor::prevalidateNonNegativeS32);  	childSetUserData("clean other time", this); @@ -1818,6 +1819,12 @@ void LLPanelLandObjects::onClickReturnOtherObjects(void* userdata)  // static  void LLPanelLandObjects::onLostFocus(LLUICtrl *caller, void* user_data)  { +	onCommitClean(caller, user_data); +} + +// static +void LLPanelLandObjects::onCommitClean(LLUICtrl *caller, void* user_data) +{  	LLPanelLandObjects	*lop = (LLPanelLandObjects *)user_data;  	LLParcel* parcel = lop->mParcel->getParcel();  	if (parcel) diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h index 9be813f8fd..fa941caf78 100644 --- a/indra/newview/llfloaterland.h +++ b/indra/newview/llfloaterland.h @@ -258,7 +258,7 @@ public:  	static void onCommitList(LLUICtrl* ctrl, void* data);  	static void onLostFocus(LLUICtrl* caller, void* user_data); -	 +	static void onCommitClean(LLUICtrl* caller, void* user_data);  	static void processParcelObjectOwnersReply(LLMessageSystem *msg, void **);  	virtual BOOL postBuild(); diff --git a/indra/newview/llfloatersellland.cpp b/indra/newview/llfloatersellland.cpp index 9bf1f785b0..ffa1e13bf2 100644 --- a/indra/newview/llfloatersellland.cpp +++ b/indra/newview/llfloatersellland.cpp @@ -492,7 +492,14 @@ void LLFloaterSellLandUI::doSellLand(void *userdata)  		args["[SALE_PRICE]"] = llformat("%d",sale_price);  		args["[NAME]"] = authorizedBuyerName; -		gViewerWindow->alertXml("ConfirmLandSaleChange", args, onConfirmSale, self); +		if (sell_to_anyone) +		{ +			gViewerWindow->alertXml("ConfirmLandSaleToAnyoneChange", args, onConfirmSale, self); +		} +		else +		{ +			gViewerWindow->alertXml("ConfirmLandSaleChange", args, onConfirmSale, self); +		}  	}  	else  	{ diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 353020d9c4..4c03a15619 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -11,7 +11,7 @@   * 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 + * ("Other License"), formally executed by you and Linden Lab.	Terms of   * the GPL can be found in doc/GPL-license.txt in this distribution, or   * online at http://secondlife.com/developers/opensource/gplv2   *  @@ -126,18 +126,18 @@ class LLMapInventoryObserver : public LLInventoryObserver  {  public:  	LLMapInventoryObserver() {} - 	virtual ~LLMapInventoryObserver() {} -  	virtual void changed(U32 mask); +	virtual ~LLMapInventoryObserver() {} +	virtual void changed(U32 mask);  };  void LLMapInventoryObserver::changed(U32 mask)  { -  	// if there's a change we're interested in. - 	if((mask & (LLInventoryObserver::CALLING_CARD | LLInventoryObserver::ADD | - 				LLInventoryObserver::REMOVE)) != 0) -  	{ - 		gFloaterWorldMap->inventoryChanged(); -  	} +	// if there's a change we're interested in. +	if((mask & (LLInventoryObserver::CALLING_CARD | LLInventoryObserver::ADD | +				LLInventoryObserver::REMOVE)) != 0) +	{ +		gFloaterWorldMap->inventoryChanged(); +	}  }  class LLMapFriendObserver : public LLFriendObserver @@ -177,8 +177,8 @@ LLFloaterWorldMap::LLFloaterWorldMap()  			  FALSE,	// drag on left  			  TRUE,		// minimize  			  TRUE),	// close - 	mInventory(NULL), - 	mInventoryObserver(NULL), +	mInventory(NULL), +	mInventoryObserver(NULL),  	mFriendObserver(NULL),  	mCompletingRegionName(""),  	mWaitingForTracker(FALSE), @@ -229,12 +229,18 @@ BOOL LLFloaterWorldMap::postBuild()  	{  		avatar_combo->selectFirstItem();  		avatar_combo->setPrearrangeCallback( onAvatarComboPrearrange ); +		avatar_combo->setTextEntryCallback( onComboTextEntry );  	}  	childSetAction("DoSearch", onLocationCommit, this);  	childSetFocusChangedCallback("location", updateSearchEnabled); -	childSetKeystrokeCallback("location", (void (*)(LLLineEditor*,void*))updateSearchEnabled, NULL); + +	LLLineEditor *location_editor = LLUICtrlFactory::getLineEditorByName(this, "location"); +	if (location_editor) +	{ +		location_editor->setKeystrokeCallback( onSearchTextEntry ); +	}  	childSetCommitCallback("search_results", onCommitSearchResult, this);  	childSetDoubleClickCallback("search_results", onClickTeleportBtn); @@ -249,6 +255,7 @@ BOOL LLFloaterWorldMap::postBuild()  	{  		landmark_combo->selectFirstItem();  		landmark_combo->setPrearrangeCallback( onLandmarkComboPrearrange ); +		landmark_combo->setTextEntryCallback( onComboTextEntry );  	}  	childSetAction("Go Home", onGoHome, this); @@ -284,9 +291,9 @@ LLFloaterWorldMap::~LLFloaterWorldMap()  	// All cleaned up by LLView destructor  	mTabs = NULL; - 	// Inventory deletes all observers on shutdown - 	mInventory = NULL; - 	mInventoryObserver = NULL; +	// Inventory deletes all observers on shutdown +	mInventory = NULL; +	mInventoryObserver = NULL;  	// avatar tracker will delete this for us.  	mFriendObserver = NULL; @@ -509,7 +516,7 @@ void LLFloaterWorldMap::draw()  	}  	childSetEnabled("Teleport", (BOOL)tracking_status); -// 	childSetEnabled("Clear", (BOOL)tracking_status); +//	childSetEnabled("Clear", (BOOL)tracking_status);  	childSetEnabled("Show Destination", (BOOL)tracking_status || gWorldMap->mIsTrackingUnknownLocation);  	childSetEnabled("copy_slurl", (mSLURL.size() > 0) ); @@ -595,7 +602,7 @@ void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )  		if (combo) name = combo->getSimple();  		mTrackedStatus = LLTracker::TRACKING_LANDMARK;  		LLTracker::trackLandmark(mLandmarkAssetIDList.get( idx ),	// assetID -								mLandmarkItemIDList.get( idx ),	// itemID +								mLandmarkItemIDList.get( idx ), // itemID  								name);			// name  		if( asset_id != sHomeID ) @@ -605,7 +612,7 @@ void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )  		}  		// We have to download both region info and landmark data, so set busy. JC -// 		getWindow()->incBusyCount(); +//		getWindow()->incBusyCount();  	}  	else  	{ @@ -1000,7 +1007,7 @@ void LLFloaterWorldMap::clearAvatarSelection(BOOL clear_ui)  void LLFloaterWorldMap::adjustZoomSliderBounds()  {  	// World size in regions -	S32 world_width_regions  = gWorldMap->getWorldWidth() / REGION_WIDTH_UNITS; +	S32 world_width_regions	 = gWorldMap->getWorldWidth() / REGION_WIDTH_UNITS;  	S32 world_height_regions = gWorldMap->getWorldHeight() / REGION_WIDTH_UNITS;  	// Pad the world size a little bit, so we have a nice border on @@ -1044,7 +1051,7 @@ void LLFloaterWorldMap::adjustZoomSliderBounds()  // static  void LLFloaterWorldMap::onPanBtn( void* userdata )  { -	if( !gFloaterWorldMap )	return; +	if( !gFloaterWorldMap ) return;  	EPanDirection direction = (EPanDirection)(intptr_t)userdata; @@ -1055,7 +1062,7 @@ void LLFloaterWorldMap::onPanBtn( void* userdata )  	case PAN_UP:	pan_y = -1;	break;  	case PAN_DOWN:	pan_y = 1;	break;  	case PAN_LEFT:	pan_x = 1;	break; -	case PAN_RIGHT:	pan_x = -1;	break; +	case PAN_RIGHT: pan_x = -1;	break;  	default:		llassert(0);	return;  	} @@ -1095,6 +1102,21 @@ void LLFloaterWorldMap::onLandmarkComboPrearrange( LLUICtrl* ctrl, void* userdat  } +void LLFloaterWorldMap::onComboTextEntry( LLLineEditor* ctrl, void* userdata ) +{ +	// Reset the tracking whenever we start typing into any of the search fields, +	// so that hitting <enter> does an auto-complete versus teleporting us to the +	// previously selected landmark/friend. +	LLTracker::clearFocus(); +} + +// static +void LLFloaterWorldMap::onSearchTextEntry( LLLineEditor* ctrl, void* userdata ) +{ +	onComboTextEntry(ctrl, userdata); +	updateSearchEnabled(ctrl, userdata); +} +  // static   void LLFloaterWorldMap::onLandmarkComboCommit( LLUICtrl* ctrl, void* userdata )  { @@ -1260,7 +1282,7 @@ void LLFloaterWorldMap::onClearBtn(void* data)  	self->mTrackedStatus = LLTracker::TRACKING_NOTHING;  	LLTracker::stopTracking((void *)(intptr_t)TRUE);  	gWorldMap->mIsTrackingUnknownLocation = FALSE; -	self->mSLURL = "";  				// Clear the SLURL since it's invalid +	self->mSLURL = "";				// Clear the SLURL since it's invalid  	self->mSetToUserPosition = TRUE;	// Revert back to the current user position  } @@ -1329,7 +1351,7 @@ void LLFloaterWorldMap::centerOnTarget(BOOL animate)  		else  		{  			// We've got the position finally, so we're no longer busy. JC -// 			getWindow()->decBusyCount(); +//			getWindow()->decBusyCount();  			pos_global = LLTracker::getTrackedPositionGlobal() - gAgent.getCameraPositionGlobal();  		}  	} diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index b98bbbbe44..c069b40929 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -10,7 +10,7 @@   * 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 + * ("Other License"), formally executed by you and Linden Lab.	Terms of   * the GPL can be found in doc/GPL-license.txt in this distribution, or   * online at http://secondlife.com/developers/opensource/gplv2   *  @@ -90,16 +90,16 @@ public:  	void			trackLocation(const LLVector3d& pos);  	void			trackEvent(const LLItemInfo &event_info);  	void			trackGenericItem(const LLItemInfo &item); -	void 			trackURL(const LLString& region_name, S32 x_coord, S32 y_coord, S32 z_coord); +	void			trackURL(const LLString& region_name, S32 x_coord, S32 y_coord, S32 z_coord);  	static const LLUUID& getHomeID() { return sHomeID; }  	// A z_attenuation of 0.0f collapses the distance into the X-Y plane -	F32 			getDistanceToDestination(const LLVector3d& pos_global, F32 z_attenuation = 0.5f) const; +	F32			getDistanceToDestination(const LLVector3d& pos_global, F32 z_attenuation = 0.5f) const; -	void 			clearLocationSelection(BOOL clear_ui = FALSE); -	void 			clearAvatarSelection(BOOL clear_ui = FALSE); -	void 			clearLandmarkSelection(BOOL clear_ui = FALSE); +	void			clearLocationSelection(BOOL clear_ui = FALSE); +	void			clearAvatarSelection(BOOL clear_ui = FALSE); +	void			clearLandmarkSelection(BOOL clear_ui = FALSE);  	// Adjust the maximally zoomed out limit of the zoom slider so you can  	// see the whole world, plus a little. @@ -118,12 +118,15 @@ protected:  	static void		onLandmarkComboPrearrange( LLUICtrl* ctrl, void* data );  	static void		onLandmarkComboCommit( LLUICtrl* ctrl, void* data ); -	 +  	static void		onAvatarComboPrearrange( LLUICtrl* ctrl, void* data );  	static void		onAvatarComboCommit( LLUICtrl* ctrl, void* data );  	static void		onCommitBackground(void* data, bool from_click); +	static void		onComboTextEntry( LLLineEditor* ctrl, void* data ); +	static void		onSearchTextEntry( LLLineEditor* ctrl, void* data ); +  	static void		onClearBtn(void*);  	static void		onFlyBtn(void*);  	static void		onClickTeleportBtn(void*); @@ -143,7 +146,7 @@ protected:  	static void		onGoToLandmarkDialog(S32 option,void* userdata);  	void			flyToLandmark();  	void			teleportToLandmark(); -	void 			setLandmarkVisited(); +	void			setLandmarkVisited();  	void			buildAvatarIDList();  	void			flyToAvatar(); @@ -154,7 +157,7 @@ protected:  	static void		onCommitLocation( LLUICtrl* ctrl, void* userdata );  	static void		onCommitSearchResult( LLUICtrl* ctrl, void* userdata ); -	void 			cacheLandmarkPosition(); +	void			cacheLandmarkPosition();  protected:  	LLTabContainerCommon*	mTabs; @@ -165,7 +168,7 @@ protected:  	LLDynamicArray<LLUUID>	mLandmarkAssetIDList;  	LLDynamicArray<LLUUID>	mLandmarkItemIDList; -	BOOL 					mHasLandmarkPosition; +	BOOL					mHasLandmarkPosition;  	static const LLUUID	sHomeID; diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index eb9addcf5c..2577474e24 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -3681,7 +3681,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )  	// SL-51858: Key presses are not being passed to the Popup menu.  	// A proper fix is non-trivial so instead just close the menu.  	LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle); -	if (menu->isOpen()) +	if (menu && menu->isOpen())  	{  		LLMenuGL::sMenuContainer->hideMenus();  	} @@ -3921,6 +3921,14 @@ BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_pare  	BOOL handled = FALSE;  	if (gFocusMgr.childHasKeyboardFocus(getRoot()))  	{ +		// SL-51858: Key presses are not being passed to the Popup menu. +		// A proper fix is non-trivial so instead just close the menu. +		LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle); +		if (menu && menu->isOpen()) +		{ +			LLMenuGL::sMenuContainer->hideMenus(); +		} +  		//do text search  		if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout"))  		{ diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index c213d26848..82a42a18b5 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -867,6 +867,12 @@ void LLGroupMgr::processGroupMembersReply(LLMessageSystem* msg, void** data)  				llinfos << "Received null group member data." << llendl;  			}  		} + +		//if group members are loaded while titles are missing, load the titles. +		if(group_datap->mTitles.size() < 1) +		{ +			gGroupMgr->sendGroupTitlesRequest(group_id); +		}  	}  	if (group_datap->mMembers.size() ==  (U32)group_datap->mMemberCount) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 12617efb67..9c9b1ad257 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -2719,6 +2719,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  						  && (LLUUID::null != item->getCreatorUUID())  						  && (item->getCreatorUUID() != gAgent.getID()));  		BOOL user_online = (LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID())); +		items.push_back("Send Instant Message Separator");  		items.push_back("Send Instant Message");  		items.push_back("Offer Teleport...");  		items.push_back("Conference Chat"); @@ -4294,21 +4295,24 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  		getClipboardEntries(true, items, disabled_items, flags);  		items.push_back("Wearable Separator"); +		  		items.push_back("Wearable Wear");  		items.push_back("Wearable Edit"); + +  		if ((flags & FIRST_SELECTED_ITEM) == 0)  		{  			disabled_items.push_back("Wearable Edit");  		} -		/*menu.appendSeparator(); -		menu.append(new LLMenuItemCallGL("Wear", -										 LLWearableBridge::onWearOnAvatar, -										 LLWearableBridge::canWearOnAvatar, -										 (void*)this)); -		menu.append(new LLMenuItemCallGL("Edit", -										 LLWearableBridge::onEditOnAvatar, -										 LLWearableBridge::canEditOnAvatar, -										 (void*)this));*/ +		//menu.appendSeparator(); +		//menu.append(new LLMenuItemCallGL("Wear", +		//								 LLWearableBridge::onWearOnAvatar, +		//								 LLWearableBridge::canWearOnAvatar, +		//								 (void*)this)); +		//menu.append(new LLMenuItemCallGL("Edit", +		//								 LLWearableBridge::onEditOnAvatar, +		//								 LLWearableBridge::canEditOnAvatar, +		//								 (void*)this));  		if( item && (item->getType() == LLAssetType::AT_CLOTHING) )  		{ diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp index 36e3f9a5e9..2f5f154b77 100644 --- a/indra/newview/llmaniptranslate.cpp +++ b/indra/newview/llmaniptranslate.cpp @@ -2132,15 +2132,12 @@ void LLManipTranslate::renderTranslationHandles()  				LLVector3 arrow_axis;  				getManipAxis(first_object, which_arrow[face], arrow_axis); -				if (fabs(angle_between(camera_axis, arrow_axis) - F_PI_BY_TWO) < F_PI_BY_TWO - HANDLE_HIDE_ANGLE) -				{ -					renderArrow(which_arrow[face], -								mManipPart, -								(face >= 3) ? -mConeSize : mConeSize, -								(face >= 3) ? -mArrowLengthMeters : mArrowLengthMeters, -								mConeSize, -								FALSE); -				} +				renderArrow(which_arrow[face], +							mManipPart, +							(face >= 3) ? -mConeSize : mConeSize, +							(face >= 3) ? -mArrowLengthMeters : mArrowLengthMeters, +							mConeSize, +							FALSE);  			}  		}  	} diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index f36e282ea0..4d02af8fae 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -62,6 +62,8 @@  #include "llviewergenericmessage.h"	// for gGenericDispatcher  #include "llviewerwindow.h"  #include "llworld.h" //for particle system banning +#include "llviewerobject.h"  +#include "llviewerobjectlist.h"  LLMuteList* gMuteListp = NULL; @@ -513,8 +515,21 @@ BOOL LLMuteList::saveToFile(const LLString& filename)  BOOL LLMuteList::isMuted(const LLUUID& id, const LLString& name, U32 flags) const  { +	LLUUID id_to_check = id; +	 +	// for objects, check for muting on their parent prim +	LLViewerObject *objectp = gObjectList.findObject(id); +	if ((objectp) && (!objectp->isAvatar())) +	{ +		LLViewerObject *parentp = (LLViewerObject *)objectp->getParent(); +		if (parentp) +		{ +			id_to_check = parentp->getID(); +		} +	} +	  	// don't need name or type for lookup -	LLMute mute(id); +	LLMute mute(id_to_check);  	mute_set_t::const_iterator mute_it = mMutes.find(mute);  	if (mute_it != mMutes.end())  	{ diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 5b43497f03..c090fd9749 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -365,6 +365,7 @@ void LLPanelAvatarSecondLife::onDoubleClickGroup(void* data)  	if(group_list)  	{  		LLScrollListItem* item = group_list->getFirstSelected(); +		  		if(item && item->getUUID().notNull())  		{  			llinfos << "Show group info " << item->getUUID() << llendl; @@ -1565,6 +1566,8 @@ void LLPanelAvatar::resetGroupList()  				group_string += group_data.mName;  				LLSD row; + +				row["id"] = id ;  				row["columns"][0]["value"] = group_string;  				row["columns"][0]["font"] = "SANSSERIF_SMALL";  				row["columns"][0]["width"] = 0; @@ -2010,8 +2013,7 @@ void LLPanelAvatar::processAvatarGroupsReply(LLMessageSystem *msg, void**)  				LLString group_string;  				if (group_id.notNull())  				{ -					group_string.assign("Member of "); -					group_string.append(group_name); +					group_string.assign(group_name);  				}  				else  				{ diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 04fb54b0a7..7163a71bc3 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -808,17 +808,21 @@ void LLPanelClassified::confirmPublish(S32 option)  	}  	// Tell all the widgets to reset their dirty state since the ad was just saved -	mSnapshotCtrl->resetDirty(); -	mNameEditor->resetDirty(); -	mDescEditor->resetDirty(); -	mLocationEditor->resetDirty(); +	if (mSnapshotCtrl) +		mSnapshotCtrl->resetDirty(); +	if (mNameEditor) +		mNameEditor->resetDirty(); +	if (mDescEditor) +		mDescEditor->resetDirty(); +	if (mLocationEditor) +		mLocationEditor->resetDirty();  	mLocationChanged = false; -	mCategoryCombo->resetDirty(); -	mMatureCheck->resetDirty(); +	if (mCategoryCombo) +		mCategoryCombo->resetDirty(); +	if (mMatureCheck) +		mMatureCheck->resetDirty();  	if (mAutoRenewCheck) -	{  		mAutoRenewCheck->resetDirty(); -	}  }  // static diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index e4f7d1e0b2..660de69fb0 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -66,12 +66,14 @@ LLPreview::LLPreview(const std::string& name) :  	mUserResized(FALSE),  	mCloseAfterSave(FALSE),  	mAssetStatus(PREVIEW_ASSET_UNLOADED), -	mItem(NULL) +	mItem(NULL), +	mDirty(TRUE)  {  	// don't add to instance list, since ItemID is null  	mAuxItem = new LLInventoryItem; // (LLPointer is auto-deleted)  	// don't necessarily steal focus on creation -- sometimes these guys pop up without user action  	mAutoFocus = FALSE; +	gInventory.addObserver(this);  }  LLPreview::LLPreview(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_uuid, const LLUUID& object_uuid, BOOL allow_resize, S32 min_width, S32 min_height, LLPointer<LLViewerInventoryItem> inv_item ) @@ -84,7 +86,8 @@ LLPreview::LLPreview(const std::string& name, const LLRect& rect, const std::str  	mUserResized(FALSE),  	mCloseAfterSave(FALSE),  	mAssetStatus(PREVIEW_ASSET_UNLOADED), -	mItem(inv_item) +	mItem(inv_item), +	mDirty(TRUE)  {  	mAuxItem = new LLInventoryItem;  	// don't necessarily steal focus on creation -- sometimes these guys pop up without user action @@ -94,7 +97,7 @@ LLPreview::LLPreview(const std::string& name, const LLRect& rect, const std::str  	{  		sInstances[mItemUUID] = this;  	} - +	gInventory.addObserver(this);  }  LLPreview::~LLPreview() @@ -118,6 +121,7 @@ LLPreview::~LLPreview()  			}  		}  	} +	gInventory.removeObserver(this);  }  void LLPreview::setItemID(const LLUUID& item_id) @@ -215,6 +219,7 @@ void LLPreview::onCommit()  		{  			new_item->updateServer(FALSE);  			gInventory.updateItem(new_item); +			gInventory.notifyObservers();  			// If the item is an attachment that is currently being worn,  			// update the object itself. @@ -238,6 +243,34 @@ void LLPreview::onCommit()  	}  } +void LLPreview::changed(U32 mask) +{ +	mDirty = TRUE; +} + +void LLPreview::draw() +{ +	LLFloater::draw(); +	if (mDirty) +	{ +		mDirty = FALSE; +		const LLViewerInventoryItem *item = getItem(); +		if (item) +		{ +			refreshFromItem(item); +		} +	} +} + +void LLPreview::refreshFromItem(const LLInventoryItem* item) +{ +	setTitle(llformat("%s: %s",getTitleName(),item->getName().c_str())); +	childSetText("desc",item->getDescription()); + +	BOOL can_agent_manipulate = item->getPermissions().allowModifyBy(gAgent.getID()); +	childSetEnabled("desc",can_agent_manipulate); +} +  // static   void LLPreview::onText(LLUICtrl*, void* userdata)  { diff --git a/indra/newview/llpreview.h b/indra/newview/llpreview.h index ae986f5aae..97cd2d5b07 100644 --- a/indra/newview/llpreview.h +++ b/indra/newview/llpreview.h @@ -38,6 +38,7 @@  #include "lluuid.h"  #include "llviewerinventory.h"  #include "lltabcontainer.h" +#include "llinventorymodel.h"  #include <map>  class LLLineEditor; @@ -61,7 +62,7 @@ protected:  	static std::map<LLUUID, LLViewHandle> sAutoOpenPreviewHandles;  }; -class LLPreview : public LLFloater +class LLPreview : public LLFloater, LLInventoryObserver  {  public:  	typedef enum e_asset_status @@ -116,6 +117,10 @@ public:  	void setNotecardInfo(const LLUUID& notecard_inv_id, const LLUUID& object_id)  	{ mNotecardInventoryID = notecard_inv_id; mObjectID = object_id; } +	// llview +	virtual void draw(); +	void refreshFromItem(const LLInventoryItem* item); +	  protected:  	virtual void onCommit(); @@ -124,7 +129,11 @@ protected:  	static void onText(LLUICtrl*, void* userdata);  	static void onRadio(LLUICtrl*, void* userdata); - +	// for LLInventoryObserver  +	virtual void changed(U32 mask);	 +	BOOL mDirty; +	virtual const char *getTitleName() const { return "Preview"; } +	  protected:  	LLUUID mItemUUID;  	LLUUID	mSourceID; diff --git a/indra/newview/llpreviewanim.h b/indra/newview/llpreviewanim.h index bb6ec759e4..37cbd49725 100644 --- a/indra/newview/llpreviewanim.h +++ b/indra/newview/llpreviewanim.h @@ -50,7 +50,8 @@ public:  protected:  	virtual void onClose(bool app_quitting); - +	virtual const char *getTitleName() const { return "Animation"; } +	  	LLAnimPauseRequest	mPauseRequest;  	LLUUID		mItemID;  	LLString	mTitle; diff --git a/indra/newview/llpreviewgesture.h b/indra/newview/llpreviewgesture.h index 5c84ee0188..4dea34ba1c 100644 --- a/indra/newview/llpreviewgesture.h +++ b/indra/newview/llpreviewgesture.h @@ -137,6 +137,8 @@ protected:  	static void onDonePreview(LLMultiGesture* gesture, void* data); +	virtual const char *getTitleName() const { return "Gesture"; } +  protected:  	// LLPreview contains mDescEditor  	LLLineEditor*	mTriggerEditor; diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index dc56494d7f..eef8c0d636 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -148,6 +148,10 @@ LLPreviewNotecard::LLPreviewNotecard(const std::string& name,  	gAgent.changeCameraToDefault();  } +LLPreviewNotecard::~LLPreviewNotecard() +{ +} +  BOOL LLPreviewNotecard::postBuild()  {  	LLViewerTextEditor *ed = (LLViewerTextEditor *)gUICtrlFactory->getTextEditorByName(this, "Notecard Editor"); @@ -213,7 +217,7 @@ BOOL LLPreviewNotecard::handleKeyHere(KEY key, MASK mask,  			return TRUE;  		}  	} -	return FALSE; +	return LLPreview::handleKeyHere(key, mask, called_from_parent);  }  // virtual diff --git a/indra/newview/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h index 9909284d5f..233246ceaa 100644 --- a/indra/newview/llpreviewnotecard.h +++ b/indra/newview/llpreviewnotecard.h @@ -54,7 +54,8 @@ public:  					  const LLUUID& asset_id = LLUUID::null,  					  BOOL show_keep_discard = FALSE,  					  LLPointer<LLViewerInventoryItem> inv_item = NULL); - +	virtual ~LLPreviewNotecard(); +	  	// llpreview	  	virtual bool saveItem(LLPointer<LLInventoryItem>* itemptr); @@ -102,6 +103,8 @@ protected:  	static void handleSaveChangesDialog(S32 option, void* userdata); +	virtual const char *getTitleName() const { return "Note"; } +  protected:  	LLViewerTextEditor* mEditor;  	LLButton* mSaveBtn; diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 3dfeb2c3ac..a9b36f3978 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -128,6 +128,8 @@ protected:  	static void onErrorList(LLUICtrl*, void* user_data); + 	virtual const char *getTitleName() const { return "Script"; } +  private:  	LLString		mSampleText;  	std::string		mHelpFile; @@ -189,6 +191,8 @@ protected:  protected: + + 	virtual const char *getTitleName() const { return "Script"; }  	LLScriptEdCore* mScriptEd;  	// Can safely close only after both text and bytecode are uploaded  	S32 mPendingUploads; diff --git a/indra/newview/llpreviewsound.h b/indra/newview/llpreviewsound.h index 74df017def..b56035f34e 100644 --- a/indra/newview/llpreviewsound.h +++ b/indra/newview/llpreviewsound.h @@ -44,6 +44,9 @@ public:  	static void playSound( void* userdata );  	static void auditionSound( void* userdata ); +protected: +	virtual const char *getTitleName() const { return "Sound"; } +  };  #endif  // LL_LLPREVIEWSOUND_H diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 4c8d4efb25..78d066f85f 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -286,7 +286,7 @@ void LLPreviewTexture::draw()  						LLFontGL::DROP_SHADOW);  				}  			} -		} +		}   	}  } diff --git a/indra/newview/llpreviewtexture.h b/indra/newview/llpreviewtexture.h index b7850de92e..8ed5210c46 100644 --- a/indra/newview/llpreviewtexture.h +++ b/indra/newview/llpreviewtexture.h @@ -80,6 +80,8 @@ protected:  	void				init();  	void				updateAspectRatio(); +	virtual const char *getTitleName() const { return "Texture"; } +	  protected:  	LLUUID						mImageID;  	LLPointer<LLViewerImage>		mImage; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 3c29cfdbfc..e2e076e364 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -3994,7 +3994,11 @@ void LLSelectMgr::sendListToRegions(const LLString& message_name,  	switch(send_type)  	{  	  case SEND_ONLY_ROOTS: -		getSelection()->applyToRootNodes(&pusheditable); +		  if(message_name == "ObjectBuy") +			getSelection()->applyToRootNodes(&pushroots); +		  else +			getSelection()->applyToRootNodes(&pusheditable); +		    		break;  	  case SEND_INDIVIDUALS:  		getSelection()->applyToNodes(&pushall); @@ -6062,23 +6066,19 @@ LLViewerObject* LLObjectSelection::getFirstDeleteableObject()  		bool apply(LLSelectNode* node)  		{  			LLViewerObject* obj = node->getObject(); -			// you can delete an object if permissions allow it, you are -			// the owner, you are an officer in the group that owns the -			// object, or you are not the owner but it is on land you own -			// or land owned by your group. (whew!) +			// you can delete an object if you are the owner +			// or you have permission to modify it.  			if(    (obj->permModify())   				|| (obj->permYouOwner())  				|| (!obj->permAnyOwner())			// public -				|| (obj->isOverAgentOwnedLand()) -				|| (obj->isOverGroupOwnedLand())  				)  			{  				if( !obj->isAttachment() )  				{ -					return TRUE; +					return true;  				}  			} -			return true; +			return false;  		}  	} func;  	LLSelectNode* node = getFirstNode(&func); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index c319ef97af..302291ab52 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -932,9 +932,15 @@ BOOL idle_startup()  			gSavedSettings.setBOOL("UseDebugMenus", TRUE);  			requested_options.push_back("god-connect");  		} -		LLAppViewer::instance()->getLoginURIs(); -		sAuthUris = LLAppViewer::instance()->getLoginURIs(); - +		const std::vector<std::string>& uris = LLAppViewer::instance()->getLoginURIs(); +		std::vector<std::string>::const_iterator iter, end; +		for (iter = uris.begin(), end = uris.end(); iter != end; ++iter) +		{ +			std::vector<std::string> rewritten; +			rewritten = LLSRV::rewriteURI(*iter); +			sAuthUris.insert(sAuthUris.end(), +							 rewritten.begin(), rewritten.end()); +		}  		sAuthUriNum = 0;  		auth_method = "login_to_simulator";  		auth_desc = "Logging in.  "; @@ -2161,7 +2167,7 @@ BOOL idle_startup()  				else  				{  					args["[TYPE]"] = "home"; -					args["[HELP]"] = "\nYou may want to set a new home location."; +					args["[HELP]"] = "You may want to set a new home location.";  				}  				gViewerWindow->alertXml("AvatarMoved", args);  			} diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 856943da6e..4d49d33184 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -375,7 +375,7 @@ void LLStatusBar::refresh()  		x += buttonRect.getWidth();  	} -	BOOL have_voice = gVoiceClient->getAreaVoiceDisabled() ? FALSE : TRUE; +	BOOL have_voice = parcel && parcel->getVoiceEnabled();   	childSetVisible("status_voice", have_voice);  	if (have_voice)  	{ diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index e6eca31cd0..413b26309d 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1204,6 +1204,7 @@ BOOL LLToolDragAndDrop::handleDropTextureProtections(LLViewerObject* hit_obj,  		// if the asset is already in the object's inventory   		// then it can always be added to a side.  		// This saves some work if the task's inventory is already loaded +		// and ensures that the texture item is only added once.  		return TRUE;  	} @@ -1241,7 +1242,10 @@ BOOL LLToolDragAndDrop::handleDropTextureProtections(LLViewerObject* hit_obj,  				return FALSE;  			}  		} -		hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true); +		// Add the texture item to the target object's inventory. +		hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true); + 		// TODO: Check to see if adding the item was successful; if not, then +		// we should return false here.  	}  	else if(!item->getPermissions().allowOperationBy(PERM_TRANSFER,  													 gAgent.getID())) @@ -1253,8 +1257,10 @@ BOOL LLToolDragAndDrop::handleDropTextureProtections(LLViewerObject* hit_obj,  		}  		// *FIX: may want to make sure agent can paint hit_obj. -		// make sure the object has the texture in it's inventory. -		hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true); +		// Add the texture item to the target object's inventory. +		hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true); + 		// TODO: Check to see if adding the item was successful; if not, then +		// we should return false here.  	}  	return TRUE;  } diff --git a/indra/newview/lltracker.cpp b/indra/newview/lltracker.cpp index 4bab92269c..594ecb5591 100644 --- a/indra/newview/lltracker.cpp +++ b/indra/newview/lltracker.cpp @@ -639,6 +639,10 @@ void LLTracker::stopTrackingLocation(BOOL clear_ui)  	mTrackingLocationType = LOCATION_NOTHING;  } +void LLTracker::clearFocus() +{ +	instance()->mTrackingStatus = TRACKING_NOTHING; +}  void LLTracker::drawMarker(const LLVector3d& pos_global, const LLColor4& color)  { diff --git a/indra/newview/lltracker.h b/indra/newview/lltracker.h index c7e09d0d3d..5f7ad98fe5 100644 --- a/indra/newview/lltracker.h +++ b/indra/newview/lltracker.h @@ -82,7 +82,8 @@ public:  	static ETrackingLocationType getTrackedLocationType() { return instance()->mTrackingLocationType; }  	static BOOL isTracking(void*) { return (BOOL) instance()->mTrackingStatus; }  	static void stopTracking(void*); - +	static void clearFocus(); +	  	static const LLUUID& getTrackedLandmarkAssetID() { return instance()->mTrackedLandmarkAssetID; }  	static const LLUUID& getTrackedLandmarkItemID()	 { return instance()->mTrackedLandmarkItemID; } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 43e8589176..b5316d29e0 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -104,14 +104,9 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLViewerInventoryItem* other)  }  LLViewerInventoryItem::LLViewerInventoryItem(const LLInventoryItem *other) : -	LLInventoryItem(other) +	LLInventoryItem(other), +	mIsComplete(TRUE)  { -	LLInventoryItem::copy(other); -	if (!mIsComplete) -	{ -		llwarns << "LLViewerInventoryItem copy constructor for incomplete item" -			<< mUUID << llendl; -	}  } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index b5f53e5d88..7aa25266fe 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -6053,12 +6053,39 @@ BOOL object_selected_and_point_valid(void *user_data)  		(selection->getFirstRootObject()->getNVPair("AssetContainer") == NULL);  } + +BOOL object_is_wearable() +{ +	if (!object_selected_and_point_valid(NULL)) +	{ +		return FALSE; +	} +	if (sitting_on_selection()) +	{ +		return FALSE; +	} +	LLObjectSelectionHandle selection = gSelectMgr->getSelection(); +	for (LLObjectSelection::valid_root_iterator iter = gSelectMgr->getSelection()->valid_root_begin(); +		 iter != gSelectMgr->getSelection()->valid_root_end(); iter++) +	{ +		LLSelectNode* node = *iter;		 +		if (node->mPermissions->getOwner() == gAgent.getID()) +		{ +			return TRUE; +		} +	} +	return FALSE; +} + +  // Also for seeing if object can be attached.  See above.  class LLObjectEnableWear : public view_listener_t  {  	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)  	{ -		return object_selected_and_point_valid(NULL); +		bool is_wearable = object_selected_and_point_valid(NULL); +		gMenuHolder->findControl(userdata["control"].asString())->setValue(is_wearable); +		return TRUE;  	}  }; diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index f109ea417b..fa92bfb217 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -131,6 +131,7 @@  #include "pipeline.h"  #include "llappviewer.h"  #include "llfloaterworldmap.h" +#include "llkeythrottle.h"  #include "llviewerdisplay.h"  #include "llkeythrottle.h" @@ -4407,6 +4408,10 @@ void script_question_cb(S32 option, void* user_data)  		notify_cautioned_script_question(cbdata, orig, allowed);  	} +	if ( option == 2 ) +	{ +		gMuteListp->add(LLMute(cbdata->mItemID, cbdata->mObjectName, LLMute::OBJECT)); +	}  	delete cbdata;  } diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index f0d6fd11c3..b0d1d3daca 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -66,6 +66,7 @@  #include "llworld.h"  #include "lloverlaybar.h"  #include "roles_constants.h" +#include "llweb.h"  const F32 PARCEL_COLLISION_DRAW_SECS = 1.f; @@ -1720,6 +1721,10 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use  				std::string mediaUrl = std::string ( parcel->getMediaURL () );  				LLString::trim(mediaUrl); +				// clean spaces and whatnot  +				mediaUrl = LLWeb::escapeURL(mediaUrl); + +				  				// something changed  				LLMediaEngine* me = LLMediaEngine::getInstance();  				if (  ( me->getUrl () != mediaUrl ) @@ -1837,6 +1842,10 @@ void prepare_video(const LLParcel *parcel)  	{  		mediaUrl = std::string ( parcel->getMediaURL () );  	} + +	// clean spaces and whatnot  +	mediaUrl = LLWeb::escapeURL(mediaUrl); +	  	LLMediaEngine::getInstance ()->setUrl ( mediaUrl );  	LLMediaEngine::getInstance ()->setImageUUID ( parcel->getMediaID () );  	LLMediaEngine::getInstance ()->setAutoScaled ( parcel->getMediaAutoScale () ? TRUE : FALSE );  // (U8 instead of BOOL for future expansion) diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 49883eb8c6..a3611b2272 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2446,6 +2446,10 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)  			return TRUE;  		} +		//if quit from menu, turn off the Keyboardmode for the menu. +		if(LLMenuGL::getKeyboardMode()) +			LLMenuGL::setKeyboardMode(FALSE); +  		// *TODO: get this to play well with mouselook and hidden  		// cursor modes, etc, and re-enable.  		//if (gFocusMgr.getMouseCapture()) diff --git a/indra/win_updater/updater.cpp b/indra/win_updater/updater.cpp index e25383bfef..f849e4e9ad 100644 --- a/indra/win_updater/updater.cpp +++ b/indra/win_updater/updater.cpp @@ -41,6 +41,7 @@  #define BUFSIZE 8192  int  gTotalBytesRead = 0; +DWORD gTotalBytes = -1;  HWND gWindow = NULL;  WCHAR gProgress[256];  char* gUpdateURL; @@ -129,6 +130,9 @@ int WINAPI get_url_into_file(WCHAR *uri, char *path, int *cancelled)  		return success;  	} +	DWORD sizeof_total_bytes = sizeof(gTotalBytes); +	HttpQueryInfo(hdownload, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &gTotalBytes, &sizeof_total_bytes, NULL); +	  	DWORD total_bytes = 0;  	success = InternetQueryDataAvailable(hdownload, &total_bytes, 0, 0);  	if (success == FALSE) @@ -187,7 +191,11 @@ int WINAPI get_url_into_file(WCHAR *uri, char *path, int *cancelled)  			gTotalBytesRead += int(bytes_read); -			wsprintf(gProgress, L"Downloaded: %dK", gTotalBytesRead / 1024); +			if (gTotalBytes != -1) +				wsprintf(gProgress, L"Downloaded: %d%%", 100 * gTotalBytesRead / gTotalBytes); +			else +				wsprintf(gProgress, L"Downloaded: %dK", gTotalBytesRead / 1024); +  		}  #if _DEBUG | 
