Make the MRU code not so bizzarely overcomplicated
Originally committed to SVN as r5502.
This commit is contained in:
parent
7824348f10
commit
87abcddd87
6 changed files with 78 additions and 127 deletions
|
@ -18,18 +18,13 @@
|
||||||
/// @brief Most Recently Used (MRU) Lists
|
/// @brief Most Recently Used (MRU) Lists
|
||||||
/// @ingroup libaegisub
|
/// @ingroup libaegisub
|
||||||
|
|
||||||
#ifndef LAGI_PRE
|
|
||||||
#include <fstream>
|
|
||||||
#include <time.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "libaegisub/cajun/writer.h"
|
#include "libaegisub/cajun/writer.h"
|
||||||
|
|
||||||
#include "libaegisub/access.h"
|
#include "libaegisub/access.h"
|
||||||
|
#include "libaegisub/io.h"
|
||||||
#include "libaegisub/json.h"
|
#include "libaegisub/json.h"
|
||||||
#include "libaegisub/log.h"
|
#include "libaegisub/log.h"
|
||||||
#include "libaegisub/mru.h"
|
#include "libaegisub/mru.h"
|
||||||
#include "libaegisub/io.h"
|
|
||||||
|
|
||||||
namespace agi {
|
namespace agi {
|
||||||
|
|
||||||
|
@ -53,72 +48,42 @@ MRUManager::MRUManager(const std::string &config, const std::string &default_con
|
||||||
|
|
||||||
MRUManager::~MRUManager() {
|
MRUManager::~MRUManager() {
|
||||||
Flush();
|
Flush();
|
||||||
|
}
|
||||||
|
|
||||||
for (MRUMap::iterator i = mru.begin(); i != mru.end(); ++i) {
|
MRUManager::MRUListMap &MRUManager::Find(std::string const& key) {
|
||||||
delete i->second;
|
MRUMap::iterator index = mru.find(key);
|
||||||
}
|
if (index == mru.end())
|
||||||
|
throw MRUErrorInvalidKey("Invalid key value");
|
||||||
|
return index->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MRUManager::Add(const std::string &key, const std::string &entry) {
|
void MRUManager::Add(const std::string &key, const std::string &entry) {
|
||||||
MRUMap::iterator index;
|
MRUListMap &map = Find(key);
|
||||||
|
map.remove(entry);
|
||||||
if ((index = mru.find(key)) != mru.end()) {
|
map.push_front(entry);
|
||||||
MRUListMap &map = *index->second;
|
Prune(map);
|
||||||
|
|
||||||
// Remove the file before adding it.
|
|
||||||
Remove(key, entry);
|
|
||||||
|
|
||||||
map.insert(std::pair<time_t, std::string>(time(NULL), entry));
|
|
||||||
|
|
||||||
Prune(map);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw MRUErrorInvalidKey("Invalid key value");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MRUManager::Remove(const std::string &key, const std::string &entry) {
|
void MRUManager::Remove(const std::string &key, const std::string &entry) {
|
||||||
MRUMap::iterator index;
|
Find(key).remove(entry);
|
||||||
|
|
||||||
if ((index = mru.find(key)) != mru.end()) {
|
|
||||||
MRUListMap &map = *index->second;
|
|
||||||
for (MRUListMap::iterator map_idx = map.begin(); map_idx != map.end();) {
|
|
||||||
if (map_idx->second == entry)
|
|
||||||
map.erase(map_idx++);
|
|
||||||
else
|
|
||||||
++map_idx;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw MRUErrorInvalidKey("Invalid key value");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const MRUManager::MRUListMap* MRUManager::Get(const std::string &key) {
|
const MRUManager::MRUListMap* MRUManager::Get(const std::string &key) {
|
||||||
MRUMap::iterator index;
|
return &Find(key);
|
||||||
|
|
||||||
if ((index = mru.find(key)) != mru.end()) {
|
|
||||||
return index->second;
|
|
||||||
} else {
|
|
||||||
throw MRUErrorInvalidKey("Invalid key value");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string const& MRUManager::GetEntry(const std::string &key, size_t entry) {
|
||||||
const std::string MRUManager::GetEntry(const std::string &key, const int entry) {
|
|
||||||
const MRUManager::MRUListMap *map = Get(key);
|
const MRUManager::MRUListMap *map = Get(key);
|
||||||
|
|
||||||
MRUListMap::const_iterator index = map->begin();
|
if (entry > map->size())
|
||||||
|
|
||||||
if ((unsigned int)entry > map->size())
|
|
||||||
throw MRUErrorIndexOutOfRange("Requested element index is out of range.");
|
throw MRUErrorIndexOutOfRange("Requested element index is out of range.");
|
||||||
|
|
||||||
std::advance(index, entry);
|
MRUListMap::const_iterator index = map->begin();
|
||||||
|
advance(index, entry);
|
||||||
return index->second;
|
return *index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,61 +91,34 @@ void MRUManager::Flush() {
|
||||||
json::Object out;
|
json::Object out;
|
||||||
|
|
||||||
for (MRUMap::const_iterator i = mru.begin(); i != mru.end(); ++i) {
|
for (MRUMap::const_iterator i = mru.begin(); i != mru.end(); ++i) {
|
||||||
json::Array array;
|
json::Array &array = out[i->first];
|
||||||
MRUListMap *map_list = i->second;
|
const MRUListMap &map_list = i->second;
|
||||||
|
|
||||||
for (MRUListMap::const_iterator i_lst = map_list->begin(); i_lst != map_list->end(); ++i_lst) {
|
for (MRUListMap::const_iterator i_lst = map_list.begin(); i_lst != map_list.end(); ++i_lst) {
|
||||||
json::Object obj;
|
array.Insert(json::String(*i_lst));
|
||||||
obj["time"] = json::Number((double)i_lst->first);
|
|
||||||
obj["entry"] = json::String(i_lst->second);
|
|
||||||
array.Insert(obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out[i->first] = array;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
io::Save file(config_name);
|
json::Writer::Write(out, io::Save(config_name).Get());
|
||||||
std::ofstream& ofp = file.Get();
|
|
||||||
json::Writer::Write(out, ofp);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @brief Prune MRUListMap to the desired length.
|
/// @brief Prune MRUListMap to the desired length.
|
||||||
/// This uses the user-set values for MRU list length.
|
/// This uses the user-set values for MRU list length.
|
||||||
inline void MRUManager::Prune(MRUListMap& map) {
|
inline void MRUManager::Prune(MRUListMap& map) {
|
||||||
unsigned int size = 16;
|
map.resize(std::min(16u, map.size()));
|
||||||
|
}
|
||||||
|
|
||||||
MRUListMap::iterator index = map.begin();;
|
static json::String cast_str(json::UnknownElement const& e) {
|
||||||
|
return static_cast<json::String>(e);
|
||||||
if (map.size() >= size) {
|
|
||||||
std::advance(index, size);
|
|
||||||
|
|
||||||
// Use a range incase the storage number shrinks.
|
|
||||||
map.erase(index, map.end());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Load MRU Lists.
|
/// @brief Load MRU Lists.
|
||||||
/// @param key List name.
|
/// @param key List name.
|
||||||
/// @param array json::Array of values.
|
/// @param array json::Array of values.
|
||||||
void MRUManager::Load(const std::string &key, const json::Array& array) {
|
void MRUManager::Load(const std::string &key, const json::Array& array) {
|
||||||
json::Array::const_iterator index(array.Begin()), indexEnd(array.End());
|
transform(array.Begin(), array.End(), back_inserter(mru[key]), cast_str);
|
||||||
|
Prune(mru[key]);
|
||||||
MRUListMap *map = new MRUListMap();
|
|
||||||
|
|
||||||
for (; index != indexEnd; ++index) {
|
|
||||||
const json::Object& obj = *index;
|
|
||||||
|
|
||||||
time_t time = (time_t)(json::Number)obj["time"];
|
|
||||||
std::string entry = (json::String)obj["entry"];
|
|
||||||
|
|
||||||
map->insert(make_pair(time, entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
mru[key] = map;
|
|
||||||
Prune(*map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#ifndef LAGI_PRE
|
#ifndef LAGI_PRE
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -47,9 +48,7 @@ class MRUManager {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// @brief Map for time->value pairs.
|
/// @brief Map for time->value pairs.
|
||||||
/// @param int Last time loaded
|
typedef std::list<std::string> MRUListMap;
|
||||||
/// @param std::string File or value that was last loaded.
|
|
||||||
typedef std::multimap<time_t, std::string, std::greater_equal<time_t> > MRUListMap;
|
|
||||||
|
|
||||||
/// @brief Constructor
|
/// @brief Constructor
|
||||||
/// @param config File to load MRU values from
|
/// @param config File to load MRU values from
|
||||||
|
@ -79,7 +78,7 @@ public:
|
||||||
/// @param key List name
|
/// @param key List name
|
||||||
/// @param entry 0-base position of entry
|
/// @param entry 0-base position of entry
|
||||||
/// @exception MRUErrorInvalidKey thrown when an invalid key is used.
|
/// @exception MRUErrorInvalidKey thrown when an invalid key is used.
|
||||||
const std::string GetEntry(const std::string &key, const int entry);
|
std::string const& GetEntry(const std::string &key, size_t entry);
|
||||||
|
|
||||||
/// Write MRU lists to disk.
|
/// Write MRU lists to disk.
|
||||||
void Flush();
|
void Flush();
|
||||||
|
@ -92,13 +91,14 @@ private:
|
||||||
/// @brief Map for MRUListMap values.
|
/// @brief Map for MRUListMap values.
|
||||||
/// @param std::string Name
|
/// @param std::string Name
|
||||||
/// @param MRUListMap instance.
|
/// @param MRUListMap instance.
|
||||||
typedef std::map<std::string, MRUListMap*> MRUMap;
|
typedef std::map<std::string, MRUListMap> MRUMap;
|
||||||
|
|
||||||
/// Internal MRUMap values.
|
/// Internal MRUMap values.
|
||||||
MRUMap mru;
|
MRUMap mru;
|
||||||
|
|
||||||
void Load(const std::string &key, const ::json::Array& array);
|
void Load(const std::string &key, const ::json::Array& array);
|
||||||
inline void Prune(MRUListMap& map);
|
inline void Prune(MRUListMap& map);
|
||||||
|
MRUListMap &Find(std::string const& key);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace agi
|
} // namespace agi
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
|
#ifndef AGI_PRE
|
||||||
|
#include <algorithm>
|
||||||
|
#endif
|
||||||
|
|
||||||
wxArrayString lagi_MRU_wxAS(const wxString &list) {
|
wxArrayString lagi_MRU_wxAS(const wxString &list) {
|
||||||
|
const agi::MRUManager::MRUListMap *map = config::mru->Get(STD_STR(list));
|
||||||
wxArrayString work;
|
wxArrayString work;
|
||||||
|
work.reserve(map->size());
|
||||||
const agi::MRUManager::MRUListMap *map_list = config::mru->Get(STD_STR(list));
|
transform(map->begin(), map->end(), std::back_inserter(work), lagi_wxString);
|
||||||
|
|
||||||
for (agi::MRUManager::MRUListMap::const_iterator i_lst = map_list->begin(); i_lst != map_list->end(); ++i_lst) {
|
|
||||||
work.Add(wxString(i_lst->second.c_str(), wxConvUTF8));
|
|
||||||
}
|
|
||||||
|
|
||||||
return work;
|
return work;
|
||||||
}
|
}
|
||||||
|
|
|
@ -648,7 +648,7 @@ void FrameMain::RebuildRecentList(const char *root_command, const char *mru_name
|
||||||
ss << "/";
|
ss << "/";
|
||||||
ss << i;
|
ss << i;
|
||||||
|
|
||||||
wxFileName shortname(lagi_wxString(it->second));
|
wxFileName shortname(lagi_wxString(*it));
|
||||||
|
|
||||||
menu->Append(cmd::id(ss.str()),
|
menu->Append(cmd::id(ss.str()),
|
||||||
wxString::Format("%s%d %s", i <= 9 ? "&" : "", i + 1, shortname.GetFullName()));
|
wxString::Format("%s%d %s", i <= 9 ? "&" : "", i + 1, shortname.GetFullName()));
|
||||||
|
|
|
@ -57,8 +57,8 @@ Menu::Menu() {
|
||||||
const json::Object::Member& member = *index;
|
const json::Object::Member& member = *index;
|
||||||
const json::Array& array = member.element;
|
const json::Array& array = member.element;
|
||||||
delete BuildMenu(member.name, array);
|
delete BuildMenu(member.name, array);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu::~Menu() {}
|
Menu::~Menu() {}
|
||||||
|
|
||||||
|
@ -69,11 +69,11 @@ wxMenu* Menu::GetMenu(std::string name) {
|
||||||
|
|
||||||
if ((index = map.find(name)) != map.end()) {
|
if ((index = map.find(name)) != map.end()) {
|
||||||
return index->second;
|
return index->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_E("menu/invalid") << "Invalid index name: " << name;
|
LOG_E("menu/invalid") << "Invalid index name: " << name;
|
||||||
throw MenuInvalidName("Unknown index");
|
throw MenuInvalidName("Unknown index");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
|
||||||
if (type == Menu::Spacer) {
|
if (type == Menu::Spacer) {
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const json::String& command = obj["command"];
|
const json::String& command = obj["command"];
|
||||||
|
@ -101,13 +101,13 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
|
||||||
|
|
||||||
std::string cmd_name = type == Menu::Submenu ? name_submenu : command.Value();
|
std::string cmd_name = type == Menu::Submenu ? name_submenu : command.Value();
|
||||||
cmd::Command *cmd;
|
cmd::Command *cmd;
|
||||||
try {
|
try {
|
||||||
cmd = cmd::get(cmd_name);
|
cmd = cmd::get(cmd_name);
|
||||||
}
|
}
|
||||||
catch (CommandNotFound const&) {
|
catch (CommandNotFound const&) {
|
||||||
LOG_W("menu/command/not_found") << "Command '" << cmd_name << "' not found; skipping";
|
LOG_W("menu/command/not_found") << "Command '" << cmd_name << "' not found; skipping";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString display = cmd->StrMenu() + "\t" + hotkey::get_hotkey_str_first("Default", cmd_name);
|
wxString display = cmd->StrMenu() + "\t" + hotkey::get_hotkey_str_first("Default", cmd_name);
|
||||||
wxString descr = cmd->StrHelp();
|
wxString descr = cmd->StrHelp();
|
||||||
|
@ -116,17 +116,17 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
|
||||||
case Menu::Option: {
|
case Menu::Option: {
|
||||||
wxMenuItem *menu_item = new wxMenuItem(menu, cmd::id(command.Value()), display, descr, wxITEM_NORMAL);
|
wxMenuItem *menu_item = new wxMenuItem(menu, cmd::id(command.Value()), display, descr, wxITEM_NORMAL);
|
||||||
menu->Append(menu_item);
|
menu->Append(menu_item);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Menu::Check: {
|
case Menu::Check: {
|
||||||
menu->AppendCheckItem(cmd::id(command.Value()), display, descr);
|
menu->AppendCheckItem(cmd::id(command.Value()), display, descr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Menu::Radio: {
|
case Menu::Radio: {
|
||||||
menu->AppendRadioItem(cmd::id(command.Value()), display, descr);
|
menu->AppendRadioItem(cmd::id(command.Value()), display, descr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Menu::Recent: {
|
case Menu::Recent: {
|
||||||
|
@ -135,7 +135,7 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
|
||||||
menu->Append(menu_item);
|
menu->Append(menu_item);
|
||||||
map.insert(MTPair(command.Value(), menu_new));
|
map.insert(MTPair(command.Value(), menu_new));
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Menu::Submenu: {
|
case Menu::Submenu: {
|
||||||
|
@ -150,10 +150,10 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
|
||||||
menu->Append(menu_item);
|
menu->Append(menu_item);
|
||||||
} else {
|
} else {
|
||||||
main_menu->Append(menu_new, display);
|
main_menu->Append(menu_new, display);
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
} // for index
|
} // for index
|
||||||
|
|
||||||
|
|
|
@ -48,13 +48,7 @@ TEST_F(lagi_mru, MRUConstructFromString) {
|
||||||
|
|
||||||
TEST_F(lagi_mru, MRUConstructInvalid) {
|
TEST_F(lagi_mru, MRUConstructInvalid) {
|
||||||
util::copy("data/mru_invalid.json", "data/mru_tmp");
|
util::copy("data/mru_invalid.json", "data/mru_tmp");
|
||||||
|
EXPECT_ANY_THROW(agi::MRUManager("data/mru_tmp", default_mru));
|
||||||
const std::string nonexistent("data/mru_tmp");
|
|
||||||
agi::MRUManager mru(nonexistent, default_mru);
|
|
||||||
|
|
||||||
// Make sure it didn't load from the file.
|
|
||||||
EXPECT_THROW(mru.Add("Invalid", "/path/to/file"), agi::MRUErrorInvalidKey);
|
|
||||||
EXPECT_NO_THROW(mru.Add("Valid_Int", "/path/to/file"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(lagi_mru, MRUEntryAdd) {
|
TEST_F(lagi_mru, MRUEntryAdd) {
|
||||||
|
@ -82,6 +76,25 @@ TEST_F(lagi_mru, MRUKeyValid) {
|
||||||
EXPECT_NO_THROW(mru.Add("Valid", "/path/to/file"));
|
EXPECT_NO_THROW(mru.Add("Valid", "/path/to/file"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(lagi_mru, MRUAddSeveral) {
|
||||||
|
util::copy("data/mru_ok.json", "data/mru_tmp");
|
||||||
|
agi::MRUManager mru("data/mru_tmp", default_mru);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(mru.Add("Valid", "/file/1"));
|
||||||
|
EXPECT_NO_THROW(mru.Add("Valid", "/file/2"));
|
||||||
|
EXPECT_NO_THROW(mru.Add("Valid", "/file/3"));
|
||||||
|
EXPECT_NO_THROW(mru.Add("Valid", "/file/1"));
|
||||||
|
EXPECT_NO_THROW(mru.Add("Valid", "/file/1"));
|
||||||
|
EXPECT_NO_THROW(mru.Add("Valid", "/file/3"));
|
||||||
|
|
||||||
|
agi::MRUManager::MRUListMap::const_iterator entry = mru.Get("Valid")->begin();
|
||||||
|
EXPECT_STREQ("/file/3", (*entry++).c_str());
|
||||||
|
EXPECT_STREQ("/file/1", (*entry++).c_str());
|
||||||
|
EXPECT_STREQ("/file/2", (*entry++).c_str());
|
||||||
|
EXPECT_EQ(mru.Get("Valid")->end(), entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Check to make sure an entry is really removed. This was fixed in
|
// Check to make sure an entry is really removed. This was fixed in
|
||||||
// r4347, the entry was being removed from a copy of the map internally.
|
// r4347, the entry was being removed from a copy of the map internally.
|
||||||
|
@ -94,6 +107,6 @@ TEST_F(lagi_mru, MRUEntryRemove_r4347) {
|
||||||
const agi::MRUManager::MRUListMap *map_list = mru.Get("Valid");
|
const agi::MRUManager::MRUListMap *map_list = mru.Get("Valid");
|
||||||
agi::MRUManager::MRUListMap::const_iterator i_lst = map_list->begin();
|
agi::MRUManager::MRUListMap::const_iterator i_lst = map_list->begin();
|
||||||
|
|
||||||
if ((i_lst != map_list->end()) && (i_lst->second == "/path/to/file"))
|
if ((i_lst != map_list->end()) && (*i_lst == "/path/to/file"))
|
||||||
FAIL() << "r4347 regression, Entry exists after remove";
|
FAIL() << "r4347 regression, Entry exists after remove";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue