diff options
Diffstat (limited to 'indra/newview/llkeyconflict.cpp')
-rw-r--r-- | indra/newview/llkeyconflict.cpp | 852 |
1 files changed, 0 insertions, 852 deletions
diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp deleted file mode 100644 index a94836c59d..0000000000 --- a/indra/newview/llkeyconflict.cpp +++ /dev/null @@ -1,852 +0,0 @@ -/** - * @file llkeyconflict.cpp - * @brief - * - * $LicenseInfo:firstyear=2019&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2019, 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; - * version 2.1 of the License only. - * - * 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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -/* - * App-wide preferences. Note that these are not per-user, - * because we need to load many preferences before we have - * a login name. - */ - -#include "llviewerprecompiledheaders.h" - -#include "llkeyconflict.h" - -#include "llinitparam.h" -#include "llkeyboard.h" -#include "lltrans.h" -#include "llviewercontrol.h" -#include "llviewerinput.h" -#include "llviewermenu.h" -#include "llxuiparser.h" - -static const std::string saved_settings_key_controls[] = { "placeholder" }; // add settings from gSavedSettings here - -static const std::string filename_default = "key_bindings.xml"; -static const std::string filename_temporary = "key_bindings_tmp.xml"; // used to apply uncommited changes on the go. - -// LLKeyboard::stringFromMask is meant for UI and is OS dependent, -// so this class uses it's own version -std::string string_from_mask(MASK mask) -{ - std::string res; - if ((mask & MASK_CONTROL) != 0) - { - res = "CTL"; - } - if ((mask & MASK_ALT) != 0) - { - if (!res.empty()) res += "_"; - res += "ALT"; - } - if ((mask & MASK_SHIFT) != 0) - { - if (!res.empty()) res += "_"; - res += "SHIFT"; - } - - if (mask == MASK_NONE) - { - res = "NONE"; - } - return res; -} - -std::string string_from_mouse(EMouseClickType click, bool translate) -{ - std::string res; - switch (click) - { - case CLICK_LEFT: - res = "LMB"; - break; - case CLICK_MIDDLE: - res = "MMB"; - break; - case CLICK_RIGHT: - res = "RMB"; - break; - case CLICK_BUTTON4: - res = "MB4"; - break; - case CLICK_BUTTON5: - res = "MB5"; - break; - case CLICK_DOUBLELEFT: - res = "Double LMB"; - break; - default: - break; - } - - if (translate && !res.empty()) - { - res = LLTrans::getString(res); - } - return res; -} - -// LLKeyConflictHandler - -S32 LLKeyConflictHandler::sTemporaryFileUseCount = 0; - -LLKeyConflictHandler::LLKeyConflictHandler() -: mHasUnsavedChanges(false), - mUsesTemporaryFile(false), - mLoadMode(MODE_COUNT) -{ -} - -LLKeyConflictHandler::LLKeyConflictHandler(ESourceMode mode) -: mHasUnsavedChanges(false), - mUsesTemporaryFile(false), - mLoadMode(mode) -{ - loadFromSettings(mode); -} - -LLKeyConflictHandler::~LLKeyConflictHandler() -{ - clearUnsavedChanges(); - // Note: does not reset bindings if temporary file was used -} - -bool LLKeyConflictHandler::canHandleControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask) -{ - return mControlsMap[control_name].canHandle(mouse_ind, key, mask); -} - -bool LLKeyConflictHandler::canHandleKey(const std::string &control_name, KEY key, MASK mask) -{ - return canHandleControl(control_name, CLICK_NONE, key, mask); -} - -bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, EMouseClickType mouse_ind, MASK mask) -{ - return canHandleControl(control_name, mouse_ind, KEY_NONE, mask); -} - -bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, S32 mouse_ind, MASK mask) -{ - return canHandleControl(control_name, (EMouseClickType)mouse_ind, KEY_NONE, mask); -} - -bool LLKeyConflictHandler::canAssignControl(const std::string &control_name) -{ - control_map_t::iterator iter = mControlsMap.find(control_name); - if (iter != mControlsMap.end()) - { - return iter->second.mAssignable; - } - // If we don't know this control means it wasn't assigned by user yet and thus is editable - return true; -} - -// static -bool LLKeyConflictHandler::isReservedByMenu(const KEY &key, const MASK &mask) -{ - if (key == KEY_NONE) - { - return false; - } - return (gMenuBarView && gMenuBarView->hasAccelerator(key, mask)) - || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(key, mask)); -} - -// static -bool LLKeyConflictHandler::isReservedByMenu(const LLKeyData &data) -{ - if (data.mMouse != CLICK_NONE || data.mKey == KEY_NONE) - { - return false; - } - return (gMenuBarView && gMenuBarView->hasAccelerator(data.mKey, data.mMask)) - || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(data.mKey, data.mMask)); -} - -bool LLKeyConflictHandler::registerControl(const std::string &control_name, U32 index, EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask) -{ - if (control_name.empty()) - { - return false; - } - LLKeyConflict &type_data = mControlsMap[control_name]; - if (!type_data.mAssignable) - { - LL_ERRS() << "Error in code, user or system should not be able to change certain controls" << LL_ENDL; - } - LLKeyData data(mouse, key, mask, ignore_mask); - if (type_data.mKeyBind.getKeyData(index) == data) - { - return true; - } - if (isReservedByMenu(data)) - { - return false; - } - if (removeConflicts(data, type_data.mConflictMask)) - { - type_data.mKeyBind.replaceKeyData(data, index); - mHasUnsavedChanges = true; - return true; - } - // control already in use/blocked - return false; -} - -LLKeyData LLKeyConflictHandler::getControl(const std::string &control_name, U32 index) -{ - if (control_name.empty()) - { - return LLKeyData(); - } - return mControlsMap[control_name].getKeyData(index); -} - -// static -std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata) -{ - std::string result; - - if (keydata.mMask != MASK_NONE && keydata.mKey != KEY_NONE) - { - result = LLKeyboard::stringFromAccelerator(keydata.mMask, keydata.mKey); - } - else if (keydata.mKey != KEY_NONE) - { - result = LLKeyboard::stringFromKey(keydata.mKey); - } - else if (keydata.mMask != MASK_NONE) - { - result = LLKeyboard::stringFromAccelerator(keydata.mMask); - } - - result += string_from_mouse(keydata.mMouse, true); - - return result; -} - -std::string LLKeyConflictHandler::getControlString(const std::string &control_name, U32 index) -{ - if (control_name.empty()) - { - return ""; - } - return getStringFromKeyData(mControlsMap[control_name].getKeyData(index)); -} - -void LLKeyConflictHandler::loadFromControlSettings(const std::string &name) -{ - LLControlVariablePtr var = gSavedSettings.getControl(name); - if (var) - { - LLKeyBind bind(var->getValue()); - LLKeyConflict key(bind, true, 0); - mControlsMap[name] = key; - } -} - -void LLKeyConflictHandler::loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination) -{ - for (LLInitParam::ParamIterator<LLViewerInput::KeyBinding>::const_iterator it = keymode.bindings.begin(), - end_it = keymode.bindings.end(); - it != end_it; - ++it) - { - KEY key; - MASK mask; - EMouseClickType mouse = CLICK_NONE; - if (it->mouse.isProvided()) - { - LLViewerInput::mouseFromString(it->mouse.getValue(), &mouse); - } - if (it->key.getValue().empty()) - { - key = KEY_NONE; - } - else - { - LLKeyboard::keyFromString(it->key, &key); - } - LLKeyboard::maskFromString(it->mask, &mask); - // Note: it->command is also the name of UI element, howhever xml we are loading from - // might not know all the commands, so UI will have to know what to fill by its own - LLKeyConflict &type_data = (*destination)[it->command]; - type_data.mAssignable = true; - type_data.mKeyBind.addKeyData(mouse, key, mask, true); - } -} - -bool LLKeyConflictHandler::loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination) -{ - if (filename.empty()) - { - return false; - } - - bool res = false; - - LLViewerInput::Keys keys; - LLSimpleXUIParser parser; - - if (parser.readXUI(filename, keys) - && keys.validateBlock()) - { - switch (load_mode) - { - case MODE_FIRST_PERSON: - if (keys.first_person.isProvided()) - { - loadFromSettings(keys.first_person, destination); - res = true; - } - break; - case MODE_THIRD_PERSON: - if (keys.third_person.isProvided()) - { - loadFromSettings(keys.third_person, destination); - res = true; - } - break; - case MODE_EDIT_AVATAR: - if (keys.edit_avatar.isProvided()) - { - loadFromSettings(keys.edit_avatar, destination); - res = true; - } - break; - case MODE_SITTING: - if (keys.sitting.isProvided()) - { - loadFromSettings(keys.sitting, destination); - res = true; - } - break; - default: - LL_ERRS() << "Not implememted mode " << load_mode << LL_ENDL; - break; - } - } - return res; -} - -void LLKeyConflictHandler::loadFromSettings(ESourceMode load_mode) -{ - mControlsMap.clear(); - mDefaultsMap.clear(); - - // E.X. In case we need placeholder keys for conflict resolution. - generatePlaceholders(load_mode); - - if (load_mode == MODE_SAVED_SETTINGS) - { - // load settings clss knows about, but it also possible to load settings by name separately - const S32 size = std::extent<decltype(saved_settings_key_controls)>::value; - for (U32 i = 0; i < size; i++) - { - loadFromControlSettings(saved_settings_key_controls[i]); - } - } - else - { - // load defaults - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename_default); - if (!loadFromSettings(load_mode, filename, &mDefaultsMap)) - { - LL_WARNS() << "Failed to load default settings, aborting" << LL_ENDL; - return; - } - - // load user's - filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); - if (!gDirUtilp->fileExists(filename) || !loadFromSettings(load_mode, filename, &mControlsMap)) - { - // mind placeholders - mControlsMap.insert(mDefaultsMap.begin(), mDefaultsMap.end()); - } - } - mLoadMode = load_mode; -} - -void LLKeyConflictHandler::saveToSettings(bool temporary) -{ - if (mControlsMap.empty()) - { - return; - } - - if (mLoadMode == MODE_SAVED_SETTINGS) - { - // Does not support 'temporary', preferences handle that themself - // so in case of saved settings we just do not clear mHasUnsavedChanges - control_map_t::iterator iter = mControlsMap.begin(); - control_map_t::iterator end = mControlsMap.end(); - - for (; iter != end; ++iter) - { - if (iter->first.empty()) - { - continue; - } - - LLKeyConflict &key = iter->second; - key.mKeyBind.trimEmpty(); - if (!key.mAssignable) - { - continue; - } - - if (gSavedSettings.controlExists(iter->first)) - { - gSavedSettings.setLLSD(iter->first, key.mKeyBind.asLLSD()); - } - else if (!key.mKeyBind.empty()) - { - // Note: this is currently not in use, might be better for load mechanics to ask for and retain control group - // otherwise settings loaded from other control groups will end in this one - LL_INFOS() << "Creating new keybinding " << iter->first << LL_ENDL; - gSavedSettings.declareLLSD(iter->first, key.mKeyBind.asLLSD(), "comment", LLControlVariable::PERSIST_ALWAYS); - } - } - } - else - { - // Determine what file to load and load full copy of that file - std::string filename; - - if (temporary) - { - filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); - if (!gDirUtilp->fileExists(filename)) - { - filename.clear(); - } - } - - if (filename.empty()) - { - filename = gDirUtilp->findFile(filename_default, - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - } - - LLViewerInput::Keys keys; - LLSimpleXUIParser parser; - - if (parser.readXUI(filename, keys) - && keys.validateBlock()) - { - // replace category we edited - - // mode is a HACK to correctly reset bindings without reparsing whole file and avoid doing - // own param container (which will face issues with inasseesible members of LLInitParam) - LLViewerInput::KeyMode mode; - LLViewerInput::KeyBinding binding; - - control_map_t::iterator iter = mControlsMap.begin(); - control_map_t::iterator end = mControlsMap.end(); - for (; iter != end; ++iter) - { - // By default xml have (had) up to 6 elements per function - // eventually it will be cleaned up and UI will only shows 3 per function, - // so make sure to cleanup. - // Also this helps in keeping file small. - iter->second.mKeyBind.trimEmpty(); - U32 size = iter->second.mKeyBind.getDataCount(); - for (U32 i = 0; i < size; ++i) - { - if (iter->first.empty()) - { - continue; - } - - LLKeyConflict &key = iter->second; - key.mKeyBind.trimEmpty(); - if (key.mKeyBind.empty() || !key.mAssignable) - { - continue; - } - - LLKeyData data = key.mKeyBind.getKeyData(i); - // Still write empty LLKeyData to make sure we will maintain UI position - if (data.mKey == KEY_NONE) - { - // Might be better idea to be consistent and use NONE. LLViewerInput can work with both cases - binding.key = ""; - } - else - { - binding.key = LLKeyboard::stringFromKey(data.mKey, false /*Do not localize*/); - } - binding.mask = string_from_mask(data.mMask); - if (data.mMouse == CLICK_NONE) - { - binding.mouse.setProvided(false); - } - else - { - // set() because 'optional', for compatibility purposes - // just copy old keys.xml and rename to key_bindings.xml, it should work - binding.mouse.set(string_from_mouse(data.mMouse, false), true); - } - binding.command = iter->first; - mode.bindings.add(binding); - } - } - - switch (mLoadMode) - { - case MODE_FIRST_PERSON: - if (keys.first_person.isProvided()) - { - keys.first_person.bindings.set(mode.bindings, true); - } - break; - case MODE_THIRD_PERSON: - if (keys.third_person.isProvided()) - { - keys.third_person.bindings.set(mode.bindings, true); - } - break; - case MODE_EDIT_AVATAR: - if (keys.edit_avatar.isProvided()) - { - keys.edit_avatar.bindings.set(mode.bindings, true); - } - break; - case MODE_SITTING: - if (keys.sitting.isProvided()) - { - keys.sitting.bindings.set(mode.bindings, true); - } - break; - default: - LL_ERRS() << "Not implememted mode " << mLoadMode << LL_ENDL; - break; - } - - if (temporary) - { - // write to temporary xml and use it for gViewerInput - filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); - if (!mUsesTemporaryFile) - { - mUsesTemporaryFile = true; - sTemporaryFileUseCount++; - } - } - else - { - // write back to user's xml and use it for gViewerInput - filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); - // Don't reset mUsesTemporaryFile, it will be reset at cleanup stage - } - - LLXMLNodePtr output_node = new LLXMLNode("keys", false); - LLXUIParser parser; - parser.writeXUI(output_node, keys); - - // Write the resulting XML to file - if (!output_node->isNull()) - { - LLFILE *fp = LLFile::fopen(filename, "w"); - if (fp != NULL) - { - LLXMLNode::writeHeaderToFile(fp); - output_node->writeToFile(fp); - fclose(fp); - } - } - // Now force a rebind for keyboard - if (gDirUtilp->fileExists(filename)) - { - // Ideally instead of rebinding immediately we should shedule - // the rebind since single file can have multiple handlers, - // one per mode, saving simultaneously. - // Or whatever uses LLKeyConflictHandler should control the process. - gViewerInput.loadBindingsXML(filename); - } - } - } - - if (!temporary) - { - // will remove any temporary file if there were any - clearUnsavedChanges(); - } -} - -LLKeyData LLKeyConflictHandler::getDefaultControl(const std::string &control_name, U32 index) -{ - if (control_name.empty()) - { - return LLKeyData(); - } - if (mLoadMode == MODE_SAVED_SETTINGS) - { - LLControlVariablePtr var = gSavedSettings.getControl(control_name); - if (var) - { - return LLKeyBind(var->getDefault()).getKeyData(index); - } - return LLKeyData(); - } - else - { - control_map_t::iterator iter = mDefaultsMap.find(control_name); - if (iter != mDefaultsMap.end()) - { - return iter->second.mKeyBind.getKeyData(index); - } - return LLKeyData(); - } -} - -void LLKeyConflictHandler::resetToDefault(const std::string &control_name, U32 index) -{ - if (control_name.empty()) - { - return; - } - LLKeyData data = getDefaultControl(control_name, index); - - if (data != mControlsMap[control_name].getKeyData(index)) - { - // reset controls that might have been switched to our current control - removeConflicts(data, mControlsMap[control_name].mConflictMask); - mControlsMap[control_name].setKeyData(data, index); - } -} - -void LLKeyConflictHandler::resetToDefaultAndResolve(const std::string &control_name, bool ignore_conflicts) -{ - if (control_name.empty()) - { - return; - } - if (mLoadMode == MODE_SAVED_SETTINGS) - { - LLControlVariablePtr var = gSavedSettings.getControl(control_name); - if (var) - { - LLKeyBind bind(var->getDefault()); - if (!ignore_conflicts) - { - for (S32 i = 0; i < bind.getDataCount(); ++i) - { - removeConflicts(bind.getKeyData(i), mControlsMap[control_name].mConflictMask); - } - } - mControlsMap[control_name].mKeyBind = bind; - } - else - { - mControlsMap[control_name].mKeyBind.clear(); - } - } - else - { - control_map_t::iterator iter = mDefaultsMap.find(control_name); - if (iter != mDefaultsMap.end()) - { - if (!ignore_conflicts) - { - for (S32 i = 0; i < iter->second.mKeyBind.getDataCount(); ++i) - { - removeConflicts(iter->second.mKeyBind.getKeyData(i), mControlsMap[control_name].mConflictMask); - } - } - mControlsMap[control_name].mKeyBind = iter->second.mKeyBind; - } - else - { - mControlsMap[control_name].mKeyBind.clear(); - } - } -} - -void LLKeyConflictHandler::resetToDefault(const std::string &control_name) -{ - // reset specific binding without ignoring conflicts - resetToDefaultAndResolve(control_name, false); -} - -void LLKeyConflictHandler::resetToDefaults(ESourceMode mode) -{ - if (mode == MODE_SAVED_SETTINGS) - { - control_map_t::iterator iter = mControlsMap.begin(); - control_map_t::iterator end = mControlsMap.end(); - - for (; iter != end; ++iter) - { - resetToDefaultAndResolve(iter->first, true); - } - } - else - { - mControlsMap.clear(); - generatePlaceholders(mode); - mControlsMap.insert(mDefaultsMap.begin(), mDefaultsMap.end()); - } - - mHasUnsavedChanges = true; -} - -void LLKeyConflictHandler::resetToDefaults() -{ - if (!empty()) - { - resetToDefaults(mLoadMode); - } - else - { - // not optimal since: - // 1. We are not sure that mLoadMode was set - // 2. We are not sure if there are any changes in comparison to default - // 3. We are loading 'current' only to replace it - // but it is reliable and works Todo: consider optimizing. - loadFromSettings(mLoadMode); - resetToDefaults(mLoadMode); - } -} - -void LLKeyConflictHandler::clear() -{ - if (clearUnsavedChanges()) - { - // temporary file was removed, this means we were using it and need to reload keyboard's bindings - resetKeyboardBindings(); - } - mControlsMap.clear(); - mDefaultsMap.clear(); -} - -// static -void LLKeyConflictHandler::resetKeyboardBindings() -{ - // Try to load User's bindings first - std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); - if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file)) - { - // Failed to load custom bindings, try default ones - key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename_default); - if (!gViewerInput.loadBindingsXML(key_bindings_file)) - { - LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL; - } - } -} - -void LLKeyConflictHandler::generatePlaceholders(ESourceMode load_mode) -{ - // These controls are meant to cause conflicts when user tries to assign same control somewhere else - // also this can be used to pre-record controls that should not conflict or to assign conflict groups/masks - /*registerTemporaryControl(CONTROL_RESERVED_MENU, CLICK_RIGHT, KEY_NONE, MASK_NONE, 0); - registerTemporaryControl(CONTROL_DELETE, CLICK_NONE, KEY_DELETE, MASK_NONE, 0);*/ -} - -bool LLKeyConflictHandler::removeConflicts(const LLKeyData &data, const U32 &conlict_mask) -{ - if (conlict_mask == CONFLICT_NOTHING) - { - // Can't conflict - return true; - } - std::map<std::string, S32> conflict_list; - control_map_t::iterator cntrl_iter = mControlsMap.begin(); - control_map_t::iterator cntrl_end = mControlsMap.end(); - for (; cntrl_iter != cntrl_end; ++cntrl_iter) - { - S32 index = cntrl_iter->second.mKeyBind.findKeyData(data); - if (index >= 0 - && cntrl_iter->second.mConflictMask != CONFLICT_NOTHING - && (cntrl_iter->second.mConflictMask & conlict_mask) != 0) - { - if (cntrl_iter->second.mAssignable) - { - // Potentially we can have multiple conflict flags conflicting - // including unassignable keys. - // So record the conflict and find all others before doing any changes. - // Assume that there is only one conflict per bind - conflict_list[cntrl_iter->first] = index; - } - else - { - return false; - } - } - } - - std::map<std::string, S32>::iterator cnflct_iter = conflict_list.begin(); - std::map<std::string, S32>::iterator cnflct_end = conflict_list.end(); - for (; cnflct_iter != cnflct_end; ++cnflct_iter) - { - mControlsMap[cnflct_iter->first].mKeyBind.resetKeyData(cnflct_iter->second); - } - return true; -} - -void LLKeyConflictHandler::registerTemporaryControl(const std::string &control_name, EMouseClickType mouse, KEY key, MASK mask, U32 conflict_mask) -{ - LLKeyConflict *type_data = &mControlsMap[control_name]; - type_data->mAssignable = false; - type_data->mConflictMask = conflict_mask; - type_data->mKeyBind.addKeyData(mouse, key, mask, false); -} - -bool LLKeyConflictHandler::clearUnsavedChanges() -{ - bool result = false; - mHasUnsavedChanges = false; - - if (mUsesTemporaryFile) - { - mUsesTemporaryFile = false; - sTemporaryFileUseCount--; - if (!sTemporaryFileUseCount) - { - result = clearTemporaryFile(); - } - // else: might be usefull to overwrite content of temp file with defaults - // but at the moment there is no such need - } - return result; -} - -//static -bool LLKeyConflictHandler::clearTemporaryFile() -{ - // At the moment single file needs five handlers (one per mode), so doing this - // will remove file for all hadlers - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); - if (gDirUtilp->fileExists(filename)) - { - LLFile::remove(filename); - return true; - } - return false; -} - |