Spell check added
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
#include "llui.h"
|
||||
#include "lluictrlfactory.h"
|
||||
#include "llclipboard.h"
|
||||
#include "../newview/lgghunspell_wrapper.h"
|
||||
|
||||
//
|
||||
// Imported globals
|
||||
@@ -118,6 +119,7 @@ LLLineEditor::LLLineEditor(const std::string& name, const LLRect& rect,
|
||||
mLastSelectionY(-1),
|
||||
mLastSelectionStart(-1),
|
||||
mLastSelectionEnd(-1),
|
||||
mLastContextMenuX(-1),
|
||||
mPrevalidateFunc( prevalidate_func ),
|
||||
mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ),
|
||||
mFgColor( LLUI::sColorsGroup->getColor( "TextFgColor" ) ),
|
||||
@@ -137,7 +139,8 @@ LLLineEditor::LLLineEditor(const std::string& name, const LLRect& rect,
|
||||
mReadOnly(FALSE),
|
||||
mHaveHistory(FALSE),
|
||||
mImage( sImage ),
|
||||
mReplaceNewlinesWithSpaces( TRUE )
|
||||
mReplaceNewlinesWithSpaces( TRUE ),
|
||||
mSpellCheckable( FALSE )
|
||||
{
|
||||
llassert( max_length_bytes > 0 );
|
||||
|
||||
@@ -187,10 +190,7 @@ LLLineEditor::LLLineEditor(const std::string& name, const LLRect& rect,
|
||||
menu->append(new LLMenuItemCallGL("Paste", context_paste, NULL, this));
|
||||
menu->append(new LLMenuItemCallGL("Delete", context_delete, NULL, this));
|
||||
menu->append(new LLMenuItemCallGL("Select All", context_selectall, NULL, this));
|
||||
menu->appendSeparator("Transep");
|
||||
LLMenuGL* translatemenu = new LLMenuGL("Translate To");
|
||||
translatemenu->setCanTearOff(FALSE);
|
||||
|
||||
menu->appendSeparator("Spelsep");
|
||||
//menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor"));
|
||||
menu->setCanTearOff(FALSE);
|
||||
menu->setVisible(FALSE);
|
||||
@@ -454,6 +454,89 @@ void LLLineEditor::context_copy(void* data)
|
||||
if(line)line->copy();
|
||||
}
|
||||
|
||||
|
||||
void LLLineEditor::spell_correct(void* data)
|
||||
{
|
||||
SpellMenuBind* tempBind = (SpellMenuBind*)data;
|
||||
LLLineEditor* line = tempBind->origin;
|
||||
if(tempBind && line)
|
||||
{
|
||||
llinfos << ((LLMenuItemCallGL *)(tempBind->menuItem))->getName() << " : " << tempBind->origin->getName() << " : " << tempBind->word << llendl;
|
||||
if(line)line->spellReplace(tempBind);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLLineEditor::spell_show(void * data)
|
||||
{
|
||||
SpellMenuBind* tempBind = (SpellMenuBind*)data;
|
||||
LLLineEditor* line = tempBind->origin;
|
||||
|
||||
if (tempBind && line)
|
||||
{
|
||||
BOOL show = (tempBind->word == "Show Misspellings");
|
||||
glggHunSpell->setSpellCheckHighlight(show);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<S32> LLLineEditor::getMisspelledWordsPositions()
|
||||
{
|
||||
std::vector<S32> thePosesOfBadWords;
|
||||
const LLWString& text = mText.getWString();
|
||||
|
||||
//llinfos << "end of box is at " << cursorloc << " and end of text is at " << text.length() << llendl;
|
||||
S32 wordStart=0;
|
||||
S32 wordEnd=mStartSpellHere;
|
||||
while(wordEnd < mEndSpellHere)
|
||||
{
|
||||
//go through all the chars... XD
|
||||
if( LLTextEditor::isPartOfWord( text[wordEnd] ) )
|
||||
|
||||
{
|
||||
// Select word the cursor is over
|
||||
while ((wordEnd > 0) && LLTextEditor::isPartOfWord(text[wordEnd-1]))
|
||||
{
|
||||
wordEnd--;
|
||||
}
|
||||
wordStart=wordEnd;
|
||||
while ((wordEnd < (S32)text.length()) && LLTextEditor::isPartOfWord( text[wordEnd] ) )
|
||||
{
|
||||
wordEnd++;
|
||||
}
|
||||
//got a word :D
|
||||
std::string selectedWord(std::string(text.begin(),
|
||||
text.end()).substr(wordStart,wordEnd-wordStart));
|
||||
|
||||
if(!glggHunSpell->isSpelledRight(selectedWord))
|
||||
{
|
||||
//misspelled word here, and you have just right clicked on it!
|
||||
//get the center of this word..
|
||||
//S32 center = llround( (wordEnd-wordStart)/2 ) + wordStart;
|
||||
//turn this cursor position into a pixel pos
|
||||
//center = findPixelNearestPos(center-getCursor());
|
||||
|
||||
thePosesOfBadWords.push_back(
|
||||
wordStart);
|
||||
thePosesOfBadWords.push_back(wordEnd);
|
||||
}
|
||||
}
|
||||
wordEnd++;
|
||||
}
|
||||
return thePosesOfBadWords;
|
||||
}
|
||||
|
||||
|
||||
void LLLineEditor::spell_add(void* data)
|
||||
{
|
||||
SpellMenuBind* tempBind = (SpellMenuBind*)data;
|
||||
if(tempBind)
|
||||
{
|
||||
glggHunSpell->addWordToCustomDictionary(tempBind->word);
|
||||
tempBind->origin->mPrevSpelledText="";//make it update
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLLineEditor::context_paste(void* data)
|
||||
{
|
||||
LLLineEditor* line = (LLLineEditor*)data;
|
||||
@@ -568,6 +651,10 @@ BOOL LLLineEditor::handleRightMouseDown( S32 x, S32 y, MASK mask )
|
||||
setFocus(TRUE);
|
||||
|
||||
//setCursorAtLocalPos( x);
|
||||
S32 wordStart = 0;
|
||||
S32 wordLen = 0;
|
||||
S32 pos = calculateCursorFromMouse(x);
|
||||
|
||||
LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
|
||||
if (menu)
|
||||
{
|
||||
@@ -575,7 +662,81 @@ BOOL LLLineEditor::handleRightMouseDown( S32 x, S32 y, MASK mask )
|
||||
{
|
||||
menu->setVisible(FALSE);
|
||||
}
|
||||
for (int i = 0;i<(int)suggestionMenuItems.size();i++)
|
||||
{
|
||||
SpellMenuBind * tempBind = suggestionMenuItems[i];
|
||||
if(tempBind)
|
||||
{
|
||||
menu->remove((LLMenuItemCallGL *)tempBind->menuItem);
|
||||
((LLMenuItemCallGL *)tempBind->menuItem)->die();
|
||||
//delete tempBind->menuItem;
|
||||
//tempBind->menuItem = NULL;
|
||||
delete tempBind;
|
||||
}
|
||||
}
|
||||
suggestionMenuItems.clear();
|
||||
|
||||
// spell_check="true" in xui
|
||||
menu->setItemVisible("Spelsep", !mReadOnly && mSpellCheckable);
|
||||
if (!mReadOnly && mSpellCheckable)
|
||||
{
|
||||
// search for word matches
|
||||
bool is_word_part = getWordBoundriesAt(pos, &wordStart, &wordLen);
|
||||
if (is_word_part)
|
||||
{
|
||||
const LLWString& text = mText.getWString();
|
||||
std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart,wordLen));
|
||||
|
||||
if (!glggHunSpell->isSpelledRight(selectedWord))
|
||||
{
|
||||
//misspelled word here, and you have just right clicked on it!
|
||||
std::vector<std::string> suggs = glggHunSpell->getSuggestionList(selectedWord);
|
||||
|
||||
for (int i = 0; i<(int)suggs.size() ;i++)
|
||||
{
|
||||
SpellMenuBind * tempStruct = new SpellMenuBind;
|
||||
tempStruct->origin = this;
|
||||
tempStruct->word = suggs[i];
|
||||
tempStruct->wordPositionEnd = wordStart + wordLen;
|
||||
tempStruct->wordPositionStart=wordStart;
|
||||
LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL(
|
||||
tempStruct->word, spell_correct, NULL, tempStruct);
|
||||
//new LLMenuItemCallGL("Select All", context_selectall, NULL, this));
|
||||
tempStruct->menuItem = suggMenuItem;
|
||||
suggestionMenuItems.push_back(tempStruct);
|
||||
menu->append(suggMenuItem);
|
||||
}
|
||||
SpellMenuBind * tempStruct = new SpellMenuBind;
|
||||
tempStruct->origin = this;
|
||||
tempStruct->word = selectedWord;
|
||||
tempStruct->wordPositionEnd = wordStart + wordLen;
|
||||
tempStruct->wordPositionStart=wordStart;
|
||||
LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL(
|
||||
"Add Word", spell_add, NULL, tempStruct);
|
||||
tempStruct->menuItem = suggMenuItem;
|
||||
suggestionMenuItems.push_back(tempStruct);
|
||||
menu->append(suggMenuItem);
|
||||
}
|
||||
}
|
||||
|
||||
SpellMenuBind * tempStruct = new SpellMenuBind;
|
||||
tempStruct->origin = this;
|
||||
if (glggHunSpell->getSpellCheckHighlight())
|
||||
{
|
||||
tempStruct->word = "Hide Misspellings";
|
||||
}
|
||||
else
|
||||
{
|
||||
tempStruct->word = "Show Misspellings";
|
||||
}
|
||||
LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL(
|
||||
tempStruct->word, spell_show, NULL, tempStruct);
|
||||
tempStruct->menuItem = suggMenuItem;
|
||||
suggestionMenuItems.push_back(tempStruct);
|
||||
menu->append(suggMenuItem);
|
||||
}
|
||||
|
||||
mLastContextMenuX = x;
|
||||
menu->buildDrawLabels();
|
||||
menu->updateParent(LLMenuGL::sMenuContainer);
|
||||
LLMenuGL::showPopup(this, menu, x, y);
|
||||
@@ -892,6 +1053,26 @@ S32 LLLineEditor::nextWordPos(S32 cursorPos) const
|
||||
return cursorPos;
|
||||
}
|
||||
|
||||
BOOL LLLineEditor::getWordBoundriesAt(const S32 at, S32* word_begin, S32* word_length) const
|
||||
{
|
||||
const LLWString& wtext = mText.getWString();
|
||||
S32 pos = at;
|
||||
if (LLTextEditor::isPartOfWord(wtext[pos]))
|
||||
{
|
||||
while ( (pos > 0) && LLTextEditor::isPartOfWord(wtext[pos - 1]) )
|
||||
{
|
||||
pos--;
|
||||
}
|
||||
*word_begin = pos;
|
||||
while ( (pos < (S32)wtext.length()) && LLTextEditor::isPartOfWord(wtext[pos]) )
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
*word_length = pos - *word_begin;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)
|
||||
{
|
||||
@@ -1046,6 +1227,16 @@ void LLLineEditor::copy()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLLineEditor::spellReplace(SpellMenuBind* spellData)
|
||||
{
|
||||
mText.erase(spellData->wordPositionStart,
|
||||
spellData->wordPositionEnd - spellData->wordPositionStart);
|
||||
insert(spellData->word,spellData->wordPositionStart);
|
||||
mCursorPos+=spellData->word.length() - (spellData->wordPositionEnd-spellData->wordPositionStart);
|
||||
}
|
||||
|
||||
|
||||
void LLLineEditor::insert(std::string what, S32 wher)
|
||||
{
|
||||
LLLineEditorRollback rollback(this);
|
||||
@@ -1063,6 +1254,7 @@ void LLLineEditor::insert(std::string what, S32 wher)
|
||||
else if( mKeystrokeCallback )
|
||||
mKeystrokeCallback( this, mCallbackUserData );
|
||||
}
|
||||
|
||||
BOOL LLLineEditor::canPaste() const
|
||||
{
|
||||
return !mReadOnly && gClipboard.canPasteString();
|
||||
@@ -1588,6 +1780,57 @@ void LLLineEditor::doDelete()
|
||||
}
|
||||
|
||||
|
||||
void LLLineEditor::drawMisspelled(LLRect background)
|
||||
{
|
||||
if (!mReadOnly && mSpellCheckable)
|
||||
{
|
||||
S32 newStartSpellHere =mScrollHPos;
|
||||
S32 cursorloc =calculateCursorFromMouse(mMaxHPixels);
|
||||
S32 newStopSpellHere = ( ((S32)mText.length())>cursorloc)?cursorloc:(S32)mText.length();
|
||||
|
||||
F32 elapsed = mSpellTimer.getElapsedTimeF32();
|
||||
if(S32(elapsed / 1) & 1)
|
||||
{
|
||||
if(isSpellDirty()||(newStartSpellHere!=mStartSpellHere)||(newStopSpellHere!=mEndSpellHere))
|
||||
{
|
||||
mStartSpellHere=newStartSpellHere;
|
||||
mEndSpellHere= newStopSpellHere;
|
||||
resetSpellDirty();
|
||||
misspellLocations=getMisspelledWordsPositions();
|
||||
}
|
||||
}
|
||||
|
||||
if (glggHunSpell->getSpellCheckHighlight())
|
||||
{
|
||||
for (int i =0; i<(int)misspellLocations.size(); i++)
|
||||
{
|
||||
S32 wstart =findPixelNearestPos( misspellLocations[i]-getCursor());
|
||||
S32 wend = findPixelNearestPos(misspellLocations[++i]-getCursor());
|
||||
S32 maxw = getRect().getWidth();
|
||||
|
||||
if (wend > maxw)
|
||||
{
|
||||
wend = maxw;
|
||||
}
|
||||
if (wstart > maxw)
|
||||
{
|
||||
wstart = maxw;
|
||||
}
|
||||
gGL.color4ub(255,0,0,200);
|
||||
//3 line zig zags..
|
||||
while (wstart < wend)
|
||||
{
|
||||
gl_line_2d(wstart, background.mBottom-1, wstart+3, background.mBottom+2);
|
||||
gl_line_2d(wstart+3, background.mBottom+2, wstart+6, background.mBottom-1);
|
||||
wstart+=6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void LLLineEditor::draw()
|
||||
{
|
||||
S32 text_len = mText.length();
|
||||
@@ -1776,6 +2019,9 @@ void LLLineEditor::draw()
|
||||
mBorder->setVisible(FALSE); // no more programmatic art.
|
||||
#endif
|
||||
|
||||
drawMisspelled(background);
|
||||
resetSpellDirty();
|
||||
|
||||
// If we're editing...
|
||||
if( gFocusMgr.getKeyboardFocus() == this)
|
||||
{
|
||||
@@ -2455,6 +2701,11 @@ LLView* LLLineEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory
|
||||
{
|
||||
line_editor->setCommitOnFocusLost(commit_on_focus_lost);
|
||||
}
|
||||
BOOL spell_checking = FALSE;
|
||||
if (node->getAttributeBOOL("spell_check", spell_checking))
|
||||
{
|
||||
line_editor->setSpellCheckable(spell_checking);
|
||||
}
|
||||
|
||||
line_editor->setColorParameters(node);
|
||||
|
||||
|
||||
@@ -96,6 +96,16 @@ public:
|
||||
/*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char);
|
||||
/*virtual*/ void onMouseCaptureLost();
|
||||
|
||||
struct SpellMenuBind
|
||||
{
|
||||
LLLineEditor* origin;
|
||||
void * menuItem;
|
||||
std::string word;
|
||||
S32 wordPositionStart;
|
||||
S32 wordPositionEnd;
|
||||
};
|
||||
|
||||
virtual void spellReplace(SpellMenuBind* spellData);
|
||||
virtual void insert(std::string what,S32 wher);
|
||||
|
||||
// LLEditMenuHandler overrides
|
||||
@@ -127,8 +137,14 @@ public:
|
||||
static void context_paste(void* data);
|
||||
static void context_delete(void* data);
|
||||
static void context_selectall(void* data);
|
||||
static void spell_correct(void* data);
|
||||
static void spell_show(void* data);
|
||||
static void spell_add(void* data);
|
||||
|
||||
std::vector<S32> getMisspelledWordsPositions();
|
||||
// view overrides
|
||||
virtual void draw();
|
||||
void drawMisspelled(LLRect background);
|
||||
virtual void reshape(S32 width,S32 height,BOOL called_from_parent=TRUE);
|
||||
virtual void onFocusReceived();
|
||||
virtual void onFocusLost();
|
||||
@@ -143,6 +159,8 @@ public:
|
||||
virtual void onCommit();
|
||||
virtual BOOL isDirty() const { return mText.getString() != mPrevText; } // Returns TRUE if user changed value at all
|
||||
virtual void resetDirty() { mPrevText = mText.getString(); } // Clear dirty state
|
||||
virtual BOOL isSpellDirty() const { return mText.getString() != mPrevSpelledText; } // Returns TRUE if user changed value at all
|
||||
virtual void resetSpellDirty() { mPrevSpelledText = mText.getString(); } // Clear dirty state
|
||||
|
||||
// assumes UTF8 text
|
||||
virtual void setValue(const LLSD& value ) { setText(value.asString()); }
|
||||
@@ -178,6 +196,7 @@ public:
|
||||
void setWriteableBgColor( const LLColor4& c ) { mWriteableBgColor = c; }
|
||||
void setReadOnlyBgColor( const LLColor4& c ) { mReadOnlyBgColor = c; }
|
||||
void setFocusBgColor(const LLColor4& c) { mFocusBgColor = c; }
|
||||
void setSpellCheckable(BOOL b) { mSpellCheckable = b; }
|
||||
|
||||
const LLColor4& getFgColor() const { return mFgColor; }
|
||||
const LLColor4& getReadOnlyFgColor() const { return mReadOnlyFgColor; }
|
||||
@@ -194,6 +213,7 @@ public:
|
||||
// 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 getWordBoundriesAt(const S32 at, S32* word_begin, S32* word_length) const;
|
||||
|
||||
BOOL hasSelection() const { return (mSelectionStart != mSelectionEnd); }
|
||||
void startSelection();
|
||||
@@ -268,6 +288,14 @@ protected:
|
||||
LLUIString mText; // The string being edited.
|
||||
std::string mPrevText; // Saved string for 'ESC' revert
|
||||
LLUIString mLabel; // text label that is visible when no user text provided
|
||||
std::string mPrevSpelledText; // saved string so we know whether to respell or not
|
||||
std::vector<S32> misspellLocations; // where all the misspelled words are
|
||||
S32 mStartSpellHere; // the position of the first char on the screen, stored so we know when to update
|
||||
S32 mEndSpellHere; // the location of the last char on the screen
|
||||
BOOL mSpellCheckable; // set in xui as "spell_check". Default value for a field
|
||||
LLFrameTimer mSpellTimer;
|
||||
std::vector<SpellMenuBind* > suggestionMenuItems;
|
||||
S32 mLastContextMenuX;
|
||||
|
||||
// line history support:
|
||||
BOOL mHaveHistory; // flag for enabled line history
|
||||
|
||||
@@ -2525,6 +2525,11 @@ BOOL LLMenuGL::handleJumpKey(KEY key)
|
||||
// Add the menu item to this menu.
|
||||
BOOL LLMenuGL::append( LLMenuItemGL* item )
|
||||
{
|
||||
if (mSpilloverMenu)
|
||||
{
|
||||
return mSpilloverMenu->append(item);
|
||||
}
|
||||
|
||||
mItems.push_back( item );
|
||||
addChild( item );
|
||||
arrange();
|
||||
@@ -2572,6 +2577,31 @@ BOOL LLMenuGL::appendMenu( LLMenuGL* menu )
|
||||
return success;
|
||||
}
|
||||
|
||||
// Remove a menu item from this menu.
|
||||
BOOL LLMenuGL::remove( LLMenuItemGL* item )
|
||||
{
|
||||
if (mSpilloverMenu)
|
||||
{
|
||||
cleanupSpilloverBranch();
|
||||
}
|
||||
|
||||
item_list_t::iterator found_iter = std::find(mItems.begin(), mItems.end(), item);
|
||||
if (found_iter != mItems.end())
|
||||
{
|
||||
mItems.erase(found_iter);
|
||||
}
|
||||
|
||||
removeChild( item );
|
||||
|
||||
// We keep it around in case someone is pointing at it.
|
||||
// The caller can delete it if it's safe.
|
||||
// Note that getMenu() will still not work since its parent isn't a menu.
|
||||
sMenuContainer->addChild( item );
|
||||
|
||||
arrange();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void LLMenuGL::setEnabledSubMenus(BOOL enable)
|
||||
{
|
||||
setEnabled(enable);
|
||||
@@ -2829,6 +2859,11 @@ void LLMenuGL::updateParent(LLView* parentp)
|
||||
{
|
||||
(*item_iter)->updateBranchParent(parentp);
|
||||
}
|
||||
|
||||
if (mSpilloverMenu)
|
||||
{
|
||||
mSpilloverMenu->updateParent(parentp);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask)
|
||||
|
||||
@@ -442,6 +442,9 @@ public:
|
||||
// Add the menu item to this menu.
|
||||
virtual BOOL append( LLMenuItemGL* item );
|
||||
|
||||
// Remove a menu item from this menu.
|
||||
virtual BOOL remove( LLMenuItemGL* item );
|
||||
|
||||
// *NOTE:Mani - appendNoArrange() should be removed when merging to skinning/viewer2.0
|
||||
// Its added as a fix to a viewer 1.23 bug that has already been address by skinning work.
|
||||
virtual BOOL appendNoArrange( LLMenuItemGL* item );
|
||||
|
||||
@@ -60,13 +60,14 @@
|
||||
#include "lltextparser.h"
|
||||
#include <queue>
|
||||
#include "llmenugl.h"
|
||||
#include "../newview/lgghunspell_wrapper.h"
|
||||
|
||||
//
|
||||
// Globals
|
||||
//
|
||||
static LLRegisterWidget<LLTextEditor> r("simple_text_editor");
|
||||
|
||||
BOOL gDebugTextEditorTips = FALSE;
|
||||
static BOOL bSpellCheckText;
|
||||
|
||||
//
|
||||
// Constants
|
||||
@@ -289,8 +290,11 @@ LLTextEditor::LLTextEditor(
|
||||
mMouseDownY(0),
|
||||
mLastSelectionX(-1),
|
||||
mLastSelectionY(-1),
|
||||
mLastContextMenuX(-1),
|
||||
mLastContextMenuY(-1),
|
||||
mReflowNeeded(FALSE),
|
||||
mScrollNeeded(FALSE)
|
||||
mScrollNeeded(FALSE),
|
||||
mSpellCheckable(FALSE)
|
||||
{
|
||||
mSourceID.generate();
|
||||
|
||||
@@ -354,6 +358,7 @@ LLTextEditor::LLTextEditor(
|
||||
menu->append(new LLMenuItemCallGL("Paste", context_paste, NULL, this));
|
||||
menu->append(new LLMenuItemCallGL("Delete", context_delete, NULL, this));
|
||||
menu->append(new LLMenuItemCallGL("Select All", context_selectall, NULL, this));
|
||||
menu->appendSeparator("Spelsep");
|
||||
menu->setCanTearOff(FALSE);
|
||||
menu->setVisible(FALSE);
|
||||
mPopupMenuHandle = menu->getHandle();
|
||||
@@ -387,6 +392,85 @@ void LLTextEditor::context_copy(void* data)
|
||||
LLTextEditor* line = (LLTextEditor*)data;
|
||||
if(line)line->copy();
|
||||
}
|
||||
|
||||
|
||||
void LLTextEditor::spell_correct(void* data)
|
||||
{
|
||||
SpellMenuBind* tempBind = (SpellMenuBind*)data;
|
||||
LLTextEditor* line = tempBind->origin;
|
||||
if(tempBind && line)
|
||||
{
|
||||
llinfos << tempBind->menuItem->getName() << " : " << tempBind->origin->getName() << " : " << tempBind->word << llendl;
|
||||
if(line)line->spellReplace(tempBind);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLTextEditor::spell_show(void * data)
|
||||
{
|
||||
SpellMenuBind* tempBind = (SpellMenuBind*)data;
|
||||
LLTextEditor* line = tempBind->origin;
|
||||
|
||||
if(tempBind && line)
|
||||
{
|
||||
BOOL show = (tempBind->word == "Show Misspellings");
|
||||
glggHunSpell->setSpellCheckHighlight(show);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<S32> LLTextEditor::getMisspelledWordsPositions()
|
||||
{
|
||||
resetSpellDirty();
|
||||
std::vector<S32> thePosesOfBadWords;
|
||||
LLWString& text = mWText;
|
||||
S32 wordStart=0;
|
||||
S32 wordEnd=spellStart;//start at the scroll start
|
||||
while(wordEnd < spellEnd)
|
||||
{
|
||||
//go through all the chars... XD
|
||||
if( LLTextEditor::isPartOfWord( text[wordEnd] ) )
|
||||
{
|
||||
// Select word the cursor is over
|
||||
while ((wordEnd > 0) && LLTextEditor::isPartOfWord(text[wordEnd-1]))
|
||||
{
|
||||
wordEnd--;
|
||||
}
|
||||
wordStart=wordEnd;
|
||||
while ((wordEnd < (S32)text.length()) && LLTextEditor::isPartOfWord( text[wordEnd] ) )
|
||||
{
|
||||
wordEnd++;
|
||||
}
|
||||
//got a word :D
|
||||
|
||||
std::string regText(text.begin(),text.end());
|
||||
std::string selectedWord(regText.substr(wordStart,wordEnd-wordStart));
|
||||
|
||||
if(!glggHunSpell->isSpelledRight(selectedWord))
|
||||
{
|
||||
//misspelled word here, and you have just right clicked on it
|
||||
|
||||
thePosesOfBadWords.push_back(wordStart);
|
||||
thePosesOfBadWords.push_back(wordEnd);
|
||||
}
|
||||
}
|
||||
wordEnd++;
|
||||
}
|
||||
return thePosesOfBadWords;
|
||||
}
|
||||
|
||||
|
||||
void LLTextEditor::spell_add(void* data)
|
||||
{
|
||||
SpellMenuBind* tempBind = (SpellMenuBind*)data;
|
||||
if(tempBind)
|
||||
{
|
||||
glggHunSpell->addWordToCustomDictionary(tempBind->word);
|
||||
tempBind->origin->mPrevSpelledText.erase();//make it update
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLTextEditor::context_paste(void* data)
|
||||
{
|
||||
LLTextEditor* line = (LLTextEditor*)data;
|
||||
@@ -779,6 +863,26 @@ S32 LLTextEditor::nextWordPos(S32 cursorPos) const
|
||||
return cursorPos;
|
||||
}
|
||||
|
||||
BOOL LLTextEditor::getWordBoundriesAt(const S32 at, S32* word_begin, S32* word_length) const
|
||||
{
|
||||
S32 pos = at;
|
||||
if (isPartOfWord(mWText[pos]))
|
||||
{
|
||||
while ( (pos > 0) && isPartOfWord(mWText[pos - 1]) )
|
||||
{
|
||||
pos--;
|
||||
}
|
||||
*word_begin = pos;
|
||||
while ( (pos < getLength()) && isPartOfWord(mWText[pos]) )
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
*word_length = pos - *word_begin;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
S32 LLTextEditor::getLineStart( S32 line ) const
|
||||
{
|
||||
S32 num_lines = getLineCount();
|
||||
@@ -1268,11 +1372,91 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
|
||||
BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask )
|
||||
{
|
||||
setFocus(TRUE);
|
||||
llinfos << "Right mouse click - Opening menu." << llendl;
|
||||
|
||||
//setCursorAtLocalPos( x, y, TRUE );
|
||||
S32 wordStart = 0;
|
||||
S32 wordLen = 0;
|
||||
S32 pos = getCursorPosFromLocalCoord(x,y,TRUE);
|
||||
|
||||
LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
|
||||
if (menu)
|
||||
{
|
||||
for(int i = 0;i<(int)suggestionMenuItems.size();i++)
|
||||
{
|
||||
SpellMenuBind * tempBind = suggestionMenuItems[i];
|
||||
if(tempBind)
|
||||
{
|
||||
menu->remove(tempBind->menuItem);
|
||||
tempBind->menuItem->die();
|
||||
//delete tempBind->menuItem;
|
||||
//tempBind->menuItem = NULL;
|
||||
delete tempBind;
|
||||
}
|
||||
}
|
||||
suggestionMenuItems.clear();
|
||||
|
||||
// spell_check="true" in xui
|
||||
menu->setItemVisible("Spelsep", !mReadOnly && mSpellCheckable);
|
||||
if (!mReadOnly && mSpellCheckable)
|
||||
{
|
||||
bool is_word_part = getWordBoundriesAt(pos, &wordStart, &wordLen);
|
||||
if (is_word_part)
|
||||
{
|
||||
const LLWString &text = mWText;
|
||||
std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart,wordLen));
|
||||
|
||||
if (!glggHunSpell->isSpelledRight(selectedWord))
|
||||
{
|
||||
//misspelled word here, and you have just right clicked on it!
|
||||
std::vector<std::string> suggs = glggHunSpell->getSuggestionList(selectedWord);
|
||||
|
||||
for (int i = 0; i<(int)suggs.size(); i++)
|
||||
{
|
||||
SpellMenuBind * tempStruct = new SpellMenuBind;
|
||||
tempStruct->origin = this;
|
||||
tempStruct->word = suggs[i];
|
||||
tempStruct->wordPositionEnd = wordStart + wordLen;
|
||||
tempStruct->wordPositionStart=wordStart;
|
||||
tempStruct->wordY=y;
|
||||
LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL(
|
||||
tempStruct->word, spell_correct, NULL, tempStruct);
|
||||
tempStruct->menuItem = suggMenuItem;
|
||||
suggestionMenuItems.push_back(tempStruct);
|
||||
menu->append(suggMenuItem);
|
||||
}
|
||||
SpellMenuBind * tempStruct = new SpellMenuBind;
|
||||
tempStruct->origin = this;
|
||||
tempStruct->word = selectedWord;
|
||||
tempStruct->wordPositionEnd = wordStart + wordLen;
|
||||
tempStruct->wordPositionStart=wordStart;
|
||||
tempStruct->wordY=y;
|
||||
LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL(
|
||||
"Add Word", spell_add, NULL, tempStruct);
|
||||
tempStruct->menuItem = suggMenuItem;
|
||||
suggestionMenuItems.push_back(tempStruct);
|
||||
menu->append(suggMenuItem);
|
||||
}
|
||||
}
|
||||
|
||||
SpellMenuBind * tempStruct = new SpellMenuBind;
|
||||
tempStruct->origin = this;
|
||||
if (glggHunSpell->getSpellCheckHighlight())
|
||||
{
|
||||
tempStruct->word = "Hide Misspellings";
|
||||
}
|
||||
else
|
||||
{
|
||||
tempStruct->word = "Show Misspellings";
|
||||
}
|
||||
|
||||
LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL(
|
||||
tempStruct->word, spell_show, NULL, tempStruct);
|
||||
tempStruct->menuItem = suggMenuItem;
|
||||
suggestionMenuItems.push_back(tempStruct);
|
||||
menu->append(suggMenuItem);
|
||||
}
|
||||
mLastContextMenuX = x;
|
||||
mLastContextMenuY = y;
|
||||
menu->buildDrawLabels();
|
||||
menu->updateParent(LLMenuGL::sMenuContainer);
|
||||
LLMenuGL::showPopup(this, menu, x, y);
|
||||
@@ -1971,6 +2155,17 @@ BOOL LLTextEditor::canPaste() const
|
||||
}
|
||||
|
||||
|
||||
void LLTextEditor::spellReplace(SpellMenuBind* spellData)
|
||||
{
|
||||
remove( spellData->wordPositionStart,
|
||||
spellData->wordPositionEnd - spellData->wordPositionStart, TRUE );
|
||||
LLWString clean_string = utf8str_to_wstring(spellData->word);
|
||||
insert(spellData->wordPositionStart, clean_string, FALSE);
|
||||
mCursorPos+=clean_string.length() - (spellData->wordPositionEnd-spellData->wordPositionStart);
|
||||
needsReflow();
|
||||
}
|
||||
|
||||
|
||||
// paste from clipboard
|
||||
void LLTextEditor::paste()
|
||||
{
|
||||
@@ -2823,6 +3018,106 @@ void LLTextEditor::drawSelectionBackground()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLTextEditor::drawMisspelled()
|
||||
{
|
||||
if (!mReadOnly && mSpellCheckable)
|
||||
{
|
||||
if(
|
||||
( ((getLength()<400)||(false)) &&( (S32(mSpellTimer.getElapsedTimeF32() / 1) & 1) ))
|
||||
||
|
||||
(S32(mKeystrokeTimer.getElapsedTimeF32() / 1) & 1)
|
||||
)
|
||||
{
|
||||
S32 newSpellStart = getLineStart(mScrollbar->getDocPos());//start at the scroll start
|
||||
S32 newSpellEnd = getLineStart(mScrollbar->getDocPos() + 1 + mScrollbar->getDocSize()-mScrollbar->getDocPosMax());//end at the end o.o
|
||||
|
||||
if (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())
|
||||
{
|
||||
newSpellEnd = (S32)mWText.length();
|
||||
}
|
||||
if (isSpellDirty() || (newSpellEnd!=spellEnd || newSpellStart!=spellStart))
|
||||
{
|
||||
spellEnd = newSpellEnd;
|
||||
spellStart = newSpellStart;
|
||||
misspellLocations = getMisspelledWordsPositions();
|
||||
}
|
||||
}
|
||||
//draw
|
||||
if (glggHunSpell->getSpellCheckHighlight())
|
||||
{
|
||||
for (int i = 0; i<(int)misspellLocations.size() ;i++)
|
||||
{
|
||||
S32 wstart = misspellLocations[i];
|
||||
S32 wend = misspellLocations[++i];
|
||||
//start curor code mod
|
||||
const LLWString &text = mWText;
|
||||
const S32 text_len = getLength();
|
||||
// Skip through the lines we aren't drawing.
|
||||
S32 search_pos = mScrollbar->getDocPos();
|
||||
S32 num_lines = getLineCount();
|
||||
if (search_pos >= num_lines)
|
||||
{
|
||||
return;
|
||||
}
|
||||
S32 line_start = getLineStart(search_pos);
|
||||
F32 line_height = mGLFont->getLineHeight();
|
||||
F32 text_y = (F32)(mTextRect.mTop) - line_height;
|
||||
|
||||
F32 word_left = 0.f;
|
||||
F32 word_right = 0.f;
|
||||
F32 word_bottom = 0.f;
|
||||
BOOL word_visible = FALSE;
|
||||
|
||||
S32 line_end = 0;
|
||||
// Determine if the cursor is visible and if so what its coordinates are.
|
||||
while( (mTextRect.mBottom <= llround(text_y)) && (search_pos < num_lines))
|
||||
{
|
||||
line_end = text_len + 1;
|
||||
S32 next_line = -1;
|
||||
|
||||
if ((search_pos + 1) < num_lines)
|
||||
{
|
||||
next_line = getLineStart(search_pos + 1);
|
||||
line_end = next_line - 1;
|
||||
}
|
||||
const llwchar* line = text.c_str() + line_start;
|
||||
// Find the cursor and selection bounds
|
||||
if( line_start <= wstart && wend <= line_end )
|
||||
{
|
||||
word_visible = TRUE;
|
||||
word_left = (F32)mTextRect.mLeft + mGLFont->getWidthF32(line, 0, wstart - line_start, mAllowEmbeddedItems )-1.f;
|
||||
word_right = (F32)mTextRect.mLeft + mGLFont->getWidthF32(line, 0, wend - line_start, mAllowEmbeddedItems )+1.f;
|
||||
word_bottom = text_y;
|
||||
break;
|
||||
}
|
||||
// move down one line
|
||||
text_y -= line_height;
|
||||
line_start = next_line;
|
||||
search_pos++;
|
||||
}
|
||||
if (mShowLineNumbers)
|
||||
{
|
||||
word_left += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
|
||||
word_right += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
|
||||
}
|
||||
// Draw the cursor
|
||||
if (word_visible)
|
||||
{
|
||||
//end cursor code mod
|
||||
gGL.color4ub(255,0,0,200);
|
||||
while (word_left<word_right)
|
||||
{
|
||||
gl_line_2d(word_left,word_bottom-2, word_left+3,word_bottom+1);
|
||||
gl_line_2d(word_left+3,word_bottom+1, word_left+6,word_bottom-2);
|
||||
word_left += 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLTextEditor::drawCursor()
|
||||
{
|
||||
if( gFocusMgr.getKeyboardFocus() == this
|
||||
@@ -3305,7 +3600,8 @@ void LLTextEditor::draw()
|
||||
drawPreeditMarker();
|
||||
drawText();
|
||||
drawCursor();
|
||||
|
||||
drawMisspelled();
|
||||
resetSpellDirty();
|
||||
unbindEmbeddedChars(mGLFont);
|
||||
|
||||
//RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret
|
||||
@@ -4474,6 +4770,8 @@ void LLTextEditor::setTextEditorParameters(LLXMLNodePtr node)
|
||||
|
||||
node->getAttributeBOOL("track_bottom", mTrackBottom);
|
||||
|
||||
node->getAttributeBOOL("spell_check", mSpellCheckable);
|
||||
|
||||
LLColor4 color;
|
||||
if (LLUICtrlFactory::getAttributeColor(node,"cursor_color", color))
|
||||
{
|
||||
|
||||
@@ -110,7 +110,19 @@ public:
|
||||
virtual void setFocus( BOOL b );
|
||||
virtual BOOL acceptsTextInput() const;
|
||||
virtual BOOL isDirty() const { return( mLastCmd != NULL || (mPristineCmd && (mPristineCmd != mLastCmd)) ); }
|
||||
BOOL isSpellDirty() const { return mWText != mPrevSpelledText; } // Returns TRUE if user changed value at all
|
||||
void resetSpellDirty() { mPrevSpelledText = mWText; } // Clear dirty state
|
||||
|
||||
struct SpellMenuBind
|
||||
{
|
||||
LLTextEditor* origin;
|
||||
LLMenuItemCallGL * menuItem;
|
||||
std::string word;
|
||||
S32 wordPositionStart;
|
||||
S32 wordPositionEnd;
|
||||
S32 wordY;
|
||||
};
|
||||
|
||||
// LLEditMenuHandler interface
|
||||
virtual void undo();
|
||||
virtual BOOL canUndo() const;
|
||||
@@ -123,6 +135,8 @@ public:
|
||||
virtual void paste();
|
||||
virtual BOOL canPaste() const;
|
||||
|
||||
virtual void spellReplace(SpellMenuBind* spellData);
|
||||
|
||||
virtual void updatePrimary();
|
||||
virtual void copyPrimary();
|
||||
virtual void pastePrimary();
|
||||
@@ -140,6 +154,10 @@ public:
|
||||
static void context_paste(void* data);
|
||||
static void context_delete(void* data);
|
||||
static void context_selectall(void* data);
|
||||
static void spell_correct(void* data);
|
||||
static void spell_add(void* data);
|
||||
static void spell_show(void* data);
|
||||
std::vector<S32> getMisspelledWordsPositions();
|
||||
|
||||
void selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap = TRUE);
|
||||
BOOL replaceText(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive, BOOL wrap = TRUE);
|
||||
@@ -206,6 +224,7 @@ public:
|
||||
void setHighlightColor( const LLColor4& color );
|
||||
void setShadowColor( const LLColor4& color );
|
||||
LLColor4 getReadOnlyFgColor() { return mReadOnlyFgColor; }
|
||||
void setSpellCheckable(BOOL b) { mSpellCheckable = b; }
|
||||
|
||||
// Hacky methods to make it into a word-wrapping, potentially scrolling,
|
||||
// read-only text box.
|
||||
@@ -272,7 +291,7 @@ public:
|
||||
const LLTextSegment* getPreviousSegment() const;
|
||||
void getSelectedSegments(std::vector<const LLTextSegment*>& segments) const;
|
||||
|
||||
static bool isPartOfWord(llwchar c) { return (c == '_') || LLStringOps::isAlnum((char)c); }
|
||||
static bool isPartOfWord(llwchar c) { return ( (c == '_') || (c == '\'') || LLStringOps::isAlnum((char)c)); }
|
||||
|
||||
BOOL isReadOnly() { return mReadOnly; }
|
||||
protected:
|
||||
@@ -329,6 +348,7 @@ protected:
|
||||
|
||||
S32 prevWordPos(S32 cursorPos) const;
|
||||
S32 nextWordPos(S32 cursorPos) const;
|
||||
BOOL getWordBoundriesAt(const S32 at, S32* word_begin, S32* word_length) const;
|
||||
|
||||
S32 getLineCount() const { return mLineStartList.size(); }
|
||||
S32 getLineStart( S32 line ) const;
|
||||
@@ -467,6 +487,7 @@ private:
|
||||
void drawBackground();
|
||||
void drawSelectionBackground();
|
||||
void drawCursor();
|
||||
void drawMisspelled();
|
||||
void drawText();
|
||||
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);
|
||||
|
||||
@@ -497,6 +518,12 @@ private:
|
||||
mutable std::string mUTF8Text;
|
||||
mutable BOOL mTextIsUpToDate;
|
||||
|
||||
LLWString mPrevSpelledText; // saved string so we know whether to respell or not
|
||||
S32 spellStart;
|
||||
S32 spellEnd;
|
||||
std::vector<S32> misspellLocations; // where all the mispelled words are
|
||||
BOOL mSpellCheckable; // set in xui as "spell_check". Default value for a field
|
||||
|
||||
S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes
|
||||
|
||||
const LLFontGL* mGLFont;
|
||||
@@ -533,11 +560,18 @@ private:
|
||||
}
|
||||
};
|
||||
typedef std::vector<line_info> line_list_t;
|
||||
|
||||
//to keep track of what we have to remove before showing menu
|
||||
std::vector<SpellMenuBind* > suggestionMenuItems;
|
||||
S32 mLastContextMenuX;
|
||||
S32 mLastContextMenuY;
|
||||
|
||||
line_list_t mLineStartList;
|
||||
BOOL mReflowNeeded;
|
||||
BOOL mScrollNeeded;
|
||||
|
||||
LLFrameTimer mKeystrokeTimer;
|
||||
LLFrameTimer mSpellTimer;
|
||||
|
||||
LLColor4 mCursorColor;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user