Address Issue 1298: Add Auto-replace text feature, borrowed from LL~

This commit is contained in:
Inusaito Sayori
2014-04-08 02:12:51 -04:00
parent 0b2d019e61
commit b1d18c7f29
24 changed files with 10668 additions and 5 deletions

View File

@@ -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
{

View File

@@ -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()); }

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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>

View File

@@ -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>

View File

@@ -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));

View 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()
{
}

View 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 */

View File

@@ -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));

View 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");
*/

View 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

View File

@@ -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) );

View File

@@ -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

View 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>
-->

View 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>
-->

View File

@@ -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"

View File

@@ -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"/>

View File

@@ -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"/>

View 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>
-->

View 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&apos;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&apos;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>
-->

View 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&apos;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>
-->

View 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>
-->