summaryrefslogtreecommitdiff
path: root/indra/llui/lltexteditor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/lltexteditor.cpp')
-rwxr-xr-x[-rw-r--r--]indra/llui/lltexteditor.cpp499
1 files changed, 287 insertions, 212 deletions
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 5a46c7c98e..926326aaff 100644..100755
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -54,6 +54,7 @@
#include "llwindow.h"
#include "lltextparser.h"
#include "llscrollcontainer.h"
+#include "llspellcheck.h"
#include "llpanel.h"
#include "llurlregistry.h"
#include "lltooltip.h"
@@ -74,9 +75,8 @@ template class LLTextEditor* LLView::getChild<class LLTextEditor>(
//
// Constants
//
-const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32;
-const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4;
const S32 SPACES_PER_TAB = 4;
+const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on
///////////////////////////////////////////////////////////////////
@@ -234,21 +234,23 @@ LLTextEditor::Params::Params()
prevalidate_callback("prevalidate_callback"),
embedded_items("embedded_items", false),
ignore_tab("ignore_tab", true),
- show_line_numbers("show_line_numbers", false),
+ auto_indent("auto_indent", true),
default_color("default_color"),
commit_on_focus_lost("commit_on_focus_lost", false),
- show_context_menu("show_context_menu")
+ show_context_menu("show_context_menu"),
+ enable_tooltip_paste("enable_tooltip_paste")
{
addSynonym(prevalidate_callback, "text_type");
}
LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
LLTextBase(p),
+ mAutoreplaceCallback(),
mBaseDocIsPristine(TRUE),
mPristineCmd( NULL ),
mLastCmd( NULL ),
- mDefaultColor( p.default_color() ),
- mShowLineNumbers ( p.show_line_numbers ),
+ mDefaultColor( p.default_color() ),
+ mAutoIndent(p.auto_indent),
mCommitOnFocusLost( p.commit_on_focus_lost),
mAllowEmbeddedItems( p.embedded_items ),
mMouseDownX(0),
@@ -256,7 +258,10 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
mTabsToNextField(p.ignore_tab),
mPrevalidateFunc(p.prevalidate_callback()),
mContextMenu(NULL),
- mShowContextMenu(p.show_context_menu)
+ mShowContextMenu(p.show_context_menu),
+ mEnableTooltipPaste(p.enable_tooltip_paste),
+ mPassDelete(FALSE),
+ mKeepSelectionOnReturn(false)
{
mSourceID.generate();
@@ -269,14 +274,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
params.visible = p.border_visible;
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
addChild( mBorder );
-
setText(p.default_text());
-
- if (mShowLineNumbers)
- {
- mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
- updateRects();
- }
mParseOnTheFly = TRUE;
}
@@ -302,7 +300,7 @@ LLTextEditor::~LLTextEditor()
// Scrollbar is deleted by LLView
std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
-
+ mUndoStack.clear();
// context menu is owned by menu holder, not us
//delete mContextMenu;
}
@@ -592,6 +590,10 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
}
+ // Disabling parsing on the fly to avoid updating text segments
+ // until all indentation commands are executed.
+ mParseOnTheFly = FALSE;
+
// Find each start-of-line and indent it
do
{
@@ -617,6 +619,8 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
while( cur < right );
+ mParseOnTheFly = TRUE;
+
if( (right < getLength()) && (text[right] == '\n') )
{
right++;
@@ -652,6 +656,14 @@ void LLTextEditor::selectAll()
updatePrimary();
}
+void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos)
+{
+ setCursorPos(prev_cursor_pos);
+ startSelection();
+ setCursorPos(next_cursor_pos);
+ endSelection();
+}
+
BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
@@ -699,7 +711,6 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
setCursorAtLocalPos( x, y, true );
startSelection();
}
- gFocusMgr.setMouseCapture( this );
}
handled = TRUE;
@@ -708,6 +719,10 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
// Delay cursor flashing
resetCursorBlink();
+ if (handled && !gFocusMgr.getMouseCapture())
+ {
+ gFocusMgr.setMouseCapture( this );
+ }
return handled;
}
@@ -767,7 +782,7 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
setCursorAtLocalPos( clamped_x, clamped_y, true );
mSelectionEnd = mCursorPos;
}
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
getWindow()->setCursor(UI_CURSOR_IBEAM);
handled = TRUE;
}
@@ -799,7 +814,7 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
BOOL handled = FALSE;
// if I'm not currently selecting text
- if (!(hasSelection() && hasMouseCapture()))
+ if (!(mIsSelecting && hasMouseCapture()))
{
// let text segments handle mouse event
handled = LLTextBase::handleMouseUp(x, y, mask);
@@ -942,12 +957,18 @@ S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op
S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)
{
S32 end_pos = getEditableIndex(pos + length, true);
+ BOOL removedChar = FALSE;
segment_vec_t segments_to_remove;
// store text segments
getSegmentsInRange(segments_to_remove, pos, pos + length, false);
+
+ if(pos <= end_pos)
+ {
+ removedChar = execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
+ }
- return execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
+ return removedChar;
}
S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
@@ -1086,8 +1107,26 @@ void LLTextEditor::addChar(llwchar wc)
}
setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
+
+ if (!mReadOnly && mAutoreplaceCallback != NULL)
+ {
+ // autoreplace the text, if necessary
+ S32 replacement_start;
+ S32 replacement_length;
+ LLWString replacement_string;
+ S32 new_cursor_pos = mCursorPos;
+ mAutoreplaceCallback(replacement_start, replacement_length, replacement_string, new_cursor_pos, getWText());
+
+ if (replacement_length > 0 || !replacement_string.empty())
+ {
+ remove(replacement_start, replacement_length, true);
+ insert(replacement_start, replacement_string, false, LLTextSegmentPtr());
+ setCursorPos(new_cursor_pos);
+ }
+ }
}
-void LLTextEditor::addLineBreakChar()
+
+void LLTextEditor::addLineBreakChar(BOOL group_together)
{
if( !getEnabled() )
{
@@ -1105,7 +1144,7 @@ void LLTextEditor::addLineBreakChar()
LLStyleConstSP sp(new LLStyle(LLStyle::Params()));
LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos);
- S32 pos = execute(new TextCmdAddChar(mCursorPos, FALSE, '\n', segment));
+ S32 pos = execute(new TextCmdAddChar(mCursorPos, group_together, '\n', segment));
setCursorPos(mCursorPos + pos);
}
@@ -1326,7 +1365,7 @@ void LLTextEditor::cut()
}
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID );
+ LLClipboard::instance().copyToClipboard( getWText(), left_pos, length);
deleteSelection( FALSE );
onKeyStroke();
@@ -1346,12 +1385,12 @@ void LLTextEditor::copy()
}
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromSubstring(getWText(), left_pos, length, mSourceID);
+ LLClipboard::instance().copyToClipboard(getWText(), left_pos, length);
}
BOOL LLTextEditor::canPaste() const
{
- return !mReadOnly && gClipboard.canPasteString();
+ return !mReadOnly && LLClipboard::instance().isTextAvailable();
}
// paste from clipboard
@@ -1387,16 +1426,8 @@ void LLTextEditor::pasteHelper(bool is_primary)
return;
}
- LLUUID source_id;
LLWString paste;
- if (is_primary)
- {
- paste = gClipboard.getPastePrimaryWString(&source_id);
- }
- else
- {
- paste = gClipboard.getPasteWString(&source_id);
- }
+ LLClipboard::instance().pasteFromClipboard(paste, is_primary);
if (paste.empty())
{
@@ -1411,6 +1442,23 @@ void LLTextEditor::pasteHelper(bool is_primary)
// Clean up string (replace tabs and remove characters that our fonts don't support).
LLWString clean_string(paste);
+ cleanStringForPaste(clean_string);
+
+ // Insert the new text into the existing text.
+
+ //paste text with linebreaks.
+ pasteTextWithLinebreaks(clean_string);
+
+ deselect();
+
+ onKeyStroke();
+ mParseOnTheFly = TRUE;
+}
+
+
+// Clean up string (replace tabs and remove characters that our fonts don't support).
+void LLTextEditor::cleanStringForPaste(LLWString & clean_string)
+{
LLWStringUtil::replaceTabsWithSpaces(clean_string, SPACES_PER_TAB);
if( mAllowEmbeddedItems )
{
@@ -1429,37 +1477,38 @@ void LLTextEditor::pasteHelper(bool is_primary)
}
}
}
+}
- // Insert the new text into the existing text.
- //paste text with linebreaks.
+void LLTextEditor::pasteTextWithLinebreaks(LLWString & clean_string)
+{
std::basic_string<llwchar>::size_type start = 0;
std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start);
- while(pos!=-1)
+ while((pos != -1) && (pos != clean_string.length() -1))
{
if(pos!=start)
{
std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,pos-start);
- setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr()));
+ setCursorPos(mCursorPos + insert(mCursorPos, str, TRUE, LLTextSegmentPtr()));
}
- addLineBreakChar();
-
+ addLineBreakChar(TRUE); // Add a line break and group with the next addition.
+
start = pos+1;
pos = clean_string.find('\n',start);
}
- std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,clean_string.length()-start);
- setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr()));
-
- deselect();
-
- onKeyStroke();
- mParseOnTheFly = TRUE;
+ if (pos != start)
+ {
+ std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,clean_string.length()-start);
+ setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr()));
+ }
+ else
+ {
+ addLineBreakChar(FALSE); // Add a line break and end the grouping.
+ }
}
-
-
// copy selection to primary
void LLTextEditor::copyPrimary()
{
@@ -1469,12 +1518,12 @@ void LLTextEditor::copyPrimary()
}
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromPrimarySubstring(getWText(), left_pos, length, mSourceID);
+ LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true);
}
BOOL LLTextEditor::canPastePrimary() const
{
- return !mReadOnly && gClipboard.canPastePrimaryString();
+ return !mReadOnly && LLClipboard::instance().isTextAvailable(true);
}
void LLTextEditor::updatePrimary()
@@ -1560,7 +1609,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
}
}
- if (handled)
+ if (handled && !gFocusMgr.getMouseCapture())
{
updatePrimary();
}
@@ -1604,11 +1653,14 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)
case KEY_RETURN:
if (mask == MASK_NONE)
{
- if( hasSelection() )
+ if( hasSelection() && !mKeepSelectionOnReturn )
{
deleteSelection(FALSE);
}
- autoIndent(); // TODO: make this optional
+ if (mAutoIndent)
+ {
+ autoIndent();
+ }
}
else
{
@@ -1680,19 +1732,50 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
{
return FALSE;
}
-
+
if (mReadOnly && mScroller)
{
handled = (mScroller && mScroller->handleKeyHere( key, mask ))
|| handleSelectionKey(key, mask)
|| handleControlKey(key, mask);
+ }
+ else
+ {
+ if (mEnableTooltipPaste &&
+ LLToolTipMgr::instance().toolTipVisible() &&
+ KEY_TAB == key)
+ { // Paste the first line of a tooltip into the editor
+ std::string message;
+ LLToolTipMgr::instance().getToolTipMessage(message);
+ LLWString tool_tip_text(utf8str_to_wstring(message));
+
+ if (tool_tip_text.size() > 0)
+ {
+ // Delete any selected characters (the tooltip text replaces them)
+ if(hasSelection())
+ {
+ deleteSelection(TRUE);
+ }
+
+ std::basic_string<llwchar>::size_type pos = tool_tip_text.find('\n',0);
+ if (pos != -1)
+ { // Extract the first line of the tooltip
+ tool_tip_text = std::basic_string<llwchar>(tool_tip_text, 0, pos);
+ }
+
+ // Add the text
+ cleanStringForPaste(tool_tip_text);
+ pasteTextWithLinebreaks(tool_tip_text);
+ handled = TRUE;
+ }
+ }
+ else
+ { // Normal key handling
+ handled = handleNavigationKey( key, mask )
+ || handleSelectionKey(key, mask)
+ || handleControlKey(key, mask)
+ || handleSpecialKey(key, mask);
}
- else
- {
- handled = handleNavigationKey( key, mask )
- || handleSelectionKey(key, mask)
- || handleControlKey(key, mask)
- || handleSpecialKey(key, mask);
}
if( handled )
@@ -1748,7 +1831,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
// virtual
BOOL LLTextEditor::canDoDelete() const
{
- return !mReadOnly && ( hasSelection() || (mCursorPos < getLength()) );
+ return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) );
}
void LLTextEditor::doDelete()
@@ -1889,8 +1972,7 @@ void LLTextEditor::onFocusReceived()
updateAllowingLanguageInput();
}
-// virtual, from LLView
-void LLTextEditor::onFocusLost()
+void LLTextEditor::focusLostHelper()
{
updateAllowingLanguageInput();
@@ -1907,7 +1989,11 @@ void LLTextEditor::onFocusLost()
// Make sure cursor is shown again
getWindow()->showCursorFromMouseMove();
+}
+void LLTextEditor::onFocusLost()
+{
+ focusLostHelper();
LLTextBase::onFocusLost();
}
@@ -1934,6 +2020,7 @@ void LLTextEditor::showContextMenu(S32 x, S32 y)
{
if (!mContextMenu)
{
+ llassert(LLMenuGL::sMenuContainer != NULL);
mContextMenu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_text_editor.xml",
LLMenuGL::sMenuContainer,
LLMenuHolderGL::child_registry_t::instance());
@@ -1955,7 +2042,38 @@ void LLTextEditor::showContextMenu(S32 x, S32 y)
S32 screen_x, screen_y;
localPointToScreen(x, y, &screen_x, &screen_y);
- mContextMenu->show(screen_x, screen_y);
+
+ setCursorAtLocalPos(x, y, false);
+ if (hasSelection())
+ {
+ if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) )
+ {
+ deselect();
+ }
+ else
+ {
+ setCursorPos(llmax(mSelectionStart, mSelectionEnd));
+ }
+ }
+
+ bool use_spellcheck = getSpellCheck(), is_misspelled = false;
+ if (use_spellcheck)
+ {
+ mSuggestionList.clear();
+
+ // If the cursor is on a misspelled word, retrieve suggestions for it
+ std::string misspelled_word = getMisspelledWord(mCursorPos);
+ if ((is_misspelled = !misspelled_word.empty()) == true)
+ {
+ LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList);
+ }
+ }
+
+ mContextMenu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty()));
+ mContextMenu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled));
+ mContextMenu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled));
+ mContextMenu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled));
+ mContextMenu->show(screen_x, screen_y, this);
}
@@ -1986,7 +2104,7 @@ void LLTextEditor::drawPreeditMarker()
return;
}
- const S32 line_height = llround( mDefaultFont->getLineHeight() );
+ const S32 line_height = mFont->getLineHeight();
S32 line_start = getLineStart(cur_line);
S32 line_y = mVisibleTextRect.mTop - line_height;
@@ -2022,36 +2140,41 @@ void LLTextEditor::drawPreeditMarker()
continue;
}
- S32 preedit_left = mVisibleTextRect.mLeft;
+ line_info& line = mLineInfoList[cur_line];
+ LLRect text_rect(line.mRect);
+ text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents
+ text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position
+
+ S32 preedit_left = text_rect.mLeft;
if (left > line_start)
{
- preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start);
+ preedit_left += mFont->getWidth(text, line_start, left - line_start);
}
- S32 preedit_right = mVisibleTextRect.mLeft;
+ S32 preedit_right = text_rect.mLeft;
if (right < line_end)
{
- preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start);
+ preedit_right += mFont->getWidth(text, line_start, right - line_start);
}
else
{
- preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start);
+ preedit_right += mFont->getWidth(text, line_start, line_end - line_start);
}
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.get() * preedit_standout_brightness + mWriteableBgColor.get() * (1 - preedit_standout_brightness)).setAlpha(1.0f));
+ text_rect.mBottom + mFont->getDescenderHeight() - 1,
+ preedit_right - preedit_standout_gap - 1,
+ text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_standout_thickness,
+ (mCursorColor.get() * preedit_standout_brightness + mWriteableBgColor.get() * (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.get() * preedit_marker_brightness + mWriteableBgColor.get() * (1 - preedit_marker_brightness)).setAlpha(1.0f));
+ text_rect.mBottom + mFont->getDescenderHeight() - 1,
+ preedit_right - preedit_marker_gap - 1,
+ text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_marker_thickness,
+ (mCursorColor.get() * preedit_marker_brightness + mWriteableBgColor.get() * (1 - preedit_marker_brightness)).setAlpha(1.0f));
}
}
}
@@ -2063,69 +2186,6 @@ void LLTextEditor::drawPreeditMarker()
}
}
-
-void LLTextEditor::drawLineNumbers()
-{
- LLGLSUIDefault gls_ui;
- LLRect scrolled_view_rect = getVisibleDocumentRect();
- LLRect content_rect = getVisibleTextRect();
- LLLocalClipRect clip(content_rect);
- S32 first_line = getFirstVisibleLine();
- S32 num_lines = getLineCount();
- if (first_line >= num_lines)
- {
- return;
- }
-
- S32 cursor_line = mLineInfoList[getLineNumFromDocIndex(mCursorPos)].mLineNum;
-
- if (mShowLineNumbers)
- {
- S32 left = 0;
- S32 top = getRect().getHeight();
- S32 bottom = 0;
-
- gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only
- gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator
-
- S32 last_line_num = -1;
-
- for (S32 cur_line = first_line; cur_line < num_lines; cur_line++)
- {
- line_info& line = mLineInfoList[cur_line];
-
- if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mVisibleTextRect.mBottom)
- {
- break;
- }
-
- S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom;
- // draw the line numbers
- if(line.mLineNum != last_line_num && line.mRect.mTop <= scrolled_view_rect.mTop)
- {
- const LLFontGL *num_font = LLFontGL::getFontMonospace();
- const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum ));
- BOOL is_cur_line = cursor_line == line.mLineNum;
- const U8 style = is_cur_line ? LLFontGL::BOLD : LLFontGL::NORMAL;
- const LLColor4 fg_color = is_cur_line ? mCursorColor : mReadOnlyFgColor;
- num_font->render(
- ltext, // string to draw
- 0, // begin offset
- UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2, // x
- line_bottom, // y
- fg_color,
- LLFontGL::RIGHT, // horizontal alignment
- LLFontGL::BOTTOM, // vertical alignment
- style,
- LLFontGL::NO_SHADOW,
- S32_MAX, // max chars
- UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2); // max pixels
- last_line_num = line.mLineNum;
- }
- }
- }
-}
-
void LLTextEditor::draw()
{
{
@@ -2134,11 +2194,11 @@ void LLTextEditor::draw()
LLRect clip_rect(mVisibleTextRect);
clip_rect.stretch(1);
LLLocalClipRect clip(clip_rect);
- drawPreeditMarker();
}
LLTextBase::draw();
- drawLineNumbers();
+
+ drawPreeditMarker();
//RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret
// when in readonly mode
@@ -2204,7 +2264,8 @@ void LLTextEditor::autoIndent()
S32 i;
LLWString text = getWText();
- while( ' ' == text[line_start] )
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
+ while(( ' ' == text[line_start] ) && (space_count < offset))
{
space_count++;
line_start++;
@@ -2244,6 +2305,22 @@ void LLTextEditor::insertText(const std::string &new_text)
setEnabled( enabled );
}
+void LLTextEditor::insertText(LLWString &new_text)
+{
+ BOOL enabled = getEnabled();
+ setEnabled( TRUE );
+
+ // Delete any selected characters (the insertion replaces them)
+ if( hasSelection() )
+ {
+ deleteSelection(TRUE);
+ }
+
+ setCursorPos(mCursorPos + insert( mCursorPos, new_text, FALSE, LLTextSegmentPtr() ));
+
+ setEnabled( enabled );
+}
+
void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo)
{
// Save old state
@@ -2361,54 +2438,6 @@ BOOL LLTextEditor::tryToRevertToPristineState()
return isPristine(); // TRUE => success
}
-
-static LLFastTimer::DeclareTimer FTM_SYNTAX_HIGHLIGHTING("Syntax Highlighting");
-void LLTextEditor::loadKeywords(const std::string& filename,
- const std::vector<std::string>& funcs,
- const std::vector<std::string>& tooltips,
- const LLColor3& color)
-{
- LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
- if(mKeywords.loadFromFile(filename))
- {
- S32 count = llmin(funcs.size(), tooltips.size());
- for(S32 i = 0; i < count; i++)
- {
- std::string name = utf8str_trim(funcs[i]);
- mKeywords.addToken(LLKeywordToken::WORD, name, color, tooltips[i] );
- }
- segment_vec_t segment_list;
- mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
-
- mSegments.clear();
- segment_set_t::iterator insert_it = mSegments.begin();
- for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it)
- {
- insert_it = mSegments.insert(insert_it, *list_it);
- }
- }
-}
-
-void LLTextEditor::updateSegments()
-{
- if (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly)
- {
- LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
- // HACK: No non-ascii keywords for now
- segment_vec_t segment_list;
- mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
-
- clearSegments();
- segment_set_t::iterator insert_it = mSegments.begin();
- for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it)
- {
- insertSegment(*list_it);
- }
- }
-
- LLTextBase::updateSegments();
-}
-
void LLTextEditor::updateLinkSegments()
{
LLWString wtext = getWText();
@@ -2419,12 +2448,30 @@ void LLTextEditor::updateLinkSegments()
LLTextSegment *segment = *it;
if (segment && segment->getStyle() && segment->getStyle()->isLink())
{
- // if the link's label (what the user can edit) is a valid Url,
- // then update the link's HREF to be the same as the label text.
- // This lets users edit Urls in-place.
LLStyleConstSP style = segment->getStyle();
LLStyleSP new_style(new LLStyle(*style));
LLWString url_label = wtext.substr(segment->getStart(), segment->getEnd()-segment->getStart());
+
+ segment_set_t::const_iterator next_it = mSegments.upper_bound(segment);
+ LLTextSegment *next_segment = *next_it;
+ if (next_segment)
+ {
+ LLWString next_url_label = wtext.substr(next_segment->getStart(), next_segment->getEnd()-next_segment->getStart());
+ std::string link_check = wstring_to_utf8str(url_label) + wstring_to_utf8str(next_url_label);
+ LLUrlMatch match;
+
+ if ( LLUrlRegistry::instance().findUrl(link_check, match))
+ {
+ if(match.getQuery() == wstring_to_utf8str(next_url_label))
+ {
+ continue;
+ }
+ }
+ }
+
+ // if the link's label (what the user can edit) is a valid Url,
+ // then update the link's HREF to be the same as the label text.
+ // This lets users edit Urls in-place.
if (LLUrlRegistry::instance().hasUrl(url_label))
{
std::string new_url = wstring_to_utf8str(url_label);
@@ -2465,20 +2512,20 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
instream.getline(tbuf, MAX_STRING);
if( 1 != sscanf(tbuf, "Linden text version %d", &version) )
{
- llwarns << "Invalid Linden text file header " << llendl;
+ LL_WARNS() << "Invalid Linden text file header " << LL_ENDL;
return FALSE;
}
if( 1 != version )
{
- llwarns << "Invalid Linden text file version: " << version << llendl;
+ LL_WARNS() << "Invalid Linden text file version: " << version << LL_ENDL;
return FALSE;
}
instream.getline(tbuf, MAX_STRING);
if( 0 != sscanf(tbuf, "{") )
{
- llwarns << "Invalid Linden text file format" << llendl;
+ LL_WARNS() << "Invalid Linden text file format" << LL_ENDL;
return FALSE;
}
@@ -2486,13 +2533,13 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
instream.getline(tbuf, MAX_STRING);
if( 1 != sscanf(tbuf, "Text length %d", &text_len) )
{
- llwarns << "Invalid Linden text length field" << llendl;
+ LL_WARNS() << "Invalid Linden text length field" << LL_ENDL;
return FALSE;
}
if( text_len > mMaxTextByteLength )
{
- llwarns << "Invalid Linden text length: " << text_len << llendl;
+ LL_WARNS() << "Invalid Linden text length: " << text_len << LL_ENDL;
return FALSE;
}
@@ -2501,21 +2548,21 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
char* text = new char[ text_len + 1];
if (text == NULL)
{
- llerrs << "Memory allocation failure." << llendl;
+ LL_ERRS() << "Memory allocation failure." << LL_ENDL;
return FALSE;
}
instream.get(text, text_len + 1, '\0');
text[text_len] = '\0';
if( text_len != (S32)strlen(text) )/* Flawfinder: ignore */
{
- llwarns << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << llendl;/* Flawfinder: ignore */
+ LL_WARNS() << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << LL_ENDL;/* Flawfinder: ignore */
success = FALSE;
}
instream.getline(tbuf, MAX_STRING);
if( success && (0 != sscanf(tbuf, "}")) )
{
- llwarns << "Invalid Linden text file format: missing terminal }" << llendl;
+ LL_WARNS() << "Invalid Linden text file format: missing terminal }" << LL_ENDL;
success = FALSE;
}
@@ -2574,11 +2621,23 @@ BOOL LLTextEditor::hasPreeditString() const
void LLTextEditor::resetPreedit()
{
+ if (hasSelection())
+ {
+ if (hasPreeditString())
+ {
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
+ deselect();
+ }
+ else
+ {
+ deleteSelection(TRUE);
+ }
+ }
if (hasPreeditString())
{
if (hasSelection())
{
- llwarns << "Preedit and selection!" << llendl;
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
deselect();
}
@@ -2629,7 +2688,10 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string,
{
mPreeditOverwrittenWString.clear();
}
- insertStringNoUndo(insert_preedit_at, mPreeditWString);
+
+ segment_vec_t segments;
+ //pass empty segments to let "insertStringNoUndo" make new LLNormalTextSegment and insert it, if needed.
+ insertStringNoUndo(insert_preedit_at, mPreeditWString, &segments);
mPreeditStandouts = preedit_standouts;
@@ -2693,11 +2755,11 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
const LLWString textString(getWText());
const llwchar * const text = textString.c_str();
- const S32 line_height = llround(mDefaultFont->getLineHeight());
+ const S32 line_height = mFont->getLineHeight();
if (coord)
{
- const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start);
+ const S32 query_x = mVisibleTextRect.mLeft + mFont->getWidth(text, current_line_start, query - current_line_start);
const S32 query_y = mVisibleTextRect.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);
@@ -2709,17 +2771,17 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
S32 preedit_left = mVisibleTextRect.mLeft;
if (preedit_left_position > current_line_start)
{
- preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start);
+ preedit_left += mFont->getWidth(text, current_line_start, preedit_left_position - current_line_start);
}
S32 preedit_right = mVisibleTextRect.mLeft;
if (preedit_right_position < current_line_end)
{
- preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
+ preedit_right += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
}
else
{
- preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start);
+ preedit_right += mFont->getWidth(text, current_line_start, current_line_end - current_line_start);
}
const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height;
@@ -2768,7 +2830,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)
setCursorPos(position);
if (hasPreeditString())
{
- llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl;
+ LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL;
}
mPreeditWString = LLWString( getWText(), position, length );
if (length > 0)
@@ -2796,7 +2858,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)
S32 LLTextEditor::getPreeditFontSize() const
{
- return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
+ return ll_round((F32)mFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]);
}
BOOL LLTextEditor::isDirty() const
@@ -2824,6 +2886,9 @@ void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& cal
void LLTextEditor::onKeyStroke()
{
mKeystrokeSignal(this);
+
+ mSpellCheckStart = mSpellCheckEnd = -1;
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
//virtual
@@ -2832,3 +2897,13 @@ void LLTextEditor::clear()
getViewModel()->setDisplay(LLWStringUtil::null);
clearSegments();
}
+
+bool LLTextEditor::canLoadOrSaveToFile()
+{
+ return !mReadOnly;
+}
+
+S32 LLTextEditor::spacesPerTab()
+{
+ return SPACES_PER_TAB;
+}