diff options
Diffstat (limited to 'linden')
7 files changed, 176 insertions, 156 deletions
diff --git a/linden/indra/llcommon/llstring.h b/linden/indra/llcommon/llstring.h index 7b08fd3..2d76eca 100644 --- a/linden/indra/llcommon/llstring.h +++ b/linden/indra/llcommon/llstring.h | |||
@@ -133,26 +133,32 @@ struct char_traits<U16> | |||
133 | class LLStringOps | 133 | class LLStringOps |
134 | { | 134 | { |
135 | public: | 135 | public: |
136 | static char toUpper(char elem) { return toupper(elem); } | 136 | static char toUpper(char elem) { return toupper((unsigned char)elem); } |
137 | static llwchar toUpper(llwchar elem) { return towupper(elem); } | 137 | static llwchar toUpper(llwchar elem) { return towupper(elem); } |
138 | 138 | ||
139 | static char toLower(char elem) { return tolower(elem); } | 139 | static char toLower(char elem) { return tolower((unsigned char)elem); } |
140 | static llwchar toLower(llwchar elem) { return towlower(elem); } | 140 | static llwchar toLower(llwchar elem) { return towlower(elem); } |
141 | 141 | ||
142 | static BOOL isSpace(char elem) { return isspace(elem) != 0; } | 142 | static bool isSpace(char elem) { return isspace((unsigned char)elem) != 0; } |
143 | static BOOL isSpace(llwchar elem) { return iswspace(elem) != 0; } | 143 | static bool isSpace(llwchar elem) { return iswspace(elem) != 0; } |
144 | 144 | ||
145 | static BOOL isUpper(char elem) { return isupper(elem) != 0; } | 145 | static bool isUpper(char elem) { return isupper((unsigned char)elem) != 0; } |
146 | static BOOL isUpper(llwchar elem) { return iswupper(elem) != 0; } | 146 | static bool isUpper(llwchar elem) { return iswupper(elem) != 0; } |
147 | 147 | ||
148 | static BOOL isLower(char elem) { return islower(elem) != 0; } | 148 | static bool isLower(char elem) { return islower((unsigned char)elem) != 0; } |
149 | static BOOL isLower(llwchar elem) { return iswlower(elem) != 0; } | 149 | static bool isLower(llwchar elem) { return iswlower(elem) != 0; } |
150 | |||
151 | static bool isDigit(char a) { return isdigit((unsigned char)a) != 0; } | ||
152 | static bool isDigit(llwchar a) { return iswdigit(a) != 0; } | ||
153 | |||
154 | static bool isPunct(char a) { return ispunct((unsigned char)a) != 0; } | ||
155 | static bool isPunct(llwchar a) { return iswpunct(a) != 0; } | ||
156 | |||
157 | static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; } | ||
158 | static bool isAlnum(llwchar a) { return iswalnum(a) != 0; } | ||
150 | 159 | ||
151 | static S32 collate(const char* a, const char* b) { return strcoll(a, b); } | 160 | static S32 collate(const char* a, const char* b) { return strcoll(a, b); } |
152 | static S32 collate(const llwchar* a, const llwchar* b); | 161 | static S32 collate(const llwchar* a, const llwchar* b); |
153 | |||
154 | static BOOL isDigit(char a) { return isdigit(a) != 0; } | ||
155 | static BOOL isDigit(llwchar a) { return iswdigit(a) != 0; } | ||
156 | }; | 162 | }; |
157 | 163 | ||
158 | /** | 164 | /** |
@@ -194,7 +200,7 @@ public: | |||
194 | typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t; | 200 | typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t; |
195 | static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map); | 201 | static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map); |
196 | 202 | ||
197 | static BOOL isValidIndex(const std::basic_string<T>& string, size_type i) | 203 | static bool isValidIndex(const std::basic_string<T>& string, size_type i) |
198 | { | 204 | { |
199 | return !string.empty() && (0 <= i) && (i <= string.size()); | 205 | return !string.empty() && (0 <= i) && (i <= string.size()); |
200 | } | 206 | } |
diff --git a/linden/indra/llui/lltexteditor.cpp b/linden/indra/llui/lltexteditor.cpp index 90199f1..72d8f17 100644 --- a/linden/indra/llui/lltexteditor.cpp +++ b/linden/indra/llui/lltexteditor.cpp | |||
@@ -260,7 +260,7 @@ LLTextEditor::LLTextEditor( | |||
260 | mIsSelecting( FALSE ), | 260 | mIsSelecting( FALSE ), |
261 | mSelectionStart( 0 ), | 261 | mSelectionStart( 0 ), |
262 | mSelectionEnd( 0 ), | 262 | mSelectionEnd( 0 ), |
263 | mScrolledToBottom( FALSE ), | 263 | mScrolledToBottom( TRUE ), |
264 | mOnScrollEndCallback( NULL ), | 264 | mOnScrollEndCallback( NULL ), |
265 | mOnScrollEndData( NULL ), | 265 | mOnScrollEndData( NULL ), |
266 | mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ), | 266 | mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ), |
@@ -277,14 +277,16 @@ LLTextEditor::LLTextEditor( | |||
277 | mCommitOnFocusLost( FALSE ), | 277 | mCommitOnFocusLost( FALSE ), |
278 | mHideScrollbarForShortDocs( FALSE ), | 278 | mHideScrollbarForShortDocs( FALSE ), |
279 | mTakesNonScrollClicks( TRUE ), | 279 | mTakesNonScrollClicks( TRUE ), |
280 | mTrackBottom( TRUE ), | 280 | mTrackBottom( FALSE ), |
281 | mAllowEmbeddedItems( allow_embedded_items ), | 281 | mAllowEmbeddedItems( allow_embedded_items ), |
282 | mAcceptCallingCardNames(FALSE), | 282 | mAcceptCallingCardNames(FALSE), |
283 | mHandleEditKeysDirectly( FALSE ), | 283 | mHandleEditKeysDirectly( FALSE ), |
284 | mMouseDownX(0), | 284 | mMouseDownX(0), |
285 | mMouseDownY(0), | 285 | mMouseDownY(0), |
286 | mLastSelectionX(-1), | 286 | mLastSelectionX(-1), |
287 | mLastSelectionY(-1) | 287 | mLastSelectionY(-1), |
288 | mReflowNeeded(FALSE), | ||
289 | mScrollNeeded(FALSE) | ||
288 | { | 290 | { |
289 | mSourceID.generate(); | 291 | mSourceID.generate(); |
290 | 292 | ||
@@ -468,6 +470,13 @@ void LLTextEditor::updateLineStartList(S32 startpos) | |||
468 | mScrollbar->setVisible(!short_doc); | 470 | mScrollbar->setVisible(!short_doc); |
469 | } | 471 | } |
470 | 472 | ||
473 | // if scrolled to bottom, stay at bottom | ||
474 | // unless user is editing text | ||
475 | // do this after updating page size | ||
476 | if (mScrolledToBottom && mTrackBottom && !hasFocus()) | ||
477 | { | ||
478 | endOfDoc(); | ||
479 | } | ||
471 | } | 480 | } |
472 | 481 | ||
473 | //////////////////////////////////////////////////////////// | 482 | //////////////////////////////////////////////////////////// |
@@ -511,8 +520,7 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str) | |||
511 | setCursorPos(0); | 520 | setCursorPos(0); |
512 | deselect(); | 521 | deselect(); |
513 | 522 | ||
514 | updateLineStartList(); | 523 | needsReflow(); |
515 | updateScrollFromCursor(); | ||
516 | 524 | ||
517 | resetDirty(); | 525 | resetDirty(); |
518 | } | 526 | } |
@@ -529,8 +537,7 @@ void LLTextEditor::setWText(const LLWString &wtext) | |||
529 | setCursorPos(0); | 537 | setCursorPos(0); |
530 | deselect(); | 538 | deselect(); |
531 | 539 | ||
532 | updateLineStartList(); | 540 | needsReflow(); |
533 | updateScrollFromCursor(); | ||
534 | 541 | ||
535 | resetDirty(); | 542 | resetDirty(); |
536 | } | 543 | } |
@@ -568,8 +575,7 @@ void LLTextEditor::setWordWrap(BOOL b) | |||
568 | setCursorPos(0); | 575 | setCursorPos(0); |
569 | deselect(); | 576 | deselect(); |
570 | 577 | ||
571 | updateLineStartList(); | 578 | needsReflow(); |
572 | updateScrollFromCursor(); | ||
573 | } | 579 | } |
574 | 580 | ||
575 | 581 | ||
@@ -734,6 +740,7 @@ S32 LLTextEditor::getLineStart( S32 line ) const | |||
734 | { | 740 | { |
735 | return 0; | 741 | return 0; |
736 | } | 742 | } |
743 | |||
737 | line = llclamp(line, 0, num_lines-1); | 744 | line = llclamp(line, 0, num_lines-1); |
738 | S32 segidx = mLineStartList[line].mSegment; | 745 | S32 segidx = mLineStartList[line].mSegment; |
739 | S32 segoffset = mLineStartList[line].mOffset; | 746 | S32 segoffset = mLineStartList[line].mOffset; |
@@ -781,14 +788,14 @@ void LLTextEditor::getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp | |||
781 | *offsetp = startpos - (*seg_iter)->getStart(); | 788 | *offsetp = startpos - (*seg_iter)->getStart(); |
782 | } | 789 | } |
783 | 790 | ||
784 | const LLTextSegment* LLTextEditor::getPreviousSegment() | 791 | const LLTextSegment* LLTextEditor::getPreviousSegment() const |
785 | { | 792 | { |
786 | // find segment index at character to left of cursor (or rightmost edge of selection) | 793 | // find segment index at character to left of cursor (or rightmost edge of selection) |
787 | S32 idx = llmax(0, getSegmentIdxAtOffset(mCursorPos) - 1); | 794 | S32 idx = llmax(0, getSegmentIdxAtOffset(mCursorPos) - 1); |
788 | return idx >= 0 ? mSegments[idx] : NULL; | 795 | return idx >= 0 ? mSegments[idx] : NULL; |
789 | } | 796 | } |
790 | 797 | ||
791 | void LLTextEditor::getSelectedSegments(std::vector<const LLTextSegment*>& segments) | 798 | void LLTextEditor::getSelectedSegments(std::vector<const LLTextSegment*>& segments) const |
792 | { | 799 | { |
793 | S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos; | 800 | S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos; |
794 | S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos; | 801 | S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos; |
@@ -875,13 +882,12 @@ void LLTextEditor::setCursor(S32 row, S32 column) | |||
875 | } | 882 | } |
876 | doc += column; | 883 | doc += column; |
877 | setCursorPos(doc - mWText.c_str()); | 884 | setCursorPos(doc - mWText.c_str()); |
878 | updateScrollFromCursor(); | ||
879 | } | 885 | } |
880 | 886 | ||
881 | void LLTextEditor::setCursorPos(S32 offset) | 887 | void LLTextEditor::setCursorPos(S32 offset) |
882 | { | 888 | { |
883 | mCursorPos = llclamp(offset, 0, (S32)getLength()); | 889 | mCursorPos = llclamp(offset, 0, (S32)getLength()); |
884 | updateScrollFromCursor(); | 890 | needsScroll(); |
885 | // reset desired x cursor position | 891 | // reset desired x cursor position |
886 | mDesiredXPixel = -1; | 892 | mDesiredXPixel = -1; |
887 | } | 893 | } |
@@ -925,7 +931,7 @@ BOOL LLTextEditor::selectionContainsLineBreaks() | |||
925 | if (hasSelection()) | 931 | if (hasSelection()) |
926 | { | 932 | { |
927 | S32 left = llmin(mSelectionStart, mSelectionEnd); | 933 | S32 left = llmin(mSelectionStart, mSelectionEnd); |
928 | S32 right = left + abs(mSelectionStart - mSelectionEnd); | 934 | S32 right = left + llabs(mSelectionStart - mSelectionEnd); |
929 | 935 | ||
930 | const LLWString &wtext = mWText; | 936 | const LLWString &wtext = mWText; |
931 | for( S32 i = left; i < right; i++ ) | 937 | for( S32 i = left; i < right; i++ ) |
@@ -981,7 +987,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces ) | |||
981 | { | 987 | { |
982 | const LLWString &text = mWText; | 988 | const LLWString &text = mWText; |
983 | S32 left = llmin( mSelectionStart, mSelectionEnd ); | 989 | S32 left = llmin( mSelectionStart, mSelectionEnd ); |
984 | S32 right = left + abs( mSelectionStart - mSelectionEnd ); | 990 | S32 right = left + llabs( mSelectionStart - mSelectionEnd ); |
985 | BOOL cursor_on_right = (mSelectionEnd > mSelectionStart); | 991 | BOOL cursor_on_right = (mSelectionEnd > mSelectionStart); |
986 | S32 cur = left; | 992 | S32 cur = left; |
987 | 993 | ||
@@ -1222,8 +1228,6 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) | |||
1222 | 1228 | ||
1223 | setCursorAtLocalPos( x, y, TRUE ); | 1229 | setCursorAtLocalPos( x, y, TRUE ); |
1224 | mSelectionEnd = mCursorPos; | 1230 | mSelectionEnd = mCursorPos; |
1225 | |||
1226 | updateScrollFromCursor(); | ||
1227 | } | 1231 | } |
1228 | 1232 | ||
1229 | lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl; | 1233 | lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl; |
@@ -1812,7 +1816,7 @@ void LLTextEditor::deleteSelection(BOOL group_with_next_op ) | |||
1812 | if( getEnabled() && hasSelection() ) | 1816 | if( getEnabled() && hasSelection() ) |
1813 | { | 1817 | { |
1814 | S32 pos = llmin( mSelectionStart, mSelectionEnd ); | 1818 | S32 pos = llmin( mSelectionStart, mSelectionEnd ); |
1815 | S32 length = abs( mSelectionStart - mSelectionEnd ); | 1819 | S32 length = llabs( mSelectionStart - mSelectionEnd ); |
1816 | 1820 | ||
1817 | remove( pos, length, group_with_next_op ); | 1821 | remove( pos, length, group_with_next_op ); |
1818 | 1822 | ||
@@ -1835,12 +1839,11 @@ void LLTextEditor::cut() | |||
1835 | return; | 1839 | return; |
1836 | } | 1840 | } |
1837 | S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); | 1841 | S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); |
1838 | S32 length = abs( mSelectionStart - mSelectionEnd ); | 1842 | S32 length = llabs( mSelectionStart - mSelectionEnd ); |
1839 | gClipboard.copyFromSubstring( mWText, left_pos, length, mSourceID ); | 1843 | gClipboard.copyFromSubstring( mWText, left_pos, length, mSourceID ); |
1840 | deleteSelection( FALSE ); | 1844 | deleteSelection( FALSE ); |
1841 | 1845 | ||
1842 | updateLineStartList(); | 1846 | needsReflow(); |
1843 | updateScrollFromCursor(); | ||
1844 | } | 1847 | } |
1845 | 1848 | ||
1846 | BOOL LLTextEditor::canCopy() const | 1849 | BOOL LLTextEditor::canCopy() const |
@@ -1856,7 +1859,7 @@ void LLTextEditor::copy() | |||
1856 | return; | 1859 | return; |
1857 | } | 1860 | } |
1858 | S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); | 1861 | S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); |
1859 | S32 length = abs( mSelectionStart - mSelectionEnd ); | 1862 | S32 length = llabs( mSelectionStart - mSelectionEnd ); |
1860 | gClipboard.copyFromSubstring(mWText, left_pos, length, mSourceID); | 1863 | gClipboard.copyFromSubstring(mWText, left_pos, length, mSourceID); |
1861 | } | 1864 | } |
1862 | 1865 | ||
@@ -1910,8 +1913,7 @@ void LLTextEditor::paste() | |||
1910 | setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE)); | 1913 | setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE)); |
1911 | deselect(); | 1914 | deselect(); |
1912 | 1915 | ||
1913 | updateLineStartList(); | 1916 | needsReflow(); |
1914 | updateScrollFromCursor(); | ||
1915 | } | 1917 | } |
1916 | 1918 | ||
1917 | 1919 | ||
@@ -2235,9 +2237,9 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) | |||
2235 | 2237 | ||
2236 | if(text_may_have_changed) | 2238 | if(text_may_have_changed) |
2237 | { | 2239 | { |
2238 | updateLineStartList(); | 2240 | needsReflow(); |
2239 | } | 2241 | } |
2240 | updateScrollFromCursor(); | 2242 | needsScroll(); |
2241 | } | 2243 | } |
2242 | } | 2244 | } |
2243 | 2245 | ||
@@ -2280,8 +2282,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) | |||
2280 | // Most keystrokes will make the selection box go away, but not all will. | 2282 | // Most keystrokes will make the selection box go away, but not all will. |
2281 | deselect(); | 2283 | deselect(); |
2282 | 2284 | ||
2283 | updateLineStartList(); | 2285 | needsReflow(); |
2284 | updateScrollFromCursor(); | ||
2285 | } | 2286 | } |
2286 | } | 2287 | } |
2287 | 2288 | ||
@@ -2339,8 +2340,7 @@ void LLTextEditor::doDelete() | |||
2339 | } | 2340 | } |
2340 | } | 2341 | } |
2341 | 2342 | ||
2342 | updateLineStartList(); | 2343 | needsReflow(); |
2343 | updateScrollFromCursor(); | ||
2344 | } | 2344 | } |
2345 | 2345 | ||
2346 | //---------------------------------------------------------------------------- | 2346 | //---------------------------------------------------------------------------- |
@@ -2383,8 +2383,7 @@ void LLTextEditor::undo() | |||
2383 | 2383 | ||
2384 | setCursorPos(pos); | 2384 | setCursorPos(pos); |
2385 | 2385 | ||
2386 | updateLineStartList(); | 2386 | needsReflow(); |
2387 | updateScrollFromCursor(); | ||
2388 | } | 2387 | } |
2389 | 2388 | ||
2390 | BOOL LLTextEditor::canRedo() const | 2389 | BOOL LLTextEditor::canRedo() const |
@@ -2426,8 +2425,7 @@ void LLTextEditor::redo() | |||
2426 | 2425 | ||
2427 | setCursorPos(pos); | 2426 | setCursorPos(pos); |
2428 | 2427 | ||
2429 | updateLineStartList(); | 2428 | needsReflow(); |
2430 | updateScrollFromCursor(); | ||
2431 | } | 2429 | } |
2432 | 2430 | ||
2433 | void LLTextEditor::onFocusReceived() | 2431 | void LLTextEditor::onFocusReceived() |
@@ -3100,6 +3098,20 @@ void LLTextEditor::drawClippedSegment(const LLWString &text, S32 seg_start, S32 | |||
3100 | 3098 | ||
3101 | void LLTextEditor::draw() | 3099 | void LLTextEditor::draw() |
3102 | { | 3100 | { |
3101 | // do on-demand reflow | ||
3102 | if (mReflowNeeded) | ||
3103 | { | ||
3104 | updateLineStartList(); | ||
3105 | mReflowNeeded = FALSE; | ||
3106 | } | ||
3107 | |||
3108 | // then update scroll position, as cursor may have moved | ||
3109 | if (mScrollNeeded) | ||
3110 | { | ||
3111 | updateScrollFromCursor(); | ||
3112 | mScrollNeeded = FALSE; | ||
3113 | } | ||
3114 | |||
3103 | { | 3115 | { |
3104 | LLLocalClipRect clip(LLRect(0, getRect().getHeight(), getRect().getWidth() - (mScrollbar->getVisible() ? SCROLLBAR_SIZE : 0), 0)); | 3116 | LLLocalClipRect clip(LLRect(0, getRect().getHeight(), getRect().getWidth() - (mScrollbar->getVisible() ? SCROLLBAR_SIZE : 0), 0)); |
3105 | 3117 | ||
@@ -3118,10 +3130,10 @@ void LLTextEditor::draw() | |||
3118 | mBorder->setKeyboardFocusHighlight( gFocusMgr.getKeyboardFocus() == this);// && !mReadOnly); | 3130 | mBorder->setKeyboardFocusHighlight( gFocusMgr.getKeyboardFocus() == this);// && !mReadOnly); |
3119 | } | 3131 | } |
3120 | 3132 | ||
3133 | LLView::draw(); // Draw children (scrollbar and border) | ||
3134 | |||
3121 | // remember if we are supposed to be at the bottom of the buffer | 3135 | // remember if we are supposed to be at the bottom of the buffer |
3122 | mScrolledToBottom = isScrolledToBottom(); | 3136 | mScrolledToBottom = isScrolledToBottom(); |
3123 | |||
3124 | LLView::draw(); // Draw children (scrollbar and border) | ||
3125 | } | 3137 | } |
3126 | 3138 | ||
3127 | 3139 | ||
@@ -3311,7 +3323,7 @@ void LLTextEditor::setCursorAndScrollToEnd() | |||
3311 | { | 3323 | { |
3312 | deselect(); | 3324 | deselect(); |
3313 | endOfDoc(); | 3325 | endOfDoc(); |
3314 | updateScrollFromCursor(); | 3326 | needsScroll(); |
3315 | } | 3327 | } |
3316 | 3328 | ||
3317 | void LLTextEditor::getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap ) | 3329 | void LLTextEditor::getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap ) |
@@ -3374,7 +3386,9 @@ void LLTextEditor::endOfLine() | |||
3374 | 3386 | ||
3375 | void LLTextEditor::endOfDoc() | 3387 | void LLTextEditor::endOfDoc() |
3376 | { | 3388 | { |
3377 | mScrollbar->setDocPos( mScrollbar->getDocPosMax() ); | 3389 | mScrollbar->setDocPos(mScrollbar->getDocPosMax()); |
3390 | mScrolledToBottom = true; | ||
3391 | |||
3378 | S32 len = getLength(); | 3392 | S32 len = getLength(); |
3379 | if( len ) | 3393 | if( len ) |
3380 | { | 3394 | { |
@@ -3438,7 +3452,7 @@ void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) | |||
3438 | // up-to-date mTextRect | 3452 | // up-to-date mTextRect |
3439 | updateTextRect(); | 3453 | updateTextRect(); |
3440 | 3454 | ||
3441 | updateLineStartList(); | 3455 | needsReflow(); |
3442 | 3456 | ||
3443 | // propagate shape information to scrollbar | 3457 | // propagate shape information to scrollbar |
3444 | mScrollbar->setDocSize( getLineCount() ); | 3458 | mScrollbar->setDocSize( getLineCount() ); |
@@ -3446,14 +3460,6 @@ void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) | |||
3446 | S32 line_height = llround( mGLFont->getLineHeight() ); | 3460 | S32 line_height = llround( mGLFont->getLineHeight() ); |
3447 | S32 page_lines = mTextRect.getHeight() / line_height; | 3461 | S32 page_lines = mTextRect.getHeight() / line_height; |
3448 | mScrollbar->setPageSize( page_lines ); | 3462 | mScrollbar->setPageSize( page_lines ); |
3449 | |||
3450 | // if scrolled to bottom, stay at bottom | ||
3451 | // unless user is editing text | ||
3452 | // do this after updating page size | ||
3453 | if (mScrolledToBottom && mTrackBottom && !hasFocus()) | ||
3454 | { | ||
3455 | endOfDoc(); | ||
3456 | } | ||
3457 | } | 3463 | } |
3458 | 3464 | ||
3459 | void LLTextEditor::autoIndent() | 3465 | void LLTextEditor::autoIndent() |
@@ -3500,8 +3506,7 @@ void LLTextEditor::insertText(const std::string &new_text) | |||
3500 | 3506 | ||
3501 | setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE )); | 3507 | setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE )); |
3502 | 3508 | ||
3503 | updateLineStartList(); | 3509 | needsReflow(); |
3504 | updateScrollFromCursor(); | ||
3505 | 3510 | ||
3506 | setEnabled( enabled ); | 3511 | setEnabled( enabled ); |
3507 | } | 3512 | } |
@@ -3600,7 +3605,7 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool | |||
3600 | mSegments.push_back(segment); | 3605 | mSegments.push_back(segment); |
3601 | } | 3606 | } |
3602 | 3607 | ||
3603 | updateLineStartList(old_length); | 3608 | needsReflow(); |
3604 | 3609 | ||
3605 | // Set the cursor and scroll position | 3610 | // Set the cursor and scroll position |
3606 | // Maintain the scroll position unless the scroll was at the end of the doc (in which | 3611 | // Maintain the scroll position unless the scroll was at the end of the doc (in which |
@@ -3639,14 +3644,6 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool | |||
3639 | { | 3644 | { |
3640 | blockUndo(); | 3645 | blockUndo(); |
3641 | } | 3646 | } |
3642 | |||
3643 | // if scrolled to bottom, stay at bottom | ||
3644 | // unless user is editing text | ||
3645 | // do this after updating page size | ||
3646 | if (mScrolledToBottom && mTrackBottom && !hasFocus()) | ||
3647 | { | ||
3648 | endOfDoc(); | ||
3649 | } | ||
3650 | } | 3647 | } |
3651 | 3648 | ||
3652 | void LLTextEditor::removeTextFromEnd(S32 num_chars) | 3649 | void LLTextEditor::removeTextFromEnd(S32 num_chars) |
@@ -3661,7 +3658,10 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars) | |||
3661 | mSelectionEnd = llclamp(mSelectionEnd, 0, len); | 3658 | mSelectionEnd = llclamp(mSelectionEnd, 0, len); |
3662 | 3659 | ||
3663 | pruneSegments(); | 3660 | pruneSegments(); |
3661 | |||
3662 | // pruneSegments will invalidate mLineStartList. | ||
3664 | updateLineStartList(); | 3663 | updateLineStartList(); |
3664 | needsScroll(); | ||
3665 | } | 3665 | } |
3666 | 3666 | ||
3667 | /////////////////////////////////////////////////////////////////// | 3667 | /////////////////////////////////////////////////////////////////// |
@@ -3759,8 +3759,7 @@ BOOL LLTextEditor::tryToRevertToPristineState() | |||
3759 | } | 3759 | } |
3760 | } | 3760 | } |
3761 | 3761 | ||
3762 | updateLineStartList(); | 3762 | needsReflow(); |
3763 | updateScrollFromCursor(); | ||
3764 | } | 3763 | } |
3765 | 3764 | ||
3766 | return isPristine(); // TRUE => success | 3765 | return isPristine(); // TRUE => success |
@@ -3808,6 +3807,7 @@ void LLTextEditor::updateSegments() | |||
3808 | { | 3807 | { |
3809 | findEmbeddedItemSegments(); | 3808 | findEmbeddedItemSegments(); |
3810 | } | 3809 | } |
3810 | |||
3811 | // Make sure we have at least one segment | 3811 | // Make sure we have at least one segment |
3812 | if (mSegments.size() == 1 && mSegments[0]->getIsDefault()) | 3812 | if (mSegments.size() == 1 && mSegments[0]->getIsDefault()) |
3813 | { | 3813 | { |
@@ -3824,6 +3824,7 @@ void LLTextEditor::updateSegments() | |||
3824 | } | 3824 | } |
3825 | 3825 | ||
3826 | // Only effective if text was removed from the end of the editor | 3826 | // Only effective if text was removed from the end of the editor |
3827 | // *NOTE: Using this will invalidate references to mSegments from mLineStartList. | ||
3827 | void LLTextEditor::pruneSegments() | 3828 | void LLTextEditor::pruneSegments() |
3828 | { | 3829 | { |
3829 | S32 len = mWText.length(); | 3830 | S32 len = mWText.length(); |
@@ -4066,9 +4067,7 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length ) | |||
4066 | setCursorPos(0); | 4067 | setCursorPos(0); |
4067 | deselect(); | 4068 | deselect(); |
4068 | 4069 | ||
4069 | updateLineStartList(); | 4070 | needsReflow(); |
4070 | updateScrollFromCursor(); | ||
4071 | |||
4072 | return success; | 4071 | return success; |
4073 | } | 4072 | } |
4074 | 4073 | ||
@@ -4496,9 +4495,8 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string, | |||
4496 | 4495 | ||
4497 | mPreeditStandouts = preedit_standouts; | 4496 | mPreeditStandouts = preedit_standouts; |
4498 | 4497 | ||
4499 | updateLineStartList(); | 4498 | needsReflow(); |
4500 | setCursorPos(insert_preedit_at + caret_position); | 4499 | setCursorPos(insert_preedit_at + caret_position); |
4501 | // updateScrollFromCursor(); | ||
4502 | 4500 | ||
4503 | // Update of the preedit should be caused by some key strokes. | 4501 | // Update of the preedit should be caused by some key strokes. |
4504 | mKeystrokeTimer.reset(); | 4502 | mKeystrokeTimer.reset(); |
diff --git a/linden/indra/llui/lltexteditor.h b/linden/indra/llui/lltexteditor.h index 3cff91d..c76395c 100644 --- a/linden/indra/llui/lltexteditor.h +++ b/linden/indra/llui/lltexteditor.h | |||
@@ -110,6 +110,7 @@ public: | |||
110 | virtual BOOL canUndo() const; | 110 | virtual BOOL canUndo() const; |
111 | virtual void redo(); | 111 | virtual void redo(); |
112 | virtual BOOL canRedo() const; | 112 | virtual BOOL canRedo() const; |
113 | |||
113 | virtual void cut(); | 114 | virtual void cut(); |
114 | virtual BOOL canCut() const; | 115 | virtual BOOL canCut() const; |
115 | virtual void copy(); | 116 | virtual void copy(); |
@@ -245,9 +246,11 @@ public: | |||
245 | llwchar getWChar(S32 pos) const { return mWText[pos]; } | 246 | llwchar getWChar(S32 pos) const { return mWText[pos]; } |
246 | LLWString getWSubString(S32 pos, S32 len) const { return mWText.substr(pos, len); } | 247 | LLWString getWSubString(S32 pos, S32 len) const { return mWText.substr(pos, len); } |
247 | 248 | ||
248 | const LLTextSegment* getCurrentSegment() { return getSegmentAtOffset(mCursorPos); } | 249 | const LLTextSegment* getCurrentSegment() const { return getSegmentAtOffset(mCursorPos); } |
249 | const LLTextSegment* getPreviousSegment(); | 250 | const LLTextSegment* getPreviousSegment() const; |
250 | void getSelectedSegments(std::vector<const LLTextSegment*>& segments); | 251 | void getSelectedSegments(std::vector<const LLTextSegment*>& segments) const; |
252 | |||
253 | static bool isPartOfWord(llwchar c) { return (c == '_') || LLStringOps::isAlnum((char)c); } | ||
251 | 254 | ||
252 | protected: | 255 | protected: |
253 | // | 256 | // |
@@ -266,8 +269,6 @@ protected: | |||
266 | void assignEmbedded(const std::string &s); | 269 | void assignEmbedded(const std::string &s); |
267 | BOOL truncate(); // Returns true if truncation occurs | 270 | BOOL truncate(); // Returns true if truncation occurs |
268 | 271 | ||
269 | static BOOL isPartOfWord(llwchar c) { return (c == '_') || isalnum(c); } | ||
270 | |||
271 | void removeCharOrTab(); | 272 | void removeCharOrTab(); |
272 | void setCursorAtLocalPos(S32 x, S32 y, BOOL round); | 273 | void setCursorAtLocalPos(S32 x, S32 y, BOOL round); |
273 | S32 getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const; | 274 | S32 getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const; |
@@ -433,6 +434,14 @@ private: | |||
433 | void drawText(); | 434 | void drawText(); |
434 | void drawClippedSegment(const LLWString &wtext, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyleSP& color, F32* right_x); | 435 | void drawClippedSegment(const LLWString &wtext, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyleSP& color, F32* right_x); |
435 | 436 | ||
437 | void needsReflow() | ||
438 | { | ||
439 | mReflowNeeded = TRUE; | ||
440 | // cursor might have moved, need to scroll | ||
441 | mScrollNeeded = TRUE; | ||
442 | } | ||
443 | void needsScroll() { mScrollNeeded = TRUE; } | ||
444 | |||
436 | // | 445 | // |
437 | // Data | 446 | // Data |
438 | // | 447 | // |
@@ -489,6 +498,8 @@ private: | |||
489 | }; | 498 | }; |
490 | typedef std::vector<line_info> line_list_t; | 499 | typedef std::vector<line_info> line_list_t; |
491 | line_list_t mLineStartList; | 500 | line_list_t mLineStartList; |
501 | BOOL mReflowNeeded; | ||
502 | BOOL mScrollNeeded; | ||
492 | 503 | ||
493 | LLFrameTimer mKeystrokeTimer; | 504 | LLFrameTimer mKeystrokeTimer; |
494 | 505 | ||
diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_chat_history.xml b/linden/indra/newview/skins/default/xui/en-us/floater_chat_history.xml index 6bd4424..a12cb2b 100644 --- a/linden/indra/newview/skins/default/xui/en-us/floater_chat_history.xml +++ b/linden/indra/newview/skins/default/xui/en-us/floater_chat_history.xml | |||
@@ -46,13 +46,15 @@ | |||
46 | bottom="28" embedded_items="false" enabled="false" | 46 | bottom="28" embedded_items="false" enabled="false" |
47 | follows="left|top|right|bottom" font="SansSerif" height="74" left="5" | 47 | follows="left|top|right|bottom" font="SansSerif" height="74" left="5" |
48 | max_length="2147483647" mouse_opaque="true" name="Chat History Editor" | 48 | max_length="2147483647" mouse_opaque="true" name="Chat History Editor" |
49 | track_bottom="true" | ||
49 | text_color="ChatHistoryTextColor" | 50 | text_color="ChatHistoryTextColor" |
50 | text_readonly_color="ChatHistoryTextColor" width="299" word_wrap="true" /> | 51 | text_readonly_color="ChatHistoryTextColor" width="299" word_wrap="true" /> |
51 | <text_editor type="string" length="1" bg_readonly_color="ChatHistoryBgColor" bg_writeable_color="ChatHistoryBgColor" | 52 | <text_editor type="string" length="1" bg_readonly_color="ChatHistoryBgColor" bg_writeable_color="ChatHistoryBgColor" |
52 | bottom="28" embedded_items="false" enabled="false" | 53 | bottom="28" embedded_items="false" enabled="false" |
53 | follows="left|top|right|bottom" font="SansSerif" height="74" left="5" | 54 | follows="left|top|right|bottom" font="SansSerif" height="74" left="5" |
54 | max_length="2147483647" mouse_opaque="true" | 55 | max_length="2147483647" mouse_opaque="true" |
55 | name="Chat History Editor with mute" text_color="ChatHistoryTextColor" | 56 | name="Chat History Editor with mute" text_color="ChatHistoryTextColor" |
57 | track_bottom="true" | ||
56 | text_readonly_color="ChatHistoryTextColor" width="300" word_wrap="true" /> | 58 | text_readonly_color="ChatHistoryTextColor" width="300" word_wrap="true" /> |
57 | <panel bottom="5" follows="left|right|bottom" left="5" name="chat_panel" right="-5" | 59 | <panel bottom="5" follows="left|right|bottom" left="5" name="chat_panel" right="-5" |
58 | tab_group="1" top="25"> | 60 | tab_group="1" top="25"> |
diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_instant_message.xml b/linden/indra/newview/skins/default/xui/en-us/floater_instant_message.xml index eb8e672..3b1c927 100644 --- a/linden/indra/newview/skins/default/xui/en-us/floater_instant_message.xml +++ b/linden/indra/newview/skins/default/xui/en-us/floater_instant_message.xml | |||
@@ -64,6 +64,7 @@ | |||
64 | follows="left|top|right|bottom" font="SansSerif" height="221" left="5" | 64 | follows="left|top|right|bottom" font="SansSerif" height="221" left="5" |
65 | max_length="2147483647" mouse_opaque="true" name="im_history" | 65 | max_length="2147483647" mouse_opaque="true" name="im_history" |
66 | text_color="ChatHistoryTextColor" | 66 | text_color="ChatHistoryTextColor" |
67 | track_bottom="true" | ||
67 | text_readonly_color="ChatHistoryTextColor" width="487" word_wrap="true" /> | 68 | text_readonly_color="ChatHistoryTextColor" width="487" word_wrap="true" /> |
68 | <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom="7" | 69 | <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom="7" |
69 | follows="left|right|bottom" font="SansSerif" height="20" | 70 | follows="left|right|bottom" font="SansSerif" height="20" |
diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_instant_message_ad_hoc.xml b/linden/indra/newview/skins/default/xui/en-us/floater_instant_message_ad_hoc.xml index eea285c..ddf344d 100644 --- a/linden/indra/newview/skins/default/xui/en-us/floater_instant_message_ad_hoc.xml +++ b/linden/indra/newview/skins/default/xui/en-us/floater_instant_message_ad_hoc.xml | |||
@@ -1,73 +1,74 @@ | |||
1 | <?xml version="1.0" encoding="utf-8" standalone="yes" ?> | 1 | <?xml version="1.0" encoding="utf-8" standalone="yes" ?> |
2 | <floater border="true" bottom="-298" can_close="true" can_drag_on_left="false" | 2 | <floater border="true" bottom="-298" can_close="true" can_drag_on_left="false" |
3 | can_minimize="false" can_resize="true" default_tab_group="1" enabled="true" | 3 | can_minimize="false" can_resize="true" default_tab_group="1" enabled="true" |
4 | follows="left|top|right|bottom" height="297" label="(unknown)" left="1" | 4 | follows="left|top|right|bottom" height="297" label="(unknown)" left="1" |
5 | min_height="225" min_width="265" mouse_opaque="true" name="im_floater" | 5 | min_height="225" min_width="265" mouse_opaque="true" name="im_floater" |
6 | rect_control="" title="(unknown)" width="501"> | 6 | rect_control="" title="(unknown)" width="501"> |
7 | <string name="ringing"> | 7 | <string name="ringing"> |
8 | Joining Voice Chat... | 8 | Joining Voice Chat... |
9 | </string> | 9 | </string> |
10 | <string name="connected"> | 10 | <string name="connected"> |
11 | Connected, click End Call to hang up | 11 | Connected, click End Call to hang up |
12 | </string> | 12 | </string> |
13 | <string name="hang_up"> | 13 | <string name="hang_up"> |
14 | Left Voice Chat | 14 | Left Voice Chat |
15 | </string> | 15 | </string> |
16 | <string name="voice_icon"> | 16 | <string name="voice_icon"> |
17 | icn_voice-groupfocus.tga | 17 | icn_voice-groupfocus.tga |
18 | </string> | 18 | </string> |
19 | <string name="title_string"> | 19 | <string name="title_string"> |
20 | Instant Message with [NAME] | 20 | Instant Message with [NAME] |
21 | </string> | 21 | </string> |
22 | <string name="typing_start_string"> | 22 | <string name="typing_start_string"> |
23 | [NAME] is typing... | 23 | [NAME] is typing... |
24 | </string> | 24 | </string> |
25 | <string name="session_start_string"> | 25 | <string name="session_start_string"> |
26 | Starting session with [NAME] please wait. | 26 | Starting session with [NAME] please wait. |
27 | </string> | 27 | </string> |
28 | <string name="default_text_label"> | 28 | <string name="default_text_label"> |
29 | Click here to instant message. | 29 | Click here to instant message. |
30 | </string> | 30 | </string> |
31 | <layout_stack border="false" bottom="0" follows="left|top|right|bottom" height="277" left="2" | 31 | <layout_stack border="false" bottom="0" follows="left|top|right|bottom" height="277" left="2" |
32 | orientation="horizontal" tab_group="1" width="495" name="panels"> | 32 | orientation="horizontal" tab_group="1" width="495" name="panels"> |
33 | <layout_panel border="false" bottom="0" default_tab_group="1" follows="left|top|bottom|right" | 33 | <layout_panel border="false" bottom="0" default_tab_group="1" follows="left|top|bottom|right" |
34 | height="295" left="0" min_width="115" name="im_contents_panel" width="495"> | 34 | height="295" left="0" min_width="115" name="im_contents_panel" width="495"> |
35 | <button bottom="-20" enabled="false" follows="left|top" halign="center" height="20" | 35 | <button bottom="-20" enabled="false" follows="left|top" halign="center" height="20" |
36 | image_overlay="icn_voice-call-start.tga" image_overlay_alignment="left" | 36 | image_overlay="icn_voice-call-start.tga" image_overlay_alignment="left" |
37 | label="Call" left="5" name="start_call_btn" width="80" /> | 37 | label="Call" left="5" name="start_call_btn" width="80" /> |
38 | <button bottom_delta="0" follows="left|top" halign="center" height="20" | 38 | <button bottom_delta="0" follows="left|top" halign="center" height="20" |
39 | image_overlay="icn_voice-call-end.tga" image_overlay_alignment="left" | 39 | image_overlay="icn_voice-call-end.tga" image_overlay_alignment="left" |
40 | label="End Call" left_delta="0" name="end_call_btn" visible="false" | 40 | label="End Call" left_delta="0" name="end_call_btn" visible="false" |
41 | width="80" /> | 41 | width="80" /> |
42 | <button bottom_delta="0" follows="right|top" height="20" label="< <" | 42 | <button bottom_delta="0" follows="right|top" height="20" label="< <" |
43 | label_selected="> >" left="463" name="toggle_active_speakers_btn" | 43 | label_selected="> >" left="463" name="toggle_active_speakers_btn" |
44 | right="496" | 44 | right="496" |
45 | tool_tip="Click here to toggle a list of active participants in this IM session." | 45 | tool_tip="Click here to toggle a list of active participants in this IM session." |
46 | visible="true" width="80" /> | 46 | visible="true" width="80" /> |
47 | <text_editor type="string" length="1" bg_readonly_color="ChatHistoryBgColor" bg_writeable_color="ChatHistoryBgColor" | 47 | <text_editor type="string" length="1" bg_readonly_color="ChatHistoryBgColor" bg_writeable_color="ChatHistoryBgColor" |
48 | bottom="-265" embedded_items="false" enabled="false" | 48 | bottom="-265" embedded_items="false" enabled="false" |
49 | follows="left|top|right|bottom" font="SansSerif" height="239" left="5" | 49 | follows="left|top|right|bottom" font="SansSerif" height="239" left="5" |
50 | max_length="2147483647" mouse_opaque="true" name="im_history" | 50 | max_length="2147483647" mouse_opaque="true" name="im_history" |
51 | text_color="ChatHistoryTextColor" | 51 | track_bottom="true" |
52 | text_readonly_color="ChatHistoryTextColor" width="490" word_wrap="true" /> | 52 | text_color="ChatHistoryTextColor" |
53 | <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom="7" | 53 | text_readonly_color="ChatHistoryTextColor" width="490" word_wrap="true" /> |
54 | enabled="true" follows="left|right|bottom" font="SansSerif" | 54 | <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom="7" |
55 | handle_edit_keys_directly="false" height="20" | 55 | enabled="true" follows="left|right|bottom" font="SansSerif" |
56 | label="Click here to instant message" left="5" max_length="1022" | 56 | handle_edit_keys_directly="false" height="20" |
57 | mouse_opaque="true" name="chat_editor" select_all_on_focus_received="false" | 57 | label="Click here to instant message" left="5" max_length="1022" |
58 | select_on_focus="false" tab_group="1" width="426" /> | 58 | mouse_opaque="true" name="chat_editor" select_all_on_focus_received="false" |
59 | <button bottom_delta="0" enabled="true" follows="right|bottom" font="SansSerif" | 59 | select_on_focus="false" tab_group="1" width="426" /> |
60 | halign="center" height="20" label="Send" left="436" mouse_opaque="true" | 60 | <button bottom_delta="0" enabled="true" follows="right|bottom" font="SansSerif" |
61 | name="send_btn" scale_image="true" width="60" /> | 61 | halign="center" height="20" label="Send" left="436" mouse_opaque="true" |
62 | </layout_panel> | 62 | name="send_btn" scale_image="true" width="60" /> |
63 | <layout_panel auto_resize="false" bottom="0" can_resize="true" | 63 | </layout_panel> |
64 | filename="panel_speaker_controls.xml" height="120" left="0" min_width="140" | 64 | <layout_panel auto_resize="false" bottom="0" can_resize="true" |
65 | name="active_speakers_panel" top_delta="0" visible="false" width="140" /> | 65 | filename="panel_speaker_controls.xml" height="120" left="0" min_width="140" |
66 | </layout_stack> | 66 | name="active_speakers_panel" top_delta="0" visible="false" width="140" /> |
67 | <string name="live_help_dialog"> | 67 | </layout_stack> |
68 | *** Welcome to Help Request *** | 68 | <string name="live_help_dialog"> |
69 | Please first check our SL Help Pages by pressing F1, or by accessing the Knowledge Base http://secondlife.com/knowledgebase/ | 69 | *** Welcome to Help Request *** |
70 | If your answer is not there, please enter your question to begin, then allow a few moments for available helpers to respond. | 70 | Please first check our SL Help Pages by pressing F1, or by accessing the Knowledge Base http://secondlife.com/knowledgebase/ |
71 | -=-=- Response times will vary, especially during peak times -=-=- | 71 | If your answer is not there, please enter your question to begin, then allow a few moments for available helpers to respond. |
72 | </string> | 72 | -=-=- Response times will vary, especially during peak times -=-=- |
73 | </floater> | 73 | </string> |
74 | </floater> | ||
diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_instant_message_group.xml b/linden/indra/newview/skins/default/xui/en-us/floater_instant_message_group.xml index 3a6e4ec..f59017e 100644 --- a/linden/indra/newview/skins/default/xui/en-us/floater_instant_message_group.xml +++ b/linden/indra/newview/skins/default/xui/en-us/floater_instant_message_group.xml | |||
@@ -64,6 +64,7 @@ | |||
64 | follows="left|top|right|bottom" font="SansSerif" left="4" | 64 | follows="left|top|right|bottom" font="SansSerif" left="4" |
65 | max_length="2147483647" mouse_opaque="true" name="im_history" | 65 | max_length="2147483647" mouse_opaque="true" name="im_history" |
66 | text_color="ChatHistoryTextColor" | 66 | text_color="ChatHistoryTextColor" |
67 | track_bottom="true" | ||
67 | text_readonly_color="ChatHistoryTextColor" top="104" width="170" | 68 | text_readonly_color="ChatHistoryTextColor" top="104" width="170" |
68 | word_wrap="true" /> | 69 | word_wrap="true" /> |
69 | <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom="7" | 70 | <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom="7" |