summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDon Kjer <don@lindenlab.com>2007-12-05 23:43:56 +0000
committerDon Kjer <don@lindenlab.com>2007-12-05 23:43:56 +0000
commitfacf67ae3226105910c983a8fa8760414bf703e9 (patch)
treeb5f7cd6b79a79f769080a65b6fe2cb6b97c8b6fb
parent45057e8881c3166c7c0ef545c02bc177922af6fb (diff)
EFFECTIVE MERGE: svn merge -r 71520:73420 svn+ssh://svn/svn/linden/branches/maintenance-3 into release
ACTUAL MERGE: svn merge -r 75074:75114 svn+ssh://svn/svn/linden/qa/maintenance-3-merge-75067 into release
-rw-r--r--doc/contributions.txt7
-rw-r--r--indra/lib/python/indra/util/llmanifest.py1
-rw-r--r--indra/llcommon/llstring.cpp78
-rw-r--r--indra/llcommon/llstring.h9
-rw-r--r--indra/llmath/llvolume.cpp8
-rw-r--r--indra/llmessage/lltransfermanager.cpp81
-rw-r--r--indra/llprimitive/llprimitive.cpp126
-rw-r--r--indra/llprimitive/llprimitive.h12
-rw-r--r--indra/llui/lllineeditor.cpp347
-rw-r--r--indra/llui/lllineeditor.h36
-rw-r--r--indra/llui/llmenugl.cpp4
-rw-r--r--indra/llui/lltextbox.cpp2
-rw-r--r--indra/llui/lltexteditor.cpp437
-rw-r--r--indra/llui/lltexteditor.h38
-rw-r--r--indra/llui/llui.cpp28
-rw-r--r--indra/llui/llui.h4
-rw-r--r--indra/llwindow/llpreeditor.h81
-rw-r--r--indra/llwindow/llwindow.h6
-rw-r--r--indra/llwindow/llwindowmacosx.cpp382
-rw-r--r--indra/llwindow/llwindowmacosx.h7
-rw-r--r--indra/llwindow/llwindowwin32.cpp640
-rw-r--r--indra/llwindow/llwindowwin32.h16
-rw-r--r--indra/mac_updater/mac_updater.cpp21
-rw-r--r--indra/newview/gpu_table.txt3
-rw-r--r--indra/newview/llappviewer.cpp5
-rw-r--r--indra/newview/llfeaturemanager.cpp6
-rw-r--r--indra/newview/llfloaterland.cpp9
-rw-r--r--indra/newview/llfloaterland.h2
-rw-r--r--indra/newview/llfloatersellland.cpp9
-rw-r--r--indra/newview/llfloaterworldmap.cpp68
-rw-r--r--indra/newview/llfloaterworldmap.h23
-rw-r--r--indra/newview/llfolderview.cpp10
-rw-r--r--indra/newview/llgroupmgr.cpp6
-rw-r--r--indra/newview/llinventorybridge.cpp22
-rw-r--r--indra/newview/llmaniptranslate.cpp15
-rw-r--r--indra/newview/llmutelist.cpp17
-rw-r--r--indra/newview/llpanelavatar.cpp6
-rw-r--r--indra/newview/llpanelclassified.cpp20
-rw-r--r--indra/newview/llpreview.cpp39
-rw-r--r--indra/newview/llpreview.h13
-rw-r--r--indra/newview/llpreviewanim.h3
-rw-r--r--indra/newview/llpreviewgesture.h2
-rw-r--r--indra/newview/llpreviewnotecard.cpp6
-rw-r--r--indra/newview/llpreviewnotecard.h5
-rw-r--r--indra/newview/llpreviewscript.h4
-rw-r--r--indra/newview/llpreviewsound.h3
-rw-r--r--indra/newview/llpreviewtexture.cpp2
-rw-r--r--indra/newview/llpreviewtexture.h2
-rw-r--r--indra/newview/llselectmgr.cpp18
-rw-r--r--indra/newview/llstartup.cpp14
-rw-r--r--indra/newview/llstatusbar.cpp2
-rw-r--r--indra/newview/lltooldraganddrop.cpp12
-rw-r--r--indra/newview/lltracker.cpp4
-rw-r--r--indra/newview/lltracker.h3
-rw-r--r--indra/newview/llviewerinventory.cpp9
-rw-r--r--indra/newview/llviewermenu.cpp29
-rw-r--r--indra/newview/llviewermessage.cpp5
-rw-r--r--indra/newview/llviewerparcelmgr.cpp9
-rw-r--r--indra/newview/llviewerwindow.cpp4
-rw-r--r--indra/win_updater/updater.cpp10
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, &param_type, sizeof(fix_len), NULL, &fix_len)) == noErr
+ && typeLongInteger == param_type
+ && (result = GetEventParameter(event, kEventParamTextInputSendText,
+ typeUnicodeText, &param_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, &param_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,
+ &param_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,
+ &param_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, &param_type, sizeof(range), NULL, &range)) == noErr
+ && typeCFRange == param_type
+ && (result = GetEventParameter(event, kEventParamTSMDocAccessSendCharactersPtr,
+ typePtr, &param_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