From d4251b41fb3d602cdb5a95b10e73e84ba7e9cacc Mon Sep 17 00:00:00 2001 From: vsonnier Date: Sat, 6 Jan 2018 10:22:14 +0100 Subject: [PATCH 1/3] Feature #486 : Save/Load bookmarks into files --- src/AppFrame.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++ src/AppFrame.h | 5 +++ src/BookmarkMgr.cpp | 59 ++++++++++++++++++++++++--------- src/BookmarkMgr.h | 7 ++-- 4 files changed, 134 insertions(+), 18 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 76a26a7..96ed628 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -417,6 +417,10 @@ AppFrame::AppFrame() : menu->Append(wxID_SAVEAS, "Save Session &As.."); menu->AppendSeparator(); menu->Append(wxID_RESET, "&Reset Session"); + menu->AppendSeparator(); + menu->Append(wxID_OPEN_BOOKMARK, "Open Bookmark"); + menu->Append(wxID_SAVE_BOOKMARK, "Save Bookmark"); + menu->Append(wxID_SAVEAS_BOOKMARK, "Save Bookmark As.."); #ifndef __APPLE__ menu->AppendSeparator(); @@ -1121,6 +1125,7 @@ bool AppFrame::actionOnMenuReset(wxCommandEvent& event) { SetTitle(CUBICSDR_TITLE); currentSessionFile = ""; + currentBookmarkFile = ""; bookmarkSplitter->Unsplit(bookmarkView); bookmarkSplitter->SplitVertically(bookmarkView, mainVisSplitter, wxGetApp().getConfig()->getBookmarkSplit()); hideBookmarksItem->Check(false); @@ -1401,6 +1406,77 @@ bool AppFrame::actionOnMenuLoadSave(wxCommandEvent& event) { return true; } + //save mecanic for bookmark files + else if (event.GetId() == wxID_SAVE_BOOKMARK) { + + if (!currentBookmarkFile.empty()) { + wxGetApp().getBookmarkMgr().saveToFile(currentBookmarkFile, false, true); + } + else { + wxFileDialog saveFileDialog(this, _("Save XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (saveFileDialog.ShowModal() == wxID_CANCEL) { + return true; + } + + // Make sure the file name actually ends in .xml + std::string fileName = saveFileDialog.GetPath().ToStdString(); + std::string lcFileName = fileName; + + std::transform(lcFileName.begin(), lcFileName.end(), lcFileName.begin(), ::tolower); + + if (lcFileName.find_last_of(".xml") != lcFileName.length() - 1) { + fileName.append(".xml"); + } + + wxGetApp().getBookmarkMgr().saveToFile(fileName, false, true); + currentBookmarkFile = fileName; + } + + return true; + } + else if (event.GetId() == wxID_OPEN_BOOKMARK) { + + wxFileDialog openFileDialog(this, _("Open XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (openFileDialog.ShowModal() == wxID_CANCEL) { + return true; + } + if (wxGetApp().getBookmarkMgr().loadFromFile(openFileDialog.GetPath().ToStdString(), false, true)) { + + wxGetApp().getBookmarkMgr().updateBookmarks(); + wxGetApp().getBookmarkMgr().updateActiveList(); + + currentBookmarkFile = openFileDialog.GetPath().ToStdString(); + } + else { + //failure at loading. + currentBookmarkFile = ""; + } + + return true; + } + else if (event.GetId() == wxID_SAVEAS_BOOKMARK) { + + wxFileDialog saveFileDialog(this, _("Save XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (saveFileDialog.ShowModal() == wxID_CANCEL) { + return true; + } + + // Make sure the file name actually ends in .xml + std::string fileName = saveFileDialog.GetPath().ToStdString(); + std::string lcFileName = fileName; + + std::transform(lcFileName.begin(), lcFileName.end(), lcFileName.begin(), ::tolower); + + if (lcFileName.find_last_of(".xml") != lcFileName.length() - 1) { + fileName.append(".xml"); + } + + wxGetApp().getBookmarkMgr().saveToFile(fileName, false, true); + currentBookmarkFile = fileName; + + return true; + } + return false; } @@ -2153,6 +2229,11 @@ bool AppFrame::loadSession(std::string fileName) { return false; } + //Check if it is a session file, read the root node. + if (l.rootNode()->getName() != "cubicsdr_session") { + return false; + } + wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, false); wxGetApp().getDemodMgr().terminateAll(); diff --git a/src/AppFrame.h b/src/AppFrame.h index 45413ce..f81541a 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -43,6 +43,10 @@ #define wxID_SET_DB_OFFSET 2012 #define wxID_ABOUT_CUBICSDR 2013 +#define wxID_OPEN_BOOKMARK 2020 +#define wxID_SAVE_BOOKMARK 2021 +#define wxID_SAVEAS_BOOKMARK 2022 + #define wxID_MAIN_SPLITTER 2050 #define wxID_VIS_SPLITTER 2051 #define wxID_BM_SPLITTER 2052 @@ -229,6 +233,7 @@ private: std::string currentTXantennaName; std::string currentSessionFile; + std::string currentBookmarkFile; FFTVisualDataThread *waterfallDataThread; diff --git a/src/BookmarkMgr.cpp b/src/BookmarkMgr.cpp index fb9e58e..521ef88 100644 --- a/src/BookmarkMgr.cpp +++ b/src/BookmarkMgr.cpp @@ -18,7 +18,8 @@ BookmarkMgr::BookmarkMgr() { //represents an empty BookMarkList that is returned by reference by some functions. const BookmarkList BookmarkMgr::emptyResults; -void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup) { +void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup, bool useFullpath) { + DataTree s("cubicsdr_bookmarks"); DataNode *header = s.rootNode()->newChild("header"); header->newChild("version")->element()->set(wxString(CUBICSDR_VERSION).ToStdWstring()); @@ -62,9 +63,18 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup) { recent_modems->newChildCloneFrom("modem", r_i->node); } - wxFileName saveFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn); - wxFileName saveFileBackup(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup"); - + wxFileName saveFile; + wxFileName saveFileBackup; + + if (useFullpath) { + saveFile.Assign(bookmarkFn); + saveFileBackup.Assign(bookmarkFn + ".backup"); + } + else { + saveFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn); + saveFileBackup.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup"); + } + if (saveFile.IsDirWritable()) { // Hopefully leave at least a readable backup in case of failure.. if (backup && saveFile.FileExists() && (!saveFileBackup.FileExists() || saveFileBackup.IsFileWritable())) { @@ -74,20 +84,28 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup) { } } -bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup) { - wxFileName loadFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn); - wxFileName failFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".failedload"); - wxFileName lastLoaded(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".lastloaded"); - wxFileName backupFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup"); +bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup, bool useFullpath) { + + wxFileName loadFile; + wxFileName failFile; + wxFileName lastLoaded; + wxFileName backupFile; + + if (useFullpath) { + loadFile.Assign(bookmarkFn); + failFile.Assign(bookmarkFn + ".failedload"); + lastLoaded.Assign(bookmarkFn + ".lastloaded"); + backupFile.Assign(bookmarkFn + ".backup"); + } + else { + loadFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn); + failFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".failedload"); + lastLoaded.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".lastloaded"); + backupFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup"); + } DataTree s; bool loadStatusOk = true; - - // Clear any active data - bmData.clear(); - clearRecents(); - clearRanges(); - bmDataSorted.clear(); // File exists but is not readable if (loadFile.FileExists() && !loadFile.IsFileReadable()) { @@ -104,6 +122,17 @@ bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup) { if (!s.LoadFromFileXML(loadFile.GetFullPath(wxPATH_NATIVE).ToStdString())) { return false; } + + //Check if it is a bookmark file, read the root node. + if (s.rootNode()->getName() != "cubicsdr_bookmarks") { + return false; + } + + // Clear any active data + bmData.clear(); + clearRecents(); + clearRanges(); + bmDataSorted.clear(); if (s.rootNode()->hasAnother("branches")) { DataNode *branches = s.rootNode()->getNext("branches"); diff --git a/src/BookmarkMgr.h b/src/BookmarkMgr.h index 1fec76e..272c1b9 100644 --- a/src/BookmarkMgr.h +++ b/src/BookmarkMgr.h @@ -78,9 +78,10 @@ typedef std::map BookmarkExpandState; class BookmarkMgr { public: BookmarkMgr(); - - void saveToFile(std::string bookmarkFn, bool backup = true); - bool loadFromFile(std::string bookmarkFn, bool backup = true); + //if useFullpath = false, use the application config dir. + //else assume bookmarkFn is a full path and use it for location. + void saveToFile(std::string bookmarkFn, bool backup = true, bool useFullpath = false); + bool loadFromFile(std::string bookmarkFn, bool backup = true, bool useFullpath = false); bool hasLastLoad(std::string bookmarkFn); bool hasBackup(std::string bookmarkFn); From 8f45451df2e773c2c6029180879b09441cc5dd6d Mon Sep 17 00:00:00 2001 From: vsonnier Date: Sat, 6 Jan 2018 16:15:40 +0100 Subject: [PATCH 2/3] Choose a valid audio output if the modem-saved one is not recognized. --- src/demod/DemodulatorMgr.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/demod/DemodulatorMgr.cpp b/src/demod/DemodulatorMgr.cpp index 9b449c6..54f0f07 100644 --- a/src/demod/DemodulatorMgr.cpp +++ b/src/demod/DemodulatorMgr.cpp @@ -526,12 +526,20 @@ DemodulatorInstancePtr DemodulatorMgr::loadInstance(DataNode *node) { //Attach to sound output: std::map::iterator i; + + bool matching_device_found = false; + for (i = outputDevices.begin(); i != outputDevices.end(); i++) { if (i->second.name == output_device) { newDemod->setOutputDevice(i->first); + matching_device_found = true; break; } } + //if no device is found, choose the first of the list anyway. + if (!matching_device_found) { + newDemod->setOutputDevice(outputDevices.begin()->first); + } return newDemod; } From f1208c7f5c98d9dab908147f6ecd80848f0a920d Mon Sep 17 00:00:00 2001 From: vsonnier Date: Mon, 8 Jan 2018 19:23:01 +0100 Subject: [PATCH 3/3] When saving bookmarks, use active information when available + Fixed std::string <=> std::wstring conversions using wxString + cleanups. --- src/BookmarkMgr.cpp | 27 +++++++++++++---- src/forms/Bookmark/BookmarkView.cpp | 45 ++++++++++++++++------------- src/util/GLFont.cpp | 8 +++-- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/BookmarkMgr.cpp b/src/BookmarkMgr.cpp index 521ef88..8cb4d5c 100644 --- a/src/BookmarkMgr.cpp +++ b/src/BookmarkMgr.cpp @@ -4,6 +4,7 @@ #include "BookmarkMgr.h" #include "CubicSDR.h" #include "DataTree.h" +#include #define BOOKMARK_RECENTS_MAX 25 @@ -49,7 +50,21 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup, bool useFullpa *group->newChild("@expanded") = (getExpandState(bmd_i.first)?std::string("true"):std::string("false")); for (auto &bm_i : bmd_i.second ) { - group->newChildCloneFrom("modem", bm_i->node); + + //if a matching demodulator exists, use its data instead to be be saved, because output_device could have been + //modified by the user. So, save that "live" version instead. + auto matchingDemod = wxGetApp().getDemodMgr().getLastDemodulatorWith(bm_i->type, + bm_i->label, + bm_i->frequency, + bm_i->bandwidth); + + if (matchingDemod != nullptr) { + + wxGetApp().getDemodMgr().saveInstance(group->newChild("modem"), matchingDemod); + } + else { + group->newChildCloneFrom("modem", bm_i->node); + } } } @@ -557,9 +572,10 @@ BookmarkEntryPtr BookmarkMgr::nodeToBookmark(DataNode *node) { std::wstring BookmarkMgr::getBookmarkEntryDisplayName(BookmarkEntryPtr bmEnt) { std::wstring dispName = bmEnt->label; - if (dispName == "") { + if (dispName == L"") { std::string freqStr = frequencyToStr(bmEnt->frequency) + " " + bmEnt->type; - dispName = wstring(freqStr.begin(),freqStr.end()); + + dispName = wxString(freqStr).ToStdWstring(); } return dispName; @@ -568,9 +584,10 @@ std::wstring BookmarkMgr::getBookmarkEntryDisplayName(BookmarkEntryPtr bmEnt) { std::wstring BookmarkMgr::getActiveDisplayName(DemodulatorInstancePtr demod) { std::wstring activeName = demod->getDemodulatorUserLabel(); - if (activeName == "") { + if (activeName == L"") { std::string wstr = frequencyToStr(demod->getFrequency()) + " " + demod->getDemodulatorType(); - activeName = std::wstring(wstr.begin(),wstr.end()); + + activeName = wxString(wstr).ToStdWstring(); } return activeName; diff --git a/src/forms/Bookmark/BookmarkView.cpp b/src/forms/Bookmark/BookmarkView.cpp index 4cb9888..a30d6a6 100644 --- a/src/forms/Bookmark/BookmarkView.cpp +++ b/src/forms/Bookmark/BookmarkView.cpp @@ -78,7 +78,8 @@ public: if (name.length() == 0) { std::string wstr = frequencyToStr(rangeEnt->startFreq) + " - " + frequencyToStr(rangeEnt->endFreq); - name = std::wstring(wstr.begin(),wstr.end()); + + name = wxString(wstr).ToStdWstring(); } m_questionText->SetLabelText(L"Are you sure you want to remove the range\n '" + name + L"'?"); @@ -103,7 +104,8 @@ public: if (name.length() == 0) { std::string wstr = frequencyToStr(rangeEnt->startFreq) + " - " + frequencyToStr(rangeEnt->endFreq); - name = std::wstring(wstr.begin(),wstr.end()); + + name = wxString(wstr).ToStdWstring(); } m_questionText->SetLabelText(L"Are you sure you want to update the range\n '" + name + L"' to the active range?"); @@ -311,9 +313,9 @@ wxTreeItemId BookmarkView::refreshBookmarks() { std::wstring fullText = labelVal + L" " + bmEnt->label + L" " + std::to_wstring(bmEnt->frequency) + - L" " + std::wstring(freqStr.begin(),freqStr.end()) + - L" " + std::wstring(bwStr.begin(),bwStr.end()) + - L" " + std::wstring(bmEnt->type.begin(),bmEnt->type.end()); + L" " + wxString(freqStr).ToStdWstring() + + L" " + wxString(bwStr).ToStdWstring() + + L" " + wxString(bmEnt->type).ToStdWstring(); if (!isKeywordMatch(fullText, searchKeywords)) { continue; @@ -379,9 +381,9 @@ void BookmarkView::doUpdateActiveList() { std::wstring fullText = activeLabel.ToStdWstring() + L" " + demod_i->getDemodulatorUserLabel() + L" " + std::to_wstring(demod_i->getFrequency()) + - L" " + std::wstring(freqStr.begin(),freqStr.end()) + - L" " + std::wstring(bwStr.begin(),bwStr.end()) + - L" " + std::wstring(mtype.begin(),mtype.end()); + L" " + wxString(freqStr).ToStdWstring() + + L" " + wxString(bwStr).ToStdWstring() + + L" " + wxString(mtype).ToStdWstring(); if (!isKeywordMatch(fullText, searchKeywords)) { continue; @@ -418,9 +420,10 @@ void BookmarkView::doUpdateActiveList() { std::wstring labelVal = re_i->label; - if (labelVal == "") { + if (labelVal == L"") { std::string wstr = frequencyToStr(re_i->startFreq) + " - " + frequencyToStr(re_i->endFreq); - labelVal = std::wstring(wstr.begin(),wstr.end()); + + labelVal = wxString(wstr).ToStdWstring(); } wxTreeItemId itm = m_treeView->AppendItem(rangeBranch, labelVal); @@ -448,9 +451,10 @@ void BookmarkView::doUpdateActiveList() { std::wstring labelVal; bmr_i->node->child("user_label")->element()->get(labelVal); - if (labelVal == "") { - std::string wstr = frequencyToStr(bmr_i->frequency) + " " + bmr_i->type; - labelVal = std::wstring(wstr.begin(),wstr.end()); + if (labelVal == L"") { + std::string str = frequencyToStr(bmr_i->frequency) + " " + bmr_i->type; + + labelVal = wxString(str).ToStdWstring(); } if (searchKeywords.size()) { @@ -460,9 +464,10 @@ void BookmarkView::doUpdateActiveList() { std::wstring fullText = labelVal + L" " + std::to_wstring(bmr_i->frequency) + - L" " + std::wstring(freqStr.begin(),freqStr.end()) + - L" " + std::wstring(bwStr.begin(),bwStr.end()) + - L" " + std::wstring(bmr_i->type.begin(),tvi->bookmarkEnt->type.end()); + + L" " + wxString(freqStr).ToStdWstring() + + L" " + wxString(bwStr).ToStdWstring() + + L" " + wxString(bmr_i->type).ToStdWstring(); if (!isKeywordMatch(fullText, searchKeywords)) { continue; @@ -971,7 +976,7 @@ void BookmarkView::rangeSelection(BookmarkRangeEntryPtr re) { std::string strFreq = frequencyToStr(re->startFreq) + "-" + frequencyToStr(re->endFreq); - m_frequencyVal->SetLabelText(std::wstring(strFreq.begin(),strFreq.end())); + m_frequencyVal->SetLabelText(wxString(strFreq)); showProps(); @@ -1473,16 +1478,16 @@ void BookmarkView::onSearchTextFocus( wxMouseEvent& event ) { void BookmarkView::onSearchText( wxCommandEvent& event ) { - wstring searchText = m_searchText->GetValue().Trim().Lower().ToStdWstring(); + std::wstring searchText = m_searchText->GetValue().Trim().Lower().ToStdWstring(); searchKeywords.clear(); if (searchText.length() != 0) { std::wstringstream searchTextLo(searchText); - wstring tmp; + std::wstring tmp; while(std::getline(searchTextLo, tmp, L' ')) { - if (tmp.length() != 0 && tmp.find(L"search.") == wstring::npos) { + if (tmp.length() != 0 && tmp.find(L"search.") == std::wstring::npos) { searchKeywords.push_back(tmp); // std::wcout << L"Keyword: " << tmp << '\n'; } diff --git a/src/util/GLFont.cpp b/src/util/GLFont.cpp index 6e1a46a..924f9f8 100644 --- a/src/util/GLFont.cpp +++ b/src/util/GLFont.cpp @@ -3,6 +3,8 @@ #include "GLFont.h" +#include + #include #include #include @@ -245,11 +247,11 @@ void GLFont::loadFontOnce() { //Re-compute the resource dir. resourceFolder = fontDefFileName.GetPath(); - std::wstring fontDefFileNamePath = fontDefFileName.GetFullPath(wxPATH_NATIVE).ToStdWstring(); + std::string fontDefFileNamePath = fontDefFileName.GetFullPath(wxPATH_NATIVE).ToStdString(); std::wifstream input; - std::string inpFileStr(fontDefFileNamePath.begin(), fontDefFileNamePath.end()); - input.open(inpFileStr, std::ios::in); + + input.open(fontDefFileNamePath, std::ios::in); std::wstring op;