Address Issue 1298: Add Auto-replace text feature, borrowed from LL~
This commit is contained in:
@@ -921,6 +921,23 @@ void LLLineEditor::addChar(const llwchar uni_char)
|
||||
|
||||
mText.insert(getCursor(), w_buf);
|
||||
setCursor(getCursor() + 1);
|
||||
|
||||
if (!mReadOnly && mAutoreplaceCallback != NULL)
|
||||
{
|
||||
// autoreplace the text, if necessary
|
||||
S32 replacement_start;
|
||||
S32 replacement_length;
|
||||
LLWString replacement_string;
|
||||
S32 new_cursor_pos = getCursor();
|
||||
mAutoreplaceCallback(replacement_start, replacement_length, replacement_string, new_cursor_pos, mText);
|
||||
|
||||
if (replacement_length > 0 || !replacement_string.empty())
|
||||
{
|
||||
mText.erase(replacement_start, replacement_length);
|
||||
mText.insert(replacement_start, replacement_string);
|
||||
setCursor(new_cursor_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -167,6 +167,10 @@ public:
|
||||
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
|
||||
|
||||
typedef boost::function<void(S32&, S32&, LLWString&, S32&, const LLWString&)> autoreplace_callback_t;
|
||||
autoreplace_callback_t mAutoreplaceCallback;
|
||||
void setAutoreplaceCallback(autoreplace_callback_t cb) { mAutoreplaceCallback = cb; }
|
||||
|
||||
// assumes UTF8 text
|
||||
virtual void setValue(const LLSD& value ) { setText(value.asString()); }
|
||||
virtual LLSD getValue() const { return LLSD(getText()); }
|
||||
|
||||
@@ -126,6 +126,7 @@ set(viewer_SOURCE_FILES
|
||||
llassetuploadresponders.cpp
|
||||
llattachmentsmgr.cpp
|
||||
llaudiosourcevo.cpp
|
||||
llautoreplace.cpp
|
||||
llavataractions.cpp
|
||||
llavatarpropertiesprocessor.cpp
|
||||
llbox.cpp
|
||||
@@ -181,6 +182,7 @@ set(viewer_SOURCE_FILES
|
||||
llfloaterabout.cpp
|
||||
llfloateractivespeakers.cpp
|
||||
llfloaterauction.cpp
|
||||
llfloaterautoreplacesettings.cpp
|
||||
llfloateravatarinfo.cpp
|
||||
llfloateravatarlist.cpp
|
||||
llfloateravatarpicker.cpp
|
||||
@@ -646,6 +648,7 @@ set(viewer_HEADER_FILES
|
||||
llassetuploadresponders.h
|
||||
llattachmentsmgr.h
|
||||
llaudiosourcevo.h
|
||||
llautoreplace.h
|
||||
llavataractions.h
|
||||
llavatarpropertiesprocessor.h
|
||||
llbox.h
|
||||
@@ -701,6 +704,7 @@ set(viewer_HEADER_FILES
|
||||
llfloaterabout.h
|
||||
llfloateractivespeakers.h
|
||||
llfloaterauction.h
|
||||
llfloaterautoreplacesettings.h
|
||||
llfloateravatarinfo.h
|
||||
llfloateravatarlist.h
|
||||
llfloateravatarpicker.h
|
||||
|
||||
8330
indra/newview/app_settings/autoreplace.xml
Normal file
8330
indra/newview/app_settings/autoreplace.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2704,6 +2704,17 @@ This should be as low as possible, but too low may break functionality</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>AutoReplace</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Replaces keywords with a configured word or phrase</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>AutoAcceptAllNewInventory</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
||||
@@ -908,6 +908,17 @@
|
||||
<key>Value</key>
|
||||
<boolean>0</boolean>
|
||||
</map>
|
||||
<key>ToolbarVisibleAutoReplace</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Whether or not the button for autoreplace is on the toolbar</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<boolean>0</boolean>
|
||||
</map>
|
||||
<key>ToolbarVisibleBeacons</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "llagent.h"
|
||||
#include "llcolorswatch.h"
|
||||
#include "llcombobox.h"
|
||||
#include "llfloaterautoreplacesettings.h"
|
||||
#include "llradiogroup.h"
|
||||
#include "lluictrlfactory.h"
|
||||
#include "llviewercontrol.h"
|
||||
@@ -92,6 +93,7 @@ LLPrefsAscentChat::LLPrefsAscentChat()
|
||||
getChild<LLUICtrl>("antispam_checkbox")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitDialogBlock, this, _1, _2));
|
||||
getChild<LLUICtrl>("Group Invites")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitDialogBlock, this, _1, _2));
|
||||
|
||||
getChild<LLUICtrl>("autoreplace")->setCommitCallback(boost::bind(LLFloaterAutoReplaceSettings::showInstance, LLSD()));
|
||||
getChild<LLUICtrl>("KeywordsOn")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1));
|
||||
getChild<LLUICtrl>("KeywordsList")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1));
|
||||
getChild<LLUICtrl>("KeywordsSound")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1));
|
||||
|
||||
802
indra/newview/llautoreplace.cpp
Normal file
802
indra/newview/llautoreplace.cpp
Normal file
@@ -0,0 +1,802 @@
|
||||
/**
|
||||
* @file llautoreplace.cpp
|
||||
* @brief Auto Replace Manager
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library 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 library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
#include "llautoreplace.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llboost.h"
|
||||
#include "llcontrol.h"
|
||||
#include "llviewercontrol.h"
|
||||
#include "llnotificationsutil.h"
|
||||
|
||||
const char* LLAutoReplace::SETTINGS_FILE_NAME = "autoreplace.xml";
|
||||
|
||||
void LLAutoReplace::autoreplaceCallback(S32& replacement_start, S32& replacement_length, LLWString& replacement_string, S32& cursor_pos, const LLWString& input_text)
|
||||
{
|
||||
// make sure these returned values are cleared in case there is no replacement
|
||||
replacement_start = 0;
|
||||
replacement_length = 0;
|
||||
replacement_string.clear();
|
||||
|
||||
static LLCachedControl<bool> perform_autoreplace(gSavedSettings, "AutoReplace", 0);
|
||||
if (perform_autoreplace)
|
||||
{
|
||||
S32 word_end = cursor_pos - 1;
|
||||
|
||||
bool at_space = (input_text[word_end] == ' ');
|
||||
bool have_word = (LLWStringUtil::isPartOfWord(input_text[word_end]));
|
||||
|
||||
if (at_space || have_word)
|
||||
{
|
||||
if (at_space && word_end > 0)
|
||||
{
|
||||
// find out if this space immediately follows a word
|
||||
word_end--;
|
||||
have_word = (LLWStringUtil::isPartOfWord(input_text[word_end]));
|
||||
}
|
||||
if (have_word)
|
||||
{
|
||||
// word_end points to the end of a word, now find the start of the word
|
||||
std::string word;
|
||||
S32 word_start = word_end;
|
||||
for (S32 back_one = word_start - 1;
|
||||
back_one >= 0 && LLWStringUtil::isPartOfWord(input_text[back_one]);
|
||||
back_one--
|
||||
)
|
||||
{
|
||||
word_start--; // walk word_start back to the beginning of the word
|
||||
}
|
||||
LL_DEBUGS("AutoReplace") << "word_start: " << word_start << " word_end: " << word_end << LL_ENDL;
|
||||
std::string str_text = std::string(input_text.begin(), input_text.end());
|
||||
std::string last_word = str_text.substr(word_start, word_end - word_start + 1);
|
||||
std::string replacement_word(mSettings.replaceWord(last_word));
|
||||
|
||||
if (replacement_word != last_word)
|
||||
{
|
||||
// The last word is one for which we have a replacement
|
||||
if (at_space)
|
||||
{
|
||||
// return the replacement string
|
||||
replacement_start = word_start;
|
||||
replacement_length = last_word.length();
|
||||
replacement_string = utf8str_to_wstring(replacement_word);
|
||||
LLWString old_string = utf8str_to_wstring(last_word);
|
||||
S32 size_change = replacement_string.size() - old_string.size();
|
||||
cursor_pos += size_change;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string LLAutoReplace::getUserSettingsFileName()
|
||||
{
|
||||
std::string path=gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "");
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, SETTINGS_FILE_NAME);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string LLAutoReplace::getAppSettingsFileName()
|
||||
{
|
||||
std::string path=gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "");
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, SETTINGS_FILE_NAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_ERRS("AutoReplace") << "Failed to get app settings directory name" << LL_ENDL;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
LLAutoReplaceSettings LLAutoReplace::getSettings()
|
||||
{
|
||||
return mSettings;
|
||||
}
|
||||
|
||||
void LLAutoReplace::setSettings(const LLAutoReplaceSettings& newSettings)
|
||||
{
|
||||
mSettings.set(newSettings);
|
||||
/// Make the newSettings active and write them to user storage
|
||||
saveToUserSettings();
|
||||
}
|
||||
|
||||
LLAutoReplace::LLAutoReplace()
|
||||
{
|
||||
}
|
||||
|
||||
void LLAutoReplace::initSingleton()
|
||||
{
|
||||
loadFromSettings();
|
||||
}
|
||||
|
||||
void LLAutoReplace::loadFromSettings()
|
||||
{
|
||||
std::string filename=getUserSettingsFileName();
|
||||
if (filename.empty())
|
||||
{
|
||||
LL_INFOS("AutoReplace") << "no valid user settings directory." << LL_ENDL;
|
||||
}
|
||||
if(gDirUtilp->fileExists(filename))
|
||||
{
|
||||
LLSD userSettings;
|
||||
llifstream file;
|
||||
file.open(filename.c_str());
|
||||
if (file.is_open())
|
||||
{
|
||||
LLSDSerialize::fromXML(userSettings, file);
|
||||
}
|
||||
file.close();
|
||||
if ( mSettings.setFromLLSD(userSettings) )
|
||||
{
|
||||
LL_INFOS("AutoReplace") << "settings loaded from '" << filename << "'" << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "invalid settings found in '" << filename << "'" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
else // no user settings found, try application settings
|
||||
{
|
||||
std::string defaultName = getAppSettingsFileName();
|
||||
LL_INFOS("AutoReplace") << " user settings file '" << filename << "' not found"<< LL_ENDL;
|
||||
|
||||
bool gotSettings = false;
|
||||
if(gDirUtilp->fileExists(defaultName))
|
||||
{
|
||||
LLSD appDefault;
|
||||
llifstream file;
|
||||
file.open(defaultName.c_str());
|
||||
if (file.is_open())
|
||||
{
|
||||
LLSDSerialize::fromXMLDocument(appDefault, file);
|
||||
}
|
||||
file.close();
|
||||
|
||||
if ( mSettings.setFromLLSD(appDefault) )
|
||||
{
|
||||
LL_INFOS("AutoReplace") << "settings loaded from '" << defaultName.c_str() << "'" << LL_ENDL;
|
||||
gotSettings = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "invalid settings found in '" << defaultName.c_str() << "'" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! gotSettings )
|
||||
{
|
||||
if (mSettings.setFromLLSD(mSettings.getExampleLLSD()))
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "no settings found; loaded example." << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "no settings found and example invalid!" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLAutoReplace::saveToUserSettings()
|
||||
{
|
||||
std::string filename=getUserSettingsFileName();
|
||||
llofstream file;
|
||||
file.open(filename.c_str());
|
||||
LLSDSerialize::toPrettyXML(mSettings.asLLSD(), file);
|
||||
file.close();
|
||||
LL_INFOS("AutoReplace") << "settings saved to '" << filename << "'" << LL_ENDL;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// LLAutoReplaceSettings
|
||||
// ================================================================
|
||||
|
||||
const std::string LLAutoReplaceSettings::AUTOREPLACE_LIST_NAME = "name"; ///< key for looking up list names
|
||||
const std::string LLAutoReplaceSettings::AUTOREPLACE_LIST_REPLACEMENTS = "replacements"; ///< key for looking up replacement map
|
||||
|
||||
LLAutoReplaceSettings::LLAutoReplaceSettings()
|
||||
{
|
||||
}
|
||||
|
||||
LLAutoReplaceSettings::LLAutoReplaceSettings(const LLAutoReplaceSettings& settings)
|
||||
{
|
||||
// copy all values through fundamental type intermediates for thread safety
|
||||
mLists = LLSD::emptyArray();
|
||||
|
||||
for ( LLSD::array_const_iterator list = settings.mLists.beginArray(), listEnd = settings.mLists.endArray();
|
||||
list != listEnd;
|
||||
list++
|
||||
)
|
||||
{
|
||||
if ( (*list).isMap() ) // can fail due to LLSD-30: ignore it
|
||||
{
|
||||
LLSD listMap = LLSD::emptyMap();
|
||||
std::string listName = (*list)[AUTOREPLACE_LIST_NAME];
|
||||
listMap[AUTOREPLACE_LIST_NAME] = listName;
|
||||
listMap[AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();
|
||||
|
||||
for ( LLSD::map_const_iterator
|
||||
entry = (*list)[AUTOREPLACE_LIST_REPLACEMENTS].beginMap(),
|
||||
entriesEnd = (*list)[AUTOREPLACE_LIST_REPLACEMENTS].endMap();
|
||||
entry != entriesEnd;
|
||||
entry++
|
||||
)
|
||||
{
|
||||
std::string keyword = entry->first;
|
||||
std::string replacement = entry->second.asString();
|
||||
listMap[AUTOREPLACE_LIST_REPLACEMENTS].insert(keyword, LLSD(replacement));
|
||||
}
|
||||
|
||||
mLists.append(listMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLAutoReplaceSettings::set(const LLAutoReplaceSettings& newSettings)
|
||||
{
|
||||
mLists = newSettings.mLists;
|
||||
}
|
||||
|
||||
bool LLAutoReplaceSettings::setFromLLSD(const LLSD& settingsFromLLSD)
|
||||
{
|
||||
bool settingsValid = true;
|
||||
|
||||
if ( settingsFromLLSD.isArray() )
|
||||
{
|
||||
for ( LLSD::array_const_iterator
|
||||
list = settingsFromLLSD.beginArray(),
|
||||
listEnd = settingsFromLLSD.endArray();
|
||||
settingsValid && list != listEnd;
|
||||
list++
|
||||
)
|
||||
{
|
||||
if ( (*list).isDefined() ) // can be undef due to LLSD-30: ignore it
|
||||
{
|
||||
settingsValid = listIsValid(*list);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
settingsValid = false;
|
||||
LL_WARNS("AutoReplace") << "settings are not an array" << LL_ENDL;
|
||||
}
|
||||
|
||||
if ( settingsValid )
|
||||
{
|
||||
mLists = settingsFromLLSD;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "invalid settings discarded; using hard coded example" << LL_ENDL;
|
||||
}
|
||||
|
||||
return settingsValid;
|
||||
}
|
||||
|
||||
bool LLAutoReplaceSettings::listNameMatches( const LLSD& list, const std::string name )
|
||||
{
|
||||
return list.isMap()
|
||||
&& list.has(AUTOREPLACE_LIST_NAME)
|
||||
&& list[AUTOREPLACE_LIST_NAME].asString() == name;
|
||||
}
|
||||
|
||||
const LLSD* LLAutoReplaceSettings::getListEntries(std::string listName)
|
||||
{
|
||||
const LLSD* returnedEntries = NULL;
|
||||
for( LLSD::array_const_iterator list = mLists.beginArray(), endList = mLists.endArray();
|
||||
returnedEntries == NULL && list != endList;
|
||||
list++
|
||||
)
|
||||
{
|
||||
const LLSD& thisList = *list;
|
||||
if ( listNameMatches(thisList, listName) )
|
||||
{
|
||||
returnedEntries = &thisList[AUTOREPLACE_LIST_REPLACEMENTS];
|
||||
}
|
||||
}
|
||||
return returnedEntries;
|
||||
}
|
||||
|
||||
std::string LLAutoReplaceSettings::replacementFor(std::string keyword, std::string listName)
|
||||
{
|
||||
std::string replacement;
|
||||
bool foundList = false;
|
||||
for( LLSD::array_const_iterator list = mLists.beginArray(), endList = mLists.endArray();
|
||||
! foundList && list != endList;
|
||||
list++
|
||||
)
|
||||
{
|
||||
const LLSD& thisList = *list;
|
||||
if ( listNameMatches(thisList, listName) )
|
||||
{
|
||||
foundList = true; // whether there is a replacement or not, we're done
|
||||
if ( thisList.isMap()
|
||||
&& thisList.has(AUTOREPLACE_LIST_REPLACEMENTS)
|
||||
&& thisList[AUTOREPLACE_LIST_REPLACEMENTS].has(keyword)
|
||||
)
|
||||
{
|
||||
replacement = thisList[AUTOREPLACE_LIST_REPLACEMENTS][keyword].asString();
|
||||
LL_DEBUGS("AutoReplace")<<"'"<<keyword<<"' -> '"<<replacement<<"'"<<LL_ENDL;
|
||||
}
|
||||
}
|
||||
if (!foundList)
|
||||
{
|
||||
LL_WARNS("AutoReplace")<<"failed to find list '"<<listName<<"'"<<LL_ENDL;
|
||||
}
|
||||
}
|
||||
if (replacement.empty())
|
||||
{
|
||||
LL_WARNS("AutoReplace")<<"failed to find '"<<keyword<<"'"<<LL_ENDL;
|
||||
}
|
||||
return replacement;
|
||||
}
|
||||
|
||||
LLSD LLAutoReplaceSettings::getListNames()
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<"====="<<LL_ENDL;
|
||||
LLSD toReturn = LLSD::emptyArray();
|
||||
S32 counter=0;
|
||||
for( LLSD::array_const_iterator list = mLists.beginArray(), endList = mLists.endArray();
|
||||
list != endList;
|
||||
list++
|
||||
)
|
||||
{
|
||||
const LLSD& thisList = *list;
|
||||
if ( thisList.isMap() )
|
||||
{
|
||||
if ( thisList.has(AUTOREPLACE_LIST_NAME) )
|
||||
{
|
||||
std::string name = thisList[AUTOREPLACE_LIST_NAME].asString();
|
||||
LL_DEBUGS("AutoReplace")<<counter<<" '"<<name<<"'"<<LL_ENDL;
|
||||
toReturn.append(LLSD(name));
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_ERRS("AutoReplace") <<counter<<" ! MISSING "<<AUTOREPLACE_LIST_NAME<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace")<<counter<<" ! not a map: "/*<<LLSD::typeString(thisList.type())*/<< LL_ENDL;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
LL_DEBUGS("AutoReplace")<<"^^^^^^"<<LL_ENDL;
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
bool LLAutoReplaceSettings::listIsValid(const LLSD& list)
|
||||
{
|
||||
bool listValid = true;
|
||||
if ( ! list.isMap() )
|
||||
{
|
||||
listValid = false;
|
||||
LL_WARNS("AutoReplace") << "list is not a map" << LL_ENDL;
|
||||
}
|
||||
else if ( ! list.has(AUTOREPLACE_LIST_NAME)
|
||||
|| ! list[AUTOREPLACE_LIST_NAME].isString()
|
||||
|| list[AUTOREPLACE_LIST_NAME].asString().empty()
|
||||
)
|
||||
{
|
||||
listValid = false;
|
||||
LL_WARNS("AutoReplace")
|
||||
<< "list found without " << AUTOREPLACE_LIST_NAME
|
||||
<< " (or it is empty)"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
else if ( ! list.has(AUTOREPLACE_LIST_REPLACEMENTS) || ! list[AUTOREPLACE_LIST_REPLACEMENTS].isMap() )
|
||||
{
|
||||
listValid = false;
|
||||
LL_WARNS("AutoReplace") << "list '" << list[AUTOREPLACE_LIST_NAME].asString() << "' without " << AUTOREPLACE_LIST_REPLACEMENTS << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( LLSD::map_const_iterator
|
||||
entry = list[AUTOREPLACE_LIST_REPLACEMENTS].beginMap(),
|
||||
entriesEnd = list[AUTOREPLACE_LIST_REPLACEMENTS].endMap();
|
||||
listValid && entry != entriesEnd;
|
||||
entry++
|
||||
)
|
||||
{
|
||||
if ( ! entry->second.isString() )
|
||||
{
|
||||
listValid = false;
|
||||
LL_WARNS("AutoReplace")
|
||||
<< "non-string replacement value found in list '"
|
||||
<< list[AUTOREPLACE_LIST_NAME].asString() << "'"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return listValid;
|
||||
}
|
||||
|
||||
const LLSD* LLAutoReplaceSettings::exportList(std::string listName)
|
||||
{
|
||||
const LLSD* exportedList = NULL;
|
||||
for ( LLSD::array_const_iterator list = mLists.beginArray(), listEnd = mLists.endArray();
|
||||
exportedList == NULL && list != listEnd;
|
||||
list++
|
||||
)
|
||||
{
|
||||
if ( listNameMatches(*list, listName) )
|
||||
{
|
||||
const LLSD& namedList = (*list);
|
||||
exportedList = &namedList;
|
||||
}
|
||||
}
|
||||
return exportedList;
|
||||
}
|
||||
|
||||
bool LLAutoReplaceSettings::listNameIsUnique(const LLSD& newList)
|
||||
{
|
||||
bool nameIsUnique = true;
|
||||
// this must always be called with a valid list, so it is safe to assume it has a name
|
||||
std::string newListName = newList[AUTOREPLACE_LIST_NAME].asString();
|
||||
for ( LLSD::array_const_iterator list = mLists.beginArray(), listEnd = mLists.endArray();
|
||||
nameIsUnique && list != listEnd;
|
||||
list++
|
||||
)
|
||||
{
|
||||
if ( listNameMatches(*list, newListName) )
|
||||
{
|
||||
LL_WARNS("AutoReplace")<<"duplicate list name '"<<newListName<<"'"<<LL_ENDL;
|
||||
nameIsUnique = false;
|
||||
}
|
||||
}
|
||||
return nameIsUnique;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void LLAutoReplaceSettings::createEmptyList(LLSD& emptyList)
|
||||
{
|
||||
emptyList = LLSD::emptyMap();
|
||||
emptyList[AUTOREPLACE_LIST_NAME] = "Empty";
|
||||
emptyList[AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();
|
||||
}
|
||||
|
||||
/* static */
|
||||
void LLAutoReplaceSettings::setListName(LLSD& list, const std::string& newName)
|
||||
{
|
||||
list[AUTOREPLACE_LIST_NAME] = newName;
|
||||
}
|
||||
|
||||
/* static */
|
||||
std::string LLAutoReplaceSettings::getListName(LLSD& list)
|
||||
{
|
||||
std::string name;
|
||||
if ( list.isMap() && list.has(AUTOREPLACE_LIST_NAME) && list[AUTOREPLACE_LIST_NAME].isString() )
|
||||
{
|
||||
name = list[AUTOREPLACE_LIST_NAME].asString();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
LLAutoReplaceSettings::AddListResult LLAutoReplaceSettings::addList(const LLSD& newList)
|
||||
{
|
||||
AddListResult result;
|
||||
if ( listIsValid( newList ) )
|
||||
{
|
||||
if ( listNameIsUnique( newList ) )
|
||||
{
|
||||
mLists.append(newList);
|
||||
result = AddListOk;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "attempt to add duplicate name" << LL_ENDL;
|
||||
result = AddListDuplicateName;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "attempt to add invalid list" << LL_ENDL;
|
||||
result = AddListInvalidList;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LLAutoReplaceSettings::AddListResult LLAutoReplaceSettings::replaceList(const LLSD& newList)
|
||||
{
|
||||
AddListResult result = AddListInvalidList;
|
||||
if ( listIsValid( newList ) )
|
||||
{
|
||||
std::string listName = newList[AUTOREPLACE_LIST_NAME].asString();
|
||||
bool listFound = false;
|
||||
S32 search_index;
|
||||
LLSD targetList;
|
||||
// The following is working around the fact that LLSD arrays containing maps also seem to have undefined entries... see LLSD-30
|
||||
for ( search_index = 0, targetList = mLists[0];
|
||||
!listFound && search_index < mLists.size();
|
||||
search_index += 1, targetList = mLists[search_index]
|
||||
)
|
||||
{
|
||||
if ( targetList.isMap() )
|
||||
{
|
||||
if ( listNameMatches( targetList, listName) )
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<"list to replace found at "<<search_index<<LL_ENDL;
|
||||
mLists.erase(search_index);
|
||||
mLists.insert(search_index, newList);
|
||||
listFound = true;
|
||||
result = AddListOk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! listFound )
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "attempt to replace unconfigured list" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "attempt to add invalid list" << LL_ENDL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LLAutoReplaceSettings::removeReplacementList(std::string listName)
|
||||
{
|
||||
bool found = false;
|
||||
for( S32 index = 0; !found && mLists[index].isDefined(); index++ )
|
||||
{
|
||||
if( listNameMatches(mLists.get(index), listName) )
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<"list '"<<listName<<"'"<<LL_ENDL;
|
||||
mLists.erase(index);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/// Move the named list up in the priority order
|
||||
bool LLAutoReplaceSettings::increaseListPriority(std::string listName)
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<listName<<LL_ENDL;
|
||||
bool found = false;
|
||||
S32 search_index, previous_index;
|
||||
LLSD targetList;
|
||||
// The following is working around the fact that LLSD arrays containing maps also seem to have undefined entries... see LLSD-30
|
||||
previous_index = -1;
|
||||
for ( search_index = 0, targetList = mLists[0];
|
||||
!found && search_index < mLists.size();
|
||||
search_index += 1, targetList = mLists[search_index]
|
||||
)
|
||||
{
|
||||
if ( targetList.isMap() )
|
||||
{
|
||||
if ( listNameMatches( targetList, listName) )
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<"found at "<<search_index<<", previous is "<<previous_index<<LL_ENDL;
|
||||
found = true;
|
||||
if (previous_index >= 0)
|
||||
{
|
||||
LL_DEBUGS("AutoReplace") << "erase "<<search_index<<LL_ENDL;
|
||||
mLists.erase(search_index);
|
||||
LL_DEBUGS("AutoReplace") << "insert at "<<previous_index<<LL_ENDL;
|
||||
mLists.insert(previous_index, targetList);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "attempted to move top list up" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
previous_index = search_index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//LL_DEBUGS("AutoReplace") << search_index<<" is "<<LLSD::typeString(targetList.type())<<LL_ENDL;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/// Move the named list down in the priority order
|
||||
bool LLAutoReplaceSettings::decreaseListPriority(std::string listName)
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<listName<<LL_ENDL;
|
||||
S32 found_index = -1;
|
||||
S32 search_index;
|
||||
for ( search_index = 0;
|
||||
found_index == -1 && search_index < mLists.size();
|
||||
search_index++
|
||||
)
|
||||
{
|
||||
if ( listNameMatches( mLists[search_index], listName) )
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<"found at index "<<search_index<<LL_ENDL;
|
||||
found_index = search_index;
|
||||
}
|
||||
}
|
||||
if (found_index != -1)
|
||||
{
|
||||
S32 next_index;
|
||||
for ( next_index = found_index+1;
|
||||
next_index < mLists.size() && ! mLists[next_index].isMap();
|
||||
next_index++
|
||||
)
|
||||
{
|
||||
// skipping over any undefined slots (see LLSD-30)
|
||||
LL_WARNS("AutoReplace")<<next_index<<" ! not a map: "/*<<LLSD::typeString(mLists[next_index].type())*/<< LL_ENDL;
|
||||
}
|
||||
if ( next_index < mLists.size() )
|
||||
{
|
||||
LLSD next_list = mLists[next_index];
|
||||
LL_DEBUGS("AutoReplace") << "erase "<<next_index<<LL_ENDL;
|
||||
mLists.erase(next_index);
|
||||
LL_DEBUGS("AutoReplace") << "insert at "<<found_index<<LL_ENDL;
|
||||
mLists.insert(found_index, next_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "attempted to move bottom list down" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "not found" << LL_ENDL;
|
||||
}
|
||||
return (found_index != -1);
|
||||
}
|
||||
|
||||
|
||||
std::string LLAutoReplaceSettings::replaceWord(const std::string currentWord)
|
||||
{
|
||||
std::string returnedWord = currentWord; // in case no replacement is found
|
||||
static LLCachedControl<bool> autoreplace_enabled(gSavedSettings, "AutoReplace", false);
|
||||
if ( autoreplace_enabled )
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<"checking '"<<currentWord<<"'"<< LL_ENDL;
|
||||
//loop through lists in order
|
||||
bool found = false;
|
||||
for( LLSD::array_const_iterator list = mLists.beginArray(), endLists = mLists.endArray();
|
||||
! found && list != endLists;
|
||||
list++
|
||||
)
|
||||
{
|
||||
const LLSD& checkList = *list;
|
||||
const LLSD& replacements = checkList[AUTOREPLACE_LIST_REPLACEMENTS];
|
||||
|
||||
if ( replacements.has(currentWord) )
|
||||
{
|
||||
found = true;
|
||||
LL_DEBUGS("AutoReplace")
|
||||
<< " found in list '" << checkList[AUTOREPLACE_LIST_NAME].asString()
|
||||
<< " => '" << replacements[currentWord].asString() << "'"
|
||||
<< LL_ENDL;
|
||||
returnedWord = replacements[currentWord].asString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnedWord;
|
||||
}
|
||||
|
||||
bool LLAutoReplaceSettings::addEntryToList(LLWString keyword, LLWString replacement, std::string listName)
|
||||
{
|
||||
bool added = false;
|
||||
|
||||
if ( ! keyword.empty() && ! replacement.empty() )
|
||||
{
|
||||
bool isOneWord = true;
|
||||
for (size_t character = 0; isOneWord && character < keyword.size(); character++ )
|
||||
{
|
||||
if ( ! LLWStringUtil::isPartOfWord(keyword[character]) )
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "keyword '" << wstring_to_utf8str(keyword) << "' not a single word (len "<<keyword.size()<<" '"<<character<<"')" << LL_ENDL;
|
||||
isOneWord = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isOneWord )
|
||||
{
|
||||
bool listFound = false;
|
||||
for( LLSD::array_iterator list = mLists.beginArray(), endLists = mLists.endArray();
|
||||
! listFound && list != endLists;
|
||||
list++
|
||||
)
|
||||
{
|
||||
if ( listNameMatches(*list, listName) )
|
||||
{
|
||||
listFound = true;
|
||||
(*list)[AUTOREPLACE_LIST_REPLACEMENTS][wstring_to_utf8str(keyword)]=wstring_to_utf8str(replacement);
|
||||
}
|
||||
}
|
||||
if (listFound)
|
||||
{
|
||||
added = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "list '" << listName << "' not found" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
bool LLAutoReplaceSettings::removeEntryFromList(std::string keyword, std::string listName)
|
||||
{
|
||||
bool found = false;
|
||||
for( LLSD::array_iterator list = mLists.beginArray(), endLists = mLists.endArray();
|
||||
! found && list != endLists;
|
||||
list++
|
||||
)
|
||||
{
|
||||
if ( listNameMatches(*list, listName) )
|
||||
{
|
||||
found = true;
|
||||
(*list)[AUTOREPLACE_LIST_REPLACEMENTS].erase(keyword);
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "list '" << listName << "' not found" << LL_ENDL;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
LLSD LLAutoReplaceSettings::getExampleLLSD()
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<LL_ENDL;
|
||||
LLSD example = LLSD::emptyArray();
|
||||
|
||||
example[0] = LLSD::emptyMap();
|
||||
example[0][AUTOREPLACE_LIST_NAME] = "Example List 1";
|
||||
example[0][AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();
|
||||
example[0][AUTOREPLACE_LIST_REPLACEMENTS]["keyword1"] = "replacement string 1";
|
||||
example[0][AUTOREPLACE_LIST_REPLACEMENTS]["keyword2"] = "replacement string 2";
|
||||
|
||||
example[1] = LLSD::emptyMap();
|
||||
example[1][AUTOREPLACE_LIST_NAME] = "Example List 2";
|
||||
example[1][AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();
|
||||
example[1][AUTOREPLACE_LIST_REPLACEMENTS]["mistake1"] = "correction 1";
|
||||
example[1][AUTOREPLACE_LIST_REPLACEMENTS]["mistake2"] = "correction 2";
|
||||
|
||||
return example;
|
||||
}
|
||||
|
||||
const LLSD& LLAutoReplaceSettings::asLLSD()
|
||||
{
|
||||
return mLists;
|
||||
}
|
||||
|
||||
LLAutoReplaceSettings::~LLAutoReplaceSettings()
|
||||
{
|
||||
}
|
||||
227
indra/newview/llautoreplace.h
Normal file
227
indra/newview/llautoreplace.h
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* @file llautoreplace.h
|
||||
* @brief Auto Replace Manager
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library 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 library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef LLAUTOREPLACE_H
|
||||
#define LLAUTOREPLACE_H
|
||||
|
||||
#include "lllineeditor.h"
|
||||
|
||||
class LLAutoReplace;
|
||||
|
||||
/** The configuration data for the LLAutoReplace object
|
||||
*
|
||||
* This is a separate class so that the LLFloaterAutoReplaceSettings
|
||||
* can have a copy of the configuration to manipulate before committing
|
||||
* the changes back to the LLAutoReplace singleton that provides the
|
||||
* autoreplace callback.
|
||||
*/
|
||||
class LLAutoReplaceSettings
|
||||
{
|
||||
public:
|
||||
LLAutoReplaceSettings();
|
||||
~LLAutoReplaceSettings();
|
||||
|
||||
/// Constructor for creating a tempory copy of the current settings
|
||||
LLAutoReplaceSettings(const LLAutoReplaceSettings& settings);
|
||||
|
||||
/// Replace the current settings with new ones and save them to the user settings file
|
||||
void set(const LLAutoReplaceSettings& newSettings);
|
||||
|
||||
/// Load the current settings read from an LLSD file
|
||||
bool setFromLLSD(const LLSD& settingsFromLLSD);
|
||||
///< @returns whether or not the settingsFromLLSD were valid
|
||||
|
||||
// ================================================================
|
||||
///@{ @name List Operations
|
||||
// ================================================================
|
||||
|
||||
/// @returns the configured list names as an LLSD Array of strings
|
||||
LLSD getListNames();
|
||||
|
||||
/// Status values returned from the addList method
|
||||
typedef enum
|
||||
{
|
||||
AddListOk,
|
||||
AddListDuplicateName,
|
||||
AddListInvalidList,
|
||||
} AddListResult;
|
||||
|
||||
/// Inserts a new list at the end of the priority order
|
||||
AddListResult addList(const LLSD& newList);
|
||||
|
||||
/// Inserts a list in place of an existing list of the same name
|
||||
AddListResult replaceList(const LLSD& newList);
|
||||
|
||||
/// Removes the named list, @returns false if not found
|
||||
bool removeReplacementList(std::string listName);
|
||||
|
||||
/// Move the named list up in the priority order
|
||||
bool increaseListPriority(std::string listName);
|
||||
///< @returns false if the list is not found
|
||||
|
||||
/// Move the named list down in the priority order
|
||||
bool decreaseListPriority(std::string listName);
|
||||
///< @returns false if the list is not found
|
||||
|
||||
/// Get a copy of just one list (for saving to an export file)
|
||||
const LLSD* exportList(std::string listName);
|
||||
/// @returns an LLSD map
|
||||
|
||||
/// Checks for required elements, and that each has the correct type.
|
||||
bool listIsValid(const LLSD& listSettings);
|
||||
|
||||
/// Checks for required elements, and that each has the correct type.
|
||||
bool listNameIs(const LLSD& listSettings);
|
||||
|
||||
/// Checks to see if a new lists name conflicts with one in the settings
|
||||
bool listNameIsUnique(const LLSD& newList);
|
||||
/// @note must be called with LLSD that has passed listIsValid
|
||||
|
||||
/// Initializes emptyList to an empty list named 'Empty'
|
||||
static void createEmptyList(LLSD& emptyList);
|
||||
|
||||
/// Resets the name of a list to a new value
|
||||
static void setListName(LLSD& list, const std::string& newName);
|
||||
|
||||
/// Gets the name of a list
|
||||
static std::string getListName(LLSD& list);
|
||||
|
||||
///@}
|
||||
// ================================================================
|
||||
///@{ @name Replacement Entry Operations
|
||||
// ================================================================
|
||||
|
||||
/// Get the replacements specified by a given list
|
||||
const LLSD* getListEntries(std::string listName);
|
||||
///< @returns an LLSD Map of keyword -> replacement test pairs
|
||||
|
||||
/// Get the replacement for the keyword from the specified list
|
||||
std::string replacementFor(std::string keyword, std::string listName);
|
||||
|
||||
/// Adds a keywword/replacement pair to the named list
|
||||
bool addEntryToList(LLWString keyword, LLWString replacement, std::string listName);
|
||||
|
||||
/// Removes the keywword and its replacement from the named list
|
||||
bool removeEntryFromList(std::string keyword, std::string listName);
|
||||
|
||||
/**
|
||||
* Look for currentWord in the lists in order, returning any substitution found
|
||||
* If no configured substitution is found, returns currentWord
|
||||
*/
|
||||
std::string replaceWord(const std::string currentWord /**< word to search for */ );
|
||||
|
||||
/// Provides a hard-coded example of settings
|
||||
LLSD getExampleLLSD();
|
||||
|
||||
/// Get the actual settings as LLSD
|
||||
const LLSD& asLLSD();
|
||||
///< @note for use only in AutoReplace::saveToUserSettings
|
||||
|
||||
private:
|
||||
/// Efficiently and safely compare list names
|
||||
bool listNameMatches( const LLSD& list, const std::string name );
|
||||
|
||||
/// The actual llsd data structure
|
||||
LLSD mLists;
|
||||
|
||||
static const std::string AUTOREPLACE_LIST_NAME; ///< key for looking up list names
|
||||
static const std::string AUTOREPLACE_LIST_REPLACEMENTS; ///< key for looking up replacement map
|
||||
|
||||
/**<
|
||||
* LLSD structure of the lists
|
||||
* - The configuration is an array (mLists),
|
||||
* - Each entry in the array is a replacement list
|
||||
* - Each replacement list is a map with three keys:
|
||||
* @verbatim
|
||||
* "name" String the name of the list
|
||||
* "replacements" Map keyword -> replacement pairs
|
||||
*
|
||||
* <llsd>
|
||||
* <array>
|
||||
* <map>
|
||||
* <key>name</key> <string>List 1</string>
|
||||
* <key>data</key>
|
||||
* <map>
|
||||
* <key>keyword1</key> <string>replacement1</string>
|
||||
* <key>keyword2</key> <string>replacement2</string>
|
||||
* </map>
|
||||
* </map>
|
||||
* <map>
|
||||
* <key>name</key> <string>List 2</string>
|
||||
* <key>data</key>
|
||||
* <map>
|
||||
* <key>keyword1</key> <string>replacement1</string>
|
||||
* <key>keyword2</key> <string>replacement2</string>
|
||||
* </map>
|
||||
* </map>
|
||||
* </array>
|
||||
* </llsd>
|
||||
* @endverbatim
|
||||
*/
|
||||
};
|
||||
|
||||
/** Provides a facility to auto-replace text dynamically as it is entered.
|
||||
*
|
||||
* When the end of a word is detected (defined as any punctuation character,
|
||||
* or any whitespace except newline or return), the preceding word is used
|
||||
* as a lookup key in an ordered list of maps. If a match is found in any
|
||||
* map, the replacement start index and length are returned along with the
|
||||
* new replacement string.
|
||||
*
|
||||
* See the autoreplaceCallback method for how to add autoreplace functionality
|
||||
* to a text entry tool.
|
||||
*/
|
||||
class LLAutoReplace : public LLSingleton<LLAutoReplace>
|
||||
{
|
||||
public:
|
||||
/// Callback that provides the hook for use in text entry methods
|
||||
void autoreplaceCallback(S32& replacement_start, S32& replacement_length, LLWString& replacement_string, S32& cursor_pos, const LLWString& input_text);
|
||||
|
||||
/// Get a copy of the current settings
|
||||
LLAutoReplaceSettings getSettings();
|
||||
|
||||
/// Commit new settings after making changes
|
||||
void setSettings(const LLAutoReplaceSettings& settings);
|
||||
|
||||
private:
|
||||
friend class LLSingleton<LLAutoReplace>;
|
||||
LLAutoReplace();
|
||||
/*virtual*/ void initSingleton();
|
||||
|
||||
LLAutoReplaceSettings mSettings; ///< configuration information
|
||||
|
||||
/// Read settings from persistent storage
|
||||
void loadFromSettings();
|
||||
|
||||
/// Make the newSettings active and write them to user storage
|
||||
void saveToUserSettings();
|
||||
|
||||
/// Compute the user settings file name
|
||||
std::string getUserSettingsFileName();
|
||||
|
||||
/// Compute the (read-ony) application settings file name
|
||||
std::string getAppSettingsFileName();
|
||||
|
||||
/// basename for the settings files
|
||||
static const char* SETTINGS_FILE_NAME;
|
||||
};
|
||||
|
||||
#endif /* LLAUTOREPLACE_H */
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "llfocusmgr.h"
|
||||
|
||||
#include "llagent.h"
|
||||
#include "llautoreplace.h"
|
||||
#include "llbutton.h"
|
||||
#include "llcombobox.h"
|
||||
#include "llcommandhandler.h" // secondlife:///app/chat/ support
|
||||
@@ -149,6 +150,7 @@ BOOL LLChatBar::postBuild()
|
||||
mInputEditor = findChild<LLLineEditor>("Chat Editor");
|
||||
if (mInputEditor)
|
||||
{
|
||||
mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2, _3, _4, _5));
|
||||
mInputEditor->setKeystrokeCallback(boost::bind(&LLChatBar::onInputEditorKeystroke,this));
|
||||
mInputEditor->setFocusLostCallback(boost::bind(&LLChatBar::onInputEditorFocusLost));
|
||||
mInputEditor->setFocusReceivedCallback(boost::bind(&LLChatBar::onInputEditorGainFocus));
|
||||
|
||||
632
indra/newview/llfloaterautoreplacesettings.cpp
Normal file
632
indra/newview/llfloaterautoreplacesettings.cpp
Normal file
@@ -0,0 +1,632 @@
|
||||
/**
|
||||
* @file llfloaterautoreplacesettings.cpp
|
||||
* @brief Auto Replace List floater
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library 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 library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
|
||||
#include "llfloaterautoreplacesettings.h"
|
||||
|
||||
#include "statemachine/aifilepicker.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
#include "lluictrlfactory.h"
|
||||
|
||||
#include "llautoreplace.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llsdutil.h"
|
||||
|
||||
#include <boost/tokenizer.hpp>
|
||||
|
||||
#include "llnotificationsutil.h"
|
||||
|
||||
|
||||
LLFloaterAutoReplaceSettings::LLFloaterAutoReplaceSettings(const LLSD& key)
|
||||
: LLFloater(/*key*/)
|
||||
, mSelectedListName("")
|
||||
, mListNames(NULL)
|
||||
, mReplacementsList(NULL)
|
||||
, mKeyword(NULL)
|
||||
, mPreviousKeyword("")
|
||||
, mReplacement(NULL)
|
||||
{
|
||||
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_autoreplace.xml");
|
||||
}
|
||||
|
||||
BOOL LLFloaterAutoReplaceSettings::postBuild(void)
|
||||
{
|
||||
// get copies of the current settings that we will operate on
|
||||
mEnabled = gSavedSettings.getBOOL("AutoReplace");
|
||||
LL_DEBUGS("AutoReplace") << ( mEnabled ? "enabled" : "disabled") << LL_ENDL;
|
||||
|
||||
mSettings = LLAutoReplace::getInstance()->getSettings();
|
||||
|
||||
// global checkbox for whether or not autoreplace is active
|
||||
LLUICtrl* enabledCheckbox = getChild<LLUICtrl>("autoreplace_enable");
|
||||
enabledCheckbox->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onAutoReplaceToggled, this));
|
||||
enabledCheckbox->setValue(LLSD(mEnabled));
|
||||
|
||||
// top row list creation and deletion
|
||||
getChild<LLUICtrl>("autoreplace_import_list")->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onImportList,this));
|
||||
getChild<LLUICtrl>("autoreplace_export_list")->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onExportList,this));
|
||||
getChild<LLUICtrl>("autoreplace_new_list")->setCommitCallback( boost::bind(&LLFloaterAutoReplaceSettings::onNewList,this));
|
||||
getChild<LLUICtrl>("autoreplace_delete_list")->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onDeleteList,this));
|
||||
|
||||
// the list of keyword->replacement lists
|
||||
mListNames = getChild<LLScrollListCtrl>("autoreplace_list_name");
|
||||
mListNames->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onSelectList, this));
|
||||
mListNames->setCommitOnSelectionChange(true);
|
||||
|
||||
// list ordering
|
||||
getChild<LLUICtrl>("autoreplace_list_up")->setCommitCallback( boost::bind(&LLFloaterAutoReplaceSettings::onListUp,this));
|
||||
getChild<LLUICtrl>("autoreplace_list_down")->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onListDown,this));
|
||||
|
||||
// keyword->replacement entry add / delete
|
||||
getChild<LLUICtrl>("autoreplace_add_entry")->setCommitCallback( boost::bind(&LLFloaterAutoReplaceSettings::onAddEntry,this));
|
||||
getChild<LLUICtrl>("autoreplace_delete_entry")->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onDeleteEntry,this));
|
||||
|
||||
// entry edits
|
||||
mKeyword = getChild<LLLineEditor>("autoreplace_keyword");
|
||||
mReplacement = getChild<LLLineEditor>("autoreplace_replacement");
|
||||
getChild<LLUICtrl>("autoreplace_save_entry")->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onSaveEntry, this));
|
||||
|
||||
// dialog termination ( Save Changes / Cancel )
|
||||
getChild<LLUICtrl>("autoreplace_save_changes")->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onSaveChanges, this));
|
||||
getChild<LLUICtrl>("autoreplace_cancel")->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::close, this, false));
|
||||
|
||||
// the list of keyword->replacement pairs
|
||||
mReplacementsList = getChild<LLScrollListCtrl>("autoreplace_list_replacements");
|
||||
mReplacementsList->setCommitCallback(boost::bind(&LLFloaterAutoReplaceSettings::onSelectEntry, this));
|
||||
mReplacementsList->setCommitOnSelectionChange(true);
|
||||
|
||||
center();
|
||||
|
||||
mSelectedListName.clear();
|
||||
updateListNames();
|
||||
updateListNamesControls();
|
||||
updateReplacementsList();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void LLFloaterAutoReplaceSettings::updateListNames()
|
||||
{
|
||||
mListNames->deleteAllItems(); // start from scratch
|
||||
|
||||
LLSD listNames = mSettings.getListNames(); // Array of Strings
|
||||
|
||||
for ( LLSD::array_const_iterator entry = listNames.beginArray(), end = listNames.endArray();
|
||||
entry != end;
|
||||
++entry
|
||||
)
|
||||
{
|
||||
const std::string& listName = entry->asString();
|
||||
mListNames->addSimpleElement(listName);
|
||||
}
|
||||
|
||||
if (!mSelectedListName.empty())
|
||||
{
|
||||
mListNames->setSelectedByValue( LLSD(mSelectedListName), true );
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::updateListNamesControls()
|
||||
{
|
||||
if ( mSelectedListName.empty() )
|
||||
{
|
||||
// There is no selected list
|
||||
|
||||
// Disable all controls that operate on the selected list
|
||||
getChild<LLButton>("autoreplace_export_list")->setEnabled(false);
|
||||
getChild<LLButton>("autoreplace_delete_list")->setEnabled(false);
|
||||
getChild<LLButton>("autoreplace_list_up")->setEnabled(false);
|
||||
getChild<LLButton>("autoreplace_list_down")->setEnabled(false);
|
||||
|
||||
mReplacementsList->deleteAllItems();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enable the controls that operate on the selected list
|
||||
getChild<LLButton>("autoreplace_export_list")->setEnabled(true);
|
||||
getChild<LLButton>("autoreplace_delete_list")->setEnabled(true);
|
||||
getChild<LLButton>("autoreplace_list_up")->setEnabled(!selectedListIsFirst());
|
||||
getChild<LLButton>("autoreplace_list_down")->setEnabled(!selectedListIsLast());
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::onSelectList()
|
||||
{
|
||||
std::string previousSelectedListName = mSelectedListName;
|
||||
// only one selection allowed
|
||||
LLSD selected = mListNames->getSelectedValue();
|
||||
if (selected.isDefined())
|
||||
{
|
||||
mSelectedListName = selected.asString();
|
||||
LL_DEBUGS("AutoReplace")<<"selected list '"<<mSelectedListName<<"'"<<LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSelectedListName.clear();
|
||||
LL_DEBUGS("AutoReplace")<<"unselected"<<LL_ENDL;
|
||||
}
|
||||
|
||||
updateListNamesControls();
|
||||
|
||||
if ( previousSelectedListName != mSelectedListName )
|
||||
{
|
||||
updateReplacementsList();
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::onSelectEntry()
|
||||
{
|
||||
LLSD selectedRow = mReplacementsList->getSelectedValue();
|
||||
if (selectedRow.isDefined())
|
||||
{
|
||||
mPreviousKeyword = selectedRow.asString();
|
||||
LL_DEBUGS("AutoReplace")<<"selected entry '"<<mPreviousKeyword<<"'"<<LL_ENDL;
|
||||
mKeyword->setValue(selectedRow);
|
||||
std::string replacement = mSettings.replacementFor(mPreviousKeyword, mSelectedListName );
|
||||
mReplacement->setValue(replacement);
|
||||
enableReplacementEntry();
|
||||
mReplacement->setFocus(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no entry selection, so the entry panel should be off
|
||||
disableReplacementEntry();
|
||||
LL_DEBUGS("AutoReplace")<<"no row selected"<<LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::updateReplacementsList()
|
||||
{
|
||||
// start from scratch, since this should only be called when the list changes
|
||||
mReplacementsList->deleteAllItems();
|
||||
|
||||
if ( mSelectedListName.empty() )
|
||||
{
|
||||
mReplacementsList->setEnabled(false);
|
||||
getChild<LLButton>("autoreplace_add_entry")->setEnabled(false);
|
||||
disableReplacementEntry();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Populate the keyword->replacement list from the selected list
|
||||
const LLSD* mappings = mSettings.getListEntries(mSelectedListName);
|
||||
for ( LLSD::map_const_iterator entry = mappings->beginMap(), end = mappings->endMap();
|
||||
entry != end;
|
||||
entry++
|
||||
)
|
||||
{
|
||||
LLSD row;
|
||||
row["id"] = entry->first;
|
||||
row["columns"][0]["column"] = "Keyword";
|
||||
row["columns"][0]["value"] = entry->first;
|
||||
row["columns"][1]["column"] = "Replacement";
|
||||
row["columns"][1]["value"] = entry->second;
|
||||
|
||||
mReplacementsList->addElement(row, ADD_BOTTOM);
|
||||
}
|
||||
|
||||
mReplacementsList->deselectAllItems(false /* don't call commit */);
|
||||
mReplacementsList->setEnabled(true);
|
||||
|
||||
getChild<LLButton>("autoreplace_add_entry")->setEnabled(true);
|
||||
disableReplacementEntry();
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::enableReplacementEntry()
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<LL_ENDL;
|
||||
mKeyword->setEnabled(true);
|
||||
mReplacement->setEnabled(true);
|
||||
getChild<LLButton>("autoreplace_save_entry")->setEnabled(true);
|
||||
getChild<LLButton>("autoreplace_delete_entry")->setEnabled(true);
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::disableReplacementEntry()
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<LL_ENDL;
|
||||
mPreviousKeyword.clear();
|
||||
mKeyword->clear();
|
||||
mKeyword->setEnabled(false);
|
||||
mReplacement->clear();
|
||||
mReplacement->setEnabled(false);
|
||||
getChild<LLButton>("autoreplace_save_entry")->setEnabled(false);
|
||||
getChild<LLButton>("autoreplace_delete_entry")->setEnabled(false);
|
||||
}
|
||||
|
||||
// called when the global settings checkbox is changed
|
||||
void LLFloaterAutoReplaceSettings::onAutoReplaceToggled()
|
||||
{
|
||||
// set our local copy of the flag, copied to the global preference in onOk
|
||||
mEnabled = childGetValue("autoreplace_enable").asBoolean();
|
||||
LL_DEBUGS("AutoReplace")<< "autoreplace_enable " << ( mEnabled ? "on" : "off" ) << LL_ENDL;
|
||||
}
|
||||
|
||||
// called when the List Up button is pressed
|
||||
void LLFloaterAutoReplaceSettings::onListUp()
|
||||
{
|
||||
S32 selectedRow = mListNames->getFirstSelectedIndex();
|
||||
LLSD selectedName = mListNames->getSelectedValue().asString();
|
||||
|
||||
if ( mSettings.increaseListPriority(selectedName) )
|
||||
{
|
||||
updateListNames();
|
||||
updateListNamesControls();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace")
|
||||
<< "invalid row ("<<selectedRow<<") selected '"<<selectedName<<"'"
|
||||
<<LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
// called when the List Down button is pressed
|
||||
void LLFloaterAutoReplaceSettings::onListDown()
|
||||
{
|
||||
S32 selectedRow = mListNames->getFirstSelectedIndex();
|
||||
std::string selectedName = mListNames->getSelectedValue().asString();
|
||||
|
||||
if ( mSettings.decreaseListPriority(selectedName) )
|
||||
{
|
||||
updateListNames();
|
||||
updateListNamesControls();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace")
|
||||
<< "invalid row ("<<selectedRow<<") selected '"<<selectedName<<"'"
|
||||
<<LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
// called when the Delete Entry button is pressed
|
||||
void LLFloaterAutoReplaceSettings::onDeleteEntry()
|
||||
{
|
||||
LLSD selectedRow = mReplacementsList->getSelectedValue();
|
||||
if (selectedRow.isDefined())
|
||||
{
|
||||
std::string keyword = selectedRow.asString();
|
||||
mReplacementsList->deleteSelectedItems(); // delete from the control
|
||||
mSettings.removeEntryFromList(keyword, mSelectedListName); // delete from the local settings copy
|
||||
disableReplacementEntry(); // no selection active, so turn off the buttons
|
||||
}
|
||||
}
|
||||
|
||||
// called when the Import List button is pressed
|
||||
void LLFloaterAutoReplaceSettings::onImportList()
|
||||
{
|
||||
AIFilePicker* picker = AIFilePicker::create();
|
||||
picker->open(FFLOAD_XML, "", "autoreplace");
|
||||
picker->run(boost::bind(&LLFloaterAutoReplaceSettings::onImportList_continued, this, picker));
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::onImportList_continued(AIFilePicker* picker)
|
||||
{
|
||||
if (picker->hasFilename())
|
||||
{
|
||||
llifstream file;
|
||||
file.open(picker->getFilename());
|
||||
LLSD newList;
|
||||
if (file.is_open())
|
||||
{
|
||||
LLSDSerialize::fromXMLDocument(newList, file);
|
||||
}
|
||||
file.close();
|
||||
|
||||
switch ( mSettings.addList(newList) )
|
||||
{
|
||||
case LLAutoReplaceSettings::AddListOk:
|
||||
mSelectedListName = LLAutoReplaceSettings::getListName(newList);
|
||||
|
||||
updateListNames();
|
||||
updateListNamesControls();
|
||||
updateReplacementsList();
|
||||
break;
|
||||
|
||||
case LLAutoReplaceSettings::AddListDuplicateName:
|
||||
{
|
||||
std::string newName = LLAutoReplaceSettings::getListName(newList);
|
||||
LL_WARNS("AutoReplace")<<"name '"<<newName<<"' is in use; prompting for new name"<<LL_ENDL;
|
||||
LLSD newPayload;
|
||||
newPayload["list"] = newList;
|
||||
LLSD args;
|
||||
args["DUPNAME"] = newName;
|
||||
|
||||
LLNotificationsUtil::add("RenameAutoReplaceList", args, newPayload,
|
||||
boost::bind(&LLFloaterAutoReplaceSettings::callbackListNameConflict, this, _1, _2));
|
||||
}
|
||||
break;
|
||||
|
||||
case LLAutoReplaceSettings::AddListInvalidList:
|
||||
LLNotificationsUtil::add("InvalidAutoReplaceList");
|
||||
LL_WARNS("AutoReplace") << "imported list was invalid" << LL_ENDL;
|
||||
|
||||
mSelectedListName.clear();
|
||||
updateListNames();
|
||||
updateListNamesControls();
|
||||
updateReplacementsList();
|
||||
break;
|
||||
|
||||
default:
|
||||
LL_ERRS("AutoReplace") << "invalid AddListResult" << LL_ENDL;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("AutoReplace") << "file selection failed for import list" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::onNewList()
|
||||
{
|
||||
LLSD payload;
|
||||
LLSD emptyList;
|
||||
LLAutoReplaceSettings::createEmptyList(emptyList);
|
||||
payload["list"] = emptyList;
|
||||
LLSD args;
|
||||
|
||||
LLNotificationsUtil::add("AddAutoReplaceList", args, payload,
|
||||
boost::bind(&LLFloaterAutoReplaceSettings::callbackNewListName, this, _1, _2));
|
||||
}
|
||||
|
||||
bool LLFloaterAutoReplaceSettings::callbackNewListName(const LLSD& notification, const LLSD& response)
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<"called"<<LL_ENDL;
|
||||
|
||||
LLSD newList = notification["payload"]["list"];
|
||||
|
||||
if ( response.has("listname") && response["listname"].isString() )
|
||||
{
|
||||
std::string newName = response["listname"].asString();
|
||||
LLAutoReplaceSettings::setListName(newList, newName);
|
||||
|
||||
switch ( mSettings.addList(newList) )
|
||||
{
|
||||
case LLAutoReplaceSettings::AddListOk:
|
||||
LL_INFOS("AutoReplace") << "added new list '"<<newName<<"'"<<LL_ENDL;
|
||||
mSelectedListName = newName;
|
||||
updateListNames();
|
||||
updateListNamesControls();
|
||||
updateReplacementsList();
|
||||
break;
|
||||
|
||||
case LLAutoReplaceSettings::AddListDuplicateName:
|
||||
{
|
||||
LL_WARNS("AutoReplace")<<"name '"<<newName<<"' is in use; prompting for new name"<<LL_ENDL;
|
||||
LLSD newPayload;
|
||||
newPayload["list"] = notification["payload"]["list"];
|
||||
LLSD args;
|
||||
args["DUPNAME"] = newName;
|
||||
|
||||
LLNotificationsUtil::add("RenameAutoReplaceList", args, newPayload,
|
||||
boost::bind(&LLFloaterAutoReplaceSettings::callbackListNameConflict, this, _1, _2));
|
||||
}
|
||||
break;
|
||||
|
||||
case LLAutoReplaceSettings::AddListInvalidList:
|
||||
LLNotificationsUtil::add("InvalidAutoReplaceList");
|
||||
|
||||
mSelectedListName.clear();
|
||||
updateListNames();
|
||||
updateListNamesControls();
|
||||
updateReplacementsList();
|
||||
break;
|
||||
|
||||
default:
|
||||
LL_ERRS("AutoReplace") << "invalid AddListResult" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_ERRS("AutoReplace") << "adding notification response" << LL_ENDL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// callback for the RenameAutoReplaceList notification
|
||||
bool LLFloaterAutoReplaceSettings::callbackListNameConflict(const LLSD& notification, const LLSD& response)
|
||||
{
|
||||
LLSD newList = notification["payload"]["list"];
|
||||
std::string listName = LLAutoReplaceSettings::getListName(newList);
|
||||
|
||||
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
|
||||
switch ( option )
|
||||
{
|
||||
case 0:
|
||||
// Replace current list
|
||||
if ( LLAutoReplaceSettings::AddListOk == mSettings.replaceList(newList) )
|
||||
{
|
||||
LL_INFOS("AutoReplace") << "replaced list '"<<listName<<"'"<<LL_ENDL;
|
||||
mSelectedListName = listName;
|
||||
updateListNames();
|
||||
updateListNamesControls();
|
||||
updateReplacementsList();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace")<<"failed to replace list '"<<listName<<"'"<<LL_ENDL;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Use New Name
|
||||
LL_INFOS("AutoReplace")<<"option 'use new name' selected"<<LL_ENDL;
|
||||
callbackNewListName(notification, response);
|
||||
break;
|
||||
|
||||
default:
|
||||
LL_ERRS("AutoReplace")<<"invalid selected option "<<option<<LL_ENDL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::onDeleteList()
|
||||
{
|
||||
std::string listName = mListNames->getSelectedValue().asString();
|
||||
if ( ! listName.empty() )
|
||||
{
|
||||
if ( mSettings.removeReplacementList(listName) )
|
||||
{
|
||||
LL_INFOS("AutoReplace")<<"deleted list '"<<listName<<"'"<<LL_ENDL;
|
||||
mReplacementsList->deleteSelectedItems(); // remove from the scrolling list
|
||||
mSelectedListName.clear();
|
||||
updateListNames();
|
||||
updateListNamesControls();
|
||||
updateReplacementsList();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AutoReplace")<<"failed to delete list '"<<listName<<"'"<<LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<"no list selected for delete"<<LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::onExportList()
|
||||
{
|
||||
std::string listName=mListNames->getFirstSelected()->getColumn(0)->getValue().asString();
|
||||
std::string listFileName = listName + ".xml";
|
||||
AIFilePicker* picker = AIFilePicker::create();
|
||||
picker->open(listFileName, FFSAVE_XML, "", "autoreplace");
|
||||
picker->run(boost::bind(&LLFloaterAutoReplaceSettings::onExportList_continued, this, picker, mSettings.exportList(listName)));
|
||||
}
|
||||
void LLFloaterAutoReplaceSettings::onExportList_continued(AIFilePicker* picker, const LLSD* list)
|
||||
{
|
||||
if (picker->hasFilename())
|
||||
{
|
||||
llofstream file;
|
||||
file.open(picker->getFilename());
|
||||
LLSDSerialize::toPrettyXML(*list, file);
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::onAddEntry()
|
||||
{
|
||||
mPreviousKeyword.clear();
|
||||
mReplacementsList->deselectAllItems(false /* don't call commit */);
|
||||
mKeyword->clear();
|
||||
mReplacement->clear();
|
||||
enableReplacementEntry();
|
||||
mKeyword->setFocus(true);
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::onSaveEntry()
|
||||
{
|
||||
LL_DEBUGS("AutoReplace")<<"called"<<LL_ENDL;
|
||||
|
||||
if ( ! mPreviousKeyword.empty() )
|
||||
{
|
||||
// delete any existing value for the key that was editted
|
||||
LL_INFOS("AutoReplace")
|
||||
<< "list '" << mSelectedListName << "' "
|
||||
<< "removed '" << mPreviousKeyword
|
||||
<< "'" << LL_ENDL;
|
||||
mSettings.removeEntryFromList( mPreviousKeyword, mSelectedListName );
|
||||
}
|
||||
|
||||
LLWString keyword = mKeyword->getWText();
|
||||
LLWString replacement = mReplacement->getWText();
|
||||
if ( mSettings.addEntryToList(keyword, replacement, mSelectedListName) )
|
||||
{
|
||||
// insert the new keyword->replacement pair
|
||||
LL_INFOS("AutoReplace")
|
||||
<< "list '" << mSelectedListName << "' "
|
||||
<< "added '" << wstring_to_utf8str(keyword)
|
||||
<< "' -> '" << wstring_to_utf8str(replacement)
|
||||
<< "'" << LL_ENDL;
|
||||
|
||||
updateReplacementsList();
|
||||
}
|
||||
else
|
||||
{
|
||||
LLNotificationsUtil::add("InvalidAutoReplaceEntry");
|
||||
LL_WARNS("AutoReplace")<<"invalid entry "
|
||||
<< "keyword '" << wstring_to_utf8str(keyword)
|
||||
<< "' replacement '" << wstring_to_utf8str(replacement)
|
||||
<< "'" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterAutoReplaceSettings::onSaveChanges()
|
||||
{
|
||||
// put our local copy of the settings into the active copy
|
||||
LLAutoReplace::getInstance()->setSettings( mSettings );
|
||||
// save our local copy of the global feature enable/disable value
|
||||
gSavedSettings.setBOOL("AutoReplace", mEnabled);
|
||||
close();
|
||||
}
|
||||
|
||||
bool LLFloaterAutoReplaceSettings::selectedListIsFirst()
|
||||
{
|
||||
bool isFirst = false;
|
||||
|
||||
if (!mSelectedListName.empty())
|
||||
{
|
||||
LLSD lists = mSettings.getListNames(); // an Array of Strings
|
||||
LLSD first = lists.get(0);
|
||||
if ( first.isString() && first.asString() == mSelectedListName )
|
||||
{
|
||||
isFirst = true;
|
||||
}
|
||||
}
|
||||
return isFirst;
|
||||
}
|
||||
|
||||
bool LLFloaterAutoReplaceSettings::selectedListIsLast()
|
||||
{
|
||||
bool isLast = false;
|
||||
|
||||
if (!mSelectedListName.empty())
|
||||
{
|
||||
LLSD last;
|
||||
LLSD lists = mSettings.getListNames(); // an Array of Strings
|
||||
for ( LLSD::array_const_iterator list = lists.beginArray(), listEnd = lists.endArray();
|
||||
list != listEnd;
|
||||
list++
|
||||
)
|
||||
{
|
||||
last = *list;
|
||||
}
|
||||
if ( last.isString() && last.asString() == mSelectedListName )
|
||||
{
|
||||
isLast = true;
|
||||
}
|
||||
}
|
||||
return isLast;
|
||||
}
|
||||
|
||||
/* TBD
|
||||
mOldText = getChild<LLLineEditor>("autoreplace_old_text");
|
||||
mNewText = getChild<LLLineEditor>("autoreplace_new_text");
|
||||
*/
|
||||
111
indra/newview/llfloaterautoreplacesettings.h
Normal file
111
indra/newview/llfloaterautoreplacesettings.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @file llfloaterautoreplacesettings.h
|
||||
* @brief Auto Replace List floater
|
||||
* @copyright Copyright (c) 2011 LordGregGreg Back
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library 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 library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LLFLOATERAUTOREPLACESETTINGS_H
|
||||
#define LLFLOATERAUTOREPLACESETTINGS_H
|
||||
|
||||
#include "llfloater.h"
|
||||
#include "llautoreplace.h"
|
||||
|
||||
class AIFilePicker;
|
||||
class LLLineEditor;
|
||||
class LLScrollListCtrl;
|
||||
|
||||
class LLFloaterAutoReplaceSettings : public LLFloater, public LLFloaterSingleton<LLFloaterAutoReplaceSettings>
|
||||
{
|
||||
public:
|
||||
LLFloaterAutoReplaceSettings(const LLSD& key = LLSD());
|
||||
|
||||
/*virtual*/ BOOL postBuild();
|
||||
|
||||
private:
|
||||
|
||||
/** @{ @name Local Copies of Settings
|
||||
* These are populated in the postBuild method with the values
|
||||
* current when the floater is instantiated, and then either
|
||||
* discarded when Cancel is pressed, or copied back to the active
|
||||
* settings if Ok is pressed.
|
||||
*/
|
||||
bool mEnabled; ///< the global preference for AutoReplace
|
||||
LLAutoReplaceSettings mSettings; ///< settings being modified
|
||||
/** @} */
|
||||
|
||||
/// convenience variable - the name of the currently selected list (if any)
|
||||
std::string mSelectedListName;
|
||||
/// the scrolling list of list names (one column, no headings, order manually controlled)
|
||||
LLScrollListCtrl* mListNames;
|
||||
/// the scroling list of keyword->replacement pairs
|
||||
LLScrollListCtrl* mReplacementsList;
|
||||
|
||||
/// the keyword for the entry editing pane
|
||||
LLLineEditor* mKeyword;
|
||||
/// saved keyword value
|
||||
std::string mPreviousKeyword;
|
||||
/// the replacement for the entry editing pane
|
||||
LLLineEditor* mReplacement;
|
||||
|
||||
/// callback for when the feature enable/disable checkbox changes
|
||||
void onAutoReplaceToggled();
|
||||
/// callback for when an entry in the list of list names is selected
|
||||
void onSelectList();
|
||||
|
||||
void onImportList();
|
||||
void onImportList_continued(AIFilePicker* picker);
|
||||
void onExportList();
|
||||
void onExportList_continued(AIFilePicker* picker, const LLSD* list);
|
||||
void onNewList();
|
||||
void onDeleteList();
|
||||
|
||||
void onListUp();
|
||||
void onListDown();
|
||||
|
||||
void onSelectEntry();
|
||||
void onAddEntry();
|
||||
void onDeleteEntry();
|
||||
void onSaveEntry();
|
||||
|
||||
void onSaveChanges();
|
||||
|
||||
/// updates the contents of the mListNames
|
||||
void updateListNames();
|
||||
/// updates the controls associated with mListNames (depends on whether a name is selected or not)
|
||||
void updateListNamesControls();
|
||||
/// updates the contents of the mReplacementsList
|
||||
void updateReplacementsList();
|
||||
/// enables the components that should only be active when a keyword is selected
|
||||
void enableReplacementEntry();
|
||||
/// disables the components that should only be active when a keyword is selected
|
||||
void disableReplacementEntry();
|
||||
|
||||
/// called from the AddAutoReplaceList notification dialog
|
||||
bool callbackNewListName(const LLSD& notification, const LLSD& response);
|
||||
/// called from the RenameAutoReplaceList notification dialog
|
||||
bool callbackListNameConflict(const LLSD& notification, const LLSD& response);
|
||||
|
||||
bool selectedListIsFirst();
|
||||
bool selectedListIsLast();
|
||||
};
|
||||
|
||||
#endif // LLFLOATERAUTOREPLACESETTINGS_H
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include "ascentkeyword.h"
|
||||
#include "llagent.h"
|
||||
#include "llautoreplace.h"
|
||||
#include "llavataractions.h"
|
||||
#include "llavatarnamecache.h"
|
||||
#include "llbutton.h"
|
||||
@@ -466,6 +467,7 @@ BOOL LLFloaterIMPanel::postBuild()
|
||||
LLAvatarNameCache::get(mOtherParticipantUUID, boost::bind(&LLFloaterIMPanel::onAvatarNameLookup, this, _2));
|
||||
|
||||
mInputEditor = getChild<LLLineEditor>("chat_editor");
|
||||
mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2, _3, _4, _5));
|
||||
mInputEditor->setFocusReceivedCallback( boost::bind(&LLFloaterIMPanel::onInputEditorFocusReceived, this) );
|
||||
mFocusLostSignal = mInputEditor->setFocusLostCallback(boost::bind(&LLFloaterIMPanel::setTyping, this, false));
|
||||
mInputEditor->setKeystrokeCallback( boost::bind(&LLFloaterIMPanel::onInputEditorKeystroke, this, _1) );
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "llfasttimerview.h"
|
||||
#include "llfloaterabout.h"
|
||||
#include "llfloateractivespeakers.h"
|
||||
#include "llfloaterautoreplacesettings.h"
|
||||
#include "llfloateravatarlist.h"
|
||||
#include "llfloaterbeacons.h"
|
||||
#include "llfloaterblacklist.h"
|
||||
@@ -219,6 +220,7 @@ struct MenuFloaterDict : public LLSingleton<MenuFloaterDict>
|
||||
registerFloater<LLFloaterRegionInfo> ("about region");
|
||||
registerFloater<LLFloaterActiveSpeakers> ("active speakers");
|
||||
registerFloater<JCFloaterAreaSearch> ("areasearch");
|
||||
registerFloater<LLFloaterAutoReplaceSettings> ("autoreplace");
|
||||
registerFloater<LLFloaterBeacons> ("beacons");
|
||||
registerFloater<LLFloaterCamera> ("camera controls");
|
||||
registerFloater<LLFloaterChat> ("chat history");
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 B |
32
indra/newview/skins/default/xui/de/floater_autoreplace.xml
Normal file
32
indra/newview/skins/default/xui/de/floater_autoreplace.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="autoreplace_floater" title="Einstellungen für automatische Ersetzung...">
|
||||
<check_box label="Automatische Ersetzung aktivieren..." name="autoreplace_enable" tool_tip="Eines oder mehrere Schlüsselwörter bei der Eingabe von Chat-Text durch die entsprechende Ersetzung substituieren"/>
|
||||
<button label="Liste importieren..." name="autoreplace_import_list" tool_tip="Exportierte Liste aus einer Datei laden."/>
|
||||
<button label="Liste exportieren..." name="autoreplace_export_list" tool_tip="Ausgewählte Liste zur Weitergabe in einer Datei speichern."/>
|
||||
<button label="Neue Liste..." name="autoreplace_new_list" tool_tip="Neue Liste erstellen."/>
|
||||
<button label="Liste löschen" name="autoreplace_delete_list" tool_tip="Ausgewählte Liste löschen."/>
|
||||
<button name="autoreplace_list_up" tool_tip="Dieser Liste eine höhere Priorität einräumen."/>
|
||||
<button name="autoreplace_list_down" tool_tip="Dieser Liste eine niedrigere Priorität einräumen."/>
|
||||
<scroll_list name="autoreplace_list_replacements">
|
||||
<scroll_list.columns label="Schlüsselwort" name="Keyword"/>
|
||||
<scroll_list.columns label="Ersetzung" name="Replacement"/>
|
||||
</scroll_list>
|
||||
<button label="Hinzufügen..." name="autoreplace_add_entry"/>
|
||||
<button label="Entfernen" name="autoreplace_delete_entry"/>
|
||||
<button label="Eintrag speichern" name="autoreplace_save_entry" tool_tip="Diesen Eintrag speichern."/>
|
||||
<button label="Änderungen speichern" name="autoreplace_save_changes" tool_tip="Alle Änderungen speichern."/>
|
||||
<button label="Abbrechen" name="autoreplace_cancel" tool_tip="Alle Änderungen löschen."/>
|
||||
</floater>
|
||||
<!--
|
||||
<text
|
||||
top_pad="10"
|
||||
left="10"
|
||||
height="16"
|
||||
width="260"
|
||||
follows="left|top"
|
||||
halign="center"
|
||||
mouse_opaque="true"
|
||||
name="autoreplace_text2">
|
||||
Entries
|
||||
</text>
|
||||
-->
|
||||
291
indra/newview/skins/default/xui/en-us/floater_autoreplace.xml
Normal file
291
indra/newview/skins/default/xui/en-us/floater_autoreplace.xml
Normal file
@@ -0,0 +1,291 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<floater
|
||||
border="true"
|
||||
can_close="true"
|
||||
can_minimize="true"
|
||||
can_resize="false"
|
||||
help_topic="autoreplace_settings"
|
||||
save_rect="true"
|
||||
height="455"
|
||||
width="490"
|
||||
name="autoreplace_floater"
|
||||
title="Auto-Replace Settings">
|
||||
<check_box
|
||||
bottom="-40"
|
||||
left_delta="15"
|
||||
height="16"
|
||||
width="100"
|
||||
follows="left|top"
|
||||
label="Enable Auto-Replace"
|
||||
name="autoreplace_enable"
|
||||
tool_tip="As you enter chat text, replace any of the keywords entered with the corresponding replacement"/>
|
||||
<view_border
|
||||
bottom_delta="-15"
|
||||
left="2"
|
||||
height="0"
|
||||
width="491"
|
||||
follows="left|top"
|
||||
bevel_style="none"
|
||||
border_thickness="1"
|
||||
mouse_opaque="false"
|
||||
name="divisor1"/>
|
||||
<button
|
||||
bottom_delta="-30"
|
||||
left="10"
|
||||
height="22"
|
||||
width="110"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_import_list"
|
||||
label="Import List..."
|
||||
tool_tip="Load a previously exported list from a file."/>
|
||||
<button
|
||||
bottom_delta="0"
|
||||
left_delta="120"
|
||||
height="22"
|
||||
width="110"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_export_list"
|
||||
label="Export List..."
|
||||
tool_tip="Save the selected list to a file so you can share it."/>
|
||||
<button
|
||||
bottom_delta="0"
|
||||
left_delta="120"
|
||||
height="22"
|
||||
width="110"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_new_list"
|
||||
label="New List..."
|
||||
tool_tip="Create a new list."/>
|
||||
<button
|
||||
bottom_delta="0"
|
||||
left_delta="120"
|
||||
height="22"
|
||||
width="110"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_delete_list"
|
||||
label="Delete List"
|
||||
tool_tip="Delete the selected list."/>
|
||||
<scroll_list
|
||||
bottom_delta="-112"
|
||||
left="10"
|
||||
height="100"
|
||||
width="370"
|
||||
follows="left|top"
|
||||
column_padding="0"
|
||||
draw_heading="false"
|
||||
multi_select="false"
|
||||
name="autoreplace_list_name"
|
||||
search_column="0">
|
||||
</scroll_list>
|
||||
<button
|
||||
bottom_delta="56"
|
||||
left_delta="380"
|
||||
height="22"
|
||||
width="90"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_list_up"
|
||||
image_overlay="up_arrow.tga"
|
||||
label=""
|
||||
tool_tip="Move this list up in priority."/>
|
||||
<button
|
||||
bottom_delta="-32"
|
||||
left_delta="0"
|
||||
height="22"
|
||||
width="90"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_list_down"
|
||||
image_overlay="down_arrow.tga"
|
||||
label=""
|
||||
tool_tip="Move this list down in priority."/>
|
||||
<view_border
|
||||
bottom_delta="-36"
|
||||
left="2"
|
||||
height="0"
|
||||
width="491"
|
||||
follows="left|top"
|
||||
bevel_style="none"
|
||||
border_thickness="1"
|
||||
mouse_opaque="false"
|
||||
name="divisor2"/>
|
||||
<scroll_list
|
||||
bottom_delta="-130"
|
||||
left="10"
|
||||
height="120"
|
||||
width="370"
|
||||
follows="left|top"
|
||||
column_padding="0"
|
||||
draw_heading="true"
|
||||
multi_select="true"
|
||||
name="autoreplace_list_replacements"
|
||||
search_column="0">
|
||||
<scroll_list.columns
|
||||
label="Keyword"
|
||||
name="Keyword"
|
||||
relative_width="0.30" />
|
||||
<scroll_list.columns
|
||||
label="Replacement"
|
||||
name="Replacement"
|
||||
relative_width="0.70" />
|
||||
</scroll_list>
|
||||
<button
|
||||
bottom_delta="64"
|
||||
left_delta="380"
|
||||
height="22"
|
||||
width="90"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_add_entry"
|
||||
label="Add..."/>
|
||||
<button
|
||||
bottom_delta="-32"
|
||||
left_delta="0"
|
||||
height="22"
|
||||
width="90"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_delete_entry"
|
||||
label="Remove"/>
|
||||
<view_border
|
||||
bottom_delta="-42"
|
||||
left="2"
|
||||
height="0"
|
||||
width="491"
|
||||
follows="left|top"
|
||||
bevel_style="none"
|
||||
border_thickness="1"
|
||||
mouse_opaque="false"
|
||||
name="divisor3"/>
|
||||
<text
|
||||
type="string"
|
||||
follows="left|top"
|
||||
height="16"
|
||||
layout="topleft"
|
||||
left="10"
|
||||
bottom_delta="-29"
|
||||
width="50">
|
||||
Keyword:
|
||||
</text>
|
||||
<line_editor
|
||||
name="autoreplace_keyword"
|
||||
follows="left|top"
|
||||
height="23"
|
||||
layout="topleft"
|
||||
left="100"
|
||||
max_length_bytes="255"
|
||||
bottom_delta="0"
|
||||
width="150"
|
||||
/>
|
||||
<text
|
||||
type="string"
|
||||
follows="left|top"
|
||||
height="16"
|
||||
layout="topleft"
|
||||
left="10"
|
||||
right="90"
|
||||
bottom_delta="-29"
|
||||
>
|
||||
Replacement:
|
||||
</text>
|
||||
<line_editor
|
||||
name="autoreplace_replacement"
|
||||
follows="left|top"
|
||||
height="23"
|
||||
layout="topleft"
|
||||
left="100"
|
||||
max_length_bytes="255"
|
||||
bottom_delta="0"
|
||||
width="280"
|
||||
/>
|
||||
<button
|
||||
bottom_delta="0"
|
||||
right="-10"
|
||||
height="22"
|
||||
width="90"
|
||||
enabled="false"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_save_entry"
|
||||
label="Save Entry"
|
||||
tool_tip="Save this entry."/>
|
||||
<view_border
|
||||
bottom_delta="-10"
|
||||
left="2"
|
||||
height="0"
|
||||
width="491"
|
||||
follows="left|top"
|
||||
bevel_style="none"
|
||||
border_thickness="1"
|
||||
mouse_opaque="false"
|
||||
name="divisor4"/>
|
||||
<button
|
||||
bottom_delta="-29"
|
||||
right="380"
|
||||
height="22"
|
||||
width="90"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_save_changes"
|
||||
label="Save Changes"
|
||||
tool_tip="Save all changes."/>
|
||||
<button
|
||||
bottom_delta="0"
|
||||
right="480"
|
||||
height="22"
|
||||
width="90"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="autoreplace_cancel"
|
||||
label="Cancel"
|
||||
tool_tip="Discard all changes."/>
|
||||
</floater>
|
||||
<!--
|
||||
<text
|
||||
top_pad="10"
|
||||
left="10"
|
||||
height="16"
|
||||
width="260"
|
||||
follows="left|top"
|
||||
halign="center"
|
||||
mouse_opaque="true"
|
||||
name="autoreplace_text2">
|
||||
Entries
|
||||
</text>
|
||||
-->
|
||||
@@ -2456,6 +2456,55 @@ Do you want to remove multiple friends from your Friends list?
|
||||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
label="Add Auto-Replace List"
|
||||
name="AddAutoReplaceList"
|
||||
type="alertmodal">
|
||||
<tag>addlist</tag>
|
||||
Name for the new list:
|
||||
<tag>confirm</tag>
|
||||
<form name="form">
|
||||
<input name="listname" type="text"/>
|
||||
<button
|
||||
default="true"
|
||||
index="0"
|
||||
name="SetName"
|
||||
text="OK"/>
|
||||
</form>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
label="Rename Auto-Replace List"
|
||||
name="RenameAutoReplaceList"
|
||||
type="alertmodal">
|
||||
The name '[DUPNAME]' is in use
|
||||
Enter a new unique name:
|
||||
<tag>confirm</tag>
|
||||
<form name="form">
|
||||
<input name="listname" type="text"/>
|
||||
<button
|
||||
default="false"
|
||||
index="0"
|
||||
name="ReplaceList"
|
||||
text="Replace Current List"/>
|
||||
<button
|
||||
default="true"
|
||||
index="1"
|
||||
name="SetName"
|
||||
text="Use New Name"/>
|
||||
</form>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
name="InvalidAutoReplaceEntry"
|
||||
type="alertmodal">
|
||||
The keyword must be a single word, and the replacement may not be empty.
|
||||
<tag>fail</tag>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
name="GodDeleteAllScriptedPublicObjectsByUser"
|
||||
|
||||
@@ -172,16 +172,17 @@ To use spellcheck, right-click a misspelled word
|
||||
(red or otherwise) and select its replacement
|
||||
</text>
|
||||
|
||||
<view_border bevel_style="none" border_thickness="1" bottom_delta="-16" follows="top" height="0" left="5" name="CmdDivisor" width="356"/>
|
||||
<button bottom_delta="-26" left="12" follows="top" height="20" width="250" label="Autoreplace Preferences" name="autoreplace"/>
|
||||
<view_border bevel_style="none" border_thickness="1" bottom_delta="-5" follows="top" height="0" left="5" name="CmdDivisor" width="376"/>
|
||||
|
||||
<check_box bottom_delta="-24" follows="left|top" font="SansSerifSmall" height="16"
|
||||
label="Highlight messages if any of them contain the terms" name="KeywordsOn" width="270"/>
|
||||
<text bottom_delta="-20" follows="top" height="20" left="12" name="keyword_txt1">(separated by commas)</text>
|
||||
<line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="-20" follows="top" height="20" left_delta="5" max_length="500" name="KeywordsList" width="300"/>
|
||||
<text bottom_delta="-24" follows="top" height="20" left_delta="15" name="EmKeyw">Is found within:</text>
|
||||
<check_box bottom_delta="3" follows="top" height="16" left_delta="100" label="Local Chat Floater" name="KeywordsInChat"/>
|
||||
<check_box bottom_delta="-15" follows="left|top" height="16" label="Instant Message Floater" name="KeywordsInIM"/>
|
||||
<check_box bottom_delta="-24" follows="top" height="16" label="Highlight the message in this color:" left_delta="-110" name="KeywordsChangeColor"/>
|
||||
<text bottom_delta="-24" follows="top" height="20" left_delta="0" name="EmKeyw">Is found within:</text>
|
||||
<check_box bottom_delta="3" follows="top" height="16" left_delta="80" label="Local Chat Floater" name="KeywordsInChat"/>
|
||||
<check_box bottom_delta="0" left_delta="120" follows="left|top" height="16" label="Instant Message Floater" name="KeywordsInIM"/>
|
||||
<check_box bottom_delta="-24" follows="top" height="16" label="Highlight the message in this color:" left_delta="-195" name="KeywordsChangeColor"/>
|
||||
<color_swatch border_color="0.45098, 0.517647, 0.607843, 1" bottom_delta="-16" can_apply_immediately="true" color="1, 1, 1, 1" follows="left|top" height="35" left_delta="210" name="KeywordsColor" tool_tip="Click to open Color Picker" width="50"/>
|
||||
<check_box bottom_delta="-10" follows="top" height="16" left_delta="-210" label="Play this sound alert: (UUID)" name="KeywordsPlaySound"/>
|
||||
<line_editor bottom_delta="-20" follows="left|top" bevel_style="in" border_style="line" border_thickness="1" height="20" left_delta="-5" max_length="36" name="KeywordsSound" width="300"/>
|
||||
|
||||
@@ -404,6 +404,11 @@
|
||||
<button.commit_callback function="ShowFloater" parameter="Preferences"/>
|
||||
</button>
|
||||
</layout_panel>
|
||||
<layout_panel name="panelautoreplace" height="24" width="50" user_resize="false" visibility_control="ToolbarVisibleAutoReplace">
|
||||
<button bottom="0" height="24" label="Autoreplace" name="auto_replace_btn" image_overlay="icn_toolbar_auto_replace.tga" image_overlay_alignment="left" image_selected="toolbar_btn_selected.tga" image_unselected="toolbar_btn_enabled.tga" image_disabled="toolbar_btn_disabled.tga" scale_image="true" width="50" follows="left|right">
|
||||
<button.commit_callback function="ShowFloater" parameter="autoreplace"/>
|
||||
</button>
|
||||
</layout_panel>
|
||||
<layout_panel name="paneldisplayname" height="24" width="50" user_resize="false" visibility_control="ToolbarVisibleDisplayName">
|
||||
<button bottom="0" height="24" label="Display Name" name="display_name_btn" image_overlay="icn_toolbar_display_name.tga" image_overlay_alignment="left" image_selected="toolbar_btn_selected.tga" image_unselected="toolbar_btn_enabled.tga" image_disabled="toolbar_btn_disabled.tga" scale_image="true" width="50" follows="left|right">
|
||||
<button.commit_callback function="ShowFloater" parameter="displayname"/>
|
||||
|
||||
32
indra/newview/skins/default/xui/es/floater_autoreplace.xml
Normal file
32
indra/newview/skins/default/xui/es/floater_autoreplace.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="autoreplace_floater" title="Configuración de reemplazo automático">
|
||||
<check_box label="Habilitar el reemplazo automático" name="autoreplace_enable" tool_tip="Al escribir el texto del chat, reemplaza las palabras clave especificadas con la sustitución correspondiente"/>
|
||||
<button label="Importar lista..." name="autoreplace_import_list" tool_tip="Carga una lista previamente exportada desde un archivo."/>
|
||||
<button label="Exportar lista..." name="autoreplace_export_list" tool_tip="Guarda la lista seleccionada en un archivo para poder compartirla."/>
|
||||
<button label="Lista nueva..." name="autoreplace_new_list" tool_tip="Crea una lista nueva."/>
|
||||
<button label="Eliminar lista" name="autoreplace_delete_list" tool_tip="Elimina la lista seleccionada."/>
|
||||
<button name="autoreplace_list_up" tool_tip="Aumenta la prioridad de esta lista."/>
|
||||
<button name="autoreplace_list_down" tool_tip="Baja la prioridad de esta lista."/>
|
||||
<scroll_list name="autoreplace_list_replacements">
|
||||
<scroll_list.columns label="Palabra clave" name="Keyword"/>
|
||||
<scroll_list.columns label="Reemplazo" name="Replacement"/>
|
||||
</scroll_list>
|
||||
<button label="Añadir..." name="autoreplace_add_entry"/>
|
||||
<button label="Eliminar" name="autoreplace_delete_entry"/>
|
||||
<button label="Guardar entrada" name="autoreplace_save_entry" tool_tip="Guarda esta entrada."/>
|
||||
<button label="Guardar cambios" name="autoreplace_save_changes" tool_tip="Guarda todos los cambios."/>
|
||||
<button label="Cancelar" name="autoreplace_cancel" tool_tip="Descarta todos los cambios."/>
|
||||
</floater>
|
||||
<!--
|
||||
<text
|
||||
top_pad="10"
|
||||
left="10"
|
||||
height="16"
|
||||
width="260"
|
||||
follows="left|top"
|
||||
halign="center"
|
||||
mouse_opaque="true"
|
||||
name="autoreplace_text2">
|
||||
Entries
|
||||
</text>
|
||||
-->
|
||||
32
indra/newview/skins/default/xui/fr/floater_autoreplace.xml
Normal file
32
indra/newview/skins/default/xui/fr/floater_autoreplace.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="autoreplace_floater" title="Paramètres Rechercher/Remplacer">
|
||||
<check_box label="Activer la fonction Rechercher/Remplacer" name="autoreplace_enable" tool_tip="Lors de la saisie du texte d'un chat, remplace chaque mot-clé spécifié par la valeur correspondante."/>
|
||||
<button label="Importer une liste..." name="autoreplace_import_list" tool_tip="Charger une liste précédemment exportée à partir d'un fichier."/>
|
||||
<button label="Exporter la liste..." name="autoreplace_export_list" tool_tip="Enregistrer la liste sélectionnée dans un fichier afin de pouvoir la partager."/>
|
||||
<button label="Nouvelle liste..." name="autoreplace_new_list" tool_tip="Créer une nouvelle liste."/>
|
||||
<button label="Supprimer la liste" name="autoreplace_delete_list" tool_tip="Supprimer la liste sélectionnée."/>
|
||||
<button name="autoreplace_list_up" tool_tip="Augmenter la priorité de cette liste."/>
|
||||
<button name="autoreplace_list_down" tool_tip="Diminuer la priorité de cette liste."/>
|
||||
<scroll_list name="autoreplace_list_replacements">
|
||||
<scroll_list.columns label="Mot-clé" name="Keyword"/>
|
||||
<scroll_list.columns label="Remplacement" name="Replacement"/>
|
||||
</scroll_list>
|
||||
<button label="Ajouter..." name="autoreplace_add_entry"/>
|
||||
<button label="Supprimer" name="autoreplace_delete_entry"/>
|
||||
<button label="Enregistrer" name="autoreplace_save_entry" tool_tip="Enregistrer cette entrée."/>
|
||||
<button label="Enregistrer les modifications" name="autoreplace_save_changes" tool_tip="Enregistrer toutes les modifications."/>
|
||||
<button label="Annuler" name="autoreplace_cancel" tool_tip="Ignorer toutes les modifications."/>
|
||||
</floater>
|
||||
<!--
|
||||
<text
|
||||
top_pad="10"
|
||||
left="10"
|
||||
height="16"
|
||||
width="260"
|
||||
follows="left|top"
|
||||
halign="center"
|
||||
mouse_opaque="true"
|
||||
name="autoreplace_text2">
|
||||
Entries
|
||||
</text>
|
||||
-->
|
||||
32
indra/newview/skins/default/xui/it/floater_autoreplace.xml
Normal file
32
indra/newview/skins/default/xui/it/floater_autoreplace.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="autoreplace_floater" title="Impostazioni sostituzione automatica">
|
||||
<check_box label="Attiva sostituzione automatica" name="autoreplace_enable" tool_tip="Quando inserisci testo nella chat, sostituisci le parole chiave inserite con l'elemento sostitutivo corrispondente."/>
|
||||
<button label="Importa lista..." name="autoreplace_import_list" tool_tip="Carica da file una lista esportata in precedenza."/>
|
||||
<button label="Esporta lista..." name="autoreplace_export_list" tool_tip="Salva la lista selezionata in un file per condividerla."/>
|
||||
<button label="Nuova lista..." name="autoreplace_new_list" tool_tip="Crea una nuova lista"/>
|
||||
<button label="Cancella lista" name="autoreplace_delete_list" tool_tip="Cancella la lista selezionata"/>
|
||||
<button name="autoreplace_list_up" tool_tip="Aumenta la priorità di questa lista."/>
|
||||
<button name="autoreplace_list_down" tool_tip="Diminuisci la priorità di questa lista."/>
|
||||
<scroll_list name="autoreplace_list_replacements">
|
||||
<scroll_list.columns label="Parola chiave" name="Keyword"/>
|
||||
<scroll_list.columns label="Sostituzione" name="Replacement"/>
|
||||
</scroll_list>
|
||||
<button label="Aggiungi..." name="autoreplace_add_entry"/>
|
||||
<button label="Rimuovi" name="autoreplace_delete_entry"/>
|
||||
<button label="Salva elemento" name="autoreplace_save_entry" tool_tip="Salva questo elemento."/>
|
||||
<button label="Salva modifiche" name="autoreplace_save_changes" tool_tip="Salva tutte le modifiche."/>
|
||||
<button label="Annulla" name="autoreplace_cancel" tool_tip="Annulla tutte le modifiche."/>
|
||||
</floater>
|
||||
<!--
|
||||
<text
|
||||
top_pad="10"
|
||||
left="10"
|
||||
height="16"
|
||||
width="260"
|
||||
follows="left|top"
|
||||
halign="center"
|
||||
mouse_opaque="true"
|
||||
name="autoreplace_text2">
|
||||
Entries
|
||||
</text>
|
||||
-->
|
||||
32
indra/newview/skins/default/xui/pt/floater_autoreplace.xml
Normal file
32
indra/newview/skins/default/xui/pt/floater_autoreplace.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<floater name="autoreplace_floater" title="Configurações da substituição automática">
|
||||
<check_box label="Habilitar substituição automática" name="autoreplace_enable" tool_tip="Ao inserir texto de bate-papo, substitua quaisquer palavras-chave inseridas pela substituição correspondente"/>
|
||||
<button label="Importar lista..." name="autoreplace_import_list" tool_tip="Carrega uma lista exportada anteriormente de um arquivo."/>
|
||||
<button label="Exportar lista..." name="autoreplace_export_list" tool_tip="Salva um arquivo da lista selecionada para compartilhá-la."/>
|
||||
<button label="Nova lista..." name="autoreplace_new_list" tool_tip="Cria uma nova lista."/>
|
||||
<button label="Excluir lista" name="autoreplace_delete_list" tool_tip="Exclui a lista selecionada."/>
|
||||
<button name="autoreplace_list_up" tool_tip="Aumenta a prioridade desta lista."/>
|
||||
<button name="autoreplace_list_down" tool_tip="Diminui a prioridade desta lista."/>
|
||||
<scroll_list name="autoreplace_list_replacements">
|
||||
<scroll_list.columns label="Palavra-chave" name="Keyword"/>
|
||||
<scroll_list.columns label="Substituição" name="Replacement"/>
|
||||
</scroll_list>
|
||||
<button label="Adicionar..." name="autoreplace_add_entry"/>
|
||||
<button label="Remover" name="autoreplace_delete_entry"/>
|
||||
<button label="Salvar entrada" name="autoreplace_save_entry" tool_tip="Salva esta entrada."/>
|
||||
<button label="Salvar alterações" name="autoreplace_save_changes" tool_tip="Salvar todas as alterações."/>
|
||||
<button label="Cancelar" name="autoreplace_cancel" tool_tip="Descarta todas as alterações."/>
|
||||
</floater>
|
||||
<!--
|
||||
<text
|
||||
top_pad="10"
|
||||
left="10"
|
||||
height="16"
|
||||
width="260"
|
||||
follows="left|top"
|
||||
halign="center"
|
||||
mouse_opaque="true"
|
||||
name="autoreplace_text2">
|
||||
Entries
|
||||
</text>
|
||||
-->
|
||||
Reference in New Issue
Block a user