Dual bookmark backup files with user notification, auto-recovery and bail; save top level branch expand states

This commit is contained in:
Charles J. Cliffe 2016-12-27 13:01:19 -05:00
parent 69e23fd13a
commit 68495b4d90
9 changed files with 222 additions and 28 deletions

View File

@ -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() {

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -27,6 +27,9 @@ IMPLEMENT_APP(CubicSDR)
#include <fstream>
#include <clocale>
#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")) {

View File

@ -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));
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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();
}