/indra/newview/llteleporthistorystorage.cpp
C++ | 255 lines | 168 code | 49 blank | 38 comment | 21 complexity | 7b798068ba8737438da1bfe0bf8f4903 MD5 | raw file
Possible License(s): LGPL-2.1
1/** 2 * @file llteleporthistorystorage.cpp 3 * @brief Teleport history 4 * 5 * $LicenseInfo:firstyear=2009&license=viewerlgpl$ 6 * Second Life Viewer Source Code 7 * Copyright (C) 2010, Linden Research, Inc. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; 12 * version 2.1 of the License only. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA 24 * $/LicenseInfo$ 25 */ 26 27#include "llviewerprecompiledheaders.h" 28 29#include "llteleporthistorystorage.h" 30 31#include "llsd.h" 32#include "llsdserialize.h" 33#include "lldir.h" 34#include "llteleporthistory.h" 35#include "llagent.h" 36 37// Max offset for two global positions to consider them as equal 38const F64 MAX_GLOBAL_POS_OFFSET = 5.0f; 39 40LLTeleportHistoryPersistentItem::LLTeleportHistoryPersistentItem(const LLSD& val) 41{ 42 mTitle = val["title"].asString(); 43 mGlobalPos.setValue(val["global_pos"]); 44 mDate = val["date"]; 45} 46 47LLSD LLTeleportHistoryPersistentItem::toLLSD() const 48{ 49 LLSD val; 50 51 val["title"] = mTitle; 52 val["global_pos"] = mGlobalPos.getValue(); 53 val["date"] = mDate; 54 55 return val; 56} 57 58struct LLSortItemsByDate 59{ 60 bool operator()(const LLTeleportHistoryPersistentItem& a, const LLTeleportHistoryPersistentItem& b) 61 { 62 return a.mDate < b.mDate; 63 } 64}; 65 66LLTeleportHistoryStorage::LLTeleportHistoryStorage() : 67 mFilename("teleport_history.txt") 68{ 69 mItems.clear(); 70 LLTeleportHistory *th = LLTeleportHistory::getInstance(); 71 if (th) 72 th->setHistoryChangedCallback(boost::bind(&LLTeleportHistoryStorage::onTeleportHistoryChange, this)); 73 74 load(); 75} 76 77LLTeleportHistoryStorage::~LLTeleportHistoryStorage() 78{ 79} 80 81void LLTeleportHistoryStorage::onTeleportHistoryChange() 82{ 83 LLTeleportHistory *th = LLTeleportHistory::getInstance(); 84 if (!th) 85 return; 86 87 // Hacky sanity check. (EXT-6798) 88 if (th->getItems().size() == 0) 89 { 90 llassert(!"Inconsistent teleport history state"); 91 return; 92 } 93 94 const LLTeleportHistoryItem &item = th->getItems()[th->getCurrentItemIndex()]; 95 96 addItem(item.mTitle, item.mGlobalPos); 97 save(); 98} 99 100void LLTeleportHistoryStorage::purgeItems() 101{ 102 mItems.clear(); 103 mHistoryChangedSignal(-1); 104} 105 106void LLTeleportHistoryStorage::addItem(const std::string title, const LLVector3d& global_pos) 107{ 108 addItem(title, global_pos, LLDate::now()); 109} 110 111 112bool LLTeleportHistoryStorage::compareByTitleAndGlobalPos(const LLTeleportHistoryPersistentItem& a, const LLTeleportHistoryPersistentItem& b) 113{ 114 return a.mTitle == b.mTitle && (a.mGlobalPos - b.mGlobalPos).length() < MAX_GLOBAL_POS_OFFSET; 115} 116 117void LLTeleportHistoryStorage::addItem(const std::string title, const LLVector3d& global_pos, const LLDate& date) 118{ 119 LLTeleportHistoryPersistentItem item(title, global_pos, date); 120 121 slurl_list_t::iterator item_iter = std::find_if(mItems.begin(), mItems.end(), 122 boost::bind(&LLTeleportHistoryStorage::compareByTitleAndGlobalPos, this, _1, item)); 123 124 // If there is such item already, remove it, since new item is more recent 125 S32 removed_index = -1; 126 if (item_iter != mItems.end()) 127 { 128 removed_index = item_iter - mItems.begin(); 129 mItems.erase(item_iter); 130 } 131 132 mItems.push_back(item); 133 134 // Check whether sorting is needed 135 if (mItems.size() > 1) 136 { 137 item_iter = mItems.end(); 138 139 item_iter--; 140 item_iter--; 141 142 // If second to last item is more recent than last, then resort items 143 if (item_iter->mDate > item.mDate) 144 { 145 removed_index = -1; 146 std::sort(mItems.begin(), mItems.end(), LLSortItemsByDate()); 147 } 148 } 149 150 mHistoryChangedSignal(removed_index); 151} 152 153void LLTeleportHistoryStorage::removeItem(S32 idx) 154{ 155 if (idx < 0 || idx >= (S32)mItems.size()) 156 return; 157 158 mItems.erase (mItems.begin() + idx); 159} 160 161void LLTeleportHistoryStorage::save() 162{ 163 // build filename for each user 164 std::string resolvedFilename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, mFilename); 165 166 // open the history file for writing 167 llofstream file (resolvedFilename); 168 if (!file.is_open()) 169 { 170 llwarns << "can't open teleport history file \"" << mFilename << "\" for writing" << llendl; 171 return; 172 } 173 174 for (size_t i=0; i<mItems.size(); i++) 175 { 176 LLSD s_item = mItems[i].toLLSD(); 177 file << LLSDOStreamer<LLSDNotationFormatter>(s_item) << std::endl; 178 } 179 180 file.close(); 181} 182 183void LLTeleportHistoryStorage::load() 184{ 185 // build filename for each user 186 std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, mFilename); 187 188 // open the history file for reading 189 llifstream file(resolved_filename); 190 if (!file.is_open()) 191 { 192 llwarns << "can't load teleport history from file \"" << mFilename << "\"" << llendl; 193 return; 194 } 195 196 // remove current entries before we load over them 197 mItems.clear(); 198 199 // the parser's destructor is protected so we cannot create in the stack. 200 LLPointer<LLSDParser> parser = new LLSDNotationParser(); 201 std::string line; 202 while (std::getline(file, line)) 203 { 204 LLSD s_item; 205 std::istringstream iss(line); 206 if (parser->parse(iss, s_item, line.length()) == LLSDParser::PARSE_FAILURE) 207 { 208 llinfos << "Parsing saved teleport history failed" << llendl; 209 break; 210 } 211 212 mItems.push_back(s_item); 213 } 214 215 file.close(); 216 217 std::sort(mItems.begin(), mItems.end(), LLSortItemsByDate()); 218 219 mHistoryChangedSignal(-1); 220} 221 222void LLTeleportHistoryStorage::dump() const 223{ 224 llinfos << "Teleport history storage dump (" << mItems.size() << " items):" << llendl; 225 226 for (size_t i=0; i<mItems.size(); i++) 227 { 228 std::stringstream line; 229 line << i << ": " << mItems[i].mTitle; 230 line << " global pos: " << mItems[i].mGlobalPos; 231 line << " date: " << mItems[i].mDate; 232 233 llinfos << line.str() << llendl; 234 } 235} 236 237boost::signals2::connection LLTeleportHistoryStorage::setHistoryChangedCallback(history_callback_t cb) 238{ 239 return mHistoryChangedSignal.connect(cb); 240} 241 242void LLTeleportHistoryStorage::goToItem(S32 idx) 243{ 244 // Validate specified index. 245 if (idx < 0 || idx >= (S32)mItems.size()) 246 { 247 llwarns << "Invalid teleport history index (" << idx << ") specified" << llendl; 248 dump(); 249 return; 250 } 251 252 // Attempt to teleport to the requested item. 253 gAgent.teleportViaLocation(mItems[idx].mGlobalPos); 254} 255