diff --git a/CMakeLists.txt b/CMakeLists.txt index cd4d4dd..9038704 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,6 +227,7 @@ SET (cubicsdr_sources src/CubicSDR.cpp src/AppFrame.cpp src/AppConfig.cpp + src/FrequencyDialog.cpp src/sdr/SDRThread.cpp src/sdr/SDRPostThread.cpp src/demod/DemodulatorPreThread.cpp @@ -269,6 +270,7 @@ SET (cubicsdr_headers src/CubicSDR.h src/AppFrame.h src/AppConfig.h + src/FrequencyDialog.h src/sdr/SDRThread.h src/sdr/SDRPostThread.h src/demod/DemodulatorPreThread.h diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index be24cd5..7d5fb54 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -13,7 +13,7 @@ #endif #include "CubicSDR.h" -#include "AppFrame.h" +#include "FrequencyDialog.h" #ifdef _OSX_APP_ #include "CoreFoundation/CoreFoundation.h" @@ -92,7 +92,7 @@ bool CubicSDR::OnInit() { t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread); t_SDR = new std::thread(&SDRThread::threadMain, sdrThread); - AppFrame *appframe = new AppFrame(); + appframe = new AppFrame(); #ifdef __APPLE__ int main_policy; @@ -271,3 +271,9 @@ int CubicSDR::getPPM() { return ppm; } + +void CubicSDR::showFrequencyInput() { + FrequencyDialog fdialog(appframe, -1, demodMgr.getActiveDemodulator()?_("Set Demodulator Frequency"):_("Set Center Frequency"), demodMgr.getActiveDemodulator(), wxPoint(-100,-100), wxSize(320, 75 )); + fdialog.ShowModal(); +} + diff --git a/src/CubicSDR.h b/src/CubicSDR.h index 38975c3..dfc7e33 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -15,6 +15,7 @@ #include "AudioThread.h" #include "DemodulatorMgr.h" #include "AppConfig.h" +#include "AppFrame.h" #define NUM_DEMODULATORS 1 @@ -56,7 +57,10 @@ public: void setPPM(int ppm_in); int getPPM(); + void showFrequencyInput(); + private: + AppFrame *appframe; AppConfig config; PrimaryGLContext *m_glContext; std::vector devs; diff --git a/src/FrequencyDialog.cpp b/src/FrequencyDialog.cpp new file mode 100644 index 0000000..30b9309 --- /dev/null +++ b/src/FrequencyDialog.cpp @@ -0,0 +1,142 @@ +#include "FrequencyDialog.h" + +#include "wx/clipbrd.h" +#include +#include +#include "CubicSDR.h" + +wxBEGIN_EVENT_TABLE(FrequencyDialog, wxDialog) EVT_CHAR_HOOK(FrequencyDialog::OnChar) +wxEND_EVENT_TABLE() + +FrequencyDialog::FrequencyDialog(wxWindow * parent, wxWindowID id, const wxString & title, DemodulatorInstance *demod, const wxPoint & position, + const wxSize & size, long style) : + wxDialog(parent, id, title, position, size, style) { + wxString freqStr; + activeDemod = demod; + + if (activeDemod) { + freqStr = frequencyToStr(activeDemod->getFrequency()); + } else { + freqStr = frequencyToStr(wxGetApp().getFrequency()); + } + + dialogText = new wxTextCtrl(this, wxID_FREQ_INPUT, freqStr, wxPoint(6, 1), wxSize(size.GetWidth() - 20, size.GetHeight() - 70), + wxTE_PROCESS_ENTER); + dialogText->SetFont(wxFont(20, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD)); + + Centre(); + + dialogText->SetSelection(-1, -1); +} + +std::string& FrequencyDialog::filterChars(std::string& s, const std::string& allowed) { + s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) { + return allowed.find(c) == std::string::npos; + }), s.end()); + return s; +} + +std::string FrequencyDialog::frequencyToStr(long long freq) { + long double freqTemp; + + freqTemp = freq; + std::string suffix(""); + std::stringstream freqStr; + + if (freqTemp >= 1.0e9) { + freqTemp /= 1.0e9; + freqStr << std::setprecision(10); + suffix = std::string("GHz"); + } else if (freqTemp >= 1.0e6) { + freqTemp /= 1.0e6; + freqStr << std::setprecision(7); + suffix = std::string("MHz"); + } else if (freqTemp >= 1.0e3) { + freqTemp /= 1.0e3; + freqStr << std::setprecision(4); + suffix = std::string("KHz"); + } + + freqStr << freqTemp; + freqStr << suffix; + + return freqStr.str(); +} + +long long FrequencyDialog::strToFrequency(std::string freqStr) { + std::string filterStr = filterChars(freqStr, std::string("0123456789.MKGmkg")); + + int numLen = filterStr.find_first_not_of("0123456789."); + + if (numLen == std::string::npos) { + numLen = freqStr.length(); + } + + std::string numPartStr = freqStr.substr(0, numLen); + std::string suffixStr = freqStr.substr(numLen); + + std::stringstream numPartStream; + numPartStream.str(numPartStr); + + long double freqTemp = 0; + + numPartStream >> freqTemp; + + if (suffixStr.length()) { + if (suffixStr.find_first_of("Gg") != std::string::npos) { + freqTemp *= 1.0e9; + } else if (suffixStr.find_first_of("Mm") != std::string::npos) { + freqTemp *= 1.0e6; + } else if (suffixStr.find_first_of("Kk") != std::string::npos) { + freqTemp *= 1.0e3; + } + } else if (numPartStr.find_first_of(".") != std::string::npos) { + freqTemp *= 1.0e6; + } + + return (long long) freqTemp; +} + +void FrequencyDialog::OnChar(wxKeyEvent& event) { + wxChar c = event.GetKeyCode(); + long long freq; + + switch (c) { + case WXK_RETURN: + case WXK_NUMPAD_ENTER: + // Do Stuff + freq = strToFrequency(dialogText->GetValue().ToStdString()); + if (activeDemod) { + activeDemod->setTracking(true); + activeDemod->setFollow(true); + activeDemod->setFrequency(freq); + activeDemod->updateLabel(freq); + } else { + wxGetApp().setFrequency(freq); + } + Close(); + break; + case WXK_ESCAPE: + Close(); + break; + } + + std::string allowed("0123456789.MKGHZmkghz"); + + if (allowed.find_first_of(c) != std::string::npos || c == WXK_DELETE || c == WXK_BACK || c == WXK_NUMPAD_DECIMAL + || (c >= WXK_NUMPAD0 && c <= WXK_NUMPAD9)) { + event.DoAllowNextEvent(); + } else if (event.ControlDown() && c == 'V') { + // Alter clipboard contents to remove unwanted chars + wxTheClipboard->Open(); + wxTextDataObject data; + wxTheClipboard->GetData(data); + std::string clipText = data.GetText().ToStdString(); + std::string pasteText = filterChars(clipText, std::string(allowed)); + wxTheClipboard->SetData(new wxTextDataObject(pasteText)); + wxTheClipboard->Close(); + event.Skip(); + } else if (c == WXK_RIGHT || c == WXK_LEFT || event.ControlDown()) { + event.Skip(); + } +} diff --git a/src/FrequencyDialog.h b/src/FrequencyDialog.h new file mode 100644 index 0000000..42ad7b0 --- /dev/null +++ b/src/FrequencyDialog.h @@ -0,0 +1,32 @@ +#pragma once + +#include "wx/dialog.h" +#include "wx/textctrl.h" +#include "wx/string.h" +#include "wx/button.h" +#include "DemodulatorInstance.h" + +#define wxID_FREQ_INPUT 3001 + +class FrequencyDialog: public wxDialog +{ +public: + + FrequencyDialog ( wxWindow * parent, wxWindowID id, const wxString & title, + DemodulatorInstance *demod = NULL, + const wxPoint & pos = wxDefaultPosition, + const wxSize & size = wxDefaultSize, + long style = wxDEFAULT_DIALOG_STYLE ); + + wxTextCtrl * dialogText; + + long long strToFrequency(std::string freqStr); + std::string frequencyToStr(long long freq); + +private: + DemodulatorInstance *activeDemod; + void OnEnter ( wxCommandEvent &event ); + void OnChar ( wxKeyEvent &event ); + std::string& filterChars(std::string& s, const std::string& allowed); + DECLARE_EVENT_TABLE() +}; diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp index d65bbaa..e6322d7 100644 --- a/src/visual/TuningCanvas.cpp +++ b/src/visual/TuningCanvas.cpp @@ -271,21 +271,28 @@ void TuningCanvas::OnMouseMoved(wxMouseEvent& event) { } else { switch (hoverState) { case TUNING_HOVER_FREQ: - setStatusText("Click, wheel or drag a digit to change frequency. Hold ALT to change PPM. Hold SHIFT to disable carry."); + setStatusText("Click, wheel or drag a digit to change frequency; SPACE for direct input. Hold ALT to change PPM. Hold SHIFT to disable carry."); break; case TUNING_HOVER_BW: setStatusText("Click, wheel or drag a digit to change bandwidth. Hold SHIFT to disable carry."); break; case TUNING_HOVER_CENTER: - setStatusText("Click, wheel or drag a digit to change center frequency. Hold SHIFT to disable carry."); + setStatusText("Click, wheel or drag a digit to change center frequency; SPACE for direct input. Hold SHIFT to disable carry."); break; case TUNING_HOVER_PPM: setStatusText("Click, wheel or drag a digit to change device PPM offset. Hold SHIFT to disable carry."); break; + case TUNING_HOVER_NONE: + setStatusText(""); + break; } } - + if (hoverState == TUNING_HOVER_BW || hoverState == TUNING_HOVER_FREQ) { + wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator()); + } else { + wxGetApp().getDemodMgr().setActiveDemodulator(NULL); + } } void TuningCanvas::OnMouseDown(wxMouseEvent& event) { @@ -339,6 +346,7 @@ void TuningCanvas::OnMouseLeftWindow(wxMouseEvent& event) { SetCursor(wxCURSOR_CROSS); hoverIndex = 0; hoverState = TUNING_HOVER_NONE; + wxGetApp().getDemodMgr().setActiveDemodulator(NULL); if (currentPPM != lastPPM) { wxGetApp().saveConfig(); @@ -359,6 +367,10 @@ void TuningCanvas::setHelpTip(std::string tip) { void TuningCanvas::OnKeyDown(wxKeyEvent& event) { InteractiveCanvas::OnKeyDown(event); + + if (event.GetKeyCode() == WXK_SPACE && (hoverState == TUNING_HOVER_CENTER || hoverState == TUNING_HOVER_FREQ)) { + wxGetApp().showFrequencyInput(); + } } void TuningCanvas::OnKeyUp(wxKeyEvent& event) { diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 77cbdb9..b00fb84 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -141,7 +141,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { ColorTheme *currentTheme = ThemeMgr::mgr.currentTheme; int last_type = wxGetApp().getDemodMgr().getLastDemodulatorType(); - if (mouseTracker.mouseInView()) { + if (mouseTracker.mouseInView() || wxGetApp().getDemodMgr().getActiveDemodulator()) { hoverAlpha += (1.0f-hoverAlpha)*0.1f; if (hoverAlpha > 1.5f) { hoverAlpha = 1.5f; @@ -193,6 +193,8 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { } } + glContext->setHoverAlpha(0); + for (int i = 0, iMax = demods.size(); i < iMax; i++) { if (activeDemodulator == demods[i] || lastActiveDemodulator == demods[i]) { continue; @@ -299,16 +301,6 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { wxGetApp().getDemodMgr().deleteThread(activeDemod); break; case 'S': - if (!activeDemod) { - break; - } - if (activeDemod->isSquelchEnabled()) { - activeDemod->setSquelchEnabled(false); - } else { - activeDemod->squelchAuto(); - } - break; - case WXK_SPACE: if (!activeDemod) { break; } @@ -318,6 +310,9 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { activeDemod->setStereo(true); } break; + case WXK_SPACE: + wxGetApp().showFrequencyInput(); + break; default: event.Skip(); return; @@ -720,23 +715,23 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(false); - setStatusText("Click and drag to change demodulator bandwidth. D to delete, SPACE for stereo."); + setStatusText("Click and drag to change demodulator bandwidth. SPACE for direct frequency input. D to delete, S for stereo."); } else { SetCursor(wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(false); - setStatusText("Click and drag to change demodulator frequency. D to delete, SPACE for stereo."); + setStatusText("Click and drag to change demodulator frequency; SPACE for direct input. D to delete, S for stereo."); } } else { SetCursor(wxCURSOR_CROSS); nextDragState = WF_DRAG_NONE; if (shiftDown) { - setStatusText("Click to create a new demodulator or hold ALT to drag range."); + setStatusText("Click to create a new demodulator or hold ALT to drag range, SPACE for direct center frequency input."); } else { setStatusText( - "Click to move active demodulator frequency or hold ALT to drag range; hold SHIFT to create new. Right drag or A / Z to Zoom. Arrow keys (+SHIFT) to move center frequency."); + "Click to move active demodulator frequency or hold ALT to drag range; hold SHIFT to create new. Right drag or A / Z to Zoom. Arrow keys (+SHIFT) to move center frequency; SPACE for direct input."); } }