diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 8aa5446..96c2882 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -678,6 +678,7 @@ AppFrame::AppFrame() : deviceChanged.store(false); devInfo = NULL; wxGetApp().deviceSelector(); + saveDisabled = false; // static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 }; // wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not"); @@ -1293,6 +1294,11 @@ void AppFrame::OnClose(wxCloseEvent& event) { } wxGetApp().getSpectrumProcessor()->removeOutput(spectrumCanvas->getVisualDataQueue()); + if (saveDisabled) { + event.Skip(); + return; + } + wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize()); wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized()); wxGetApp().getConfig()->setTheme(ThemeMgr::mgr.getTheme()); @@ -1926,6 +1932,10 @@ BookmarkView *AppFrame::getBookmarkView() { return bookmarkView; } +void AppFrame::disableSave(bool state) { + saveDisabled = state; +} + #ifdef _WIN32 bool AppFrame::canFocus() { diff --git a/src/AppFrame.h b/src/AppFrame.h index cbb0d2c..fca2a7e 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -112,6 +112,7 @@ public: bool isUserDemodBusy(); BookmarkView *getBookmarkView(); + void disableSave(bool state); #ifdef _WIN32 bool canFocus(); @@ -204,6 +205,7 @@ private: std::string rigPort; int numRigs; bool rigInit; + bool saveDisabled; #endif wxDECLARE_EVENT_TABLE(); diff --git a/src/BookmarkMgr.cpp b/src/BookmarkMgr.cpp index 5ae4c0d..ad69746 100644 --- a/src/BookmarkMgr.cpp +++ b/src/BookmarkMgr.cpp @@ -8,11 +8,18 @@ BookmarkMgr::BookmarkMgr() { rangesSorted = false; } -void BookmarkMgr::saveToFile(std::string bookmarkFn) { +void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup) { DataTree s("cubicsdr_bookmarks"); DataNode *header = s.rootNode()->newChild("header"); header->newChild("version")->element()->set(wxString(CUBICSDR_VERSION).ToStdWstring()); + DataNode *branches = s.rootNode()->newChild("branches"); + + *branches->newChild("active") = wxGetApp().getAppFrame()->getBookmarkView()->getExpandState("active")?1:0; + *branches->newChild("range") = wxGetApp().getAppFrame()->getBookmarkView()->getExpandState("range")?1:0; + *branches->newChild("bookmark") = wxGetApp().getAppFrame()->getBookmarkView()->getExpandState("bookmark")?1:0; + *branches->newChild("recent") = wxGetApp().getAppFrame()->getBookmarkView()->getExpandState("recent")?1:0; + DataNode *view_ranges = s.rootNode()->newChild("ranges"); for (auto re_i : ranges) { @@ -50,7 +57,7 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn) { if (saveFile.IsDirWritable()) { // Hopefully leave at least a readable backup in case of failure.. - if (saveFile.FileExists() && (!saveFileBackup.FileExists() || saveFileBackup.IsFileWritable())) { + if (backup && saveFile.FileExists() && (!saveFileBackup.FileExists() || saveFileBackup.IsFileWritable())) { wxCopyFile(saveFile.GetFullPath(wxPATH_NATIVE).ToStdString(), saveFileBackup.GetFullPath(wxPATH_NATIVE).ToStdString()); } s.SaveToFileXML(saveFile.GetFullPath(wxPATH_NATIVE).ToStdString()); @@ -58,20 +65,50 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn) { } -void BookmarkMgr::loadFromFile(std::string bookmarkFn) { +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"); DataTree s; + bool loadStatusOk = true; - if (!loadFile.IsFileReadable()) { - return; + // Clear any active data + bmData.erase(bmData.begin(),bmData.end()); + recents.erase(recents.begin(),recents.end()); + ranges.erase(ranges.begin(),ranges.end()); + bmDataSorted.erase(bmDataSorted.begin(),bmDataSorted.end()); + + // File exists but is not readable + if (loadFile.FileExists() && !loadFile.IsFileReadable()) { + return false; } + // New instance of bookmark savefiles + if (backup && !loadFile.FileExists() && !lastLoaded.FileExists() && !backupFile.FileExists()) { + wxGetApp().getAppFrame()->getBookmarkView()->loadDefaultRanges(); + return true; + } + + // Attempt to load file if (!s.LoadFromFileXML(loadFile.GetFullPath(wxPATH_NATIVE).ToStdString())) { - // TODO: if exists; inform user & optionally load backup - return; + return false; } - + + if (s.rootNode()->hasAnother("branches")) { + DataNode *branches = s.rootNode()->getNext("branches"); + int bActive = 1, bRange = 0, bBookmark = 1, bRecent = 1; + if (branches->hasAnother("active")) branches->getNext("active")->element()->get(bActive); + if (branches->hasAnother("range")) branches->getNext("range")->element()->get(bRange); + if (branches->hasAnother("bookmark")) branches->getNext("bookmark")->element()->get(bBookmark); + if (branches->hasAnother("recent")) branches->getNext("recent")->element()->get(bRecent); + wxGetApp().getAppFrame()->getBookmarkView()->setExpandState("active", bActive?true:false); + wxGetApp().getAppFrame()->getBookmarkView()->setExpandState("range", bRange?true:false); + wxGetApp().getAppFrame()->getBookmarkView()->setExpandState("bookmark", bBookmark?true:false); + wxGetApp().getAppFrame()->getBookmarkView()->setExpandState("recent", bRecent?true:false); + } + if (s.rootNode()->hasAnother("ranges")) { DataNode *view_ranges = s.rootNode()->getNext("ranges"); while (view_ranges->hasAnother("range")) { @@ -79,10 +116,10 @@ void BookmarkMgr::loadFromFile(std::string bookmarkFn) { BookmarkRangeEntry *re = new BookmarkRangeEntry; - range->getNext("label")->element()->get(re->label); - range->getNext("freq")->element()->get(re->freq); - range->getNext("start")->element()->get(re->startFreq); - range->getNext("end")->element()->get(re->endFreq); + if (range->hasAnother("label")) range->getNext("label")->element()->get(re->label); + if (range->hasAnother("freq")) range->getNext("freq")->element()->get(re->freq); + if (range->hasAnother("start")) range->getNext("start")->element()->get(re->startFreq); + if (range->hasAnother("end")) range->getNext("end")->element()->get(re->endFreq); addRange(re); } @@ -108,6 +145,7 @@ void BookmarkMgr::loadFromFile(std::string bookmarkFn) { addBookmark(groupName.c_str(), be); } else { std::cout << "error loading bookmarked modem.." << std::endl; + loadStatusOk = false; } } } @@ -123,9 +161,39 @@ void BookmarkMgr::loadFromFile(std::string bookmarkFn) { addRecent(be); } else { std::cout << "error loading recent modem.." << std::endl; + loadStatusOk = false; } } } + + if (backup) { + if (loadStatusOk) { // Loaded OK; keep a copy + if (loadFile.IsDirWritable()) { + if (loadFile.FileExists() && (!lastLoaded.FileExists() || lastLoaded.IsFileWritable())) { + wxCopyFile(loadFile.GetFullPath(wxPATH_NATIVE).ToStdString(), lastLoaded.GetFullPath(wxPATH_NATIVE).ToStdString()); + } + } + } else if (!loadStatusOk) { + if (loadFile.IsDirWritable()) { // Load failed; keep a copy of the failed bookmark file for analysis? + if (loadFile.FileExists() && (!failFile.FileExists() || failFile.IsFileWritable())) { + wxCopyFile(loadFile.GetFullPath(wxPATH_NATIVE).ToStdString(), failFile.GetFullPath(wxPATH_NATIVE).ToStdString()); + } + } + } + } + + return loadStatusOk; +} + + +bool BookmarkMgr::hasLastLoad(std::string bookmarkFn) { + wxFileName lastLoaded(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".lastloaded"); + return lastLoaded.FileExists() && lastLoaded.IsFileReadable(); +} + +bool BookmarkMgr::hasBackup(std::string bookmarkFn) { + wxFileName backupFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup"); + return backupFile.FileExists() && backupFile.IsFileReadable(); } void BookmarkMgr::addBookmark(std::string group, DemodulatorInstance *demod) { @@ -419,7 +487,9 @@ BookmarkEntry *BookmarkMgr::nodeToBookmark(const char *name_in, DataNode *node) if (node->hasAnother("user_label")) { node->getNext("user_label")->element()->get(be->label); } - + + node->rewindAll(); + be->node = new DataNode("node",*node); return be; diff --git a/src/BookmarkMgr.h b/src/BookmarkMgr.h index 524b1b1..34feaee 100644 --- a/src/BookmarkMgr.h +++ b/src/BookmarkMgr.h @@ -25,6 +25,12 @@ public: class BookmarkRangeEntry { public: + BookmarkRangeEntry() : label(L""), freq(0), startFreq(0), endFreq(0) { + + } + BookmarkRangeEntry(std::wstring label, long long freq, long long startFreq, long long endFreq) : label(label), freq(freq), startFreq(startFreq), endFreq(endFreq) { + } + std::mutex busy_lock; std::wstring label; @@ -63,8 +69,11 @@ class BookmarkMgr { public: BookmarkMgr(); - void saveToFile(std::string bookmarkFn); - void loadFromFile(std::string bookmarkFn); + void saveToFile(std::string bookmarkFn, bool backup = true); + bool loadFromFile(std::string bookmarkFn, bool backup = true); + + bool hasLastLoad(std::string bookmarkFn); + bool hasBackup(std::string bookmarkFn); void addBookmark(std::string group, DemodulatorInstance *demod); void addBookmark(std::string group, BookmarkEntry *be); diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index e8a7bf3..f5bebed 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -27,6 +27,9 @@ IMPLEMENT_APP(CubicSDR) #include #include +#include "ActionDialog.h" + + //#ifdef ENABLE_DIGITAL_LAB //// console output buffer for windows //#ifdef _WINDOWS @@ -133,8 +136,65 @@ long long strToFrequency(std::string freqStr) { } -CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE),agcMode(false) - { + +class ActionDialogBookmarkCatastophe : public ActionDialog { +public: + ActionDialogBookmarkCatastophe() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Last-Loaded Backup Failure :( :( :(")) { + m_questionText->SetLabelText(wxT("All attempts to recover bookmarks have failed. \nWould you like to exit without touching any more save files?\nClick OK to exit without saving; or Cancel to continue anyways.")); + } + + void doClickOK() { + wxGetApp().getAppFrame()->disableSave(true); + wxGetApp().getAppFrame()->Close(false); + } +}; + + + +class ActionDialogBookmarkBackupLoadFailed : public ActionDialog { +public: + ActionDialogBookmarkBackupLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Backup Load Failure :( :(")) { + m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks backup file. \nWould you like to attempt to load the last succssfully loaded bookmarks file?")); + } + + void doClickOK() { + if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { + if (wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.lastloaded",false)) { + wxGetApp().getBookmarkMgr().updateBookmarks(); + wxGetApp().getBookmarkMgr().updateActiveList(); + } else { + ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); + } + } + } +}; + + +class ActionDialogBookmarkLoadFailed : public ActionDialog { +public: + ActionDialogBookmarkLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Load Failure :(")) { + m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks file. \nWould you like to attempt to load the backup file?")); + } + + void doClickOK() { + bool loadOk = false; + if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) { + loadOk = wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.backup",false); + } + if (loadOk) { + wxGetApp().getBookmarkMgr().updateBookmarks(); + wxGetApp().getBookmarkMgr().updateActiveList(); + } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { + ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed()); + } else { + ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); + } + } +}; + + +CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), agcMode(false) +{ sampleRateInitialized.store(false); agcMode.store(true); soloMode.store(false); @@ -294,6 +354,19 @@ bool CubicSDR::OnInit() { // pthread_setschedparam(pthread_self(), main_policy, &main_param); //#endif + if (!wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml")) { + if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) { + ActionDialog::showDialog(new ActionDialogBookmarkLoadFailed()); + } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { + ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed()); + } else { + ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); + } + } else { + getBookmarkMgr().updateActiveList(); + getBookmarkMgr().updateBookmarks(); + } + return true; } @@ -405,7 +478,6 @@ bool CubicSDR::OnCmdLineParsed(wxCmdLineParser& parser) { } config.load(); - wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml"); #ifdef BUNDLE_SOAPY_MODS if (parser.Found("b")) { diff --git a/src/forms/Bookmark/BookmarkView.cpp b/src/forms/Bookmark/BookmarkView.cpp index 3834965..aecd3be 100644 --- a/src/forms/Bookmark/BookmarkView.cpp +++ b/src/forms/Bookmark/BookmarkView.cpp @@ -102,7 +102,7 @@ BookmarkView::BookmarkView( wxWindow* parent, wxWindowID id, const wxPoint& pos, recentBranch = m_treeView->AppendItem(rootBranch, "Recents"); expandState["active"] = true; - expandState["range"] = true; + expandState["range"] = false; expandState["bookmark"] = true; expandState["recent"] = true; @@ -579,6 +579,16 @@ bool BookmarkView::isMouseInView() { } +bool BookmarkView::getExpandState(std::string branchName) { + return expandState[branchName]; +} + + +void BookmarkView::setExpandState(std::string branchName, bool state) { + expandState[branchName] = state; +} + + void BookmarkView::hideProps() { m_frequencyLabel->Hide(); m_frequencyVal->Hide(); @@ -940,7 +950,7 @@ void BookmarkView::rangeBranchSelection() { clearButtons(); hideProps(); - m_labelText->Clear(); + m_labelText->SetValue(wxT("")); m_labelText->Show(); m_labelLabel->Show(); @@ -1386,7 +1396,7 @@ void BookmarkView::onSearchText( wxCommandEvent& event ) { while(std::getline(searchTextLo, tmp, L' ')) { if (tmp.length() != 0 && tmp.find(L"search.") == wstring::npos) { searchKeywords.push_back(tmp); - std::wcout << L"Keyword: " << tmp << '\n'; +// std::wcout << L"Keyword: " << tmp << '\n'; } } @@ -1406,6 +1416,7 @@ void BookmarkView::onSearchText( wxCommandEvent& event ) { void BookmarkView::onClearSearch( wxCommandEvent& event ) { + m_clearSearchButton->Hide(); m_searchText->SetValue(L"Search.."); m_treeView->SetFocus(); if (!searchKeywords.empty()) { @@ -1413,7 +1424,19 @@ void BookmarkView::onClearSearch( wxCommandEvent& event ) { } wxGetApp().getBookmarkMgr().updateActiveList(); wxGetApp().getBookmarkMgr().updateBookmarks(); - m_clearSearchButton->Hide(); refreshLayout(); } +void BookmarkView::loadDefaultRanges() { + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"160 Meters",1900000,1800000,2000000)); + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"80 Meters",3750000,3500000,4000000)); + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"60 Meters",5368500,5332000,5405000)); + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"40 Meters",7150000,7000000,7300000)); + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"30 Meters",10125000,10100000,10150000)); + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"20 Meters",14175000,14000000,14350000)); + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"17 Meters",18068180,17044180,19092180)); + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"15 Meters",21225000,21000000,21450000)); + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"12 Meters",24940000,24890000,24990000)); + wxGetApp().getBookmarkMgr().addRange(new BookmarkRangeEntry(L"10 Meters",28850000,28000000,29700000)); +} + diff --git a/src/forms/Bookmark/BookmarkView.h b/src/forms/Bookmark/BookmarkView.h index bdbb4d3..4ab2965 100644 --- a/src/forms/Bookmark/BookmarkView.h +++ b/src/forms/Bookmark/BookmarkView.h @@ -50,6 +50,10 @@ public: void onMenuItem(wxCommandEvent& event); bool isMouseInView(); + bool getExpandState(std::string branchName); + void setExpandState(std::string branchName, bool state); + + void loadDefaultRanges(); protected: void activeSelection(DemodulatorInstance *dsel); diff --git a/src/forms/Dialog/ActionDialog.cpp b/src/forms/Dialog/ActionDialog.cpp index 6cb6341..acb4b43 100644 --- a/src/forms/Dialog/ActionDialog.cpp +++ b/src/forms/Dialog/ActionDialog.cpp @@ -34,18 +34,20 @@ void ActionDialog::setActiveDialog(ActionDialog *dlg) { void ActionDialog::onClickCancel( wxCommandEvent& event ) { - doClickCancel(); - activeDialog->EndModal(0); + ActionDialog *dlg = activeDialog; ActionDialog::setActiveDialog(nullptr); - delete activeDialog; + dlg->EndModal(0); + doClickCancel(); + delete dlg; } void ActionDialog::onClickOK( wxCommandEvent& event ) { - doClickOK(); - activeDialog->EndModal(0); + ActionDialog *dlg = activeDialog; ActionDialog::setActiveDialog(nullptr); - delete activeDialog; + dlg->EndModal(0); + doClickOK(); + delete dlg; } diff --git a/src/util/DataTree.cpp b/src/util/DataTree.cpp index 293a8b8..850e257 100755 --- a/src/util/DataTree.cpp +++ b/src/util/DataTree.cpp @@ -561,6 +561,8 @@ DataNode *DataNode::newChildCloneFrom(const char *name_in, DataNode *cloneFrom) cloneNode->newChildCloneFrom(cNode->getName().c_str(), cNode); } + cloneFrom->rewind(); + return children.back(); }