From af203533b3647cfea035c744f1626c5ac580a310 Mon Sep 17 00:00:00 2001 From: "tmac@latestevidence.com" Date: Sat, 23 Apr 2011 18:08:59 -0400 Subject: [PATCH] Spell check added --- indra/cmake/CMakeLists.txt | 2 + indra/cmake/CopyWinLibs.cmake | 2 + indra/cmake/FindHunSpell.cmake | 34 + indra/cmake/HUNSPELL.cmake | 16 + indra/llui/lllineeditor.cpp | 261 ++++- indra/llui/lllineeditor.h | 28 + indra/llui/llmenugl.cpp | 35 + indra/llui/llmenugl.h | 3 + indra/llui/lltexteditor.cpp | 306 +++++- indra/llui/lltexteditor.h | 36 +- indra/llvfs/lldir_linux.cpp | 9 + indra/llvfs/lldir_solaris.cpp | 9 + indra/llvfs/lldir_win32.cpp | 11 +- indra/newview/CMakeLists.txt | 7 + indra/newview/app_settings/settings.xml | 11 + .../app_settings/settings_per_account.xml | 35 +- indra/newview/ascentprefssys.cpp | 214 ++-- indra/newview/ascentprefssys.h | 58 +- indra/newview/lggdicdownload.cpp | 195 ++++ indra/newview/lggdicdownload.h | 37 + indra/newview/lgghunspell_wrapper.cpp | 974 ++++++++++++++++++ indra/newview/lgghunspell_wrapper.h | 75 ++ indra/newview/llfloaterpreference.cpp | 4 +- indra/newview/llstartup.cpp | 2 + .../default/xui/en-us/floater_about_land.xml | 4 +- .../xui/en-us/floater_animation_preview.xml | 2 +- .../default/xui/en-us/floater_area_search.xml | 2 +- .../default/xui/en-us/floater_buy_land.xml | 2 +- .../xui/en-us/floater_chat_history.xml | 6 +- .../xui/en-us/floater_dictionaries.xml | 15 + .../default/xui/en-us/floater_directory.xml | 16 +- .../default/xui/en-us/floater_directory2.xml | 16 +- .../default/xui/en-us/floater_directory3.xml | 18 +- .../xui/en-us/floater_image_preview.xml | 2 +- .../xui/en-us/floater_instant_message.xml | 2 +- .../en-us/floater_instant_message_ad_hoc.xml | 2 +- .../en-us/floater_instant_message_group.xml | 2 +- .../floater_inventory_item_properties.xml | 2 +- .../xui/en-us/floater_name_description.xml | 2 +- .../xui/en-us/floater_new_outfit_dialog.xml | 2 +- .../xui/en-us/floater_notification.xml | 2 +- .../skins/default/xui/en-us/floater_pay.xml | 2 +- .../default/xui/en-us/floater_postcard.xml | 4 +- .../xui/en-us/floater_preview_animation.xml | 2 +- .../xui/en-us/floater_preview_gesture.xml | 6 +- .../xui/en-us/floater_preview_notecard.xml | 4 +- .../floater_preview_notecard_keep_discard.xml | 2 +- .../xui/en-us/floater_preview_sound.xml | 2 +- .../xui/en-us/floater_preview_texture.xml | 2 +- .../floater_preview_texture_keep_discard.xml | 2 +- .../xui/en-us/floater_report_abuse.xml | 4 +- .../default/xui/en-us/floater_report_bug.xml | 4 +- .../xui/en-us/floater_script_preview.xml | 2 +- .../xui/en-us/floater_sound_preview.xml | 2 +- .../skins/default/xui/en-us/panel_avatar.xml | 10 +- .../xui/en-us/panel_avatar_classified.xml | 2 +- .../default/xui/en-us/panel_avatar_pick.xml | 2 +- .../default/xui/en-us/panel_chat_bar.xml | 2 +- .../default/xui/en-us/panel_classified.xml | 2 +- .../skins/default/xui/en-us/panel_event.xml | 2 +- .../default/xui/en-us/panel_group_general.xml | 8 +- .../default/xui/en-us/panel_group_notices.xml | 4 +- .../default/xui/en-us/panel_group_roles.xml | 6 +- .../default/xui/en-us/panel_group_voting.xml | 2 +- .../skins/default/xui/en-us/panel_place.xml | 2 +- .../default/xui/en-us/panel_place_small.xml | 2 +- .../en-us/panel_preferences_ascent_system.xml | 46 +- .../xui/en-us/panel_preferences_im.xml | 2 +- .../xui/en-us/panel_region_covenant.xml | 2 +- .../default/xui/en-us/panel_status_bar.xml | 2 +- .../default/xui/en-us/panel_top_pick.xml | 2 +- indra/newview/viewer_manifest.py | 5 + install.xml | 40 + 73 files changed, 2442 insertions(+), 200 deletions(-) create mode 100644 indra/cmake/FindHunSpell.cmake create mode 100644 indra/cmake/HUNSPELL.cmake create mode 100644 indra/newview/lggdicdownload.cpp create mode 100644 indra/newview/lggdicdownload.h create mode 100644 indra/newview/lgghunspell_wrapper.cpp create mode 100644 indra/newview/lgghunspell_wrapper.h create mode 100644 indra/newview/skins/default/xui/en-us/floater_dictionaries.xml diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 47f6da4f8..ac6048744 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -28,6 +28,7 @@ set(cmake_SOURCE_FILES FindCARes.cmake FindELFIO.cmake FindGooglePerfTools.cmake + FindHunSpell.cmake FindMono.cmake FindMT.cmake FindMySQL.cmake @@ -39,6 +40,7 @@ set(cmake_SOURCE_FILES FreeType.cmake GStreamer010Plugin.cmake GooglePerfTools.cmake + HUNSPELL.cmake JPEG.cmake LLAddBuildTest.cmake LLAudio.cmake diff --git a/indra/cmake/CopyWinLibs.cmake b/indra/cmake/CopyWinLibs.cmake index 88d2670e3..fb13f29e4 100644 --- a/indra/cmake/CopyWinLibs.cmake +++ b/indra/cmake/CopyWinLibs.cmake @@ -25,6 +25,7 @@ set(all_targets ${all_targets} ${out_targets}) set(debug_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/debug") set(debug_files + libhunspell.dll libapr-1.dll libaprutil-1.dll libapriconv-1.dll @@ -243,6 +244,7 @@ set(release_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release") set(release_files libtcmalloc_minimal.dll fmod.dll + libhunspell.dll libapr-1.dll libaprutil-1.dll libapriconv-1.dll diff --git a/indra/cmake/FindHunSpell.cmake b/indra/cmake/FindHunSpell.cmake new file mode 100644 index 000000000..d41f9cf76 --- /dev/null +++ b/indra/cmake/FindHunSpell.cmake @@ -0,0 +1,34 @@ +FIND_PATH(HUNSPELL_INCLUDE_DIR hunspell.hxx + /usr/local/include/hunspell + /usr/local/include + /usr/include/hunspell + /usr/include + ) + +SET(HUNSPELL_NAMES ${HUNSPELL_NAMES} hunspell hunspell-1.2) +FIND_LIBRARY(HUNSPELL_LIBRARY + NAMES ${HUNSPELL_NAMES} + PATHS /usr/lib /usr/local/lib + ) + +IF(HUNSPELL_LIBRARY AND HUNSPELL_INCLUDE_DIR) + SET(HUNSPELL_LIBRARIES ${HUNSPELL_LIBRARY}) + SET(HUNSPELL_FOUND "YES") +ELSE(HUNSPELL_LIBRARY AND HUNSPELL_INCLUDE_DIR) + SET(HUNSPELL_FOUND "NO") +ENDIF(HUNSPELL_LIBRARY AND HUNSPELL_INCLUDE_DIR) + +IF(HUNSPELL_FOUND) + IF(NOT HUNSPELL_FIND_QUIETLY) + MESSAGE(STATUS "Found Hunspell: ${HUNSPELL_LIBRARIES}") + ENDIF(NOT HUNSPELL_FIND_QUIETLY) +ELSE(HUNSPELL_FOUND) + IF(HUNSPELL_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find HunSpell library") + ENDIF(HUNSPELL_FIND_REQUIRED) +ENDIF(HUNSPELL_FOUND) + +MARK_AS_ADVANCED( + HUNSPELL_LIBRARY + HUNSPELL_INCLUDE_DIR + ) diff --git a/indra/cmake/HUNSPELL.cmake b/indra/cmake/HUNSPELL.cmake new file mode 100644 index 000000000..cd5d7e00b --- /dev/null +++ b/indra/cmake/HUNSPELL.cmake @@ -0,0 +1,16 @@ +# -*- cmake -*- +include(Prebuilt) + +if (STANDALONE) + include(FindHunSpell) +else (STANDALONE) + use_prebuilt_binary(hunspell) + + set(HUNSPELL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/hunspell) + + if (LINUX OR DARWIN) + set(HUNSPELL_LIBRARY hunspell-1.2) + else (LINUX OR DARWIN) + set(HUNSPELL_LIBRARY libhunspell) + endif (LINUX OR DARWIN) +endif (STANDALONE) diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 980d705af..fe4190a2b 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -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 LLLineEditor::getMisspelledWordsPositions() +{ + std::vector 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 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); diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 1d9f7e9d4..2272c450b 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -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 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 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 suggestionMenuItems; + S32 mLastContextMenuX; // line history support: BOOL mHaveHistory; // flag for enabled line history diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index f14b9a8ad..cfa10734e 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -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) diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 26fc2944c..63f9d555d 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -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 ); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 2ea3423b0..fce0175d2 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -60,13 +60,14 @@ #include "lltextparser.h" #include #include "llmenugl.h" +#include "../newview/lgghunspell_wrapper.h" // // Globals // static LLRegisterWidget 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 LLTextEditor::getMisspelledWordsPositions() +{ + resetSpellDirty(); + std::vector 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 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_leftgetAttributeBOOL("track_bottom", mTrackBottom); + node->getAttributeBOOL("spell_check", mSpellCheckable); + LLColor4 color; if (LLUICtrlFactory::getAttributeColor(node,"cursor_color", color)) { diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 292779826..136a8528a 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -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 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& 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 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_list_t; + + //to keep track of what we have to remove before showing menu + std::vector suggestionMenuItems; + S32 mLastContextMenuX; + S32 mLastContextMenuY; + line_list_t mLineStartList; BOOL mReflowNeeded; BOOL mScrollNeeded; LLFrameTimer mKeystrokeTimer; + LLFrameTimer mSpellTimer; LLColor4 mCursorColor; diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp index ec0a4f468..6a5834fec 100644 --- a/indra/llvfs/lldir_linux.cpp +++ b/indra/llvfs/lldir_linux.cpp @@ -235,6 +235,15 @@ void LLDir_Linux::initAppDirs(const std::string &app_name) } } + res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries")); + if (res == -1) + { + if (errno != EEXIST) + { + llwarns << "Couldn't create LL_PATH_USER_SETTINGS/dictionaries dir " << getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"") << llendl; + } + } + mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); } diff --git a/indra/llvfs/lldir_solaris.cpp b/indra/llvfs/lldir_solaris.cpp index c647e2bd1..a39e13fcf 100644 --- a/indra/llvfs/lldir_solaris.cpp +++ b/indra/llvfs/lldir_solaris.cpp @@ -247,6 +247,15 @@ void LLDir_Solaris::initAppDirs(const std::string &app_name) } } + res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries")); + if (res == -1) + { + if (errno != EEXIST) + { + llwarns << "Couldn't create LL_PATH_USER_SETTINGS/dictionaries dir " << getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"") << llendl; + } + } + mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); } diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp index e5d6747d9..c41c591c5 100644 --- a/indra/llvfs/lldir_win32.cpp +++ b/indra/llvfs/lldir_win32.cpp @@ -207,7 +207,16 @@ void LLDir_Win32::initAppDirs(const std::string &app_name) llwarns << "Couldn't create LL_PATH_MOZILLA_PROFILE dir " << getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"") << llendl; } } - + + res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries")); + if (res == -1) + { + if (errno != EEXIST) + { + llwarns << "Couldn't create LL_PATH_USER_SETTINGS/dictionaries dir " << getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"") << llendl; + } + } + mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d06efa9e2..fe6309e0c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -9,6 +9,7 @@ include(DirectX) include(ELFIO) include(FMOD) include(OPENAL) +include(HUNSPELL) include(FindOpenGL) include(JsonCpp) include(LLAddBuildTest) @@ -43,6 +44,7 @@ endif (WINDOWS) include_directories( ${DBUSGLIB_INCLUDE_DIRS} + ${HUNSPELL_INCLUDE_DIR} ${ELFIO_INCLUDE_DIR} ${JSONCPP_INCLUDE_DIRS} ${LLAUDIO_INCLUDE_DIRS} @@ -67,6 +69,8 @@ set(viewer_SOURCE_FILES slfloatermediafilter.cpp floaterlocalassetbrowse.cpp aoremotectrl.cpp + lgghunspell_wrapper.cpp + lggdicdownload.cpp floaterao.cpp floatervoicelicense.cpp cofmgr.cpp @@ -561,6 +565,8 @@ set(viewer_HEADER_FILES hipporestrequest.h hippopanelgrids.h jcfloaterareasearch.h + lggdicdownload.h + lgghunspell_wrapper.h chatbar_as_cmdline.h qtoolalign.h llagent.h @@ -1440,6 +1446,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${WINDOWS_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${ELFIO_LIBRARIES} + ${HUNSPELL_LIBRARY} ) if (LINUX) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 73555f64b..44986c75f 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -11276,6 +11276,17 @@ Value 0 + SpellDownloadURL + + Comment + Base url for download dictionaries + Persist + 1 + Type + String + Value + http://kokuaviewer.org/app/dics/ + StatsAutoRun Comment diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 25fce02fa..04b55be68 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -672,6 +672,39 @@ Boolean Value 0 - + + SpellDisplay + + Comment + Turn on to highlight misspelled text in line edit boxes + Persist + 1 + Type + Boolean + Value + 1 + + SpellInstalled + + Comment + The list of installed dictionaries + Persist + 1 + Type + String + Value + EN_SL + + SpellBase + + Comment + The base dictionary to spell check with + Persist + 1 + Type + String + Value + English (United States of America) + diff --git a/indra/newview/ascentprefssys.cpp b/indra/newview/ascentprefssys.cpp index 5840e9de4..677280b1f 100644 --- a/indra/newview/ascentprefssys.cpp +++ b/indra/newview/ascentprefssys.cpp @@ -48,67 +48,11 @@ #include "llwind.h" #include "llviewernetwork.h" #include "pipeline.h" - -//System page ------------------------------------------------------------------------------ -HgB -class LLPrefsAscentSysImpl : public LLPanel -{ -public: - LLPrefsAscentSysImpl(); - /*virtual*/ ~LLPrefsAscentSysImpl() { }; - - virtual void refresh(); - - void apply(); - void cancel(); - -private: - static void onCommitCheckBox(LLUICtrl* ctrl, void* user_data); - void refreshValues(); - //General ----------------------------------------------------------------------------- - BOOL mDoubleClickTeleport; - BOOL mResetCameraAfterTP; - BOOL mOffsetTPByUserHeight; - BOOL mPreviewAnimInWorld; - BOOL mSaveScriptsAsMono; - BOOL mAlwaysRezInGroup; - //Disable Teleport Progress - //Disable Logout progress - //always show Build - BOOL mAlwaysShowFly; - //Disable camera minimum distance - BOOL mPowerUser; - BOOL mUseSystemFolder; - BOOL mUploadToSystem; - //Chat/IM ----------------------------------------------------------------------------- - BOOL mHideNotificationsInChat; - BOOL mPlayTypingSound; - BOOL mHideTypingNotification; - BOOL mEnableMUPose; - BOOL mEnableOOCAutoClose; - U32 mLinksForChattingObjects; - U32 mTimeFormat; - U32 mDateFormat; - BOOL mSecondsInChatAndIMs; - //Performance ------------------------------------------------------------------------- - BOOL mFetchInventoryOnLogin; - BOOL mEnableLLWind; - BOOL mEnableClouds; - BOOL mEnableClassicClouds; - BOOL mSpeedRez; - U32 mSpeedRezInterval; - //Command Line ------------------------------------------------------------------------ - //Privacy ----------------------------------------------------------------------------- - BOOL mBroadcastViewerEffects; - BOOL mDisablePointAtAndBeam; - BOOL mPrivateLookAt; - BOOL mShowLookAt; - BOOL mRevokePermsOnStandUp; - BOOL mDisableClickSit; -}; +#include "lgghunspell_wrapper.h" -LLPrefsAscentSysImpl::LLPrefsAscentSysImpl() - : LLPanel(std::string("Ascent")) + +LLPrefsAscentSys::LLPrefsAscentSys() { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_ascent_system.xml"); childSetCommitCallback("speed_rez_check", onCommitCheckBox, this); @@ -117,18 +61,28 @@ LLPrefsAscentSysImpl::LLPrefsAscentSysImpl() childSetCommitCallback("show_look_at_check", onCommitCheckBox, this); childSetCommitCallback("enable_clouds", onCommitCheckBox, this); + childSetCommitCallback("SpellBase", onSpellBaseComboBoxCommit, this); + childSetAction("EmSpell_EditCustom", onSpellEditCustom, this); + childSetAction("EmSpell_GetMore", onSpellGetMore, this); + childSetAction("EmSpell_Add", onSpellAdd, this); + childSetAction("EmSpell_Remove", onSpellRemove, this); + refreshValues(); refresh(); } -//static -void LLPrefsAscentSysImpl::onCommitCheckBox(LLUICtrl* ctrl, void* user_data) +LLPrefsAscentSys::~LLPrefsAscentSys() { - LLPrefsAscentSysImpl* self = (LLPrefsAscentSysImpl*)user_data; +} + +//static +void LLPrefsAscentSys::onCommitCheckBox(LLUICtrl* ctrl, void* user_data) +{ + LLPrefsAscentSys* self = (LLPrefsAscentSys*)user_data; llinfos << "Change to " << ctrl->getControlName() << " aka " << ctrl->getName() << llendl; - if (ctrl->getControlName() == "SpeedRez") + if (ctrl->getName() == "speed_rez_check") // Why is this one getControlName() and the rest are getName()? { bool enabled = self->childGetValue("speed_rez_check").asBoolean(); self->childSetEnabled("speed_rez_interval", enabled); @@ -158,7 +112,51 @@ void LLPrefsAscentSysImpl::onCommitCheckBox(LLUICtrl* ctrl, void* user_data) } } -void LLPrefsAscentSysImpl::refreshValues() +void LLPrefsAscentSys::onSpellAdd(void* data) +{ + LLPrefsAscentSys* self = (LLPrefsAscentSys*)data; + + if(self) + { + glggHunSpell->addButton(self->childGetValue("EmSpell_Avail").asString()); + } + + self->refresh(); +} + +void LLPrefsAscentSys::onSpellRemove(void* data) +{ + LLPrefsAscentSys* self = (LLPrefsAscentSys*)data; + + if(self) + { + glggHunSpell->removeButton(self->childGetValue("EmSpell_Installed").asString()); + } + + self->refresh(); +} + +void LLPrefsAscentSys::onSpellGetMore(void* data) +{ + glggHunSpell->getMoreButton(data); +} + +void LLPrefsAscentSys::onSpellEditCustom(void* data) +{ + glggHunSpell->editCustomButton(); +} + +void LLPrefsAscentSys::onSpellBaseComboBoxCommit(LLUICtrl* ctrl, void* userdata) +{ + LLComboBox* box = (LLComboBox*)ctrl; + + if (box) + { + glggHunSpell->newDictSelection(box->getValue().asString()); + } +} + +void LLPrefsAscentSys::refreshValues() { //General ----------------------------------------------------------------------------- mDoubleClickTeleport = gSavedSettings.getBOOL("DoubleClickTeleport"); @@ -204,9 +202,11 @@ void LLPrefsAscentSysImpl::refreshValues() mShowLookAt = LLHUDEffectLookAt::sDebugLookAt; mRevokePermsOnStandUp = gSavedSettings.getBOOL("RevokePermsOnStandUp"); mDisableClickSit = gSavedSettings.getBOOL("DisableClickSit"); + //Text Options ------------------------------------------------------------------------ + mSpellDisplay = gSavedSettings.getBOOL("SpellDisplay"); } -void LLPrefsAscentSysImpl::refresh() +void LLPrefsAscentSys::refresh() { //General ----------------------------------------------------------------------------- childSetValue("double_click_teleport_check", mDoubleClickTeleport); @@ -309,9 +309,59 @@ void LLPrefsAscentSysImpl::refresh() childSetValue("show_look_at_check", mShowLookAt); childSetValue("revoke_perms_on_stand_up_check", mRevokePermsOnStandUp); childSetValue("disable_click_sit_check", mDisableClickSit); + + //Text Options ------------------------------------------------------------------------ + combo = getChild("SpellBase"); + + if (combo != NULL) + { + combo->removeall(); + std::vector names = glggHunSpell->getDicts(); + + for (int i = 0; i < (int)names.size(); i++) + { + combo->add(names[i]); + } + + combo->setSimple(gSavedSettings.getString("SpellBase")); + } + + combo = getChild("EmSpell_Avail"); + + if (combo != NULL) + { + combo->removeall(); + + combo->add(""); + std::vector names = glggHunSpell->getAvailDicts(); + + for (int i = 0; i < (int)names.size(); i++) + { + combo->add(names[i]); + } + + combo->setSimple(std::string("")); + } + + combo = getChild("EmSpell_Installed"); + + if (combo != NULL) + { + combo->removeall(); + + combo->add(""); + std::vector names = glggHunSpell->getInstalledDicts(); + + for (int i = 0; i < (int)names.size(); i++) + { + combo->add(names[i]); + } + + combo->setSimple(std::string("")); + } } -void LLPrefsAscentSysImpl::cancel() +void LLPrefsAscentSys::cancel() { //General ----------------------------------------------------------------------------- childSetValue("double_click_teleport_check", mDoubleClickTeleport); @@ -359,9 +409,12 @@ void LLPrefsAscentSysImpl::cancel() childSetValue("enable_classic_clouds", mEnableClassicClouds); gLLWindEnabled = mEnableLLWind; + + //Text Options ------------------------------------------------------------------------ + childSetValue("SpellDisplay", mSpellDisplay); } -void LLPrefsAscentSysImpl::apply() +void LLPrefsAscentSys::apply() { std::string short_date, long_date, short_time, long_time, timestamp; @@ -500,30 +553,3 @@ void LLPrefsAscentSysImpl::apply() refreshValues(); refresh(); } - -//--------------------------------------------------------------------------- - -LLPrefsAscentSys::LLPrefsAscentSys() -: impl(* new LLPrefsAscentSysImpl()) -{ -} - -LLPrefsAscentSys::~LLPrefsAscentSys() -{ - delete &impl; -} - -void LLPrefsAscentSys::apply() -{ - impl.apply(); -} - -void LLPrefsAscentSys::cancel() -{ - impl.cancel(); -} - -LLPanel* LLPrefsAscentSys::getPanel() -{ - return &impl; -} diff --git a/indra/newview/ascentprefssys.h b/indra/newview/ascentprefssys.h index e09377129..f9e507d9f 100644 --- a/indra/newview/ascentprefssys.h +++ b/indra/newview/ascentprefssys.h @@ -32,10 +32,11 @@ #ifndef ASCENTPREFSSYS_H #define ASCENTPREFSSYS_H -class LLPanel; -class LLPrefsAscentSysImpl; -class LLPrefsAscentSys +#include "llpanel.h" + + +class LLPrefsAscentSys : public LLPanel { public: LLPrefsAscentSys(); @@ -43,11 +44,60 @@ public: void apply(); void cancel(); + void refresh(); LLPanel* getPanel(); protected: - LLPrefsAscentSysImpl& impl; + static void onCommitCheckBox(LLUICtrl* ctrl, void* user_data); + static void onSpellAdd(void* data); + static void onSpellRemove(void* data); + static void onSpellGetMore(void* data); + static void onSpellEditCustom(void* data); + static void onSpellBaseComboBoxCommit(LLUICtrl* ctrl, void* userdata); + void refreshValues(); + //General ----------------------------------------------------------------------------- + BOOL mDoubleClickTeleport; + BOOL mResetCameraAfterTP; + BOOL mOffsetTPByUserHeight; + BOOL mPreviewAnimInWorld; + BOOL mSaveScriptsAsMono; + BOOL mAlwaysRezInGroup; + //Disable Teleport Progress + //Disable Logout progress + //always show Build + BOOL mAlwaysShowFly; + //Disable camera minimum distance + BOOL mPowerUser; + BOOL mUseSystemFolder; + BOOL mUploadToSystem; + //Chat/IM ----------------------------------------------------------------------------- + BOOL mHideNotificationsInChat; + BOOL mPlayTypingSound; + BOOL mHideTypingNotification; + BOOL mEnableMUPose; + BOOL mEnableOOCAutoClose; + U32 mLinksForChattingObjects; + U32 mTimeFormat; + U32 mDateFormat; + BOOL mSecondsInChatAndIMs; + //Performance ------------------------------------------------------------------------- + BOOL mFetchInventoryOnLogin; + BOOL mEnableLLWind; + BOOL mEnableClouds; + BOOL mEnableClassicClouds; + BOOL mSpeedRez; + U32 mSpeedRezInterval; + //Command Line ------------------------------------------------------------------------ + //Privacy ----------------------------------------------------------------------------- + BOOL mBroadcastViewerEffects; + BOOL mDisablePointAtAndBeam; + BOOL mPrivateLookAt; + BOOL mShowLookAt; + BOOL mRevokePermsOnStandUp; + BOOL mDisableClickSit; + //Text Options ------------------------------------------------------------------------ + BOOL mSpellDisplay; }; #endif \ No newline at end of file diff --git a/indra/newview/lggdicdownload.cpp b/indra/newview/lggdicdownload.cpp new file mode 100644 index 000000000..734a42814 --- /dev/null +++ b/indra/newview/lggdicdownload.cpp @@ -0,0 +1,195 @@ +/* Copyright (c) 2009 +* +* Greg Hendrickson (LordGregGreg Back). All rights reserved. +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* 3. Neither the name Modular Systems nor the names of its contributors +* may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY MODULAR SYSTEMS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MODULAR SYSTEMS OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +* THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "llviewerprecompiledheaders.h" + +#include "lggdicdownload.h" + +#include "llagentdata.h" +#include "llcommandhandler.h" +#include "llfloater.h" +#include "lluictrlfactory.h" +#include "llagent.h" +#include "llpanel.h" +#include "llbutton.h" +#include "llcolorswatch.h" +#include "llcombobox.h" +#include "llview.h" +#include "ascentprefssys.h" +#include "llviewercontrol.h" +#include "llhttpclient.h" +#include "llbufferstream.h" + +class lggDicDownloadFloater; + +class EmeraldDicDownloader : public LLHTTPClient::Responder +{ +public: + EmeraldDicDownloader(lggDicDownloadFloater* spanel, std::string sname); + ~EmeraldDicDownloader() { } + void completedRaw( + U32 status, + const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); +private: + lggDicDownloadFloater* panel; + std::string name; +}; + + +class lggDicDownloadFloater : public LLFloater, public LLFloaterSingleton +{ +public: + lggDicDownloadFloater(const LLSD& seed); + virtual ~lggDicDownloadFloater(); + BOOL postBuild(void); + void setData(std::vector shortNames, std::vector longNames, void * data); + static void onClickDownload(void* data); + std::vector sNames; + std::vector lNames; + LLPrefsAscentSys * empanel; +}; + +lggDicDownloadFloater::~lggDicDownloadFloater() +{ +} + +lggDicDownloadFloater::lggDicDownloadFloater(const LLSD& seed) +{ + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_dictionaries.xml"); + + if (getRect().mLeft == 0 + && getRect().mBottom == 0) + { + center(); + } +} + +BOOL lggDicDownloadFloater::postBuild(void) +{ + childSetAction("Emerald_dic_download", onClickDownload, this); + return true; +} + +void lggDicDownloadFloater::setData(std::vector shortNames, std::vector longNames, void* data) +{ + sNames = shortNames; + lNames = longNames; + empanel = (LLPrefsAscentSys*)data; + + LLComboBox* comboBox = getChild("Emerald_combo_dics"); + if (comboBox != NULL) + { + comboBox->removeall(); + for (int i = 0; i < (int)lNames.size(); i++) + { + comboBox->add(lNames[i], ADD_BOTTOM); + } + comboBox->setCurrentByIndex(0); + comboBox->add("", ADD_BOTTOM); + } +} + +void lggDicDownloadFloater::onClickDownload(void* data) +{ + lggDicDownloadFloater* self = (lggDicDownloadFloater*)data; + if (self) + { + //std::string selection = self->childGetValue("Emerald_combo_dics").asString(); + LLComboBox* comboBox = self->getChild("Emerald_combo_dics"); + if (comboBox != NULL) + { + if (!comboBox->getSelectedItemLabel().empty()) + { + std::string newDict(self->sNames[comboBox->getCurrentIndex()]); + LLHTTPClient::get(gSavedSettings.getString("SpellDownloadURL")+newDict+".aff", new EmeraldDicDownloader(self,newDict+".aff")); + LLHTTPClient::get(gSavedSettings.getString("SpellDownloadURL")+newDict+".dic", new EmeraldDicDownloader(NULL,newDict+".dic")); + + LLButton* button = self->getChild("Emerald_dic_download"); + if (button) + { + // TODO: move this to xml + button->setLabel(LLStringExplicit("Downloading... Please Wait")); + button->setEnabled(FALSE); + } + } + } + } +} + +void LggDicDownload::show(BOOL showin, std::vector shortNames, std::vector longNames, void * data) +{ + if (showin) + { + lggDicDownloadFloater* dic_floater = lggDicDownloadFloater::showInstance(); + dic_floater->setData(shortNames,longNames,data); + } +} + +EmeraldDicDownloader::EmeraldDicDownloader(lggDicDownloadFloater* spanel, std::string sname) + : + panel(spanel), + name(sname) +{ +} + + +void EmeraldDicDownloader::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) +{ + if (status < 200 || status >= 300) + { + return; + } + LLBufferStream istr(channels, buffer.get()); + std::string dicpath(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries", name.c_str())); + + llofstream ostr(dicpath, std::ios::binary); + + while (istr.good() && ostr.good()) + { + ostr << istr.rdbuf(); + } + ostr.close(); + if (panel) + { + if (panel->empanel) + { + panel->empanel->refresh(); + } + else + { + llinfos << "completedRaw(): No empanel to refresh()!" << llendl; + } + + panel->close(); + } +} diff --git a/indra/newview/lggdicdownload.h b/indra/newview/lggdicdownload.h new file mode 100644 index 000000000..75f3053e9 --- /dev/null +++ b/indra/newview/lggdicdownload.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2009 +* +* Greg Hendrickson (LordGregGreg Back). All rights reserved. +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* 3. Neither the name Modular Systems nor the names of its contributors +* may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY MODULAR SYSTEMS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MODULAR SYSTEMS OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +* THE POSSIBILITY OF SUCH DAMAGE. +*/ + +class LggDicDownload +{ + public: + static void show( BOOL showw , std::vector shortNames, std::vector longNames, void * data); +}; + diff --git a/indra/newview/lgghunspell_wrapper.cpp b/indra/newview/lgghunspell_wrapper.cpp new file mode 100644 index 000000000..b8748ff2c --- /dev/null +++ b/indra/newview/lgghunspell_wrapper.cpp @@ -0,0 +1,974 @@ +/* Copyright (C) 2009 LordGregGreg Back + + This is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the viewer; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include "llviewerprecompiledheaders.h" +#include "lgghunspell_wrapper.h" +#include +#include "llweb.h" +#include "llviewercontrol.h" +#include "llviewerwindow.h" +#include "llfile.h" +#include "llhttpclient.h" +#include "lggdicdownload.h" + +lggHunSpell_Wrapper *glggHunSpell = 0; +Hunspell* lggHunSpell_Wrapper::myHunspell = 0; +// do not insert empty lines after this line until the size calculation +#define COUNTRY_CODES_RAW_START_LINE (__LINE__ + 2) +static char * countryCodesraw[] = { + (char*)"SL",(char*)"SecondLife", + (char*)"AD",(char*)"Andorra", + (char*)"AE",(char*)"United Arab Emirates", + (char*)"AF",(char*)"Afghanistan", + (char*)"AG",(char*)"Antigua & Barbuda", + (char*)"AI",(char*)"Anguilla", + (char*)"AL",(char*)"Albania", + (char*)"AM",(char*)"Armenia", + (char*)"AN",(char*)"Netherlands Antilles", + (char*)"AO",(char*)"Angola", + (char*)"AQ",(char*)"Antarctica", + (char*)"AR",(char*)"Argentina", + (char*)"AS",(char*)"American Samoa", + (char*)"AT",(char*)"Austria", + (char*)"AU",(char*)"Australia", + (char*)"AW",(char*)"Aruba", + (char*)"AZ",(char*)"Azerbaijan", + (char*)"BA",(char*)"Bosnia and Herzegovina", + (char*)"BB",(char*)"Barbados", + (char*)"BD",(char*)"Bangladesh", + (char*)"BE",(char*)"Belgium", + (char*)"BF",(char*)"Burkina Faso", + (char*)"BG",(char*)"Bulgaria", + (char*)"BH",(char*)"Bahrain", + (char*)"BI",(char*)"Burundi", + (char*)"BJ",(char*)"Benin", + (char*)"BM",(char*)"Bermuda", + (char*)"BN",(char*)"Brunei Darussalam", + (char*)"BO",(char*)"Bolivia", + (char*)"BR",(char*)"Brazil", + (char*)"BS",(char*)"Bahama", + (char*)"BT",(char*)"Bhutan", + (char*)"BU",(char*)"Burma", + (char*)"BV",(char*)"Bouvet Island", + (char*)"BW",(char*)"Botswana", + (char*)"BY",(char*)"Belarus", + (char*)"BZ",(char*)"Belize", + (char*)"CA",(char*)"Canada", + (char*)"CC",(char*)"Cocos (Keeling) Islands", + (char*)"CF",(char*)"Central African Republic", + (char*)"CG",(char*)"Congo", + (char*)"CH",(char*)"Switzerland", + (char*)"CI",(char*)"Côte D'ivoire (Ivory Coast)", + (char*)"CK",(char*)"Cook Iislands", + (char*)"CL",(char*)"Chile", + (char*)"CM",(char*)"Cameroon", + (char*)"CN",(char*)"China", + (char*)"CO",(char*)"Colombia", + (char*)"CR",(char*)"Costa Rica", + (char*)"CS",(char*)"Czechoslovakia", + (char*)"CU",(char*)"Cuba", + (char*)"CV",(char*)"Cape Verde", + (char*)"CX",(char*)"Christmas Island", + (char*)"CY",(char*)"Cyprus", + (char*)"CZ",(char*)"Czech Republic", + (char*)"DD",(char*)"German Democratic Republic", + (char*)"DE",(char*)"Germany", + (char*)"DJ",(char*)"Djibouti", + (char*)"DK",(char*)"Denmark", + (char*)"DM",(char*)"Dominica", + (char*)"DO",(char*)"Dominican Republic", + (char*)"DZ",(char*)"Algeria", + (char*)"EC",(char*)"Ecuador", + (char*)"EE",(char*)"Estonia", + (char*)"EG",(char*)"Egypt", + (char*)"EH",(char*)"Western Sahara", + (char*)"ER",(char*)"Eritrea", + (char*)"ES",(char*)"Spain", + (char*)"ET",(char*)"Ethiopia", + (char*)"FI",(char*)"Finland", + (char*)"FJ",(char*)"Fiji", + (char*)"FK",(char*)"Falkland Islands (Malvinas)", + (char*)"FM",(char*)"Micronesia", + (char*)"FO",(char*)"Faroe Islands", + (char*)"FR",(char*)"France", + (char*)"FX",(char*)"France, Metropolitan", + (char*)"GA",(char*)"Gabon", + (char*)"GB",(char*)"United Kingdom (Great Britain)", + (char*)"GD",(char*)"Grenada", + (char*)"GE",(char*)"Georgia", + (char*)"GF",(char*)"French Guiana", + (char*)"GH",(char*)"Ghana", + (char*)"GI",(char*)"Gibraltar", + (char*)"GL",(char*)"Greenland", + (char*)"GM",(char*)"Gambia", + (char*)"GN",(char*)"Guinea", + (char*)"GP",(char*)"Guadeloupe", + (char*)"GQ",(char*)"Equatorial Guinea", + (char*)"GR",(char*)"Greece", + (char*)"GS",(char*)"South Georgia and the South Sandwich Islands", + (char*)"GT",(char*)"Guatemala", + (char*)"GU",(char*)"Guam", + (char*)"GW",(char*)"Guinea-Bissau", + (char*)"GY",(char*)"Guyana", + (char*)"HK",(char*)"Hong Kong", + (char*)"HM",(char*)"Heard & McDonald Islands", + (char*)"HN",(char*)"Honduras", + (char*)"HR",(char*)"Croatia", + (char*)"HT",(char*)"Haiti", + (char*)"HU",(char*)"Hungary", + (char*)"ID",(char*)"Indonesia", + (char*)"IE",(char*)"Ireland", + (char*)"IL",(char*)"Israel", + (char*)"IN",(char*)"India", + (char*)"IO",(char*)"British Indian Ocean Territory", + (char*)"IQ",(char*)"Iraq", + (char*)"IR",(char*)"Islamic Republic of Iran", + (char*)"IS",(char*)"Iceland", + (char*)"IT",(char*)"Italy", + (char*)"JM",(char*)"Jamaica", + (char*)"JO",(char*)"Jordan", + (char*)"JP",(char*)"Japan", + (char*)"KE",(char*)"Kenya", + (char*)"KG",(char*)"Kyrgyzstan", + (char*)"KH",(char*)"Cambodia", + (char*)"KI",(char*)"Kiribati", + (char*)"KM",(char*)"Comoros", + (char*)"KN",(char*)"St. Kitts and Nevis", + (char*)"KP",(char*)"Korea, Democratic People's Republic of", + (char*)"KR",(char*)"Korea, Republic of", + (char*)"KW",(char*)"Kuwait", + (char*)"KY",(char*)"Cayman Islands", + (char*)"KZ",(char*)"Kazakhstan", + (char*)"LA",(char*)"Lao People's Democratic Republic", + (char*)"LB",(char*)"Lebanon", + (char*)"LC",(char*)"Saint Lucia", + (char*)"LI",(char*)"Liechtenstein", + (char*)"LK",(char*)"Sri Lanka", + (char*)"LR",(char*)"Liberia", + (char*)"LS",(char*)"Lesotho", + (char*)"LT",(char*)"Lithuania", + (char*)"LU",(char*)"Luxembourg", + (char*)"LV",(char*)"Latvia", + (char*)"LY",(char*)"Libyan Arab Jamahiriya", + (char*)"MA",(char*)"Morocco", + (char*)"MC",(char*)"Monaco", + (char*)"MD",(char*)"Moldova, Republic of", + (char*)"MG",(char*)"Madagascar", + (char*)"MH",(char*)"Marshall Islands", + (char*)"ML",(char*)"Mali", + (char*)"MN",(char*)"Mongolia", + (char*)"MM",(char*)"Myanmar", + (char*)"MO",(char*)"Macau", + (char*)"MP",(char*)"Northern Mariana Islands", + (char*)"MQ",(char*)"Martinique", + (char*)"MR",(char*)"Mauritania", + (char*)"MS",(char*)"Monserrat", + (char*)"MT",(char*)"Malta", + (char*)"MU",(char*)"Mauritius", + (char*)"MV",(char*)"Maldives", + (char*)"MW",(char*)"Malawi", + (char*)"MX",(char*)"Mexico", + (char*)"MY",(char*)"Malaysia", + (char*)"MZ",(char*)"Mozambique", + (char*)"NA",(char*)"Namibia", + (char*)"NC",(char*)"New Caledonia", + (char*)"NE",(char*)"Niger", + (char*)"NF",(char*)"Norfolk Island", + (char*)"NG",(char*)"Nigeria", + (char*)"NI",(char*)"Nicaragua", + (char*)"NL",(char*)"Netherlands", + (char*)"NO",(char*)"Norway", + (char*)"NP",(char*)"Nepal", + (char*)"NR",(char*)"Nauru", + (char*)"NT",(char*)"Neutral Zone", + (char*)"NU",(char*)"Niue", + (char*)"NZ",(char*)"New Zealand", + (char*)"OM",(char*)"Oman", + (char*)"PA",(char*)"Panama", + (char*)"PE",(char*)"Peru", + (char*)"PF",(char*)"French Polynesia", + (char*)"PG",(char*)"Papua New Guinea", + (char*)"PH",(char*)"Philippines", + (char*)"PK",(char*)"Pakistan", + (char*)"PL",(char*)"Poland", + (char*)"PM",(char*)"St. Pierre & Miquelon", + (char*)"PN",(char*)"Pitcairn", + (char*)"PR",(char*)"Puerto Rico", + (char*)"PT",(char*)"Portugal", + (char*)"PW",(char*)"Palau", + (char*)"PY",(char*)"Paraguay", + (char*)"QA",(char*)"Qatar", + (char*)"RE",(char*)"Réunion", + (char*)"RO",(char*)"Romania", + (char*)"RU",(char*)"Russian Federation", + (char*)"RW",(char*)"Rwanda", + (char*)"SA",(char*)"Saudi Arabia", + (char*)"SB",(char*)"Solomon Islands", + (char*)"SC",(char*)"Seychelles", + (char*)"SD",(char*)"Sudan", + (char*)"SE",(char*)"Sweden", + (char*)"SG",(char*)"Singapore", + (char*)"SH",(char*)"St. Helena", + (char*)"SI",(char*)"Slovenia", + (char*)"SJ",(char*)"Svalbard & Jan Mayen Islands", + (char*)"SK",(char*)"Slovakia", + (char*)"SL",(char*)"Sierra Leone", + (char*)"SM",(char*)"San Marino", + (char*)"SN",(char*)"Senegal", + (char*)"SO",(char*)"Somalia", + (char*)"SR",(char*)"Suriname", + (char*)"ST",(char*)"Sao Tome & Principe", + (char*)"SU",(char*)"Union of Soviet Socialist Republics", + (char*)"SV",(char*)"El Salvador", + (char*)"SY",(char*)"Syrian Arab Republic", + (char*)"SZ",(char*)"Swaziland", + (char*)"TC",(char*)"Turks & Caicos Islands", + (char*)"TD",(char*)"Chad", + (char*)"TF",(char*)"French Southern Territories", + (char*)"TG",(char*)"Togo", + (char*)"TH",(char*)"Thailand", + (char*)"TJ",(char*)"Tajikistan", + (char*)"TK",(char*)"Tokelau", + (char*)"TM",(char*)"Turkmenistan", + (char*)"TN",(char*)"Tunisia", + (char*)"TO",(char*)"Tonga", + (char*)"TP",(char*)"East Timor", + (char*)"TR",(char*)"Turkey", + (char*)"TT",(char*)"Trinidad & Tobago", + (char*)"TV",(char*)"Tuvalu", + (char*)"TW",(char*)"Taiwan, Province of China", + (char*)"TZ",(char*)"Tanzania, United Republic of", + (char*)"UA",(char*)"Ukraine", + (char*)"UG",(char*)"Uganda", + (char*)"UM",(char*)"United States Minor Outlying Islands", + (char*)"US",(char*)"United States of America", + (char*)"UY",(char*)"Uruguay", + (char*)"UZ",(char*)"Uzbekistan", + (char*)"VA",(char*)"Vatican City State", + (char*)"VC",(char*)"St. Vincent & the Grenadines", + (char*)"VE",(char*)"Venezuela", + (char*)"VG",(char*)"British Virgin Islands", + (char*)"VI",(char*)"United States Virgin Islands", + (char*)"VN",(char*)"Viet Nam", + (char*)"VU",(char*)"Vanuatu", + (char*)"WF",(char*)"Wallis & Futuna Islands", + (char*)"WS",(char*)"Samoa", + (char*)"YD",(char*)"Democratic Yemen", + (char*)"YE",(char*)"Yemen", + (char*)"YT",(char*)"Mayotte", + (char*)"YU",(char*)"Yugoslavia", + (char*)"ZA",(char*)"South Africa", + (char*)"ZM",(char*)"Zambia", + (char*)"ZR",(char*)"Zaire", + (char*)"ZW",(char*)"Zimbabwe", + (char*)"ZZ",(char*)"Unknown or unspecified country" +}; +//#define COUNTRY_CODES_RAW_SIZE ((__LINE__ - 1 - COUNTRY_CODES_RAW_START_LINE) * 2) +#define COUNTRY_CODES_RAW_SIZE 492 +#define LANGUAGE_CODES_RAW_START_LINE (__LINE__ + 2) +static char * languageCodesraw[]={ + (char*)"aa",(char*)"Afar", + (char*)"ab",(char*)"Abkhazian", + (char*)"ae",(char*)"Avestan", + (char*)"af",(char*)"Afrikaans", + (char*)"ak",(char*)"Akan", + (char*)"am",(char*)"Amharic", + (char*)"an",(char*)"Aragonese", + (char*)"ar",(char*)"Arabic", + (char*)"as",(char*)"Assamese", + (char*)"av",(char*)"Avaric", + (char*)"ay",(char*)"Aymara", + (char*)"az",(char*)"Azerbaijani", + (char*)"ba",(char*)"Bashkir", + (char*)"be",(char*)"Belarusian", + (char*)"bg",(char*)"Bulgarian", + (char*)"bh",(char*)"Bihari", + (char*)"bi",(char*)"Bislama", + (char*)"bm",(char*)"Bambara", + (char*)"bn",(char*)"Bengali", + (char*)"bo",(char*)"Tibetan", + (char*)"br",(char*)"Breton", + (char*)"bs",(char*)"Bosnian", + (char*)"ca",(char*)"Catalan", + (char*)"ce",(char*)"Chechen", + (char*)"ch",(char*)"Chamorro", + (char*)"co",(char*)"Corsican", + (char*)"cr",(char*)"Cree", + (char*)"cs",(char*)"Czech", + (char*)"cu",(char*)"ChurchSlavic", + (char*)"cv",(char*)"Chuvash", + (char*)"cy",(char*)"Welsh", + (char*)"da",(char*)"Danish", + (char*)"de",(char*)"German", + (char*)"dv",(char*)"Divehi", + (char*)"dz",(char*)"Dzongkha", + (char*)"ee",(char*)"Ewe", + (char*)"el",(char*)"ModernGreek", + (char*)"en",(char*)"English", + (char*)"eo",(char*)"Esperanto", + (char*)"es",(char*)"Spanish", + (char*)"et",(char*)"Estonian", + (char*)"eu",(char*)"Basque", + (char*)"fa",(char*)"Persian", + (char*)"ff",(char*)"Fulah", + (char*)"fi",(char*)"Finnish", + (char*)"fj",(char*)"Fijian", + (char*)"fo",(char*)"Faroese", + (char*)"fr",(char*)"French", + (char*)"fy",(char*)"WesternFrisian", + (char*)"ga",(char*)"Irish", + (char*)"gd",(char*)"Gaelic", + (char*)"gl",(char*)"Galician", + (char*)"gn",(char*)"Guaraní", + (char*)"gu",(char*)"Gujarati", + (char*)"gv",(char*)"Manx", + (char*)"ha",(char*)"Hausa", + (char*)"he",(char*)"ModernHebrew", + (char*)"hi",(char*)"Hindi", + (char*)"ho",(char*)"HiriMotu", + (char*)"hr",(char*)"Croatian", + (char*)"ht",(char*)"Haitian", + (char*)"hu",(char*)"Hungarian", + (char*)"hy",(char*)"Armenian", + (char*)"hz",(char*)"Herero", + (char*)"ia",(char*)"Interlingua", + (char*)"id",(char*)"Indonesian", + (char*)"ie",(char*)"Interlingue", + (char*)"ig",(char*)"Igbo", + (char*)"ii",(char*)"SichuanYi", + (char*)"ik",(char*)"Inupiaq", + (char*)"io",(char*)"Ido", + (char*)"is",(char*)"Icelandic", + (char*)"it",(char*)"Italian", + (char*)"iu",(char*)"Inuktitut", + (char*)"ja",(char*)"Japanese", + (char*)"jv",(char*)"Javanese", + (char*)"ka",(char*)"Georgian", + (char*)"kg",(char*)"Kongo", + (char*)"ki",(char*)"Kikuyu", + (char*)"kj",(char*)"Kwanyama", + (char*)"kk",(char*)"Kazakh", + (char*)"kl",(char*)"Kalaallisut", + (char*)"km",(char*)"CentralKhmer", + (char*)"kn",(char*)"Kannada", + (char*)"ko",(char*)"Korean", + (char*)"kr",(char*)"Kanuri", + (char*)"ks",(char*)"Kashmiri", + (char*)"ku",(char*)"Kurdish", + (char*)"kv",(char*)"Komi", + (char*)"kw",(char*)"Cornish", + (char*)"ky",(char*)"Kirghiz", + (char*)"la",(char*)"Latin", + (char*)"lb",(char*)"Luxembourgish", + (char*)"lg",(char*)"Ganda", + (char*)"li",(char*)"Limburgish", + (char*)"ln",(char*)"Lingala", + (char*)"lo",(char*)"Lao", + (char*)"lt",(char*)"Lithuanian", + (char*)"lu",(char*)"Luba-Katanga", + (char*)"lv",(char*)"Latvian", + (char*)"mg",(char*)"Malagasy", + (char*)"mh",(char*)"Marshallese", + (char*)"mi",(char*)"Maori", + (char*)"mk",(char*)"Macedonian", + (char*)"ml",(char*)"Malayalam", + (char*)"mn",(char*)"Mongolian", + (char*)"mr",(char*)"Marathi", + (char*)"ms",(char*)"Malay", + (char*)"mt",(char*)"Maltese", + (char*)"my",(char*)"Burmese", + (char*)"na",(char*)"Nauru", + (char*)"nb",(char*)"NorwegianBokmal", + (char*)"nd",(char*)"NorthNdebele", + (char*)"ne",(char*)"Nepali", + (char*)"ng",(char*)"Ndonga", + (char*)"nl",(char*)"Dutch/Flemish", + (char*)"nn",(char*)"NorwegianNynorsk", + (char*)"no",(char*)"Norwegian", + (char*)"nr",(char*)"SouthNdebele", + (char*)"nv",(char*)"Navajo/Navaho", + (char*)"ny",(char*)"Nyanja", + (char*)"oc",(char*)"Occitan", + (char*)"oj",(char*)"Ojibwa", + (char*)"om",(char*)"Oromo", + (char*)"or",(char*)"Oriya", + (char*)"os",(char*)"Ossetian", + (char*)"pa",(char*)"Panjabi", + (char*)"pi",(char*)"Pali", + (char*)"pl",(char*)"Polish", + (char*)"ps",(char*)"Pashto/Pushto", + (char*)"pt",(char*)"Portuguese", + (char*)"qu",(char*)"Quechua", + (char*)"rm",(char*)"Romansh", + (char*)"rn",(char*)"Rundi", + (char*)"ro",(char*)"Romanian", + (char*)"ru",(char*)"Russian", + (char*)"rw",(char*)"Kinyarwanda", + (char*)"sa",(char*)"Sanskrit", + (char*)"sc",(char*)"Sardinian", + (char*)"sd",(char*)"Sindhi", + (char*)"se",(char*)"NorthernSami", + (char*)"sg",(char*)"Sango", + (char*)"si",(char*)"Sinhala", + (char*)"sk",(char*)"Slovak", + (char*)"sl",(char*)"Slovene", + (char*)"sm",(char*)"Samoan", + (char*)"sn",(char*)"Shona", + (char*)"so",(char*)"Somali", + (char*)"sq",(char*)"Albanian", + (char*)"sr",(char*)"Serbian", + (char*)"ss",(char*)"Swati", + (char*)"st",(char*)"SouthernSotho", + (char*)"su",(char*)"Sundanese", + (char*)"sv",(char*)"Swedish", + (char*)"sw",(char*)"Swahili", + (char*)"ta",(char*)"Tamil", + (char*)"te",(char*)"Telugu", + (char*)"tg",(char*)"Tajik", + (char*)"th",(char*)"Thai", + (char*)"ti",(char*)"Tigrinya", + (char*)"tk",(char*)"Turkmen", + (char*)"tl",(char*)"Tagalog", + (char*)"tn",(char*)"Tswana", + (char*)"to",(char*)"Tonga", + (char*)"tr",(char*)"Turkish", + (char*)"ts",(char*)"Tsonga", + (char*)"tt",(char*)"Tatar", + (char*)"tw",(char*)"Twi", + (char*)"ty",(char*)"Tahitian", + (char*)"ug",(char*)"Uighur", + (char*)"uk",(char*)"Ukrainian", + (char*)"ur",(char*)"Urdu", + (char*)"uz",(char*)"Uzbek", + (char*)"ve",(char*)"Venda", + (char*)"vi",(char*)"Vietnamese", + (char*)"vo",(char*)"Volapük", + (char*)"wa",(char*)"Walloon", + (char*)"wo",(char*)"Wolof", + (char*)"xh",(char*)"Xhosa", + (char*)"yi",(char*)"Yiddish", + (char*)"yo",(char*)"Yoruba", + (char*)"za",(char*)"Zhuang", + (char*)"zh",(char*)"Chinese", + (char*)"zu",(char*)"Zulu" +}; +//#define LANGUAGE_CODES_RAW_SIZE ((__LINE__ - 1 - LANGUAGE_CODES_RAW_START_LINE) * 2) +#define LANGUAGE_CODES_RAW_SIZE 368 + +lggHunSpell_Wrapper::lggHunSpell_Wrapper() +{ + //languageCodes(begin(languageCodesraw), end(languageCodesraw)); +// mSpellCheckHighlight = rebind_llcontrol("SpellDisplay", &gSavedSettings, true); +} + +lggHunSpell_Wrapper::~lggHunSpell_Wrapper() +{ +} + +BOOL lggHunSpell_Wrapper::getSpellCheckHighlight() +{ + static const LLCachedControl mSpellCheckHighlight("SpellDisplay", false); + + return mSpellCheckHighlight; +} + +std::string lggHunSpell_Wrapper::getCorrectPath(std::string file) +{ + //finds out if it is in user dir, if not, takes it from app dir + std::string dicpath1(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries", file).c_str()); + if (!gDirUtilp->fileExists(dicpath1)) + { + dicpath1=gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "dictionaries", file).c_str(); + } + return dicpath1; +} + +void lggHunSpell_Wrapper::setNewDictionary(std::string newDict) +{ + + llinfos << "Setting new base dictionary long name is-> " << newDict.c_str() << llendl; + + currentBaseDic = newDict; + + //expecting a full name comming in + newDict = fullName2DictName(newDict); + + if (myHunspell) + { + delete myHunspell; + } + + std::string dicaffpath = getCorrectPath(newDict+".aff"); + std::string dicdicpath = getCorrectPath(newDict+".dic"); + + llinfos << "Setting new base dictionary -> " << dicaffpath.c_str() << llendl; + + myHunspell = new Hunspell(dicaffpath.c_str(), dicdicpath.c_str()); + llinfos << "Adding custom dictionary " << llendl; + createCustomDic(); + addDictionary("custom"); + std::vector toInstall = getInstalledDicts(); + for (int i = 0; i < (int)toInstall.size(); i++) + { + addDictionary(toInstall[i]); + } +} + +void lggHunSpell_Wrapper::createCustomDic() +{ + std::string filename(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries", "custom.dic")); + if (!gDirUtilp->fileExists(filename)) + { + llofstream export_file; + export_file.open(filename); + std::string sizePart("1\nLordGregGreg\n"); + export_file.write(sizePart.c_str(),sizePart.length()); + export_file.close(); + } +} + +void lggHunSpell_Wrapper::addWordToCustomDictionary(std::string wordToAdd) +{ + if (!myHunspell) + { + return; + } + + myHunspell->add(wordToAdd.c_str()); + std::string filename(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries", "custom.dic")); + std::vector lines; + if (gDirUtilp->fileExists(filename)) + { + //get words already there.. + llifstream importer(filename); + std::string line; + if (getline( importer, line ))//ignored the size + { + while ( getline( importer, line ) ) lines.push_back(line); + } + importer.close(); + } + + llofstream export_file; + export_file.open(filename); + std::string sizePart(llformat("%i", (int)(lines.size()+1)) + "\n"); + export_file.write(sizePart.c_str(), sizePart.length()); + for (int i = 0; i < (int)lines.size() ;i++) + { + export_file.write(std::string(lines[i]+"\n").c_str(),lines[i].length()+1); + } + //LLStringUtil::toLower(wordToAdd); + wordToAdd = wordToAdd+std::string("\n"); + export_file.write(wordToAdd.c_str(), wordToAdd.length()); + //export_file << std::hex << 10 ; + export_file.close(); +} + +BOOL lggHunSpell_Wrapper::isSpelledRight(std::string wordToCheck) +{ + if (!myHunspell || wordToCheck.length() < 3) + { + return TRUE; + } + return myHunspell->spell(wordToCheck.c_str()); +} + +std::vector lggHunSpell_Wrapper::getSuggestionList(std::string badWord) +{ + std::vector toReturn; + if (!myHunspell) + { + return toReturn; + } + + char** suggestionList; + int numberOfSuggestions = myHunspell->suggest(&suggestionList, badWord.c_str()); + if (numberOfSuggestions <= 0) + { + return toReturn; + } + for (int i = 0; i < numberOfSuggestions; i++) + { + std::string tempSugg(suggestionList[i]); + toReturn.push_back(tempSugg); + } + myHunspell->free_list(&suggestionList,numberOfSuggestions); + return toReturn; +} + +void lggHunSpell_Wrapper::debugTest(std::string testWord) +{ + llinfos << "Testing to see if " << testWord.c_str() << " is spelled correct" << llendl; + + if (isSpelledRight(testWord)) + { + llinfos << testWord.c_str() << " is spelled correctly" << llendl; + } + else + { + llinfos << testWord.c_str() << " is not spelled correctly, getting suggestions" << llendl; + std::vector suggList; + suggList.clear(); + suggList = getSuggestionList(testWord); + llinfos << "Got suggestions.. " << llendl; + + for (int i = 0; i < (int)suggList.size(); i++) + { + llinfos << "Suggestion for " << testWord.c_str() << ":" << suggList[i].c_str() << llendl; + } + } +} + +void lggHunSpell_Wrapper::initSettings() +{ + glggHunSpell = new lggHunSpell_Wrapper(); + glggHunSpell->processSettings(); +} + +void lggHunSpell_Wrapper::processSettings() +{ + //expects everything to already be in saved settings + //this will also reload and read the installed dicts + setNewDictionary(gSavedSettings.getString("SpellBase")); +} + +void lggHunSpell_Wrapper::addDictionary(std::string additionalDictionary) +{ + if (!myHunspell || additionalDictionary.empty()) + { + return; + } + + //expecting a full name here + std::string dicpath = getCorrectPath(fullName2DictName(additionalDictionary)+".dic"); + if (gDirUtilp->fileExists(dicpath)) + { + llinfos << "Adding additional dictionary -> " << dicpath.c_str() << llendl; + myHunspell->add_dic(dicpath.c_str()); + } +} + +std::string lggHunSpell_Wrapper::dictName2FullName(std::string dictName) +{ + if (dictName.empty()) + { + return dictName; + } + + std::string countryCode(""); + std::string languageCode(""); + + //remove extension + dictName = dictName.substr(0,dictName.find(".")); + + //break it up by - or _ + S32 breakPoint = dictName.find("-"); + if (breakPoint == std::string::npos) + { + breakPoint = dictName.find("_"); + } + if (breakPoint == std::string::npos) + { + //no country code given + languageCode = dictName; + } + else + { + languageCode = dictName.substr(0,breakPoint); + countryCode = dictName.substr(breakPoint+1); + } + + //get long language code + for (int i = 0; i lggHunSpell_Wrapper::getDicts() +{ + std::vector names; + std::string path_name(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "dictionaries", "")); + bool found = true; + while (found) + { + std::string name; + found = gDirUtilp->getNextFileInDir(path_name, "*.aff", name, false); + if (found) + { + names.push_back(dictName2FullName(name)); + } + } + path_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries", ""); + found = true; + while (found) + { + std::string name; + found = gDirUtilp->getNextFileInDir(path_name, "*.aff", name, false); + if (found) + { + names.push_back(dictName2FullName(name)); + } + } + + return names; +} + +std::vector lggHunSpell_Wrapper::getExtraDicts() +{ + std::vector names; + std::string path_name(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "dictionaries", "")); + bool found = true; + while (found) + { + std::string name; + found = gDirUtilp->getNextFileInDir(path_name, "*.dic", name, false); + if (found) + { + names.push_back(dictName2FullName(name)); + } + } + path_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries", ""); + found = true; + while (found) + { + std::string name; + found = gDirUtilp->getNextFileInDir(path_name, "*.dic", name, false); + if (found) + { + names.push_back(dictName2FullName(name)); + } + } + return names; +} + +std::vector lggHunSpell_Wrapper::getInstalledDicts() +{ + std::vector toReturn; + //expecting short names to be stored... + std::vector shortNames = CSV2VEC(gSavedSettings.getString("SpellInstalled")); + for (int i =0; i < (int)shortNames.size(); i++) + { + toReturn.push_back(dictName2FullName(shortNames[i])); + } + return toReturn; +} + +std::vector lggHunSpell_Wrapper::getAvailDicts() +{ + std::vector toReturn; + std::vector dics = getExtraDicts(); + std::vector installedDics = getInstalledDicts(); + for (int i = 0; i < (int)dics.size(); i++) + { + bool found = false; + for (int j = 0; j < (int)installedDics.size(); j++) + { + if (0 == LLStringUtil::compareInsensitive(dics[i], installedDics[j])) + { + found = true;//this dic is already installed + } + } + if (0 == LLStringUtil::compareInsensitive(dics[i], currentBaseDic)) + { + found = true; + } + if (0 == LLStringUtil::compareInsensitive(dics[i], "custom")) + { + found = true; + } + if (!found) + { + toReturn.push_back(dics[i]); + } + } + return toReturn; +} + +std::vector lggHunSpell_Wrapper::CSV2VEC(std::string csv) +{ + std::vector toReturn; + boost::regex re(","); + boost::sregex_token_iterator i(csv.begin(), csv.end(), re, -1); + boost::sregex_token_iterator j; + while (i != j) + { + toReturn.push_back(*i++); + } + return toReturn; +} + +std::string lggHunSpell_Wrapper::VEC2CSV(std::vector vec) +{ + std::string toReturn(""); + if (vec.size() < 1) + { + return toReturn; + } + + for (int i = 0;i < (int)vec.size() ;i++) + { + toReturn += vec[i] + ","; + } + return toReturn.erase(toReturn.length()-1); +} + +void lggHunSpell_Wrapper::addButton(std::string selection) +{ + if (selection.empty()) + { + return; + } + addDictionary(selection); + std::vector alreadyInstalled = CSV2VEC(gSavedSettings.getString("SpellInstalled")); + alreadyInstalled.push_back(fullName2DictName(selection)); + gSavedSettings.setString("SpellInstalled", VEC2CSV(alreadyInstalled)); +} + +void lggHunSpell_Wrapper::removeButton(std::string selection) +{ + if (selection.empty()) + { + return; + } + std::vector newInstalledDics; + std::vector currentlyInstalled = getInstalledDicts(); + for (int i = 0; i < (int)currentlyInstalled.size(); i++) + { + if (0 != LLStringUtil::compareInsensitive(selection, currentlyInstalled[i])) + { + newInstalledDics.push_back(fullName2DictName(currentlyInstalled[i])); + } + } + gSavedSettings.setString("SpellInstalled", VEC2CSV(newInstalledDics)); + processSettings(); +} + +void lggHunSpell_Wrapper::newDictSelection(std::string selection) +{ + currentBaseDic = selection; + gSavedSettings.setString("SpellBase", selection); + //better way to do this would be to check and see if there is a installed conflict + //and then only remove that one.. messy + gSavedSettings.setString("SpellInstalled", "en_sl"); + processSettings(); +} + +void lggHunSpell_Wrapper::getMoreButton(void* data) +{ + std::vector shortNames; + std::vector longNames; + LLSD response = LLHTTPClient::blockingGet(gSavedSettings.getString("SpellDownloadURL")+"dic_list.xml"); + if (response.has("body")) + { + const LLSD &dict_list = response["body"]; + if (dict_list.has("isComplete")) + { + LLSD dics = dict_list["data"]; + for (int i = 0; i < dics.size(); i++) + { + std::string dicFullName = dictName2FullName(dics[i].asString()); + longNames.push_back(dicFullName); + shortNames.push_back(fullName2DictName(dicFullName)); + } + LggDicDownload::show(true,shortNames,longNames, data); + } + } +} + +void lggHunSpell_Wrapper::editCustomButton() +{ + std::string dicdicpath(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries", std::string("custom.dic")).c_str()); + + if (!gDirUtilp->fileExists(dicdicpath)) + { + createCustomDic(); + //glggHunSpell->addWordToCustomDictionary("temp"); + } + + gViewerWindow->getWindow()->ShellEx(dicdicpath); +} + +void lggHunSpell_Wrapper::setSpellCheckHighlight(BOOL highlight) +{ + gSavedSettings.setBOOL("SpellDisplay", highlight); +} diff --git a/indra/newview/lgghunspell_wrapper.h b/indra/newview/lgghunspell_wrapper.h new file mode 100644 index 000000000..51c1f37a7 --- /dev/null +++ b/indra/newview/lgghunspell_wrapper.h @@ -0,0 +1,75 @@ +/* Copyright (C) 2009 LordGregGreg Back + + This is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the viewer; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef ASPELL_WRAPPER +#define ASPELL_WRAPPER 1 + +#if LL_WINDOWS +#include "hunspell/hunspelldll.h" +#else +#include "hunspell/hunspell.hxx" +#endif + +class lggHunSpell_Wrapper +{ + +public: + static Hunspell* myHunspell; + + static void initSettings(); + void processSettings(); + + std::vector getAvailDicts(); + std::vector getInstalledDicts(); + std::vector getDicts(); + std::vector getExtraDicts(); + void addDictionary(std::string additionalDictionary); + void addWordToCustomDictionary(std::string wordToAdd); + void addButton(std::string selection); + void removeButton(std::string selection); + void editCustomButton(); + void newDictSelection(std::string selection); + void getMoreButton(void * data); + static std::string dictName2FullName(std::string dictName); + static std::string fullName2DictName(std::string fullName); + void setNewDictionary(std::string newDict); + BOOL isSpelledRight(std::string wordToCheck); + std::vector getSuggestionList(std::string badWord); + S32 findNextError(std::string haystack, int startAt); + + std::vector CSV2VEC(std::string csv); + std::string VEC2CSV(std::vector vec); + + void setSpellCheckHighlight(BOOL highlight); + BOOL getSpellCheckHighlight(); + +private: + void createCustomDic(); + std::string getCorrectPath(std::string file); + lggHunSpell_Wrapper(); + ~lggHunSpell_Wrapper(); + + void debugTest(std::string testWord);//prints out debug about testing the word + std::string currentBaseDic; + //std::vector languageCodes; + //std::vector countryCodes; +// BOOL* mSpellCheckHighlight; +}; + +extern lggHunSpell_Wrapper* glggHunSpell; // the singleton hunspell wrapper + +#endif diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index d5bb4969d..82955afea 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -204,8 +204,8 @@ LLPreferenceCore::LLPreferenceCore(LLTabContainer* tab_container, LLButton * def mGridsPanel->setDefaultBtn(default_btn); mPrefsAscentSys = new LLPrefsAscentSys(); - mTabContainer->addTabPanel(mPrefsAscentSys->getPanel(), mPrefsAscentSys->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer); - mPrefsAscentSys->getPanel()->setDefaultBtn(default_btn); + mTabContainer->addTabPanel(mPrefsAscentSys, mPrefsAscentSys->getLabel(), FALSE, onTabChanged, mTabContainer); + mPrefsAscentSys->setDefaultBtn(default_btn); mPrefsAscentVan = new LLPrefsAscentVan(); mTabContainer->addTabPanel(mPrefsAscentVan->getPanel(), mPrefsAscentVan->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 3a843e5c6..d58fcffa7 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -210,6 +210,7 @@ // #include "llavatarnamecache.h" +#include "lgghunspell_wrapper.h" // [RLVa:KB] #include "rlvhandler.h" @@ -412,6 +413,7 @@ bool idle_startup() // // Initialize stuff that doesn't need data from simulators // + glggHunSpell->initSettings(); // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-10 (RLVa-1.0.0g) | Modified: RLVa-0.2.1d if ( (gSavedSettings.controlExists(RLV_SETTING_MAIN)) && (gSavedSettings.getBOOL(RLV_SETTING_MAIN)) ) diff --git a/indra/newview/skins/default/xui/en-us/floater_about_land.xml b/indra/newview/skins/default/xui/en-us/floater_about_land.xml index f1fd3617d..42c7452c4 100644 --- a/indra/newview/skins/default/xui/en-us/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en-us/floater_about_land.xml @@ -32,7 +32,7 @@ font="SansSerifSmall" handle_edit_keys_directly="false" height="52" left="96" max_length="255" mouse_opaque="true" name="Description" select_all_on_focus_received="false" select_on_focus="false" - width="350" word_wrap="true" /> + width="350" word_wrap="true" spell_check="true" /> About Land or select another parcel to show its details. + width="330" word_wrap="true" spell_check="true"> There is no Covenant provided for this Estate. + max_length="254" name="description_form" right="-10" spell_check="true" /> + font="SansSerifSmall" max_length="256" mouse_opaque="true" spell_check="true" /> Owner search string: diff --git a/indra/newview/skins/default/xui/en-us/floater_buy_land.xml b/indra/newview/skins/default/xui/en-us/floater_buy_land.xml index 1e650f29d..598f21d82 100644 --- a/indra/newview/skins/default/xui/en-us/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/en-us/floater_buy_land.xml @@ -67,7 +67,7 @@ + width="271" word_wrap="true" spell_check="true"> Loading... + text_readonly_color="ChatHistoryTextColor" width="299" word_wrap="true" spell_check="true" /> + text_readonly_color="ChatHistoryTextColor" width="300" word_wrap="true" spell_check="true"/> Gestures @@ -108,7 +108,7 @@ handle_edit_keys_directly="false" height="20" label="Click here to chat." left="0" max_length="254" mouse_opaque="true" name="Chat Editor" right="-70" select_all_on_focus_received="false" select_on_focus="false" - tab_group="1" /> + tab_group="1" spell_check="true" /> diff --git a/indra/newview/skins/default/xui/en-us/floater_dictionaries.xml b/indra/newview/skins/default/xui/en-us/floater_dictionaries.xml new file mode 100644 index 000000000..0aec71578 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/floater_dictionaries.xml @@ -0,0 +1,15 @@ + + + +Below is a list of additional dictionaries that can +be downloaded from the Imprudence website. + +Please select the dictionary you wish to have available +for install, and then press the download button. + +