diff --git a/src/BookmarkMgr.cpp b/src/BookmarkMgr.cpp index 0abf79f..ea8c78a 100644 --- a/src/BookmarkMgr.cpp +++ b/src/BookmarkMgr.cpp @@ -24,8 +24,7 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn) { } } - DataNode *recent = s.rootNode()->newChild("recent"); - DataNode *recent_modems = recent->newChild("recent_modems"); + DataNode *recent_modems = s.rootNode()->newChild("recent_modems"); for (auto demod : wxGetApp().getDemodMgr().getDemodulators()) { wxGetApp().getDemodMgr().saveInstance(recent_modems->newChild("modem"),demod); @@ -36,15 +35,67 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn) { recent_modems->newChildCloneFrom("modem", r_i->node); } - s.SaveToFileXML(wxFileName(wxGetApp().getConfig()->getConfigDir(), bookmarkFn).GetFullPath(wxPATH_NATIVE).ToStdString()); + wxFileName saveFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn); + wxFileName saveFileBackup(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup"); + + if (saveFile.IsDirWritable()) { + // Hopefully leave at least a readable backup in case of failure.. + if (saveFile.FileExists() && (!saveFileBackup.FileExists() || saveFileBackup.IsFileWritable())) { + wxCopyFile(saveFile.GetFullPath(wxPATH_NATIVE).ToStdString(), saveFileBackup.GetFullPath(wxPATH_NATIVE).ToStdString()); + } + s.SaveToFileXML(saveFile.GetFullPath(wxPATH_NATIVE).ToStdString()); + } } void BookmarkMgr::loadFromFile(std::string bookmarkFn) { - - -} + wxFileName loadFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn); + DataTree s; + + if (!loadFile.IsFileReadable()) { + return; + } + + if (!s.LoadFromFileXML(loadFile.GetFullPath(wxPATH_NATIVE).ToStdString())) { + // TODO: if exists; inform user & optionally load backup + return; + } + + if (s.rootNode()->hasAnother("modems")) { + DataNode *modems = s.rootNode()->getNext("modems"); + while (modems->hasAnother("group")) { + DataNode *group = modems->getNext("group"); + std::string groupName = "Unnamed"; + if (group->hasAnother("@name")) { + groupName = group->getNext("@name")->element()->toString(); + } + while (group->hasAnother("modem")) { + DataNode *modem = group->getNext("modem"); + BookmarkEntry *be = nodeToBookmark("modem", modem); + if (be) { + addBookmark(groupName.c_str(), be); + } else { + std::cout << "error loading bookmarked modem.." << std::endl; + } + } + } + } + + if (s.rootNode()->hasAnother("recent_modems")) { + DataNode *recent_modems = s.rootNode()->getNext("recent_modems"); + + while (recent_modems->hasAnother("modem")) { + DataNode *modem = recent_modems->getNext("modem"); + BookmarkEntry *be = nodeToBookmark("modem", modem); + if (be) { + addRecent(be); + } else { + std::cout << "error loading recent modem.." << std::endl; + } + } + } +} void BookmarkMgr::addBookmark(std::string group, DemodulatorInstance *demod) { std::lock_guard < std::mutex > lock(busy_lock); @@ -209,12 +260,19 @@ void BookmarkMgr::updateBookmarks(std::string group) { void BookmarkMgr::addRecent(DemodulatorInstance *demod) { std::lock_guard < std::mutex > lock(busy_lock); recents.push_back(demodToBookmarkEntry(demod)); - if (recents.size() > BOOKMARK_RECENTS_MAX) { - delete *(recents.begin()); - recents.erase(recents.begin(), recents.begin()+1); - } + + trimRecents(); } +void BookmarkMgr::addRecent(BookmarkEntry *be) { + std::lock_guard < std::mutex > lock(busy_lock); + + recents.push_back(be); + + trimRecents(); +} + + void BookmarkMgr::removeRecent(BookmarkEntry *be) { std::lock_guard < std::mutex > lock(busy_lock); @@ -232,6 +290,14 @@ BookmarkList BookmarkMgr::getRecents() { } + +void BookmarkMgr::trimRecents() { + if (recents.size() > BOOKMARK_RECENTS_MAX) { + delete *(recents.begin()); + recents.erase(recents.begin(), recents.begin()+1); + } +} + BookmarkEntry *BookmarkMgr::demodToBookmarkEntry(DemodulatorInstance *demod) { BookmarkEntry *be = new BookmarkEntry; @@ -245,3 +311,23 @@ BookmarkEntry *BookmarkMgr::demodToBookmarkEntry(DemodulatorInstance *demod) { return be; } + +BookmarkEntry *BookmarkMgr::nodeToBookmark(const char *name_in, DataNode *node) { + if (!node->hasAnother("frequency") || !node->hasAnother("type") || !node->hasAnother("bandwidth")) { + return nullptr; + } + + BookmarkEntry *be = new BookmarkEntry(); + node->getNext("frequency")->element()->get(be->frequency); + node->getNext("type")->element()->get(be->type); + node->getNext("bandwidth")->element()->get(be->bandwidth); + + if (node->hasAnother("user_label")) { + node->getNext("user_label")->element()->get(be->label); + } + + be->node = new DataNode("node",*node); + + return be; +} + diff --git a/src/BookmarkMgr.h b/src/BookmarkMgr.h index 14e9083..58cdecc 100644 --- a/src/BookmarkMgr.h +++ b/src/BookmarkMgr.h @@ -40,7 +40,7 @@ class BookmarkMgr { public: void saveToFile(std::string bookmarkFn); void loadFromFile(std::string bookmarkFn); - + void addBookmark(std::string group, DemodulatorInstance *demod); void addBookmark(std::string group, BookmarkEntry *be); void removeBookmark(std::string group, BookmarkEntry *be); @@ -59,13 +59,17 @@ public: void updateBookmarks(std::string group); void addRecent(DemodulatorInstance *demod); + void addRecent(BookmarkEntry *be); void removeRecent(BookmarkEntry *be); BookmarkList getRecents(); protected: + + void trimRecents(); - BookmarkEntry *demodToBookmarkEntry(DemodulatorInstance *demod); + BookmarkEntry *demodToBookmarkEntry(DemodulatorInstance *demod); + BookmarkEntry *nodeToBookmark(const char *name_in, DataNode *node); BookmarkMap bmData; BookmarkMapSorted bmDataSorted; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 6e4d3b3..e8a7bf3 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -405,6 +405,7 @@ bool CubicSDR::OnCmdLineParsed(wxCmdLineParser& parser) { } config.load(); + wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml"); #ifdef BUNDLE_SOAPY_MODS if (parser.Found("b")) { diff --git a/src/util/DataTree.cpp b/src/util/DataTree.cpp index 095f46a..293a8b8 100755 --- a/src/util/DataTree.cpp +++ b/src/util/DataTree.cpp @@ -435,7 +435,12 @@ std::string DataElement::toString() { strValue = std::to_string(floatSettingValue); } else if (dataType == DATA_NULL) { strValue = ""; - } else { + } else if (dataType == DATA_WSTRING) { + std::wstring wstr; + get(wstr); + strValue = *wstr.c_str(); + } + else { std::cout << "Unhandled DataElement toString for type: " << dataType << std::endl; } } catch (DataTypeMismatchException e) { @@ -490,6 +495,16 @@ DataNode::DataNode(const char *name_in): parentNode(NULL), ptr(0) { data_elem = new DataElement(); } +DataNode::DataNode(const char *name_in, DataNode &cloneFrom): parentNode(NULL), ptr(0) { + node_name = name_in; + data_elem = new DataElement(*cloneFrom.element()); + + // TODO: stack recursion optimization + while (cloneFrom.hasAnother()) { + DataNode *cNode = cloneFrom.getNext(); + newChildCloneFrom(cNode->getName().c_str(), cNode); + } +} DataNode::DataNode(const char *name_in, DataElement &cloneFrom): parentNode(NULL), ptr(0) { node_name = name_in; @@ -540,6 +555,7 @@ DataNode *DataNode::newChildCloneFrom(const char *name_in, DataNode *cloneFrom) childmap[name_in].push_back(children.back()); children.back()->setParentNode(*this); + // TODO: stack recursion optimization while (cloneFrom->hasAnother()) { DataNode *cNode = cloneFrom->getNext(); cloneNode->newChildCloneFrom(cNode->getName().c_str(), cNode); diff --git a/src/util/DataTree.h b/src/util/DataTree.h index 4cea1c8..5aee690 100755 --- a/src/util/DataTree.h +++ b/src/util/DataTree.h @@ -237,8 +237,9 @@ public: DataNode(); DataNode(const char *name_in); DataNode(const char *name_in, DataElement &cloneFrom); - - ~DataNode(); + DataNode(const char *name_in, DataNode &cloneFrom); + + ~DataNode(); void setName(const char *name_in); string &getName() { return node_name; }