diff --git a/CMakeLists.txt b/CMakeLists.txt index a817a1d..d54217f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,19 +6,69 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") SET(CUBICSDR_VERSION_MAJOR "0") SET(CUBICSDR_VERSION_MINOR "2") -SET(CUBICSDR_VERSION_PATCH "0") -SET(CUBICSDR_VERSION_REL "") -SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}${CUBICSDR_VERSION_REL}") +SET(CUBICSDR_VERSION_PATCH "1") +SET(CUBICSDR_VERSION_SUFFIX "-alpha-bookmark1") +SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}${CUBICSDR_VERSION_SUFFIX}") SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}") SET(CPACK_PACKAGE_VERSION_MAJOR ${CUBICSDR_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${CUBICSDR_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${CUBICSDR_VERSION_PATCH}) -SET (VERSION_SUFFIX "" CACHE STRING "Add custom version suffix to CubicSDR application title.") +SET (CUSTOM_BUILD OFF CACHE BOOL "Enable custom build options") +# Build options for custom deploys, optimization and debugging +IF(CUSTOM_BUILD) + SET (CUBICSDR_BUILD_TITLE CACHE STRING "Custom Title") + # bundle flags + SET (CUBICSDR_INSTALL_NAME CACHE "CubicSDR" "Installation Name") + SET (CUBICSDR_INSTALL_TITLE CACHE "CubicSDR" "Installation Title") + SET (CUBICSDR_HEADER_IMAGE CACHE "" "Image file to display in header") + SET (CUBICSDR_HEADER_BG CACHE "000000" "Background Color (HEX) for header") + # feature flags + SET (CUBICSDR_ENABLE_VIEW_DEMOD ON CACHE BOOL "Enable Second Demodulator Spectrum/Waterfall view.") + SET (CUBICSDR_ENABLE_VIEW_SCOPE ON CACHE BOOL "Enable Demodulator Scope/Spectrum view.") + SET (CUBICSDR_MODEM_EXCLUDE CACHE "" "Comma-separated list of modems to exclude.") + + IF (NOT CUBICSDR_HEADER_IMAGE STREQUAL "") + SET(CUBICSDR_HAS_HEADER_IMAGE TRUE) + GET_FILENAME_COMPONENT(CUBICSDR_HEADER_IMAGE_FILE "${CUBICSDR_HEADER_IMAGE}" NAME) + GET_FILENAME_COMPONENT(CUBICSDR_HEADER_IMAGE_DIR "${CUBICSDR_HEADER_IMAGE}" PATH) + ADD_DEFINITIONS( + -DCUBICSDR_HEADER_IMAGE="${CUBICSDR_HEADER_IMAGE_FILE}" + -DCUBICSDR_HEADER_BG="${CUBICSDR_HEADER_BG}" + ) + ENDIF() + + IF (NOT CUBICSDR_MODEM_EXCLUDE STREQUAL "") + ADD_DEFINITIONS( + -DCUBICSDR_MODEM_EXCLUDE="${CUBICSDR_MODEM_EXCLUDE}" + ) + ENDIF() +ELSE() + SET (CUBICSDR_BUILD_TITLE "CubicSDR v${CUBICSDR_VERSION} by Charles J. Cliffe (@ccliffe) :: www.cubicsdr.com") + # bundle flags + SET (CUBICSDR_INSTALL_NAME "CubicSDR") + SET (CUBICSDR_INSTALL_TITLE "CubicSDR ${CUBICSDR_VERSION} Installer") + SET (CUBICSDR_HEADER_IMAGE "") + SET (CUBICSDR_HEADER_BG "") + # feature flags + SET (CUBICSDR_ENABLE_VIEW_DEMOD TRUE) + SET (CUBICSDR_ENABLE_VIEW_SCOPE TRUE) + SET (CUBICSDR_EXCLUDE_MODEM "") +ENDIF() + +IF(CUBICSDR_ENABLE_VIEW_DEMOD) + ADD_DEFINITIONS( -DCUBICSDR_ENABLE_VIEW_DEMOD=1 ) +ENDIF() + +IF(CUBICSDR_ENABLE_VIEW_SCOPE) + ADD_DEFINITIONS( -DCUBICSDR_ENABLE_VIEW_SCOPE=1 ) +ENDIF() ADD_DEFINITIONS( - -DCUBICSDR_VERSION="${CUBICSDR_VERSION}${VERSION_SUFFIX}" + -DCUBICSDR_INSTALL_NAME="${CUBICSDR_INSTALL_NAME}" + -DCUBICSDR_VERSION="${CUBICSDR_VERSION}" + -DCUBICSDR_BUILD_TITLE="${CUBICSDR_BUILD_TITLE}" ) SET (ENABLE_DIGITAL_LAB OFF CACHE BOOL "Enable 'Digital Lab' testing features.") @@ -27,9 +77,9 @@ ADD_DEFINITIONS( -DENABLE_DIGITAL_LAB=1 ) IF(MSVC) - SET (ENABLE_LIQUID_EXPERIMENTAL OFF CACHE BOOL "Enable experimental liquid-dsp features (requires latest liquid-dsp installed)") + SET (ENABLE_LIQUID_EXPERIMENTAL OFF CACHE BOOL "Enable experimental liquid-dsp features (requires latest liquid-dsp installed)") ELSE() - SET (ENABLE_LIQUID_EXPERIMENTAL ON CACHE BOOL "Enable experimental liquid-dsp features (requires latest liquid-dsp installed)") + SET (ENABLE_LIQUID_EXPERIMENTAL ON CACHE BOOL "Enable experimental liquid-dsp features (requires latest liquid-dsp installed)") ENDIF() IF(ENABLE_LIQUID_EXPERIMENTAL) ADD_DEFINITIONS( @@ -50,7 +100,7 @@ if (USE_HAMLIB) include_directories(${HAMLIB_INCLUDE_DIR}) link_libraries(${HAMLIB_LIBRARY}) - ADD_DEFINITIONS(-DUSE_HAMLIB) + ADD_DEFINITIONS(-DUSE_HAMLIB) endif () macro(configure_files srcDir destDir globStr) @@ -103,16 +153,16 @@ SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NA SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME}) IF (MSVC) - include_directories ("${PROJECT_SOURCE_DIR}/external/wglext") - SET(LIQUID_INCLUDES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/include/" CACHE STRING "Liquid-DSP include directory") - SET(LIQUID_LIBRARIES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.lib" CACHE STRING "Liquid-DSP Library") - SET(LIQUID_DLL "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.dll" CACHE STRING "Liquid-DSP DLL") - SET(HAMLIB_DLLS "${PROJECT_SOURCE_DIR}/external/hamlib/${EX_PLATFORM}/libhamlib-2.dll;${PROJECT_SOURCE_DIR}/external/hamlib/${EX_PLATFORM}/libwinpthread-1.dll" CACHE STRING "HAMLIB DLLS") + include_directories ("${PROJECT_SOURCE_DIR}/external/wglext") + SET(LIQUID_INCLUDES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/include/" CACHE STRING "Liquid-DSP include directory") + SET(LIQUID_LIBRARIES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.lib" CACHE STRING "Liquid-DSP Library") + SET(LIQUID_DLL "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.dll" CACHE STRING "Liquid-DSP DLL") + SET(HAMLIB_DLLS "${PROJECT_SOURCE_DIR}/external/hamlib/${EX_PLATFORM}/libhamlib-2.dll;${PROJECT_SOURCE_DIR}/external/hamlib/${EX_PLATFORM}/libwinpthread-1.dll" CACHE STRING "HAMLIB DLLS") ELSE (MSVC) - ADD_DEFINITIONS( - -std=c++0x - -pthread - ) + ADD_DEFINITIONS( + -std=c++0x + -pthread + ) ENDIF(MSVC) find_package(OpenGL REQUIRED) @@ -128,50 +178,50 @@ find_package(SoapySDR "0.4.0" NO_MODULE REQUIRED) include_directories(${SOAPY_SDR_INCLUDE_DIR}) SET(OTHER_LIBRARIES ${SOAPY_SDR_LIBRARY} ${OTHER_LIBRARIES}) ADD_DEFINITIONS( - -DUSE_SOAPY_SDR=1 + -DUSE_SOAPY_SDR=1 ) IF (WIN32) - set(wxWidgets_USE_STATIC ON) + set(wxWidgets_USE_STATIC ON) - set(BUILD_INSTALLER OFF CACHE BOOL "Build Installer") - - # Audio device selection is not mandatory, dummy audio device is used if none are compiled in. - # Can also compile support for more than one simultaneously. - set(USE_AUDIO_DS ON CACHE BOOL "Include support for DirectSound") - set(USE_AUDIO_WASAPI OFF CACHE BOOL "Include support for WASAPI Audio") - # TODO: - # set(USE_AUDIO_ASIO OFF CACHE BOOL "Include support for ASIO Audio") + set(BUILD_INSTALLER OFF CACHE BOOL "Build Installer") + + # Audio device selection is not mandatory, dummy audio device is used if none are compiled in. + # Can also compile support for more than one simultaneously. + set(USE_AUDIO_DS ON CACHE BOOL "Include support for DirectSound") + set(USE_AUDIO_WASAPI OFF CACHE BOOL "Include support for WASAPI Audio") + # TODO: + # set(USE_AUDIO_ASIO OFF CACHE BOOL "Include support for ASIO Audio") - # WASAPI - IF(USE_AUDIO_WASAPI) - ADD_DEFINITIONS(-D__WINDOWS_WASAPI__) - IF (NOT MSVC) - SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} -luuid -lksuser) - ENDIF(NOT MSVC) - ENDIF(USE_AUDIO_WASAPI) + # WASAPI + IF(USE_AUDIO_WASAPI) + ADD_DEFINITIONS(-D__WINDOWS_WASAPI__) + IF (NOT MSVC) + SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} -luuid -lksuser) + ENDIF(NOT MSVC) + ENDIF(USE_AUDIO_WASAPI) - # DirectSound - IF (USE_AUDIO_DS) - ADD_DEFINITIONS(-D__WINDOWS_DS__) - IF (MSVC) - SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} dsound.lib) - ELSE (MSVC) - SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} -ldsound) - ENDIF (MSVC) - ENDIF(USE_AUDIO_DS) + # DirectSound + IF (USE_AUDIO_DS) + ADD_DEFINITIONS(-D__WINDOWS_DS__) + IF (MSVC) + SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} dsound.lib) + ELSE (MSVC) + SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} -ldsound) + ENDIF (MSVC) + ENDIF(USE_AUDIO_DS) - SET(USE_MINGW_PATCH OFF CACHE BOOL "Add some missing functions when compiling against mingw liquid-dsp.") - IF (USE_MINGW_PATCH) - SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} legacy_stdio_definitions.lib libgcc.a") - ADD_DEFINITIONS( - -DMINGW_PATCH=1 - ) - SET (GCC_LINKDIR "" CACHE STRING "") - IF (GCC_LINKDIR) - link_directories("${GCC_LINKDIR}") - ENDIF() - ENDIF() + SET(USE_MINGW_PATCH OFF CACHE BOOL "Add some missing functions when compiling against mingw liquid-dsp.") + IF (USE_MINGW_PATCH) + SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} legacy_stdio_definitions.lib libgcc.a") + ADD_DEFINITIONS( + -DMINGW_PATCH=1 + ) + SET (GCC_LINKDIR "" CACHE STRING "") + IF (GCC_LINKDIR) + link_directories("${GCC_LINKDIR}") + ENDIF() + ENDIF() ENDIF (WIN32) IF (UNIX AND NOT APPLE) @@ -240,22 +290,23 @@ ENDIF (APPLE) SET (cubicsdr_sources - src/CubicSDR.cpp - src/AppFrame.cpp - src/AppConfig.cpp - src/FrequencyDialog.cpp + src/CubicSDR.cpp + src/AppFrame.cpp + src/AppConfig.cpp + src/FrequencyDialog.cpp src/DemodLabelDialog.cpp src/IOThread.cpp src/ModemProperties.cpp - src/sdr/SDRDeviceInfo.cpp - src/sdr/SDRPostThread.cpp - src/sdr/SDREnumerator.cpp - src/sdr/SoapySDRThread.h - src/demod/DemodulatorPreThread.cpp - src/demod/DemodulatorThread.cpp - src/demod/DemodulatorWorkerThread.cpp - src/demod/DemodulatorInstance.cpp - src/demod/DemodulatorMgr.cpp + src/BookmarkMgr.cpp + src/sdr/SDRDeviceInfo.cpp + src/sdr/SDRPostThread.cpp + src/sdr/SDREnumerator.cpp + src/sdr/SoapySDRThread.h + src/demod/DemodulatorPreThread.cpp + src/demod/DemodulatorThread.cpp + src/demod/DemodulatorWorkerThread.cpp + src/demod/DemodulatorInstance.cpp + src/demod/DemodulatorMgr.cpp src/modules/modem/Modem.cpp src/modules/modem/ModemAnalog.cpp src/modules/modem/ModemDigital.cpp @@ -267,50 +318,55 @@ SET (cubicsdr_sources src/modules/modem/analog/ModemIQ.cpp src/modules/modem/analog/ModemLSB.cpp src/modules/modem/analog/ModemUSB.cpp - src/audio/AudioThread.cpp - src/util/Gradient.cpp - src/util/Timer.cpp - src/util/MouseTracker.cpp - src/util/GLExt.cpp - src/util/GLFont.cpp - src/util/DataTree.cpp + src/audio/AudioThread.cpp + src/util/Gradient.cpp + src/util/Timer.cpp + src/util/MouseTracker.cpp + src/util/GLExt.cpp + src/util/GLFont.cpp + src/util/DataTree.cpp src/panel/ScopePanel.cpp src/panel/SpectrumPanel.cpp src/panel/WaterfallPanel.cpp src/panel/MeterPanel.cpp src/panel/MeterPanel.h - src/visual/ColorTheme.cpp - src/visual/PrimaryGLContext.cpp - src/visual/InteractiveCanvas.cpp - src/visual/MeterCanvas.cpp - src/visual/MeterContext.cpp - src/visual/TuningCanvas.cpp - src/visual/TuningContext.cpp - src/visual/ModeSelectorCanvas.cpp - src/visual/ModeSelectorContext.cpp - src/visual/ScopeCanvas.cpp - src/visual/ScopeContext.cpp - src/visual/SpectrumCanvas.cpp - src/visual/WaterfallCanvas.cpp + src/visual/ColorTheme.cpp + src/visual/PrimaryGLContext.cpp + src/visual/InteractiveCanvas.cpp + src/visual/MeterCanvas.cpp + src/visual/MeterContext.cpp + src/visual/TuningCanvas.cpp + src/visual/TuningContext.cpp + src/visual/ModeSelectorCanvas.cpp + src/visual/ModeSelectorContext.cpp + src/visual/ScopeCanvas.cpp + src/visual/ScopeContext.cpp + src/visual/SpectrumCanvas.cpp + src/visual/WaterfallCanvas.cpp src/visual/GainCanvas.cpp - src/process/VisualProcessor.cpp - src/process/ScopeVisualProcessor.cpp - src/process/SpectrumVisualProcessor.cpp - src/process/FFTVisualDataThread.cpp - src/process/FFTDataDistributor.cpp + src/visual/ImagePanel.cpp + src/process/VisualProcessor.cpp + src/process/ScopeVisualProcessor.cpp + src/process/SpectrumVisualProcessor.cpp + src/process/FFTVisualDataThread.cpp + src/process/FFTDataDistributor.cpp src/process/SpectrumVisualDataThread.cpp - src/ui/GLPanel.cpp + src/ui/GLPanel.cpp src/forms/SDRDevices/SDRDevices.cpp src/forms/SDRDevices/SDRDevicesForm.cpp src/forms/SDRDevices/SDRDeviceAdd.cpp src/forms/SDRDevices/SDRDeviceAddForm.cpp - external/rtaudio/RtAudio.cpp - external/lodepng/lodepng.cpp - external/tinyxml/tinyxml.cpp - external/tinyxml/tinystr.cpp - external/tinyxml/tinyxmlparser.cpp - external/tinyxml/tinyxmlerror.cpp - external/cubicvr2/math/cubic_math.cpp + src/forms/Bookmark/BookmarkPanel.cpp + src/forms/Bookmark/BookmarkView.cpp + src/forms/Dialog/ActionDialogBase.cpp + src/forms/Dialog/ActionDialog.cpp + external/rtaudio/RtAudio.cpp + external/lodepng/lodepng.cpp + external/tinyxml/tinyxml.cpp + external/tinyxml/tinystr.cpp + external/tinyxml/tinyxmlparser.cpp + external/tinyxml/tinyxmlerror.cpp + external/cubicvr2/math/cubic_math.cpp ) IF(ENABLE_DIGITAL_LAB) @@ -330,33 +386,34 @@ IF(ENABLE_DIGITAL_LAB) src/modules/modem/digital/ModemQAM.cpp src/modules/modem/digital/ModemQPSK.cpp ) - IF(ENABLE_LIQUID_EXPERIMENTAL) + IF(ENABLE_LIQUID_EXPERIMENTAL) SET (cubicsdr_sources ${cubicsdr_sources} src/modules/modem/digital/ModemFSK.cpp ) - ENDIF() + ENDIF() ENDIF() SET (cubicsdr_headers - src/CubicSDRDefs.h - src/CubicSDR.h - src/AppFrame.h - src/AppConfig.h - src/FrequencyDialog.h + src/CubicSDRDefs.h + src/CubicSDR.h + src/AppFrame.h + src/AppConfig.h + src/FrequencyDialog.h src/DemodLabelDialog.h src/IOThread.h src/ModemProperties.h - src/sdr/SDRDeviceInfo.h - src/sdr/SDRPostThread.h - src/sdr/SDREnumerator.h - src/sdr/SoapySDRThread.cpp - src/demod/DemodulatorPreThread.h - src/demod/DemodulatorThread.h - src/demod/DemodulatorWorkerThread.h - src/demod/DemodulatorInstance.h - src/demod/DemodulatorMgr.h - src/demod/DemodDefs.h + src/BookmarkMgr.h + src/sdr/SDRDeviceInfo.h + src/sdr/SDRPostThread.h + src/sdr/SDREnumerator.h + src/sdr/SoapySDRThread.cpp + src/demod/DemodulatorPreThread.h + src/demod/DemodulatorThread.h + src/demod/DemodulatorWorkerThread.h + src/demod/DemodulatorInstance.h + src/demod/DemodulatorMgr.h + src/demod/DemodDefs.h src/modules/modem/Modem.h src/modules/modem/ModemAnalog.h src/modules/modem/ModemDigital.h @@ -368,64 +425,69 @@ SET (cubicsdr_headers src/modules/modem/analog/ModemIQ.h src/modules/modem/analog/ModemLSB.h src/modules/modem/analog/ModemUSB.h - src/audio/AudioThread.h - src/util/Gradient.h - src/util/Timer.h - src/util/ThreadQueue.h - src/util/MouseTracker.h - src/util/GLExt.h - src/util/GLFont.h - src/util/DataTree.h + src/audio/AudioThread.h + src/util/Gradient.h + src/util/Timer.h + src/util/ThreadQueue.h + src/util/MouseTracker.h + src/util/GLExt.h + src/util/GLFont.h + src/util/DataTree.h src/panel/ScopePanel.h src/panel/SpectrumPanel.h src/panel/WaterfallPanel.h - src/visual/ColorTheme.h - src/visual/PrimaryGLContext.h - src/visual/InteractiveCanvas.h - src/visual/MeterCanvas.h - src/visual/MeterContext.h - src/visual/TuningCanvas.h - src/visual/TuningContext.h - src/visual/ModeSelectorCanvas.h - src/visual/ModeSelectorContext.h - src/visual/ScopeCanvas.h - src/visual/ScopeContext.h - src/visual/SpectrumCanvas.h - src/visual/WaterfallCanvas.h + src/visual/ColorTheme.h + src/visual/PrimaryGLContext.h + src/visual/InteractiveCanvas.h + src/visual/MeterCanvas.h + src/visual/MeterContext.h + src/visual/TuningCanvas.h + src/visual/TuningContext.h + src/visual/ModeSelectorCanvas.h + src/visual/ModeSelectorContext.h + src/visual/ScopeCanvas.h + src/visual/ScopeContext.h + src/visual/SpectrumCanvas.h + src/visual/WaterfallCanvas.h src/visual/GainCanvas.h - src/process/VisualProcessor.h - src/process/ScopeVisualProcessor.h - src/process/SpectrumVisualProcessor.h - src/process/FFTVisualDataThread.h - src/process/FFTDataDistributor.h + src/visual/ImagePanel.h + src/process/VisualProcessor.h + src/process/ScopeVisualProcessor.h + src/process/SpectrumVisualProcessor.h + src/process/FFTVisualDataThread.h + src/process/FFTDataDistributor.h src/process/SpectrumVisualDataThread.h - src/ui/GLPanel.h - src/ui/UITestCanvas.cpp - src/ui/UITestCanvas.h - src/ui/UITestContext.cpp - src/ui/UITestContext.h + src/ui/GLPanel.h + src/ui/UITestCanvas.cpp + src/ui/UITestCanvas.h + src/ui/UITestContext.cpp + src/ui/UITestContext.h src/forms/SDRDevices/SDRDevices.h src/forms/SDRDevices/SDRDevicesForm.h src/forms/SDRDevices/SDRDeviceAdd.h src/forms/SDRDevices/SDRDeviceAddForm.h - external/rtaudio/RtAudio.h - external/lodepng/lodepng.h - external/tinyxml/tinyxml.h - external/tinyxml/tinystr.h - external/cubicvr2/math/aabb.h - external/cubicvr2/math/cubic_math.h - external/cubicvr2/math/cubic_types.h - external/cubicvr2/math/frustum.h - external/cubicvr2/math/mat3.h - external/cubicvr2/math/mat4.h - external/cubicvr2/math/plane.h - external/cubicvr2/math/quaternion.h - external/cubicvr2/math/sphere.h - external/cubicvr2/math/transform.h - external/cubicvr2/math/triangle.h - external/cubicvr2/math/vec2.h - external/cubicvr2/math/vec3.h - external/cubicvr2/math/vec4.h + src/forms/Bookmark/BookmarkPanel.h + src/forms/Bookmark/BookmarkView.h + src/forms/Dialog/ActionDialogBase.h + src/forms/Dialog/ActionDialog.h + external/rtaudio/RtAudio.h + external/lodepng/lodepng.h + external/tinyxml/tinyxml.h + external/tinyxml/tinystr.h + external/cubicvr2/math/aabb.h + external/cubicvr2/math/cubic_math.h + external/cubicvr2/math/cubic_types.h + external/cubicvr2/math/frustum.h + external/cubicvr2/math/mat3.h + external/cubicvr2/math/mat4.h + external/cubicvr2/math/plane.h + external/cubicvr2/math/quaternion.h + external/cubicvr2/math/sphere.h + external/cubicvr2/math/transform.h + external/cubicvr2/math/triangle.h + external/cubicvr2/math/vec2.h + external/cubicvr2/math/vec3.h + external/cubicvr2/math/vec4.h ) IF(ENABLE_DIGITAL_LAB) @@ -495,6 +557,8 @@ set(REG_EXT "[^/]*([.]cpp|[.]c|[.]h|[.]hpp)$") SOURCE_GROUP("Base" REGULAR_EXPRESSION "src/${REG_EXT}") SOURCE_GROUP("Forms\\SDRDevices" REGULAR_EXPRESSION "src/forms/SDRDevices/${REG_EXT}") +SOURCE_GROUP("Forms\\Bookmark" REGULAR_EXPRESSION "src/forms/Bookmark/${REG_EXT}") +SOURCE_GROUP("Forms\\Dialog" REGULAR_EXPRESSION "src/forms/Dialog/${REG_EXT}") SOURCE_GROUP("SDR" REGULAR_EXPRESSION "src/sdr/${REG_EXT}") IF(USE_HAMLIB) SOURCE_GROUP("Rig" REGULAR_EXPRESSION "src/rig/${REG_EXT}") @@ -518,26 +582,28 @@ SOURCE_GROUP("_ext-TinyXML" REGULAR_EXPRESSION "external/tinyxml/.*${REG_EXT}") SOURCE_GROUP("_ext-CubicVR2" REGULAR_EXPRESSION "external/cubicvr2/.*${REG_EXT}") include_directories ( - ${PROJECT_SOURCE_DIR}/src/forms/SDRDevices - ${PROJECT_SOURCE_DIR}/src/forms/DigitalConsole - ${PROJECT_SOURCE_DIR}/src/sdr - ${PROJECT_SOURCE_DIR}/src/demod - ${PROJECT_SOURCE_DIR}/src/modules - ${PROJECT_SOURCE_DIR}/src/modules/modem - ${PROJECT_SOURCE_DIR}/src/modules/modem/digital - ${PROJECT_SOURCE_DIR}/src/modules/modem/analog - ${PROJECT_SOURCE_DIR}/src/audio - ${PROJECT_SOURCE_DIR}/src/util - ${PROJECT_SOURCE_DIR}/src/panel - ${PROJECT_SOURCE_DIR}/src/visual - ${PROJECT_SOURCE_DIR}/src/process - ${PROJECT_SOURCE_DIR}/src/ui - ${PROJECT_SOURCE_DIR}/src/rig - ${PROJECT_SOURCE_DIR}/src - ${PROJECT_SOURCE_DIR}/external/rtaudio - ${PROJECT_SOURCE_DIR}/external/lodepng - ${PROJECT_SOURCE_DIR}/external/tinyxml - ${PROJECT_SOURCE_DIR}/external/cubicvr2/math + ${PROJECT_SOURCE_DIR}/src/forms/SDRDevices + ${PROJECT_SOURCE_DIR}/src/forms/DigitalConsole + ${PROJECT_SOURCE_DIR}/src/forms/Bookmark + ${PROJECT_SOURCE_DIR}/src/forms/Dialog + ${PROJECT_SOURCE_DIR}/src/sdr + ${PROJECT_SOURCE_DIR}/src/demod + ${PROJECT_SOURCE_DIR}/src/modules + ${PROJECT_SOURCE_DIR}/src/modules/modem + ${PROJECT_SOURCE_DIR}/src/modules/modem/digital + ${PROJECT_SOURCE_DIR}/src/modules/modem/analog + ${PROJECT_SOURCE_DIR}/src/audio + ${PROJECT_SOURCE_DIR}/src/util + ${PROJECT_SOURCE_DIR}/src/panel + ${PROJECT_SOURCE_DIR}/src/visual + ${PROJECT_SOURCE_DIR}/src/process + ${PROJECT_SOURCE_DIR}/src/ui + ${PROJECT_SOURCE_DIR}/src/rig + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/external/rtaudio + ${PROJECT_SOURCE_DIR}/external/lodepng + ${PROJECT_SOURCE_DIR}/external/tinyxml + ${PROJECT_SOURCE_DIR}/external/cubicvr2/math ) set(RES_FILES "") @@ -546,11 +612,11 @@ if(MINGW OR MSVC) set(CMAKE_RC_COMPILER_INIT windres) ENABLE_LANGUAGE(RC) IF(EX_PLATFORM EQUAL 64) - SET(RC_TARGET "pe-x86-64") + SET(RC_TARGET "pe-x86-64") ELSE(EX_PLATFORM EQUAL 64) - SET(RC_TARGET "pe-i386") + SET(RC_TARGET "pe-i386") ENDIF(EX_PLATFORM EQUAL 64) - + SET(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") endif(MINGW OR MSVC) @@ -558,9 +624,12 @@ IF (NOT BUNDLE_APP) configure_files(${PROJECT_SOURCE_DIR}/font ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/fonts "*.fnt") configure_files(${PROJECT_SOURCE_DIR}/font ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/fonts "*.png") configure_files(${PROJECT_SOURCE_DIR}/icon ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} CubicSDR.ico) - IF(MSVC) - configure_files(${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/ ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") - ENDIF() + IF(MSVC) + configure_files(${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/ ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") + ENDIF() + IF (CUBICSDR_HAS_HEADER_IMAGE) + configure_files(${CUBICSDR_HEADER_IMAGE_DIR} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} ${CUBICSDR_HEADER_IMAGE_FILE}) + ENDIF() add_executable(CubicSDR ${cubicsdr_sources} ${cubicsdr_headers} ${RES_FILES}) target_link_libraries(CubicSDR ${LIQUID_LIB} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${OTHER_LIBRARIES}) ENDIF (NOT BUNDLE_APP) @@ -575,6 +644,8 @@ IF (MSVC) set_target_properties(CubicSDR PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") set_target_properties(CubicSDR PROPERTIES COMPILE_DEFINITIONS_MINSIZEREL "_WINDOWS") set(CMAKE_CREATE_WIN32_EXE "/SUBSYSTEM:WINDOWS /ENTRY:\"mainCRTStartup\"") + set_target_properties (CubicSDR PROPERTIES OUTPUT_NAME "${CUBICSDR_INSTALL_NAME}") + ENDIF(MSVC) IF (APPLE) @@ -596,20 +667,20 @@ IF (APPLE AND BUNDLE_APP) set(BUNDLE_MIR_SDR OFF CACHE BOOL "Bundle mir_sdr for personal use only -- do not distribute.") IF (BUNDLE_SOAPY_MODS) - ADD_DEFINITIONS( - -DBUNDLE_SOAPY_MODS=1 - ) - set(BUNDLED_MODS_ONLY OFF CACHE BOOL "Use bundled mods only") - IF (BUNDLED_MODS_ONLY) - ADD_DEFINITIONS( - -DBUNDLED_MODS_ONLY=1 - ) - ENDIF() + ADD_DEFINITIONS( + -DBUNDLE_SOAPY_MODS=1 + ) + set(BUNDLED_MODS_ONLY OFF CACHE BOOL "Use bundled mods only") + IF (BUNDLED_MODS_ONLY) + ADD_DEFINITIONS( + -DBUNDLED_MODS_ONLY=1 + ) + ENDIF() ENDIF() ADD_DEFINITIONS( - -std=c++0x - -pthread + -std=c++0x + -pthread -D_OSX_APP_ ) @@ -639,7 +710,7 @@ IF (APPLE AND BUNDLE_APP) MACOSX_BUNDLE_INFO_STRING "CubicSDR Open-Source Software-Defined Radio Application" MACOSX_BUNDLE_BUNDLE_NAME "CubicSDR" MACOSX_BUNDLE_BUNDLE_VERSION "${CUBICSDR_VERSION}" - MACOSX_BUNDLE_LONG_VERSION_STRING "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}.${CUBICSDR_VERSION_REL}" + MACOSX_BUNDLE_LONG_VERSION_STRING "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}${CUBICSDR_VERSION_SUFFIX}" MACOSX_BUNDLE_SHORT_VERSION_STRING "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}" MACOSX_BUNDLE_GUI_IDENTIFIER "com.cubicproductions.cubicsdr" MACOSX_BUNDLE_ICON_FILE "CubicSDR.icns" @@ -651,9 +722,10 @@ IF (APPLE AND BUNDLE_APP) # SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) IF (BUNDLE_SOAPY_MODS) - message(STATUS "SOAPY_ROOT: ${SOAPY_SDR_ROOT}") - file(GLOB SOAPY_MODS ${SOAPY_SDR_ROOT}/lib/SoapySDR/modules/*.so) + SET(SOAPY_SDR_MOD_PATH "${SOAPY_SDR_ROOT}/lib/SoapySDR/modules/${SOAPY_SDR_ABI_VERSION}") + + file(GLOB SOAPY_MODS ${SOAPY_MOD_PATH}/*.so) FOREACH(SOAPY_MOD_FILE ${SOAPY_MODS}) INSTALL( FILES "${SOAPY_MOD_FILE}" @@ -734,25 +806,26 @@ IF (APPLE AND BUNDLE_APP) include(CPack) ENDIF() + IF(APPLE AND NOT BUNDLE_APP) IF (NOT CMAKE_INSTALL_PREFIX) SET(CMAKE_INSTALL_PREFIX "/usr/") ENDIF() - ADD_DEFINITIONS( - -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/" - ) + ADD_DEFINITIONS( + -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/" + ) set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro") - INSTALL(TARGETS CubicSDR DESTINATION bin) + INSTALL(TARGETS CubicSDR DESTINATION bin) install(FILES ${PROJECT_SOURCE_DIR}/src/CubicSDR.png - DESTINATION share/cubicsdr) + DESTINATION share/cubicsdr) install(FILES ${CUBICSDR_FONTS} - DESTINATION share/cubicsdr/fonts) + DESTINATION share/cubicsdr/fonts) CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" @@ -768,181 +841,201 @@ IF (WIN32 AND NOT BUILD_INSTALLER) ADD_DEFINITIONS( -DRES_FOLDER="../share/cubicsdr/" ) - + INSTALL(TARGETS CubicSDR DESTINATION bin) - INSTALL(FILES - ${LIQUID_DLL} - DESTINATION bin) + INSTALL(FILES + ${LIQUID_DLL} + DESTINATION bin) - IF(USE_HAMLIB) - FOREACH(HAMLIB_DLL ${HAMLIB_DLLS}) - message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}") + IF(USE_HAMLIB) + FOREACH(HAMLIB_DLL ${HAMLIB_DLLS}) + message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}") INSTALL( FILES "${HAMLIB_DLL}" DESTINATION bin ) - ENDFOREACH() - ENDIF() + ENDFOREACH() + ENDIF() INSTALL(FILES - ${PROJECT_SOURCE_DIR}/src/CubicSDR.png - DESTINATION share/cubicsdr) + ${PROJECT_SOURCE_DIR}/src/CubicSDR.png + DESTINATION share/cubicsdr) INSTALL(FILES - ${CUBICSDR_FONTS} - DESTINATION share/cubicsdr/fonts) + ${CUBICSDR_FONTS} + DESTINATION share/cubicsdr/fonts) + + IF (CUBICSDR_HAS_HEADER_IMAGE) + INSTALL(FILES + ${CUBICSDR_HEADER_IMAGE} + DESTINATION share/cubicsdr/) + ENDIF() + ENDIF() IF (WIN32 AND BUILD_INSTALLER) set(BUNDLE_SOAPY_MODS OFF CACHE BOOL "Bundle local SoapySDR modules") - set(CPACK_GENERATOR NSIS) - set(CPACK_PACKAGE_NAME "CubicSDR") - set(CPACK_PACKAGE_VENDOR "cubicsdr.com") - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "CubicSDR ${CUBICSDR_VERSION} Installer") - set(CPACK_PACKAGE_INSTALL_DIRECTORY "CubicSDR") - SET(CPACK_NSIS_INSTALLED_ICON_NAME "CubicSDR.ico") - SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") - set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/icon\\\\NSIS_Header.bmp") - IF(EX_PLATFORM EQUAL 64) - SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") - SET(CPACK_NSIS_PACKAGE_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY}") - SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION}") - set(CMAKE_CL_64 TRUE) # This gets around a bug in the CPack installer name generation for MinGW 64-bit since 2.8 - ELSE(EX_PLATFORM EQUAL 64) - SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES") - SET(CPACK_NSIS_PACKAGE_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} (x86)") - SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION} (x86)") - set(CMAKE_CL_64 FALSE) - ENDIF(EX_PLATFORM EQUAL 64) - - set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") - install(TARGETS CubicSDR RUNTIME DESTINATION .) + set(CPACK_GENERATOR NSIS) + set(CPACK_PACKAGE_NAME "${CUBICSDR_INSTALL_NAME}") + set(CPACK_NSIS_DISPLAY_NAME "${CUBICSDR_INSTALL_TITLE}") + set(CPACK_PACKAGE_VENDOR "cubicsdr.com") + set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CUBICSDR_INSTALL_NAME}") + SET(CPACK_NSIS_INSTALLED_ICON_NAME "CubicSDR.ico") + SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") + set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/icon\\\\NSIS_Header.bmp") + IF(EX_PLATFORM EQUAL 64) + SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") + SET(CPACK_NSIS_PACKAGE_NAME "${CUBICSDR_INSTALL_NAME}") + SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CUBICSDR_INSTALL_NAME} ${CPACK_PACKAGE_VERSION}") + set(CMAKE_CL_64 TRUE) # This gets around a bug in the CPack installer name generation for MinGW 64-bit since 2.8 + ELSE(EX_PLATFORM EQUAL 64) + SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES") + SET(CPACK_NSIS_PACKAGE_NAME "${CUBICSDR_INSTALL_NAME} (x86)") + SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CUBICSDR_INSTALL_NAME} ${CPACK_PACKAGE_VERSION} (x86)") + set(CMAKE_CL_64 FALSE) + ENDIF(EX_PLATFORM EQUAL 64) + + set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") + install(TARGETS CubicSDR RUNTIME DESTINATION .) - install(FILES + install(FILES ${PROJECT_SOURCE_DIR}/icon/CubicSDR.ico - ${LIQUID_DLL} - DESTINATION .) + ${LIQUID_DLL} + DESTINATION .) - install(FILES + install(FILES ${CUBICSDR_FONTS} - DESTINATION fonts) - - IF(USE_HAMLIB) - FOREACH(HAMLIB_DLL ${HAMLIB_DLLS}) - message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}") + DESTINATION fonts) + + IF (CUBICSDR_HAS_HEADER_IMAGE) + INSTALL(FILES + ${CUBICSDR_HEADER_IMAGE} + DESTINATION .) + ENDIF() + + IF(USE_HAMLIB) + FOREACH(HAMLIB_DLL ${HAMLIB_DLLS}) + message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}") INSTALL( FILES - ${HAMLIB_DLL} - DESTINATION .) - ENDFOREACH() - ENDIF() + ${HAMLIB_DLL} + DESTINATION .) + ENDFOREACH() + ENDIF() - IF (BUNDLE_SOAPY_MODS) - ADD_DEFINITIONS( - -DBUNDLE_SOAPY_MODS=1 - ) - set(BUNDLED_MODS_ONLY OFF CACHE BOOL "Use bundled mods only") - IF (BUNDLED_MODS_ONLY) - ADD_DEFINITIONS( - -DBUNDLED_MODS_ONLY=1 - ) - ENDIF() - - file(GLOB SOAPY_BINS ${SOAPY_SDR_ROOT}/bin/*.dll) - file(GLOB SOAPY_MODS ${SOAPY_SDR_ROOT}/lib/SoapySDR/modules/*.dll) - message(STATUS "SOAPY_BINS: ${SOAPY_BINS}") - message(STATUS "SOAPY_MODS: ${SOAPY_MODS}") - install(FILES ${SOAPY_BINS} DESTINATION .) - install(FILES ${SOAPY_MODS} DESTINATION modules) - ENDIF(BUNDLE_SOAPY_MODS) - - IF(MSVC AND EX_PLATFORM EQUAL 32) - install(FILES - ${PROJECT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/libgcc_s_dw2-1.dll - DESTINATION .) - ENDIF(MSVC AND EX_PLATFORM EQUAL 32) + IF (BUNDLE_SOAPY_MODS) + ADD_DEFINITIONS( + -DBUNDLE_SOAPY_MODS=1 + ) + set(BUNDLED_MODS_ONLY OFF CACHE BOOL "Use bundled mods only") + IF (BUNDLED_MODS_ONLY) + ADD_DEFINITIONS( + -DBUNDLED_MODS_ONLY=1 + ) + ENDIF() + + file(GLOB SOAPY_BINS ${SOAPY_SDR_ROOT}/bin/*.dll) + file(GLOB SOAPY_MODS ${SOAPY_SDR_ROOT}/lib/SoapySDR/modules${SOAPY_SDR_ABI_VERSION}/*.dll) + message(STATUS "SOAPY_BINS: ${SOAPY_BINS}") + message(STATUS "SOAPY_MODS: ${SOAPY_MODS}") + install(FILES ${SOAPY_BINS} DESTINATION .) + install(FILES ${SOAPY_MODS} DESTINATION modules) + ENDIF(BUNDLE_SOAPY_MODS) + + IF(MSVC AND EX_PLATFORM EQUAL 32) + install(FILES + ${PROJECT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/libgcc_s_dw2-1.dll + DESTINATION .) + ENDIF(MSVC AND EX_PLATFORM EQUAL 32) - set(CPACK_PACKAGE_EXECUTABLES CubicSDR "CubicSDR") + set(CPACK_PACKAGE_EXECUTABLES CubicSDR "CubicSDR") - IF (MSVC) - install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/vc_redist.${EX_PLATFORM_NAME}.exe DESTINATION vc_redist) - set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\vc_redist\\\\vc_redist.${EX_PLATFORM_NAME}.exe\\\" /q:a'") - ENDIF (MSVC) - - - INCLUDE(CPack) + IF (MSVC) + install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/vc_redist.${EX_PLATFORM_NAME}.exe DESTINATION vc_redist) + set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\vc_redist\\\\vc_redist.${EX_PLATFORM_NAME}.exe\\\" /q:a'") + ENDIF (MSVC) + + + INCLUDE(CPack) ENDIF (WIN32 AND BUILD_INSTALLER) IF (UNIX AND NOT APPLE AND BUILD_DEB) set(CPACK_GENERATOR DEB) - set(CPACK_PACKAGE_NAME "CubicSDR") - SET(CPACK_DEBIAN_PACKAGE_DEPENDS " libwxgtk3.0-0, libpulse0") - SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Charles J. Cliffe ") - SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "CubicSDR Software Defined Radio application v${CUBICSDR_VERSION}") - SET(CPACK_DEBIAN_PACKAGE_SECTION "comm") + set(CPACK_PACKAGE_NAME "CubicSDR") + SET(CPACK_DEBIAN_PACKAGE_DEPENDS " libwxgtk3.0-0, libpulse0") + SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Charles J. Cliffe ") + SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "CubicSDR Software Defined Radio application v${CUBICSDR_VERSION}") + SET(CPACK_DEBIAN_PACKAGE_SECTION "comm") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") - SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${EX_PLATFORM_NAME}") + SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${EX_PLATFORM_NAME}") IF (NOT CMAKE_INSTALL_PREFIX) SET(CMAKE_INSTALL_PREFIX "/usr/") ENDIF() - ADD_DEFINITIONS( - -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/" - -D_FORTIFY_SOURCE=2 - ) - + ADD_DEFINITIONS( + -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/" + -D_FORTIFY_SOURCE=2 + ) + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro") - CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/external/deb/deb_post.sh.in" - "${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE) + CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/external/deb/deb_post.sh.in" + "${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE) - CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in" - "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE) + CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in" + "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE) - INSTALL(TARGETS CubicSDR DESTINATION bin) + INSTALL(TARGETS CubicSDR DESTINATION bin) install(FILES ${PROJECT_SOURCE_DIR}/src/CubicSDR.png - DESTINATION share/cubicsdr) + DESTINATION share/cubicsdr) install(FILES ${CUBICSDR_FONTS} - DESTINATION share/cubicsdr/fonts) + DESTINATION share/cubicsdr/fonts) INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" DESTINATION share/applications) - INCLUDE(CPack) + INCLUDE(CPack) ENDIF() IF(UNIX AND NOT APPLE AND NOT BUILD_DEB) IF (NOT CMAKE_INSTALL_PREFIX) SET(CMAKE_INSTALL_PREFIX "/usr/") ENDIF() - ADD_DEFINITIONS( - -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/" - ) + ADD_DEFINITIONS( + -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/" + ) set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro") - CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in" - "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE) + CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in" + "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE) - INSTALL(TARGETS CubicSDR DESTINATION bin) + INSTALL(TARGETS CubicSDR DESTINATION bin) INSTALL(FILES ${PROJECT_SOURCE_DIR}/src/CubicSDR.png - DESTINATION share/cubicsdr) + DESTINATION share/cubicsdr) INSTALL(FILES ${CUBICSDR_FONTS} - DESTINATION share/cubicsdr/fonts) + DESTINATION share/cubicsdr/fonts) + + + IF (CUBICSDR_HAS_HEADER_IMAGE) + INSTALL(FILES + ${CUBICSDR_HEADER_IMAGE} + DESTINATION share/cubicsdr) + ENDIF() INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" - DESTINATION share/applications) + DESTINATION share/applications) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/external/deb/deb_post.sh.in" - "${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE) + "${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE) CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" diff --git a/src/AppConfig.cpp b/src/AppConfig.cpp index be10acb..4c7fc5c 100644 --- a/src/AppConfig.cpp +++ b/src/AppConfig.cpp @@ -31,7 +31,6 @@ long long DeviceConfig::getOffset() { return offset.load(); } - void DeviceConfig::setSampleRate(long srate) { sampleRate.store(srate); } @@ -292,7 +291,13 @@ AppConfig::AppConfig() : configName("") { centerFreq.store(100000000); waterfallLinesPerSec.store(DEFAULT_WATERFALL_LPS); spectrumAvgSpeed.store(0.65f); + dbOffset.store(0); modemPropsCollapsed.store(false); + mainSplit = -1; + visSplit = -1; + bookmarkSplit = 200; + bookmarksVisible.store(true); + #ifdef USE_HAMLIB rigEnabled.store(false); rigModel.store(1); @@ -428,6 +433,14 @@ float AppConfig::getSpectrumAvgSpeed() { return spectrumAvgSpeed.load(); } +void AppConfig::setDBOffset(int offset) { + this->dbOffset.store(offset); +} + +int AppConfig::getDBOffset() { + return dbOffset.load(); +} + void AppConfig::setManualDevices(std::vector manuals) { manualDevices = manuals; } @@ -436,6 +449,39 @@ std::vector AppConfig::getManualDevices() { return manualDevices; } +void AppConfig::setMainSplit(float value) { + mainSplit.store(value); +} + +float AppConfig::getMainSplit() { + return mainSplit.load(); +} + +void AppConfig::setVisSplit(float value) { + visSplit.store(value); +} + +float AppConfig::getVisSplit() { + return visSplit.load(); +} + +void AppConfig::setBookmarkSplit(float value) { + bookmarkSplit.store(value); +} + +float AppConfig::getBookmarkSplit() { + return bookmarkSplit.load(); +} + +void AppConfig::setBookmarksVisible(bool state) { + bookmarksVisible.store(state); +} + +bool AppConfig::getBookmarksVisible() { + return bookmarksVisible.load(); +} + + void AppConfig::setConfigName(std::string configName) { this->configName = configName; } @@ -481,6 +527,12 @@ bool AppConfig::save() { *window_node->newChild("waterfall_lps") = waterfallLinesPerSec.load(); *window_node->newChild("spectrum_avg") = spectrumAvgSpeed.load(); *window_node->newChild("modemprops_collapsed") = modemPropsCollapsed.load();; + *window_node->newChild("db_offset") = dbOffset.load(); + + *window_node->newChild("main_split") = mainSplit.load(); + *window_node->newChild("vis_split") = visSplit.load(); + *window_node->newChild("bookmark_split") = bookmarkSplit.load(); + *window_node->newChild("bookmark_visible") = bookmarksVisible.load(); } DataNode *devices_node = cfg.rootNode()->newChild("devices"); @@ -631,6 +683,37 @@ bool AppConfig::load() { win_node->getNext("modemprops_collapsed")->element()->get(mpc); modemPropsCollapsed.store(mpc?true:false); } + + if (win_node->hasAnother("db_offset")) { + DataNode *offset_node = win_node->getNext("db_offset"); + int offsetValue = 0; + offset_node->element()->get(offsetValue); + setDBOffset(offsetValue); + } + + if (win_node->hasAnother("main_split")) { + float gVal; + win_node->getNext("main_split")->element()->get(gVal); + mainSplit.store(gVal); + } + + if (win_node->hasAnother("vis_split")) { + float gVal; + win_node->getNext("vis_split")->element()->get(gVal); + visSplit.store(gVal); + } + + if (win_node->hasAnother("bookmark_split")) { + float gVal; + win_node->getNext("bookmark_split")->element()->get(gVal); + bookmarkSplit.store(gVal); + } + + if (win_node->hasAnother("bookmark_visible")) { + int bVal; + win_node->getNext("bookmark_visible")->element()->get(bVal); + bookmarksVisible.store(bVal); + } } if (cfg.rootNode()->hasAnother("devices")) { diff --git a/src/AppConfig.h b/src/AppConfig.h index dfb0766..ca5c77d 100644 --- a/src/AppConfig.h +++ b/src/AppConfig.h @@ -115,9 +115,25 @@ public: void setSpectrumAvgSpeed(float avgSpeed); float getSpectrumAvgSpeed(); + void setDBOffset(int offset); + int getDBOffset(); + void setManualDevices(std::vector manuals); std::vector getManualDevices(); + void setMainSplit(float value); + float getMainSplit(); + + void setVisSplit(float value); + float getVisSplit(); + + void setBookmarkSplit(float value); + float getBookmarkSplit(); + + void setBookmarksVisible(bool state); + bool getBookmarksVisible(); + + #if USE_HAMLIB int getRigModel(); void setRigModel(int rigModel); @@ -160,11 +176,12 @@ private: std::atomic_llong snap; std::atomic_llong centerFreq; std::atomic_int waterfallLinesPerSec; - std::atomic spectrumAvgSpeed; + std::atomic spectrumAvgSpeed, mainSplit, visSplit, bookmarkSplit; + std::atomic_int dbOffset; std::vector manualDevices; #if USE_HAMLIB std::atomic_int rigModel, rigRate; std::string rigPort; - std::atomic_bool rigEnabled, rigFollowMode, rigControlMode, rigCenterLock, rigFollowModem; + std::atomic_bool rigEnabled, rigFollowMode, rigControlMode, rigCenterLock, rigFollowModem, bookmarksVisible; #endif }; diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index aca8bfb..9563e33 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -23,6 +23,7 @@ #include "DataTree.h" #include "ColorTheme.h" #include "DemodulatorMgr.h" +#include "ImagePanel.h" #include @@ -46,6 +47,10 @@ wxEND_EVENT_TABLE() #include "RigThread.h" #endif + +/* split a string by 'seperator' into a vector of string */ +std::vector str_explode(const std::string &seperator, const std::string &in_str); + #define APPFRAME_MODEMPROPS_MINSIZE 20 #define APPFRAME_MODEMPROPS_MAXSIZE 240 @@ -57,7 +62,6 @@ AppFrame::AppFrame() : #endif wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); - wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL); demodTray = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodTunerTray = new wxBoxSizer(wxHORIZONTAL); @@ -68,10 +72,30 @@ AppFrame::AppFrame() : //attribList.PlatformDefaults().MinRGBA(8, 8, 8, 8).DoubleBuffer().Depth(16).EndList(); mainSplitter = new wxSplitterWindow( this, wxID_MAIN_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE ); - mainSplitter->SetSashGravity(10.0/37.0); + mainSplitter->SetSashGravity(10.0f / 37.0f); mainSplitter->SetMinimumPaneSize(1); + wxPanel *demodPanel = new wxPanel(mainSplitter, wxID_ANY); + +#ifdef CUBICSDR_HEADER_IMAGE + wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath()); + std::string headerPath = exePath.GetPath().ToStdString(); + headerPath += filePathSeparator + std::string("" CUBICSDR_HEADER_IMAGE); + wxInitAllImageHandlers(); + + ImagePanel *imgPanel = new ImagePanel(demodPanel, headerPath, wxBITMAP_TYPE_ANY); + + std::string headerBgColor = "" CUBICSDR_HEADER_BG; + if (headerBgColor != "") { + imgPanel->SetBackgroundColour(wxColour(headerBgColor)); + } + + imgPanel->SetBestFittingSize(wxSize(200, 0)); + + demodTray->Add(imgPanel, 0, wxEXPAND | wxALL, 0); + demodTray->AddSpacer(1); +#endif gainCanvas = new GainCanvas(demodPanel, attribList); @@ -80,16 +104,24 @@ AppFrame::AppFrame() : gainSpacerItem = demodTray->AddSpacer(1); gainSpacerItem->Show(false); + std::string modemListArr[] = { "FM", "FMS", "NBFM", "AM", "LSB", "USB", "DSB", "I/Q" }; + std::vector modemList( modemListArr, modemListArr + 8 ); + +#ifdef CUBICSDR_MODEM_EXCLUDE + std::string excludeListStr = "" CUBICSDR_MODEM_EXCLUDE; + std::vector excludeList = str_explode(",",excludeListStr); + for (auto ex_i : excludeList) { + std::vector::iterator found_i = std::find(modemList.begin(),modemList.end(),ex_i); + if (found_i != modemList.end()) { + modemList.erase(found_i); + } + } +#endif + demodModeSelector = new ModeSelectorCanvas(demodPanel, attribList); - demodModeSelector->addChoice("FM"); - demodModeSelector->addChoice("FMS"); - demodModeSelector->addChoice("NBFM"); - demodModeSelector->addChoice("AM"); - demodModeSelector->addChoice("LSB"); - demodModeSelector->addChoice("USB"); - demodModeSelector->addChoice("DSB"); - demodModeSelector->addChoice("I/Q"); - demodModeSelector->setSelection("FM"); + for (auto mt_i : modemList) { + demodModeSelector->addChoice(mt_i); + } demodModeSelector->setHelpTip("Choose modulation type: Frequency Modulation (Hotkey F), Amplitude Modulation (A) and Lower (L), Upper (U), Double Side-Band and more."); demodModeSelector->SetMinSize(wxSize(50,-1)); demodModeSelector->SetMaxSize(wxSize(50,-1)); @@ -120,13 +152,19 @@ AppFrame::AppFrame() : modemProps->SetMinSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE,-1)); modemProps->SetMaxSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE,-1)); - modemProps->Hide(); + ModemArgInfoList dummyInfo; + modemProps->initProperties(dummyInfo, nullptr); + modemProps->updateTheme(); + demodTray->Add(modemProps, 15, wxEXPAND | wxALL, 0); #ifndef __APPLE__ demodTray->AddSpacer(1); #endif - + +#if CUBICSDR_ENABLE_VIEW_DEMOD + wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL); + wxGetApp().getDemodSpectrumProcessor()->setup(1024); demodSpectrumCanvas = new SpectrumCanvas(demodPanel, attribList); demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000); @@ -150,7 +188,11 @@ AppFrame::AppFrame() : demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0); demodTray->AddSpacer(1); - +#else + demodSpectrumCanvas = nullptr; + demodWaterfallCanvas = nullptr; +#endif + demodSignalMeter = new MeterCanvas(demodPanel, attribList); demodSignalMeter->setMax(DEMOD_SIGNAL_MAX); demodSignalMeter->setMin(DEMOD_SIGNAL_MIN); @@ -163,6 +205,7 @@ AppFrame::AppFrame() : demodTray->AddSpacer(1); +#if CUBICSDR_ENABLE_VIEW_SCOPE scopeCanvas = new ScopeCanvas(demodPanel, attribList); scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum."); scopeCanvas->SetMinSize(wxSize(128,-1)); @@ -171,7 +214,10 @@ AppFrame::AppFrame() : wxGetApp().getScopeProcessor()->attachOutput(scopeCanvas->getInputQueue()); demodScopeTray->AddSpacer(1); - +#else + scopeCanvas = nullptr; +#endif + deltaLockButton = new ModeSelectorCanvas(demodPanel, attribList); deltaLockButton->addChoice(1, "V"); deltaLockButton->setPadding(-1,-1); @@ -235,10 +281,13 @@ AppFrame::AppFrame() : // vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0); // vbox->AddSpacer(1); - - mainVisSplitter = new wxSplitterWindow( mainSplitter, wxID_VIS_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE ); - mainVisSplitter->SetSashGravity(6.0/25.0); + bookmarkSplitter = new wxSplitterWindow( mainSplitter, wxID_BM_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE ); + bookmarkSplitter->SetMinimumPaneSize(1); + bookmarkSplitter->SetSashGravity(1.0f / 20.0f); + + mainVisSplitter = new wxSplitterWindow( bookmarkSplitter, wxID_VIS_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE ); mainVisSplitter->SetMinimumPaneSize(1); + mainVisSplitter->SetSashGravity(6.0f / 25.0f); // mainVisSplitter->Connect( wxEVT_IDLE, wxIdleEventHandler( AppFrame::mainVisSplitterIdle ), NULL, this ); @@ -248,6 +297,7 @@ AppFrame::AppFrame() : wxGetApp().getSpectrumProcessor()->setup(2048); spectrumCanvas = new SpectrumCanvas(spectrumPanel, attribList); spectrumCanvas->setShowDb(true); + spectrumCanvas->setUseDBOfs(true); spectrumCanvas->setScaleFactorEnabled(true); wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue()); @@ -312,10 +362,19 @@ AppFrame::AppFrame() : // vbox->Add(wfSizer, 20, wxEXPAND | wxALL, 0); mainVisSplitter->SplitHorizontally( spectrumPanel, waterfallPanel, 0 ); - mainSplitter->SplitHorizontally( demodPanel, mainVisSplitter ); + + bookmarkView = new BookmarkView(bookmarkSplitter, wxID_ANY, wxDefaultPosition, wxSize(120,-1)); + + bookmarkSplitter->SplitVertically( bookmarkView, mainVisSplitter ); + mainSplitter->SplitHorizontally( demodPanel, bookmarkSplitter ); + + if (!wxGetApp().getConfig()->getBookmarksVisible()) { + bookmarkSplitter->Unsplit(bookmarkView); + bookmarkSplitter->Layout(); + } vbox->Add(mainSplitter, 1, wxEXPAND | wxALL, 0); - + // TODO: refactor these.. waterfallCanvas->attachSpectrumCanvas(spectrumCanvas); spectrumCanvas->attachWaterfallCanvas(waterfallCanvas); @@ -378,6 +437,8 @@ AppFrame::AppFrame() : } i++; } + + wxGetApp().getDemodMgr().setOutputDevices(outputDevices); // // for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { // wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE + mdevices_i->first, mdevices_i->second.name, wxT("Description?")); @@ -470,6 +531,9 @@ AppFrame::AppFrame() : themeMenu->AppendRadioItem(wxID_THEME_HD, "HD")->Check(themeId==COLOR_THEME_HD); displayMenu->AppendSubMenu(themeMenu, wxT("&Color Scheme")); + + hideBookmarksItem = displayMenu->AppendCheckItem(wxID_DISPLAY_BOOKMARKS, wxT("Hide Bookmarks")); + hideBookmarksItem->Check(!wxGetApp().getConfig()->getBookmarksVisible()); GLFont::setScale((GLFont::GLFontScale)fontScale); @@ -588,13 +652,27 @@ AppFrame::AppFrame() : waterfallCanvas->setLinesPerSecond(wflps); ThemeMgr::mgr.setTheme(wxGetApp().getConfig()->getTheme()); + bookmarkView->updateTheme(); int mpc =wxGetApp().getConfig()->getModemPropsCollapsed(); if (mpc) { modemProps->setCollapsed(true); } - + + int msPos = wxGetApp().getConfig()->getMainSplit(); + if (msPos != -1) { + mainSplitter->SetSashPosition(msPos); + } + int bsPos = wxGetApp().getConfig()->getBookmarkSplit(); + if (bsPos != -1) { + bookmarkSplitter->SetSashPosition(bsPos); + } + int vsPos = wxGetApp().getConfig()->getVisSplit(); + if (vsPos != -1) { + mainVisSplitter->SetSashPosition(vsPos); + } + Show(); #ifdef _WIN32 @@ -611,6 +689,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"); @@ -652,6 +731,7 @@ void AppFrame::updateDeviceParams() { newSettingsMenu->AppendSeparator(); + newSettingsMenu->Append(wxID_SET_DB_OFFSET, "Power Level Offset"); newSettingsMenu->Append(wxID_SET_FREQ_OFFSET, "Frequency Offset"); if (devInfo->hasCORR(SOAPY_SDR_RX, 0)) { @@ -867,6 +947,12 @@ void AppFrame::OnMenu(wxCommandEvent& event) { if (ofs != -1) { wxGetApp().setOffset(ofs); } + } else if (event.GetId() == wxID_SET_DB_OFFSET) { + long ofs = wxGetNumberFromUser("Shift the displayed RF power level by this amount.\ni.e. -30 for -30 dB", "Decibels (dB)", + "Power Level Offset", wxGetApp().getConfig()->getDBOffset(), -1000, 1000, this); + if (ofs != -1) { + wxGetApp().getConfig()->setDBOffset(ofs); + } } else if (event.GetId() == wxID_AGC_CONTROL) { if (wxGetApp().getDevice() == NULL) { agcMenuItem->Check(true); @@ -937,6 +1023,9 @@ void AppFrame::OnMenu(wxCommandEvent& event) { demodTuner->Refresh(); SetTitle(CUBICSDR_TITLE); currentSessionFile = ""; + bookmarkSplitter->Unsplit(bookmarkView); + bookmarkSplitter->SplitVertically( bookmarkView, mainVisSplitter, wxGetApp().getConfig()->getBookmarkSplit() ); + hideBookmarksItem->Check(false); } else if (event.GetId() == wxID_CLOSE || event.GetId() == wxID_EXIT) { Close(false); } else if (event.GetId() == wxID_THEME_DEFAULT) { @@ -969,6 +1058,14 @@ void AppFrame::OnMenu(wxCommandEvent& event) { GLFont::setScale(GLFont::GLFONT_SCALE_LARGE); //force all windows refresh Refresh(); + } else if (event.GetId() == wxID_DISPLAY_BOOKMARKS) { + if (hideBookmarksItem->IsChecked()) { + bookmarkSplitter->Unsplit(bookmarkView); + bookmarkSplitter->Layout(); + } else { + bookmarkSplitter->SplitVertically( bookmarkView, mainVisSplitter, wxGetApp().getConfig()->getBookmarkSplit() ); + bookmarkSplitter->Layout(); + } } if (event.GetId() >= wxID_SETTINGS_BASE && event.GetId() < settingsIdMax) { @@ -1029,6 +1126,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) { spectrumAvgMeter->Refresh(); gainCanvas->setThemeColors(); modemProps->updateTheme(); + bookmarkView->updateTheme(); } switch (event.GetId()) { @@ -1212,10 +1310,17 @@ void AppFrame::OnMenu(wxCommandEvent& event) { void AppFrame::OnClose(wxCloseEvent& event) { wxGetApp().closeDeviceSelector(); - wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodSpectrumCanvas->getVisualDataQueue()); - wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodWaterfallCanvas->getVisualDataQueue()); + if (wxGetApp().getDemodSpectrumProcessor()) { + wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodSpectrumCanvas->getVisualDataQueue()); + wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodWaterfallCanvas->getVisualDataQueue()); + } 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()); @@ -1226,6 +1331,10 @@ void AppFrame::OnClose(wxCloseEvent& event) { wxGetApp().getConfig()->setWaterfallLinesPerSec(waterfallDataThread->getLinesPerSecond()); wxGetApp().getConfig()->setManualDevices(SDREnumerator::getManuals()); wxGetApp().getConfig()->setModemPropsCollapsed(modemProps->isCollapsed()); + wxGetApp().getConfig()->setMainSplit(mainSplitter->GetSashPosition()); + wxGetApp().getConfig()->setVisSplit(mainVisSplitter->GetSashPosition()); + if (!hideBookmarksItem->IsChecked()) wxGetApp().getConfig()->setBookmarkSplit(bookmarkSplitter->GetSashPosition()); + wxGetApp().getConfig()->setBookmarksVisible(!hideBookmarksItem->IsChecked()); #ifdef USE_HAMLIB wxGetApp().getConfig()->setRigEnabled(rigEnableMenuItem->IsChecked()); wxGetApp().getConfig()->setRigModel(rigModel); @@ -1237,6 +1346,7 @@ void AppFrame::OnClose(wxCloseEvent& event) { wxGetApp().getConfig()->setRigFollowModem(rigFollowModemMenuItem->IsChecked()); #endif wxGetApp().getConfig()->save(); + wxGetApp().getBookmarkMgr().saveToFile("bookmarks.xml"); event.Skip(); } @@ -1244,6 +1354,7 @@ void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) { new AppFrame(); } + void AppFrame::OnThread(wxCommandEvent& event) { event.Skip(); } @@ -1284,7 +1395,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { demodGainMeter->setInputValue(demod->getGain()); wxGetApp().getDemodMgr().setLastGain(demod->getGain()); int outputDevice = demod->getOutputDevice(); - scopeCanvas->setDeviceName(outputDevices[outputDevice].name); + if (scopeCanvas) scopeCanvas->setDeviceName(outputDevices[outputDevice].name); // outputDeviceMenuItems[outputDevice]->Check(true); std::string dType = demod->getDemodulatorType(); demodModeSelector->setSelection(dType); @@ -1296,7 +1407,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { modemPropertiesUpdated.store(true); demodTuner->setHalfBand(dType=="USB" || dType=="LSB"); } - if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { + if (!demodWaterfallCanvas || demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { long long centerFreq = demod->getFrequency(); unsigned int demodBw = (unsigned int) ceil((float) demod->getBandwidth() * 2.25); @@ -1317,7 +1428,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { demodBw = 20000; } - if (centerFreq != demodWaterfallCanvas->getCenterFrequency()) { + if (demodWaterfallCanvas && centerFreq != demodWaterfallCanvas->getCenterFrequency()) { demodWaterfallCanvas->setCenterFrequency(centerFreq); demodSpectrumCanvas->setCenterFrequency(centerFreq); } @@ -1403,8 +1514,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) { } } - demodWaterfallCanvas->setBandwidth(demodBw); - demodSpectrumCanvas->setBandwidth(demodBw); + if (demodWaterfallCanvas) { + demodWaterfallCanvas->setBandwidth(demodBw); + demodSpectrumCanvas->setBandwidth(demodBw); + } } demodSignalMeter->setLevel(demod->getSignalLevel()); @@ -1460,9 +1573,9 @@ void AppFrame::OnIdle(wxIdleEvent& event) { demodGainMeter->setLevel(demodGainMeter->getInputValue()); } - if (wxGetApp().getFrequency() != demodWaterfallCanvas->getCenterFrequency()) { + if (demodWaterfallCanvas && wxGetApp().getFrequency() != demodWaterfallCanvas->getCenterFrequency()) { demodWaterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); - demodSpectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); + if (demodSpectrumCanvas) demodSpectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); } if (spectrumCanvas->getViewState() && abs(wxGetApp().getFrequency()-spectrumCanvas->getCenterFrequency()) > (wxGetApp().getSampleRate()/2)) { spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); @@ -1479,15 +1592,17 @@ void AppFrame::OnIdle(wxIdleEvent& event) { } } - scopeCanvas->setPPMMode(demodTuner->isAltDown()); + if (scopeCanvas) { + scopeCanvas->setPPMMode(demodTuner->isAltDown()); + + scopeCanvas->setShowDb(spectrumCanvas->getShowDb()); + wxGetApp().getScopeProcessor()->setScopeEnabled(scopeCanvas->scopeVisible()); + wxGetApp().getScopeProcessor()->setSpectrumEnabled(scopeCanvas->spectrumVisible()); + wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0)); + + wxGetApp().getScopeProcessor()->run(); + } - scopeCanvas->setShowDb(spectrumCanvas->getShowDb()); - wxGetApp().getScopeProcessor()->setScopeEnabled(scopeCanvas->scopeVisible()); - wxGetApp().getScopeProcessor()->setSpectrumEnabled(scopeCanvas->spectrumVisible()); - wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0)); - - wxGetApp().getScopeProcessor()->run(); - SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor(); if (spectrumAvgMeter->inputChanged()) { @@ -1505,9 +1620,11 @@ void AppFrame::OnIdle(wxIdleEvent& event) { } SpectrumVisualProcessor *dproc = wxGetApp().getDemodSpectrumProcessor(); - - dproc->setView(demodWaterfallCanvas->getViewState(), demodWaterfallCanvas->getCenterFrequency(),demodWaterfallCanvas->getBandwidth()); + if (dproc) { + dproc->setView(demodWaterfallCanvas->getViewState(), demodWaterfallCanvas->getCenterFrequency(),demodWaterfallCanvas->getBandwidth()); + } + SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor(); if (waterfallSpeedMeter->inputChanged()) { @@ -1539,13 +1656,15 @@ void AppFrame::OnIdle(wxIdleEvent& event) { ModemDigitalOutputConsole *outp = (ModemDigitalOutputConsole *)demod->getOutput(); if (!outp->getDialog()) { outp->setTitle(demod->getDemodulatorType() + ": " + frequencyToStr(demod->getFrequency())); - outp->setDialog(new DigitalConsole(this, outp)); + outp->setDialog(new DigitalConsole(this, outp)) ; } demod->showOutput(); } #endif - } else if (!demod) { - modemProps->Hide(); + } else if (!demod && modemPropertiesUpdated.load()) { + ModemArgInfoList dummyInfo; + modemProps->initProperties(dummyInfo, nullptr); + modemProps->updateTheme(); demodTray->Layout(); } @@ -1566,7 +1685,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { wxGetApp().getSpectrumProcessor()->setPeakHold(peakHoldMode == 1); //make the peak hold act on the current dmod also, like a zoomed-in version. - wxGetApp().getDemodSpectrumProcessor()->setPeakHold(peakHoldMode == 1); + if (wxGetApp().getDemodSpectrumProcessor()) wxGetApp().getDemodSpectrumProcessor()->setPeakHold(peakHoldMode == 1); peakHoldButton->clearModeChanged(); } @@ -1580,7 +1699,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { #endif #ifdef _WIN32 - if (scopeCanvas->HasFocus()) { + if (scopeCanvas && scopeCanvas->HasFocus()) { waterfallCanvas->SetFocus(); } #endif @@ -1652,35 +1771,10 @@ void AppFrame::saveSession(std::string fileName) { DataNode *demods = s.rootNode()->newChild("demodulators"); std::vector &instances = wxGetApp().getDemodMgr().getDemodulators(); - std::vector::iterator instance_i; - for (instance_i = instances.begin(); instance_i != instances.end(); instance_i++) { + + for (auto instance_i : instances) { DataNode *demod = demods->newChild("demodulator"); - *demod->newChild("bandwidth") = (*instance_i)->getBandwidth(); - *demod->newChild("frequency") = (*instance_i)->getFrequency(); - *demod->newChild("type") = (*instance_i)->getDemodulatorType(); - - demod->newChild("user_label")->element()->set((*instance_i)->getDemodulatorUserLabel()); - - *demod->newChild("squelch_level") = (*instance_i)->getSquelchLevel(); - *demod->newChild("squelch_enabled") = (*instance_i)->isSquelchEnabled() ? 1 : 0; - *demod->newChild("output_device") = outputDevices[(*instance_i)->getOutputDevice()].name; - *demod->newChild("gain") = (*instance_i)->getGain(); - *demod->newChild("muted") = (*instance_i)->isMuted() ? 1 : 0; - if ((*instance_i)->isDeltaLock()) { - *demod->newChild("delta_lock") = (*instance_i)->isDeltaLock() ? 1 : 0; - *demod->newChild("delta_ofs") = (*instance_i)->getDeltaLockOfs(); - } - if ((*instance_i) == wxGetApp().getDemodMgr().getLastActiveDemodulator()) { - *demod->newChild("active") = 1; - } - - ModemSettings saveSettings = (*instance_i)->readModemSettings(); - if (saveSettings.size()) { - DataNode *settingsNode = demod->newChild("settings"); - for (ModemSettings::const_iterator msi = saveSettings.begin(); msi != saveSettings.end(); msi++) { - *settingsNode->newChild(msi->first.c_str()) = msi->second; - } - } + wxGetApp().getDemodMgr().saveInstance(demod, instance_i); } //end for demodulators // Make sure the file name actually ends in .xml @@ -1754,7 +1848,6 @@ bool AppFrame::loadSession(std::string fileName) { DataNode *demodulators = l.rootNode()->getNext("demodulators"); - int numDemodulators = 0; std::vector demodsLoaded; while (demodulators->hasAnother("demodulator")) { @@ -1764,122 +1857,15 @@ bool AppFrame::loadSession(std::string fileName) { continue; } - long bandwidth = *demod->getNext("bandwidth"); - long long freq = *demod->getNext("frequency"); - float squelch_level = demod->hasAnother("squelch_level") ? (float) *demod->getNext("squelch_level") : 0; - int squelch_enabled = demod->hasAnother("squelch_enabled") ? (int) *demod->getNext("squelch_enabled") : 0; - int muted = demod->hasAnother("muted") ? (int) *demod->getNext("muted") : 0; - int delta_locked = demod->hasAnother("delta_lock") ? (int) *demod->getNext("delta_lock") : 0; - int delta_ofs = demod->hasAnother("delta_ofs") ? (int) *demod->getNext("delta_ofs") : 0; - std::string output_device = demod->hasAnother("output_device") ? string(*(demod->getNext("output_device"))) : ""; - float gain = demod->hasAnother("gain") ? (float) *demod->getNext("gain") : 1.0; + newDemod = wxGetApp().getDemodMgr().loadInstance(demod); - std::string type = "FM"; - - - DataNode *demodTypeNode = demod->hasAnother("type")?demod->getNext("type"):nullptr; - - if (demodTypeNode && demodTypeNode->element()->getDataType() == DATA_INT) { - int legacyType = *demodTypeNode; - int legacyStereo = demod->hasAnother("stereo") ? (int) *demod->getNext("stereo") : 0; - switch (legacyType) { // legacy demod ID - case 1: type = legacyStereo?"FMS":"FM"; break; - case 2: type = "AM"; break; - case 3: type = "LSB"; break; - case 4: type = "USB"; break; - case 5: type = "DSB"; break; - case 6: type = "ASK"; break; - case 7: type = "APSK"; break; - case 8: type = "BPSK"; break; - case 9: type = "DPSK"; break; - case 10: type = "PSK"; break; - case 11: type = "OOK"; break; - case 12: type = "ST"; break; - case 13: type = "SQAM"; break; - case 14: type = "QAM"; break; - case 15: type = "QPSK"; break; - case 16: type = "I/Q"; break; - default: type = "FM"; break; - } - } else if (demodTypeNode && demodTypeNode->element()->getDataType() == DATA_STRING) { - demodTypeNode->element()->get(type); - } - - //read the user label associated with the demodulator - std::wstring user_label = L""; - - DataNode *demodUserLabel = demod->hasAnother("user_label") ? demod->getNext("user_label") : nullptr; - - if (demodUserLabel) { - - demodUserLabel->element()->get(user_label); - } - - - ModemSettings mSettings; - - if (demod->hasAnother("settings")) { - DataNode *modemSettings = demod->getNext("settings"); - for (int msi = 0, numSettings = modemSettings->numChildren(); msi < numSettings; msi++) { - DataNode *settingNode = modemSettings->child(msi); - std::string keyName = settingNode->getName(); - std::string strSettingValue = settingNode->element()->toString(); - - if (keyName != "" && strSettingValue != "") { - mSettings[keyName] = strSettingValue; - } - } - } - - - - newDemod = wxGetApp().getDemodMgr().newThread(); - if (demod->hasAnother("active")) { loadedActiveDemod = newDemod; } - numDemodulators++; - newDemod->setDemodulatorType(type); - newDemod->setDemodulatorUserLabel(user_label); - newDemod->writeModemSettings(mSettings); - newDemod->setBandwidth(bandwidth); - newDemod->setFrequency(freq); - newDemod->setGain(gain); - newDemod->updateLabel(freq); - newDemod->setMuted(muted?true:false); - if (delta_locked) { - newDemod->setDeltaLock(true); - newDemod->setDeltaLockOfs(delta_ofs); - } - if (squelch_enabled) { - newDemod->setSquelchEnabled(true); - newDemod->setSquelchLevel(squelch_level); - } - - bool found_device = false; - std::map::iterator i; - for (i = outputDevices.begin(); i != outputDevices.end(); i++) { - if (i->second.name == output_device) { - newDemod->setOutputDevice(i->first); - found_device = true; - } - } - -// if (!found_device) { -// std::cout << "\tWarning: named output device '" << output_device << "' was not found. Using default output."; -// } - newDemod->run(); newDemod->setActive(true); demodsLoaded.push_back(newDemod); -// wxGetApp().bindDemodulator(newDemod); - - std::cout << "\tAdded demodulator at frequency " << newDemod->getFrequency() << " type " << type << std::endl; -// std::cout << "\t\tBandwidth: " << bandwidth << std::endl; -// std::cout << "\t\tSquelch Level: " << squelch_level << std::endl; -// std::cout << "\t\tSquelch Enabled: " << (squelch_enabled ? "true" : "false") << std::endl; -// std::cout << "\t\tOutput Device: " << output_device << std::endl; } if (demodsLoaded.size()) { @@ -1925,6 +1911,8 @@ bool AppFrame::loadSession(std::string fileName) { GetStatusBar()->SetStatusText(wxString::Format(wxT("Loaded session file: %s"), currentSessionFile.c_str())); SetTitle(wxString::Format(wxT("%s: %s"), CUBICSDR_TITLE, filePart.c_str())); + wxGetApp().getBookmarkMgr().updateActiveList(); + return true; } @@ -1945,7 +1933,7 @@ void AppFrame::setMainWaterfallFFTSize(int fftSize) { } void AppFrame::setScopeDeviceName(std::string deviceName) { - scopeCanvas->setDeviceName(deviceName); + if (scopeCanvas) scopeCanvas->setDeviceName(deviceName); } @@ -1957,12 +1945,20 @@ void AppFrame::refreshGainUI() { bool AppFrame::isUserDemodBusy() { return (modemProps && modemProps->isMouseInView()) || (waterfallCanvas->isMouseInView() && waterfallCanvas->isMouseDown()) - || (demodWaterfallCanvas->isMouseInView() && demodWaterfallCanvas->isMouseDown()) + || (demodWaterfallCanvas && demodWaterfallCanvas->isMouseInView() && demodWaterfallCanvas->isMouseDown()) || (wxGetApp().getDemodMgr().getLastActiveDemodulator() && wxGetApp().getDemodMgr().getActiveDemodulator() && wxGetApp().getDemodMgr().getLastActiveDemodulator() != wxGetApp().getDemodMgr().getActiveDemodulator()); } +BookmarkView *AppFrame::getBookmarkView() { + return bookmarkView; +} + +void AppFrame::disableSave(bool state) { + saveDisabled = state; +} + #ifdef _WIN32 bool AppFrame::canFocus() { @@ -2022,6 +2018,10 @@ int AppFrame::OnGlobalKeyDown(wxKeyEvent &event) { return -1; } + if (bookmarkView && bookmarkView->isMouseInView()) { + return -1; + } + DemodulatorInstance *demod = nullptr, *lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); int snap = wxGetApp().getFrequencySnap(); @@ -2123,6 +2123,10 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) { return -1; } + if (bookmarkView && bookmarkView->isMouseInView()) { + return -1; + } + if (event.ControlDown()) { return 1; } @@ -2195,7 +2199,7 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) { break; case 'P': wxGetApp().getSpectrumProcessor()->setPeakHold(!wxGetApp().getSpectrumProcessor()->getPeakHold()); - wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold()); + if (wxGetApp().getDemodSpectrumProcessor()) wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold()); peakHoldButton->setSelection(wxGetApp().getSpectrumProcessor()->getPeakHold()?1:0); peakHoldButton->clearModeChanged(); break; @@ -2246,3 +2250,42 @@ void AppFrame::setViewState(long long center_freq) { spectrumCanvas->disableView(); waterfallCanvas->disableView(); } + + +long long AppFrame::getViewCenterFreq() { + return waterfallCanvas->getCenterFrequency(); + +} + + +int AppFrame::getViewBandwidth() { + return waterfallCanvas->getBandwidth(); +} + + +/* split a string by 'seperator' into a vector of string */ +std::vector str_explode(const std::string &seperator, const std::string &in_str) +{ + std::vector vect_out; + + int i = 0, j = 0; + int seperator_len = seperator.length(); + int str_len = in_str.length(); + + while(i < str_len) + { + j = in_str.find_first_of(seperator,i); + + if (j == std::string::npos && i < str_len) j = str_len; + + if (j == std::string::npos) break; + + vect_out.push_back(in_str.substr(i,j-i)); + + i = j; + + i+=seperator_len; + } + + return vect_out; +} diff --git a/src/AppFrame.h b/src/AppFrame.h index 834e33e..f186e9e 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "PrimaryGLContext.h" @@ -22,6 +24,7 @@ #include "ModemProperties.h" //#include "UITestCanvas.h" #include "FrequencyDialog.h" +#include "BookmarkView.h" #include @@ -35,9 +38,11 @@ #define wxID_AGC_CONTROL 2009 #define wxID_SDR_START_STOP 2010 #define wxID_LOW_PERF 2011 +#define wxID_SET_DB_OFFSET 2012 #define wxID_MAIN_SPLITTER 2050 #define wxID_VIS_SPLITTER 2051 +#define wxID_BM_SPLITTER 2052 #define wxID_THEME_DEFAULT 2100 #define wxID_THEME_SHARP 2101 @@ -47,6 +52,8 @@ #define wxID_THEME_HD 2105 #define wxID_THEME_RADAR 2106 +#define wxID_DISPLAY_BOOKMARKS 2107 + #define wxID_BANDWIDTH_BASE 2150 #define wxID_BANDWIDTH_MANUAL 2200 @@ -76,6 +83,7 @@ class AppFrame: public wxFrame { public: AppFrame(); ~AppFrame(); + void OnThread(wxCommandEvent& event); void OnEventInput(wxThreadEvent& event); void initDeviceParams(SDRDeviceInfo *devInfo); @@ -103,9 +111,14 @@ public: void refreshGainUI(); void setViewState(long long center_freq, int bandwidth); void setViewState(long long center_freq); - + + long long getViewCenterFreq(); + int getViewBandwidth(); bool isUserDemodBusy(); - + + BookmarkView *getBookmarkView(); + void disableSave(bool state); + #ifdef _WIN32 bool canFocus(); #endif @@ -136,8 +149,9 @@ private: ModeSelectorCanvas *demodMuteButton, *peakHoldButton, *soloModeButton, *deltaLockButton; GainCanvas *gainCanvas; wxSizerItem *gainSizerItem, *gainSpacerItem; - wxSplitterWindow *mainVisSplitter, *mainSplitter; + wxSplitterWindow *mainVisSplitter, *mainSplitter, *bookmarkSplitter; wxBoxSizer *demodTray; + BookmarkView *bookmarkView; DemodulatorInstance *activeDemodulator; @@ -174,7 +188,7 @@ private: wxMenuItem *showTipMenuItem; bool lowPerfMode; - + #ifdef USE_HAMLIB void enableRig(); void disableRig(); @@ -187,6 +201,7 @@ private: wxMenuItem *rigCenterLockMenuItem; wxMenuItem *rigFollowModemMenuItem; wxMenuItem *sdrIFMenuItem; + wxMenuItem *hideBookmarksItem; std::map rigSerialMenuItems; std::map rigModelMenuItems; int rigModel; @@ -196,6 +211,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 new file mode 100644 index 0000000..22e34c7 --- /dev/null +++ b/src/BookmarkMgr.cpp @@ -0,0 +1,521 @@ +#include "BookmarkMgr.h" +#include "CubicSDR.h" +#include "DataTree.h" + +#define BOOKMARK_RECENTS_MAX 25 + +BookmarkMgr::BookmarkMgr() { + rangesSorted = false; +} + +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) { + DataNode *range = view_ranges->newChild("range"); + *range->newChild("label") = re_i->label; + *range->newChild("freq") = re_i->freq; + *range->newChild("start") = re_i->startFreq; + *range->newChild("end") = re_i->endFreq; + } + + DataNode *modems = s.rootNode()->newChild("modems"); + + for (auto &bmd_i : bmData) { + DataNode *group = modems->newChild("group"); + *group->newChild("@name") = bmd_i.first; + *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); + } + } + + DataNode *recent_modems = s.rootNode()->newChild("recent_modems"); + + for (auto demod : wxGetApp().getDemodMgr().getDemodulators()) { + wxGetApp().getDemodMgr().saveInstance(recent_modems->newChild("modem"),demod); + } + + for (auto &r_i : this->recents) { + recent_modems->newChildCloneFrom("modem", r_i->node); + } + + 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 (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()); + } +} + + +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; + + // 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())) { + 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")) { + DataNode *range = view_ranges->getNext("range"); + + BookmarkRangeEntry *re = new BookmarkRangeEntry; + + 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); + } + } + + if (s.rootNode()->hasAnother("modems")) { + DataNode *modems = s.rootNode()->getNext("modems"); + while (modems->hasAnother("group")) { + DataNode *group = modems->getNext("group"); + std::string expandState = "true"; + std::string groupName = "Unnamed"; + if (group->hasAnother("@name")) { + groupName = group->getNext("@name")->element()->toString(); + } + if (group->hasAnother("@expanded")) { + expandState = group->getNext("@expanded")->element()->toString(); + } + setExpandState(groupName, (expandState == "true")); + 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; + loadStatusOk = false; + } + } + } + } + + 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; + 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) { + std::lock_guard < std::mutex > lock(busy_lock); + + BookmarkEntry *be = demodToBookmarkEntry(demod); + + wxGetApp().getDemodMgr().saveInstance(be->node, demod); + + bmData[group].push_back(be); + bmDataSorted[group] = false; +} + +void BookmarkMgr::addBookmark(std::string group, BookmarkEntry *be) { + std::lock_guard < std::mutex > lock(busy_lock); + + bmData[group].push_back(be); + bmDataSorted[group] = false; +} + + +void BookmarkMgr::removeBookmark(std::string group, BookmarkEntry *be) { + std::lock_guard < std::mutex > lockData(busy_lock); + std::lock_guard < std::mutex > lockEnt(be->busy_lock); + + if (bmData.find(group) == bmData.end()) { + return; + } + + BookmarkList::iterator i = std::find(bmData[group].begin(), bmData[group].end(), be); + + if (i != bmData[group].end()) { + delete *i; + bmData[group].erase(i); + } +} + +void BookmarkMgr::removeBookmark(BookmarkEntry *be) { + std::lock_guard < std::mutex > lockData(busy_lock); + std::lock_guard < std::mutex > lockEnt(be->busy_lock); + + for (auto &bmd_i : bmData) { + BookmarkList::iterator i = std::find(bmd_i.second.begin(), bmd_i.second.end(), be); + if (i != bmd_i.second.end()) { + bmd_i.second.erase(i); + } + } +} + +void BookmarkMgr::moveBookmark(BookmarkEntry *be, std::string group) { + std::lock_guard < std::mutex > lockData(busy_lock); + std::lock_guard < std::mutex > lockEnt(be->busy_lock); + + for (auto &bmd_i : bmData) { + BookmarkList::iterator i = std::find(bmd_i.second.begin(), bmd_i.second.end(), be); + if (i != bmd_i.second.end()) { + if (bmd_i.first == group) { + return; + } + bmData[group].push_back(*i); + bmd_i.second.erase(i); + bmDataSorted[group] = false; + bmDataSorted[bmd_i.first] = false; + return; + } + } +} + + +void BookmarkMgr::addGroup(std::string group) { + std::lock_guard < std::mutex > lock(busy_lock); + + if (bmData.find(group) == bmData.end()) { + BookmarkList dummy = bmData[group]; + } +} + +void BookmarkMgr::removeGroup(std::string group) { + std::lock_guard < std::mutex > lock(busy_lock); + + BookmarkMap::iterator i = bmData.find(group); + + if (i != bmData.end()) { + for (auto ii : bmData[group]) { + delete ii; + } + bmData.erase(group); + } +} + +void BookmarkMgr::renameGroup(std::string group, std::string ngroup) { + if (group == ngroup) { + return; + } + + std::lock_guard < std::mutex > lock(busy_lock); + + BookmarkMap::iterator i = bmData.find(group); + BookmarkMap::iterator it = bmData.find(ngroup); + + if (i != bmData.end() && it != bmData.end()) { + for (auto ii : bmData[group]) { + bmData[ngroup].push_back(ii); + } + bmData.erase(group); + } else if (i != bmData.end()) { + bmData[ngroup] = bmData[group]; + bmData.erase(group); + } +} + +BookmarkList BookmarkMgr::getBookmarks(std::string group) { + std::lock_guard < std::mutex > lock(busy_lock); + + if (bmData.find(group) == bmData.end()) { + BookmarkList results; + return results; + } + + if (!bmDataSorted[group]) { + std::sort(bmData[group].begin(), bmData[group].end(), BookmarkEntryCompare()); + bmDataSorted[group] = true; + } + + return bmData[group]; +} + + +void BookmarkMgr::getGroups(BookmarkNames &arr) { + for (BookmarkMap::iterator i = bmData.begin(); i!= bmData.end(); ++i) { + arr.push_back(i->first); + } +} + +void BookmarkMgr::getGroups(wxArrayString &arr) { + for (BookmarkMap::iterator i = bmData.begin(); i!= bmData.end(); ++i) { + arr.push_back(i->first); + } +} + + +void BookmarkMgr::setExpandState(std::string groupName, bool state) { + expandState[groupName] = state; +} + + +bool BookmarkMgr::getExpandState(std::string groupName) { + if (expandState.find(groupName) == expandState.end()) { + return true; + } + return expandState[groupName]; +} + + +void BookmarkMgr::updateActiveList() { + BookmarkView *bmv = wxGetApp().getAppFrame()->getBookmarkView(); + + if (bmv) { + bmv->updateActiveList(); + } +} + +void BookmarkMgr::updateBookmarks() { + BookmarkView *bmv = wxGetApp().getAppFrame()->getBookmarkView(); + + if (bmv) { + bmv->updateBookmarks(); + } +} + +void BookmarkMgr::updateBookmarks(std::string group) { + BookmarkView *bmv = wxGetApp().getAppFrame()->getBookmarkView(); + + if (bmv) { + bmv->updateBookmarks(group); + } +} + + +void BookmarkMgr::addRecent(DemodulatorInstance *demod) { + std::lock_guard < std::mutex > lock(busy_lock); + recents.push_back(demodToBookmarkEntry(demod)); + + 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); + + BookmarkList::iterator bm_i = std::find(recents.begin(),recents.end(), be); + + if (bm_i != recents.end()) { + recents.erase(bm_i); + } +} + + +BookmarkList BookmarkMgr::getRecents() { + + return BookmarkList(recents.rbegin(), recents.rend()); +} + + +void BookmarkMgr::clearRecents() { + std::lock_guard < std::mutex > lock(busy_lock); + + recents.erase(recents.begin(),recents.end()); +} + + +void BookmarkMgr::trimRecents() { + if (recents.size() > BOOKMARK_RECENTS_MAX) { + delete *(recents.begin()); + recents.erase(recents.begin(), recents.begin()+1); + } +} + + +void BookmarkMgr::addRange(BookmarkRangeEntry *re) { + std::lock_guard < std::mutex > lock(busy_lock); + + ranges.push_back(re); + rangesSorted = false; +} + + + +void BookmarkMgr::removeRange(BookmarkRangeEntry *re) { + std::lock_guard < std::mutex > lock(busy_lock); + + BookmarkRangeList::iterator re_i = std::find(ranges.begin(), ranges.end(), re); + + if (re_i != ranges.end()) { + ranges.erase(re_i); + } +} + + +BookmarkRangeList BookmarkMgr::getRanges() { + std::lock_guard < std::mutex > lock(busy_lock); + + if (!rangesSorted) { + std::sort(ranges.begin(), ranges.end(), BookmarkRangeEntryCompare()); + rangesSorted = true; + } + + return ranges; +} + + +void BookmarkMgr::clearRanges() { + std::lock_guard < std::mutex > lock(busy_lock); + + ranges.erase(ranges.begin(),ranges.end()); +} + + +BookmarkEntry *BookmarkMgr::demodToBookmarkEntry(DemodulatorInstance *demod) { + BookmarkEntry *be = new BookmarkEntry; + + be->bandwidth = demod->getBandwidth(); + be->type = demod->getDemodulatorType(); + be->label = demod->getDemodulatorUserLabel(); + be->frequency = demod->getFrequency(); + + be->node = new DataNode; + wxGetApp().getDemodMgr().saveInstance(be->node, 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); + } + + node->rewindAll(); + + be->node = new DataNode("node",*node); + + return be; +} + + +std::wstring BookmarkMgr::getBookmarkEntryDisplayName(BookmarkEntry *bmEnt) { + std::wstring dispName = bmEnt->label; + + if (dispName == "") { + std::string freqStr = frequencyToStr(bmEnt->frequency) + " " + bmEnt->type; + dispName = wstring(freqStr.begin(),freqStr.end()); + } + + return dispName; +} + +std::wstring BookmarkMgr::getActiveDisplayName(DemodulatorInstance *demod) { + std::wstring activeName = demod->getDemodulatorUserLabel(); + + if (activeName == "") { + std::string wstr = frequencyToStr(demod->getFrequency()) + " " + demod->getDemodulatorType(); + activeName = std::wstring(wstr.begin(),wstr.end()); + } + + return activeName; +} + diff --git a/src/BookmarkMgr.h b/src/BookmarkMgr.h new file mode 100644 index 0000000..101490c --- /dev/null +++ b/src/BookmarkMgr.h @@ -0,0 +1,129 @@ +#pragma once + +#include + +#include +#include + +#include "DemodulatorInstance.h" + +class DataNode; + +class BookmarkEntry { +public: + std::mutex busy_lock; + + std::string type; + std::wstring label; + std::wstring userLabel; + + long long frequency; + int bandwidth; + + DataNode *node; +}; + + +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; + + long long freq; + long long startFreq; + long long endFreq; +}; + + +struct BookmarkEntryCompare : public std::binary_function +{ + bool operator()(const BookmarkEntry *a, BookmarkEntry *b) const + { + return a->frequency < b->frequency; + } +}; + + +struct BookmarkRangeEntryCompare : public std::binary_function +{ + bool operator()(const BookmarkRangeEntry *a, BookmarkRangeEntry *b) const + { + return a->freq < b->freq; + } +}; + +typedef std::vector BookmarkList; +typedef std::vector BookmarkRangeList; +typedef std::map BookmarkMap; +typedef std::map BookmarkMapSorted; +typedef std::vector BookmarkNames; +typedef std::map BookmarkExpandState; + +class BookmarkMgr { +public: + BookmarkMgr(); + + 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); + void removeBookmark(std::string group, BookmarkEntry *be); + void removeBookmark(BookmarkEntry *be); + void moveBookmark(BookmarkEntry *be, std::string group); + + void addGroup(std::string group); + void removeGroup(std::string group); + void renameGroup(std::string group, std::string ngroup); + BookmarkList getBookmarks(std::string group); + void getGroups(BookmarkNames &arr); + void getGroups(wxArrayString &arr); + + void setExpandState(std::string groupName, bool state); + bool getExpandState(std::string groupName); + + void updateActiveList(); + void updateBookmarks(); + void updateBookmarks(std::string group); + + void addRecent(DemodulatorInstance *demod); + void addRecent(BookmarkEntry *be); + void removeRecent(BookmarkEntry *be); + BookmarkList getRecents(); + void clearRecents(); + + void addRange(BookmarkRangeEntry *re); + void removeRange(BookmarkRangeEntry *re); + BookmarkRangeList getRanges(); + void clearRanges(); + + + static std::wstring getBookmarkEntryDisplayName(BookmarkEntry *bmEnt); + static std::wstring getActiveDisplayName(DemodulatorInstance *demod); + +protected: + + void trimRecents(); + + BookmarkEntry *demodToBookmarkEntry(DemodulatorInstance *demod); + BookmarkEntry *nodeToBookmark(const char *name_in, DataNode *node); + + BookmarkMap bmData; + BookmarkMapSorted bmDataSorted; + BookmarkList recents; + BookmarkRangeList ranges; + bool rangesSorted; + std::mutex busy_lock; + + BookmarkExpandState expandState; +}; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 282f67b..8292292 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -30,6 +30,9 @@ IMPLEMENT_APP(CubicSDR) #include #include +#include "ActionDialog.h" + + //#ifdef ENABLE_DIGITAL_LAB //// console output buffer for windows //#ifdef _WINDOWS @@ -136,8 +139,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); @@ -231,25 +291,16 @@ bool CubicSDR::OnInit() { // Visual Data spectrumVisualThread = new SpectrumVisualDataThread(); - demodVisualThread = new SpectrumVisualDataThread(); pipeIQVisualData = new DemodulatorThreadInputQueue(); pipeIQVisualData->set_max_num_items(1); - - pipeDemodIQVisualData = new DemodulatorThreadInputQueue(); - pipeDemodIQVisualData->set_max_num_items(1); pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue(); pipeWaterfallIQVisualData->set_max_num_items(128); - getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData); getSpectrumProcessor()->setInput(pipeIQVisualData); getSpectrumProcessor()->setHideDC(true); - pipeAudioVisualData = new DemodulatorThreadOutputQueue(); - pipeAudioVisualData->set_max_num_items(1); - - scopeProcessor.setInput(pipeAudioVisualData); // I/Q Data pipeSDRIQData = new SDRThreadIQDataQueue(); @@ -263,12 +314,32 @@ bool CubicSDR::OnInit() { sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData); sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData); - sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData); t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread); t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread); + +#if CUBICSDR_ENABLE_VIEW_SCOPE + pipeAudioVisualData = new DemodulatorThreadOutputQueue(); + pipeAudioVisualData->set_max_num_items(1); + + scopeProcessor.setInput(pipeAudioVisualData); +#else + pipeAudioVisualData = nullptr; +#endif + +#if CUBICSDR_ENABLE_VIEW_DEMOD + demodVisualThread = new SpectrumVisualDataThread(); + pipeDemodIQVisualData = new DemodulatorThreadInputQueue(); + pipeDemodIQVisualData->set_max_num_items(1); + if (getDemodSpectrumProcessor()) getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData); + sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData); t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread); - +#else + demodVisualThread = nullptr; + pipeDemodIQVisualData = nullptr; + t_DemodVisual = nullptr; +#endif + sdrEnum = new SDREnumerator(); SDREnumerator::setManuals(config.getManualDevices()); @@ -286,6 +357,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; } @@ -316,16 +400,20 @@ int CubicSDR::OnExit() { std::cout << "Terminating Visual Processor threads.." << std::endl; spectrumVisualThread->terminate(); - demodVisualThread->terminate(); - + if (demodVisualThread) { + demodVisualThread->terminate(); + } + //Wait nicely sdrPostThread->isTerminated(1000); spectrumVisualThread->isTerminated(1000); - demodVisualThread->isTerminated(1000); + if (demodVisualThread) { + demodVisualThread->isTerminated(1000); + } //Then join the thread themselves t_PostSDR->join(); - t_DemodVisual->join(); + if (t_DemodVisual) t_DemodVisual->join(); t_SpectrumVisual->join(); //Now only we can delete @@ -494,7 +582,9 @@ void CubicSDR::setFrequency(long long freq) { getSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold()); //make the peak hold act on the current dmod also, like a zoomed-in version. - getDemodSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold()); + if (getDemodSpectrumProcessor()) { + getDemodSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold()); + } } long long CubicSDR::getOffset() { @@ -655,7 +745,11 @@ SpectrumVisualProcessor *CubicSDR::getSpectrumProcessor() { } SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() { - return demodVisualThread->getProcessor(); + if (demodVisualThread) { + return demodVisualThread->getProcessor(); + } else { + return nullptr; + } } DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() { @@ -674,6 +768,10 @@ DemodulatorMgr &CubicSDR::getDemodMgr() { return demodMgr; } +BookmarkMgr &CubicSDR::getBookmarkMgr() { + return bookmarkMgr; +} + SDRPostThread *CubicSDR::getSDRPostThread() { return sdrPostThread; } @@ -707,6 +805,7 @@ void CubicSDR::removeDemodulator(DemodulatorInstance *demod) { } demod->setActive(false); sdrPostThread->removeDemodulator(demod); + wxGetApp().getAppFrame()->notifyUpdateModemProperties(); } std::vector* CubicSDR::getDevices() { @@ -752,6 +851,7 @@ void CubicSDR::showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetM switch (targetMode) { case FrequencyDialog::FDIALOG_TARGET_DEFAULT: + case FrequencyDialog::FDIALOG_TARGET_FREQ: title = demodMgr.getActiveDemodulator()?demodTitle:freqTitle; break; case FrequencyDialog::FDIALOG_TARGET_BANDWIDTH: diff --git a/src/CubicSDR.h b/src/CubicSDR.h index a7de9b9..bbd82df 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -26,6 +26,7 @@ #include "AppFrame.h" #include "FrequencyDialog.h" #include "DemodLabelDialog.h" +#include "BookmarkMgr.h" #include "ScopeVisualProcessor.h" #include "SpectrumVisualProcessor.h" @@ -96,6 +97,9 @@ public: void setOffset(long long ofs); long long getOffset(); + + void setDBOffset(int ofs); + int getDBOffset(); void setSampleRate(long long rate_in); long long getSampleRate(); @@ -114,6 +118,7 @@ public: DemodulatorThreadInputQueue* getWaterfallVisualQueue(); DemodulatorThreadInputQueue* getActiveDemodVisualQueue(); DemodulatorMgr &getDemodMgr(); + BookmarkMgr &getBookmarkMgr(); SDRPostThread *getSDRPostThread(); SDRThread *getSDRThread(); @@ -182,6 +187,7 @@ private: std::vector *devs = nullptr; DemodulatorMgr demodMgr; + BookmarkMgr bookmarkMgr; std::atomic_llong frequency; std::atomic_llong offset; diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 26ae857..2f172ae 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -3,7 +3,7 @@ #pragma once -#define CUBICSDR_TITLE "CubicSDR v" CUBICSDR_VERSION " by Charles J. Cliffe (@ccliffe) :: www.cubicsdr.com" +#define CUBICSDR_TITLE "" CUBICSDR_BUILD_TITLE #ifndef __BYTE_ORDER #ifdef _WIN32 diff --git a/src/DemodLabelDialog.cpp b/src/DemodLabelDialog.cpp index 17ff37e..251c356 100644 --- a/src/DemodLabelDialog.cpp +++ b/src/DemodLabelDialog.cpp @@ -60,7 +60,7 @@ void DemodLabelDialog::OnChar(wxKeyEvent& event) { else { activeDemod->setDemodulatorUserLabel(L""); } - + wxGetApp().getBookmarkMgr().updateActiveList(); Close(); break; case WXK_ESCAPE: diff --git a/src/ModemProperties.cpp b/src/ModemProperties.cpp index 84037bd..9984bdc 100644 --- a/src/ModemProperties.cpp +++ b/src/ModemProperties.cpp @@ -33,26 +33,10 @@ void ModemProperties::OnShow(wxShowEvent & /* event */) { } void ModemProperties::updateTheme() { - wxColour bgColor( - (unsigned char) (ThemeMgr::mgr.currentTheme->generalBackground.r * 255.0), - (unsigned char) (ThemeMgr::mgr.currentTheme->generalBackground.g * 255.0), - (unsigned char) (ThemeMgr::mgr.currentTheme->generalBackground.b * 255.0)); - - wxColour textColor( - (unsigned char) (ThemeMgr::mgr.currentTheme->text.r * 255.0), - (unsigned char) (ThemeMgr::mgr.currentTheme->text.g * 255.0), - (unsigned char) (ThemeMgr::mgr.currentTheme->text.b * 255.0)); - - wxColour btn( - (unsigned char) (ThemeMgr::mgr.currentTheme->button.r * 255.0), - (unsigned char) (ThemeMgr::mgr.currentTheme->button.g * 255.0), - (unsigned char) (ThemeMgr::mgr.currentTheme->button.b * 255.0)); - - wxColour btnHl( - (unsigned char) (ThemeMgr::mgr.currentTheme->buttonHighlight.r * 255.0), - (unsigned char) (ThemeMgr::mgr.currentTheme->buttonHighlight.g * 255.0), - (unsigned char) (ThemeMgr::mgr.currentTheme->buttonHighlight.b * 255.0)); - + wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground); + wxColour textColor(ThemeMgr::mgr.currentTheme->text); + wxColour btn(ThemeMgr::mgr.currentTheme->button); + wxColour btnHl(ThemeMgr::mgr.currentTheme->buttonHighlight); m_propertyGrid->SetEmptySpaceColour(bgColor); m_propertyGrid->SetCellBackgroundColour(bgColor); @@ -125,10 +109,8 @@ void ModemProperties::initProperties(ModemArgInfoList newArgs, DemodulatorInstan m_propertyGrid->Clear(); if (!demodInstance) { - Hide(); + m_propertyGrid->Append(new wxPropertyCategory("Modem Settings")); return; - } else { - Show(); } m_propertyGrid->Append(new wxPropertyCategory(demodInstance->getDemodulatorType() + " Settings")); diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index 4a255b8..61f4432 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -83,6 +83,8 @@ DemodulatorInstance::~DemodulatorInstance() { delete pipeIQDemodData; delete threadQueueControl; delete pipeAudioData; + + wxGetApp().getBookmarkMgr().updateActiveList(); } void DemodulatorInstance::setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue) { @@ -121,6 +123,7 @@ void DemodulatorInstance::run() { active = true; + wxGetApp().getBookmarkMgr().updateActiveList(); } void DemodulatorInstance::updateLabel(long long freq) { @@ -128,6 +131,7 @@ void DemodulatorInstance::updateLabel(long long freq) { newLabel.precision(3); newLabel << std::fixed << ((long double) freq / 1000000.0); setLabel(newLabel.str()); + wxGetApp().getBookmarkMgr().updateActiveList(); } void DemodulatorInstance::terminate() { @@ -230,6 +234,8 @@ void DemodulatorInstance::setActive(bool state) { tracking = false; } active = state; + + wxGetApp().getBookmarkMgr().updateActiveList(); } void DemodulatorInstance::squelchAuto() { @@ -334,7 +340,9 @@ void DemodulatorInstance::setDemodulatorType(std::string demod_type_in) { outp->setTitle(getDemodulatorType() + ": " + frequencyToStr(getFrequency())); } #endif -} + } + + wxGetApp().getBookmarkMgr().updateActiveList(); } std::string DemodulatorInstance::getDemodulatorType() { @@ -394,6 +402,10 @@ void DemodulatorInstance::setFrequency(long long freq) { wxGetApp().getRigThread()->setFrequency(freq,true); } #endif + + if (this->isActive()) { + wxGetApp().getBookmarkMgr().updateActiveList(); + } } long long DemodulatorInstance::getFrequency() { diff --git a/src/demod/DemodulatorMgr.cpp b/src/demod/DemodulatorMgr.cpp index d4da312..c3bff6c 100644 --- a/src/demod/DemodulatorMgr.cpp +++ b/src/demod/DemodulatorMgr.cpp @@ -4,15 +4,18 @@ #include #include #include -#include "CubicSDR.h" #include #include #include +#include "DemodulatorMgr.h" + #if USE_HAMLIB #include "RigThread.h" #endif +#include "DataTree.h" + bool demodFreqCompare (DemodulatorInstance *i, DemodulatorInstance *j) { return (i->getFrequency()getFrequency()); } bool inactiveCompare (DemodulatorInstance *i, DemodulatorInstance *j) { return (i->isActive()isActive()); } @@ -133,6 +136,8 @@ DemodulatorInstance *DemodulatorMgr::getFirstDemodulator() { void DemodulatorMgr::deleteThread(DemodulatorInstance *demod) { std::lock_guard < std::recursive_mutex > lock(demods_busy); + + wxGetApp().getBookmarkMgr().addRecent(demod); std::vector::iterator i; @@ -218,6 +223,7 @@ void DemodulatorMgr::setActiveDemodulator(DemodulatorInstance *demod, bool tempo wxGetApp().getRigThread()->setFrequency(lastActiveDemodulator.load()->getFrequency(),true); } #endif + wxGetApp().getBookmarkMgr().updateActiveList(); } else { std::lock_guard < std::recursive_mutex > lock(demods_busy); garbageCollect(); @@ -371,3 +377,138 @@ ModemSettings DemodulatorMgr::getLastModemSettings(std::string modemType) { void DemodulatorMgr::setLastModemSettings(std::string modemType, ModemSettings settings) { lastModemSettings[modemType] = settings; } + +void DemodulatorMgr::setOutputDevices(std::map devs) { + outputDevices = devs; +} + +void DemodulatorMgr::saveInstance(DataNode *node, DemodulatorInstance *inst) { + *node->newChild("bandwidth") = inst->getBandwidth(); + *node->newChild("frequency") = inst->getFrequency(); + *node->newChild("type") = inst->getDemodulatorType(); + + node->newChild("user_label")->element()->set(inst->getDemodulatorUserLabel()); + + *node->newChild("squelch_level") = inst->getSquelchLevel(); + *node->newChild("squelch_enabled") = inst->isSquelchEnabled() ? 1 : 0; + *node->newChild("output_device") = outputDevices[inst->getOutputDevice()].name; + *node->newChild("gain") = inst->getGain(); + *node->newChild("muted") = inst->isMuted() ? 1 : 0; + if (inst->isDeltaLock()) { + *node->newChild("delta_lock") = inst->isDeltaLock() ? 1 : 0; + *node->newChild("delta_ofs") = inst->getDeltaLockOfs(); + } + if (inst == getLastActiveDemodulator()) { + *node->newChild("active") = 1; + } + + ModemSettings saveSettings = inst->readModemSettings(); + if (saveSettings.size()) { + DataNode *settingsNode = node->newChild("settings"); + for (ModemSettings::const_iterator msi = saveSettings.begin(); msi != saveSettings.end(); msi++) { + *settingsNode->newChild(msi->first.c_str()) = msi->second; + } + } + +} + +DemodulatorInstance *DemodulatorMgr::loadInstance(DataNode *node) { + DemodulatorInstance *newDemod = nullptr; + + node->rewindAll(); + + long bandwidth = *node->getNext("bandwidth"); + long long freq = *node->getNext("frequency"); + float squelch_level = node->hasAnother("squelch_level") ? (float) *node->getNext("squelch_level") : 0; + int squelch_enabled = node->hasAnother("squelch_enabled") ? (int) *node->getNext("squelch_enabled") : 0; + int muted = node->hasAnother("muted") ? (int) *node->getNext("muted") : 0; + int delta_locked = node->hasAnother("delta_lock") ? (int) *node->getNext("delta_lock") : 0; + int delta_ofs = node->hasAnother("delta_ofs") ? (int) *node->getNext("delta_ofs") : 0; + std::string output_device = node->hasAnother("output_device") ? string(*(node->getNext("output_device"))) : ""; + float gain = node->hasAnother("gain") ? (float) *node->getNext("gain") : 1.0; + + std::string type = "FM"; + + DataNode *demodTypeNode = node->hasAnother("type")?node->getNext("type"):nullptr; + + if (demodTypeNode && demodTypeNode->element()->getDataType() == DATA_INT) { + int legacyType = *demodTypeNode; + int legacyStereo = node->hasAnother("stereo") ? (int) *node->getNext("stereo") : 0; + switch (legacyType) { // legacy demod ID + case 1: type = legacyStereo?"FMS":"FM"; break; + case 2: type = "AM"; break; + case 3: type = "LSB"; break; + case 4: type = "USB"; break; + case 5: type = "DSB"; break; + case 6: type = "ASK"; break; + case 7: type = "APSK"; break; + case 8: type = "BPSK"; break; + case 9: type = "DPSK"; break; + case 10: type = "PSK"; break; + case 11: type = "OOK"; break; + case 12: type = "ST"; break; + case 13: type = "SQAM"; break; + case 14: type = "QAM"; break; + case 15: type = "QPSK"; break; + case 16: type = "I/Q"; break; + default: type = "FM"; break; + } + } else if (demodTypeNode && demodTypeNode->element()->getDataType() == DATA_STRING) { + demodTypeNode->element()->get(type); + } + + //read the user label associated with the demodulator + std::wstring user_label = L""; + + DataNode *demodUserLabel = node->hasAnother("user_label") ? node->getNext("user_label") : nullptr; + + if (demodUserLabel) { + demodUserLabel->element()->get(user_label); + } + + ModemSettings mSettings; + + if (node->hasAnother("settings")) { + DataNode *modemSettings = node->getNext("settings"); + for (int msi = 0, numSettings = modemSettings->numChildren(); msi < numSettings; msi++) { + DataNode *settingNode = modemSettings->child(msi); + std::string keyName = settingNode->getName(); + std::string strSettingValue = settingNode->element()->toString(); + + if (keyName != "" && strSettingValue != "") { + mSettings[keyName] = strSettingValue; + } + } + } + + newDemod = wxGetApp().getDemodMgr().newThread(); + + newDemod->setDemodulatorType(type); + newDemod->setDemodulatorUserLabel(user_label); + newDemod->writeModemSettings(mSettings); + newDemod->setBandwidth(bandwidth); + newDemod->setFrequency(freq); + newDemod->setGain(gain); + newDemod->updateLabel(freq); + newDemod->setMuted(muted?true:false); + if (delta_locked) { + newDemod->setDeltaLock(true); + newDemod->setDeltaLockOfs(delta_ofs); + } + if (squelch_enabled) { + newDemod->setSquelchEnabled(true); + newDemod->setSquelchLevel(squelch_level); + } + + bool found_device = false; + std::map::iterator i; + for (i = outputDevices.begin(); i != outputDevices.end(); i++) { + if (i->second.name == output_device) { + newDemod->setOutputDevice(i->first); + found_device = true; + } + } + + return newDemod; +} + diff --git a/src/demod/DemodulatorMgr.h b/src/demod/DemodulatorMgr.h index df0c60a..41b0945 100644 --- a/src/demod/DemodulatorMgr.h +++ b/src/demod/DemodulatorMgr.h @@ -9,6 +9,8 @@ #include "DemodulatorInstance.h" +class DataNode; + class DemodulatorMgr { public: DemodulatorMgr(); @@ -57,6 +59,10 @@ public: void updateLastState(); + void setOutputDevices(std::map devs); + void saveInstance(DataNode *node, DemodulatorInstance *inst); + DemodulatorInstance *loadInstance(DataNode *node); + private: void garbageCollect(); @@ -82,4 +88,5 @@ private: std::recursive_mutex demods_busy; std::map lastModemSettings; + std::map outputDevices; }; diff --git a/src/forms/Bookmark/BookmarkPanel.cpp b/src/forms/Bookmark/BookmarkPanel.cpp new file mode 100644 index 0000000..8e0d6f1 --- /dev/null +++ b/src/forms/Bookmark/BookmarkPanel.cpp @@ -0,0 +1,136 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Aug 23 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "BookmarkPanel.h" + +/////////////////////////////////////////////////////////////////////////// + +BookmarkPanel::BookmarkPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + wxBoxSizer* bSizer1; + bSizer1 = new wxBoxSizer( wxVERTICAL ); + + m_searchText = new wxTextCtrl( this, wxID_ANY, wxT("Search.."), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); + bSizer1->Add( m_searchText, 0, wxALL|wxEXPAND, 5 ); + + m_clearSearchButton = new wxButton( this, wxID_ANY, wxT("Clear Search"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer1->Add( m_clearSearchButton, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + + m_treeView = new wxTreeCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE|wxTR_EDIT_LABELS|wxTR_HAS_VARIABLE_ROW_HEIGHT|wxTR_HIDE_ROOT|wxTR_SINGLE ); + bSizer1->Add( m_treeView, 1, wxEXPAND, 5 ); + + m_propPanel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxFlexGridSizer* fgPropSizer; + fgPropSizer = new wxFlexGridSizer( 0, 2, 0, 0 ); + fgPropSizer->AddGrowableCol( 1 ); + fgPropSizer->SetFlexibleDirection( wxBOTH ); + fgPropSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_labelLabel = new wxStaticText( m_propPanel, wxID_ANY, wxT("Label"), wxDefaultPosition, wxDefaultSize, 0 ); + m_labelLabel->Wrap( -1 ); + fgPropSizer->Add( m_labelLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + m_labelText = new wxTextCtrl( m_propPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); + fgPropSizer->Add( m_labelText, 0, wxALL|wxEXPAND, 5 ); + + m_frequencyLabel = new wxStaticText( m_propPanel, wxID_ANY, wxT("Freq"), wxDefaultPosition, wxDefaultSize, 0 ); + m_frequencyLabel->Wrap( -1 ); + fgPropSizer->Add( m_frequencyLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + m_frequencyVal = new wxStaticText( m_propPanel, wxID_ANY, wxT("FrequencyVal"), wxDefaultPosition, wxDefaultSize, 0 ); + m_frequencyVal->Wrap( -1 ); + fgPropSizer->Add( m_frequencyVal, 0, wxALL, 5 ); + + m_bandwidthLabel = new wxStaticText( m_propPanel, wxID_ANY, wxT("BW"), wxDefaultPosition, wxDefaultSize, 0 ); + m_bandwidthLabel->Wrap( -1 ); + fgPropSizer->Add( m_bandwidthLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + m_bandwidthVal = new wxStaticText( m_propPanel, wxID_ANY, wxT("BandwidthVal"), wxDefaultPosition, wxDefaultSize, 0 ); + m_bandwidthVal->Wrap( -1 ); + fgPropSizer->Add( m_bandwidthVal, 0, wxALL, 5 ); + + m_modulationLabel = new wxStaticText( m_propPanel, wxID_ANY, wxT("Type"), wxDefaultPosition, wxDefaultSize, 0 ); + m_modulationLabel->Wrap( -1 ); + fgPropSizer->Add( m_modulationLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + m_modulationVal = new wxStaticText( m_propPanel, wxID_ANY, wxT("TypeVal"), wxDefaultPosition, wxDefaultSize, 0 ); + m_modulationVal->Wrap( -1 ); + fgPropSizer->Add( m_modulationVal, 0, wxALL, 5 ); + + + m_propPanel->SetSizer( fgPropSizer ); + m_propPanel->Layout(); + fgPropSizer->Fit( m_propPanel ); + bSizer1->Add( m_propPanel, 0, wxALL|wxBOTTOM|wxEXPAND|wxTOP, 5 ); + + m_buttonPanel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* m_buttonPanelSizer; + m_buttonPanelSizer = new wxBoxSizer( wxVERTICAL ); + + + m_buttonPanel->SetSizer( m_buttonPanelSizer ); + m_buttonPanel->Layout(); + m_buttonPanelSizer->Fit( m_buttonPanel ); + bSizer1->Add( m_buttonPanel, 0, wxALL|wxEXPAND, 5 ); + + + this->SetSizer( bSizer1 ); + this->Layout(); + m_updateTimer.SetOwner( this, wxID_ANY ); + + // Connect Events + this->Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( BookmarkPanel::onEnterWindow ) ); + this->Connect( wxEVT_LEAVE_WINDOW, wxMouseEventHandler( BookmarkPanel::onLeaveWindow ) ); + this->Connect( wxEVT_MOTION, wxMouseEventHandler( BookmarkPanel::onMotion ) ); + m_searchText->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( BookmarkPanel::onSearchTextFocus ), NULL, this ); + m_searchText->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( BookmarkPanel::onSearchText ), NULL, this ); + m_clearSearchButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BookmarkPanel::onClearSearch ), NULL, this ); + m_treeView->Connect( wxEVT_MOTION, wxMouseEventHandler( BookmarkPanel::onMotion ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEventHandler( BookmarkPanel::onTreeBeginDrag ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, wxTreeEventHandler( BookmarkPanel::onTreeBeginLabelEdit ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_END_DRAG, wxTreeEventHandler( BookmarkPanel::onTreeEndDrag ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_END_LABEL_EDIT, wxTreeEventHandler( BookmarkPanel::onTreeEndLabelEdit ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEventHandler( BookmarkPanel::onTreeActivate ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEventHandler( BookmarkPanel::onTreeCollapse ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEventHandler( BookmarkPanel::onTreeExpanded ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEventHandler( BookmarkPanel::onTreeItemGetTooltip ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEventHandler( BookmarkPanel::onTreeItemMenu ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( BookmarkPanel::onTreeSelect ), NULL, this ); + m_treeView->Connect( wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEventHandler( BookmarkPanel::onTreeSelectChanging ), NULL, this ); + m_labelText->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( BookmarkPanel::onLabelText ), NULL, this ); + m_frequencyVal->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( BookmarkPanel::onDoubleClickFreq ), NULL, this ); + m_bandwidthVal->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( BookmarkPanel::onDoubleClickBandwidth ), NULL, this ); + this->Connect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( BookmarkPanel::onUpdateTimer ) ); +} + +BookmarkPanel::~BookmarkPanel() +{ + // Disconnect Events + this->Disconnect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( BookmarkPanel::onEnterWindow ) ); + this->Disconnect( wxEVT_LEAVE_WINDOW, wxMouseEventHandler( BookmarkPanel::onLeaveWindow ) ); + this->Disconnect( wxEVT_MOTION, wxMouseEventHandler( BookmarkPanel::onMotion ) ); + m_searchText->Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( BookmarkPanel::onSearchTextFocus ), NULL, this ); + m_searchText->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( BookmarkPanel::onSearchText ), NULL, this ); + m_clearSearchButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BookmarkPanel::onClearSearch ), NULL, this ); + m_treeView->Disconnect( wxEVT_MOTION, wxMouseEventHandler( BookmarkPanel::onMotion ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEventHandler( BookmarkPanel::onTreeBeginDrag ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, wxTreeEventHandler( BookmarkPanel::onTreeBeginLabelEdit ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_END_DRAG, wxTreeEventHandler( BookmarkPanel::onTreeEndDrag ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_END_LABEL_EDIT, wxTreeEventHandler( BookmarkPanel::onTreeEndLabelEdit ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEventHandler( BookmarkPanel::onTreeActivate ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEventHandler( BookmarkPanel::onTreeCollapse ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEventHandler( BookmarkPanel::onTreeExpanded ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEventHandler( BookmarkPanel::onTreeItemGetTooltip ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEventHandler( BookmarkPanel::onTreeItemMenu ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( BookmarkPanel::onTreeSelect ), NULL, this ); + m_treeView->Disconnect( wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEventHandler( BookmarkPanel::onTreeSelectChanging ), NULL, this ); + m_labelText->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( BookmarkPanel::onLabelText ), NULL, this ); + m_frequencyVal->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( BookmarkPanel::onDoubleClickFreq ), NULL, this ); + m_bandwidthVal->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( BookmarkPanel::onDoubleClickBandwidth ), NULL, this ); + this->Disconnect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( BookmarkPanel::onUpdateTimer ) ); + +} diff --git a/src/forms/Bookmark/BookmarkPanel.fbp b/src/forms/Bookmark/BookmarkPanel.fbp new file mode 100644 index 0000000..8214f9d --- /dev/null +++ b/src/forms/Bookmark/BookmarkPanel.fbp @@ -0,0 +1,1230 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + BookmarkPanel + 1000 + none + 0 + BookmarkPanel + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + + 1 + 1 + impl_virtual + + + 0 + wxID_ANY + + + BookmarkPanel + + 169,471 + + + + + wxTAB_TRAVERSAL + + + + + + + + onEnterWindow + + + + + + onLeaveWindow + + + + + + + onMotion + + + + + + + + + + + + bSizer1 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_searchText + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PROCESS_ENTER + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + Search.. + + + + + + + + + + + + onSearchTextFocus + + + + + + + + + + + + + + onSearchText + + + + + + + + 5 + wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Clear Search + + 0 + + + 0 + + 1 + m_clearSearchButton + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + onClearSearch + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_treeView + 1 + + + protected + 1 + + Resizable + 1 + + wxTR_DEFAULT_STYLE|wxTR_EDIT_LABELS|wxTR_HAS_VARIABLE_ROW_HEIGHT|wxTR_HIDE_ROOT|wxTR_SINGLE + + 0 + + + + + + + + + + + + + + + + + + onMotion + + + + + + + + + onTreeBeginDrag + onTreeBeginLabelEdit + + + onTreeEndDrag + onTreeEndLabelEdit + + onTreeActivate + onTreeCollapse + + onTreeExpanded + + onTreeItemGetTooltip + onTreeItemMenu + + + + onTreeSelect + onTreeSelectChanging + + + + + + + 5 + wxALL|wxBOTTOM|wxEXPAND|wxTOP + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_propPanel + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + 2 + wxBOTH + 1 + + 0 + + fgPropSizer + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Label + + 0 + + + 0 + + 1 + m_labelLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_labelText + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PROCESS_ENTER + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + onLabelText + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Freq + + 0 + + + 0 + + 1 + m_frequencyLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + FrequencyVal + + 0 + + + 0 + + 1 + m_frequencyVal + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + onDoubleClickFreq + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + BW + + 0 + + + 0 + + 1 + m_bandwidthLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + BandwidthVal + + 0 + + + 0 + + 1 + m_bandwidthVal + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + onDoubleClickBandwidth + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Type + + 0 + + + 0 + + 1 + m_modulationLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + TypeVal + + 0 + + + 0 + + 1 + m_modulationVal + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_buttonPanel + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + m_buttonPanelSizer + wxVERTICAL + none + + + + + + 0 + wxID_ANY + m_updateTimer + 0 + 500 + protected + onUpdateTimer + + + + diff --git a/src/forms/Bookmark/BookmarkPanel.h b/src/forms/Bookmark/BookmarkPanel.h new file mode 100644 index 0000000..ecb09cb --- /dev/null +++ b/src/forms/Bookmark/BookmarkPanel.h @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Aug 23 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __BOOKMARKPANEL_H__ +#define __BOOKMARKPANEL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class BookmarkPanel +/////////////////////////////////////////////////////////////////////////////// +class BookmarkPanel : public wxPanel +{ + private: + + protected: + wxTextCtrl* m_searchText; + wxButton* m_clearSearchButton; + wxTreeCtrl* m_treeView; + wxPanel* m_propPanel; + wxStaticText* m_labelLabel; + wxTextCtrl* m_labelText; + wxStaticText* m_frequencyLabel; + wxStaticText* m_frequencyVal; + wxStaticText* m_bandwidthLabel; + wxStaticText* m_bandwidthVal; + wxStaticText* m_modulationLabel; + wxStaticText* m_modulationVal; + wxPanel* m_buttonPanel; + wxTimer m_updateTimer; + + // Virtual event handlers, overide them in your derived class + virtual void onEnterWindow( wxMouseEvent& event ) { event.Skip(); } + virtual void onLeaveWindow( wxMouseEvent& event ) { event.Skip(); } + virtual void onMotion( wxMouseEvent& event ) { event.Skip(); } + virtual void onSearchTextFocus( wxMouseEvent& event ) { event.Skip(); } + virtual void onSearchText( wxCommandEvent& event ) { event.Skip(); } + virtual void onClearSearch( wxCommandEvent& event ) { event.Skip(); } + virtual void onTreeBeginDrag( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeBeginLabelEdit( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeEndDrag( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeEndLabelEdit( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeActivate( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeCollapse( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeExpanded( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeItemGetTooltip( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeItemMenu( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeSelect( wxTreeEvent& event ) { event.Skip(); } + virtual void onTreeSelectChanging( wxTreeEvent& event ) { event.Skip(); } + virtual void onLabelText( wxCommandEvent& event ) { event.Skip(); } + virtual void onDoubleClickFreq( wxMouseEvent& event ) { event.Skip(); } + virtual void onDoubleClickBandwidth( wxMouseEvent& event ) { event.Skip(); } + virtual void onUpdateTimer( wxTimerEvent& event ) { event.Skip(); } + + + public: + + BookmarkPanel( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 169,471 ), long style = wxTAB_TRAVERSAL ); + ~BookmarkPanel(); + +}; + +#endif //__BOOKMARKPANEL_H__ diff --git a/src/forms/Bookmark/BookmarkView.cpp b/src/forms/Bookmark/BookmarkView.cpp new file mode 100644 index 0000000..6ce6571 --- /dev/null +++ b/src/forms/Bookmark/BookmarkView.cpp @@ -0,0 +1,1476 @@ +#include +#include + +#include +#include + +#include "BookmarkView.h" +#include "CubicSDR.h" +#include "ActionDialog.h" + + +#define wxCONTEXT_ADD_GROUP_ID 1000 + +#define BOOKMARK_VIEW_CHOICE_DEFAULT "Bookmark.." +#define BOOKMARK_VIEW_CHOICE_MOVE "Move to.." +#define BOOKMARK_VIEW_CHOICE_NEW "(New Group..)" + +#define BOOKMARK_VIEW_STR_ADD_GROUP "Add Group" +#define BOOKMARK_VIEW_STR_ADD_GROUP_DESC "Enter Group Name" +#define BOOKMARK_VIEW_STR_UNNAMED "Unnamed" +#define BOOKMARK_VIEW_STR_CLEAR_RECENT "Clear Recents" +#define BOOKMARK_VIEW_STR_RENAME_GROUP "Rename Group" + + +BookmarkViewVisualDragItem::BookmarkViewVisualDragItem(wxString labelValue) : wxDialog(NULL, wxID_ANY, L"", wxPoint(20,20), wxSize(-1,-1), wxSTAY_ON_TOP | wxALL ) { + + wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *label = new wxStaticText( this, wxID_ANY, labelValue, wxDefaultPosition, wxDefaultSize, wxEXPAND ); + + sizer->Add(label, 1, wxALL | wxEXPAND, 5); + + SetSizerAndFit(sizer); + + Show(); +} + +class ActionDialogRemoveBookmark : public ActionDialog { +public: + ActionDialogRemoveBookmark( BookmarkEntry *be ) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Remove Bookmark?")) { + subject = be; + m_questionText->SetLabelText(wxT("Are you sure you want to remove the bookmark\n '" + BookmarkMgr::getBookmarkEntryDisplayName(subject) + "'?")); + } + + void doClickOK() { + wxGetApp().getBookmarkMgr().removeBookmark(subject); + wxGetApp().getBookmarkMgr().updateBookmarks(); + } + +private: + BookmarkEntry *subject; +}; + +class ActionDialogRemoveGroup : public ActionDialog { +public: + ActionDialogRemoveGroup( std::string groupName ) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Remove Group?")) { + subject = groupName; + m_questionText->SetLabelText(wxT("Warning: Are you sure you want to remove the group\n '" + subject + "' AND ALL BOOKMARKS WITHIN IT?")); + } + + void doClickOK() { + wxGetApp().getBookmarkMgr().removeGroup(subject); + wxGetApp().getBookmarkMgr().updateBookmarks(); + } + +private: + std::string subject; +}; + + +class ActionDialogRemoveRange : public ActionDialog { +public: + ActionDialogRemoveRange( BookmarkRangeEntry *rangeEnt ) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Remove Range?")) { + subject = rangeEnt; + + std::wstring name = rangeEnt->label; + + if (name.length() == 0) { + std::string wstr = frequencyToStr(rangeEnt->startFreq) + " - " + frequencyToStr(rangeEnt->endFreq); + name = std::wstring(wstr.begin(),wstr.end()); + } + + m_questionText->SetLabelText(L"Are you sure you want to remove the range\n '" + name + L"'?"); + } + + void doClickOK() { + wxGetApp().getBookmarkMgr().removeRange(subject); + wxGetApp().getBookmarkMgr().updateActiveList(); + } + +private: + BookmarkRangeEntry *subject; +}; + + + + + +BookmarkView::BookmarkView( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : BookmarkPanel(parent, id, pos, size, style) { + + rootBranch = m_treeView->AddRoot("Root"); + activeBranch = m_treeView->AppendItem(rootBranch, "Active"); + rangeBranch = m_treeView->AppendItem(rootBranch, "View Ranges"); + bookmarkBranch = m_treeView->AppendItem(rootBranch, "Bookmarks"); + recentBranch = m_treeView->AppendItem(rootBranch, "Recents"); + + expandState["active"] = true; + expandState["range"] = false; + expandState["bookmark"] = true; + expandState["recent"] = true; + + doUpdateActive.store(true); + doUpdateBookmarks.store(true); + bookmarkChoice = nullptr; + dragItem = nullptr; + dragItemId = nullptr; + editingLabel = false; + + m_clearSearchButton->Hide(); + hideProps(); + + m_updateTimer.Start(500); + mouseInView.store(false); + + visualDragItem = nullptr; + nextEnt = nullptr; + nextDemod = nullptr; +} + + +void BookmarkView::onUpdateTimer( wxTimerEvent& event ) { + if (!this->IsShown()) { + return; + } + if (doUpdateActive.load()) { + doUpdateActiveList(); + + doUpdateActive.store(false); + } + if (doUpdateBookmarks.load()) { + wxTreeItemId bmSel = refreshBookmarks(); + if (bmSel) { + m_treeView->SelectItem(bmSel); + } + doUpdateBookmarks.store(false); + } +} + + +void BookmarkView::updateTheme() { + wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground); + wxColour textColor(ThemeMgr::mgr.currentTheme->text); + wxColour btn(ThemeMgr::mgr.currentTheme->button); + wxColour btnHl(ThemeMgr::mgr.currentTheme->buttonHighlight); + + SetBackgroundColour(bgColor); + + m_treeView->SetBackgroundColour(bgColor); + m_treeView->SetForegroundColour(textColor); + + m_propPanel->SetBackgroundColour(bgColor); + m_buttonPanel->SetBackgroundColour(bgColor); + + m_propPanel->SetForegroundColour(textColor); + + m_labelLabel->SetForegroundColour(textColor); + m_frequencyVal->SetForegroundColour(textColor); + m_frequencyLabel->SetForegroundColour(textColor); + m_bandwidthVal->SetForegroundColour(textColor); + m_bandwidthLabel->SetForegroundColour(textColor); + m_modulationVal->SetForegroundColour(textColor); + m_modulationLabel->SetForegroundColour(textColor); + + refreshLayout(); +} + + +void BookmarkView::updateActiveList() { + doUpdateActive.store(true); +} + + +void BookmarkView::updateBookmarks() { + doUpdateBookmarks.store(true); +} + + +void BookmarkView::updateBookmarks(std::string group) { + doUpdateBookmarkGroup.insert(group); + doUpdateBookmarks.store(true); +} + +bool BookmarkView::isKeywordMatch(std::wstring search_str, std::vector &keywords) { + wstring str = search_str; + std::transform(str.begin(), str.end(), str.begin(), towlower); + + for (auto k : keywords) { + if (str.find(k) == wstring::npos) { + return false; + } + } + + return true; +} + +wxTreeItemId BookmarkView::refreshBookmarks() { + + TreeViewItem *prevSel = itemToTVI(m_treeView->GetSelection()); + + BookmarkNames groupNames; + wxGetApp().getBookmarkMgr().getGroups(groupNames); + + if (doUpdateBookmarkGroup.size()) { // Nothing for the moment.. + doUpdateBookmarkGroup.erase(doUpdateBookmarkGroup.begin(), doUpdateBookmarkGroup.end()); + } + + wxTreeItemId bmSelFound = nullptr; + + std::map groupExpandState; + bool searchState = (searchKeywords.size() != 0); + + groups.erase(groups.begin(),groups.end()); + m_treeView->DeleteChildren(bookmarkBranch); + + bool bmExpandState = expandState["bookmark"]; + + for (auto gn_i : groupNames) { + TreeViewItem* tvi = new TreeViewItem(); + tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP; + tvi->groupName = gn_i; + wxTreeItemId group_itm = m_treeView->AppendItem(bookmarkBranch, gn_i); + m_treeView->SetItemData(group_itm, tvi); + groups[gn_i] = group_itm; + if (prevSel != nullptr && prevSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP && gn_i == prevSel->groupName) { + bmSelFound = group_itm; + } + } + + if (searchState || bmExpandState) { + m_treeView->Expand(bookmarkBranch); + } else { + m_treeView->Collapse(bookmarkBranch); + } + + for (auto gn_i : groupNames) { + wxTreeItemId groupItem = groups[gn_i]; + + bool groupExpanded = searchState || wxGetApp().getBookmarkMgr().getExpandState(gn_i); + + BookmarkList bmList = wxGetApp().getBookmarkMgr().getBookmarks(gn_i); + for (auto &bmEnt : bmList) { + std::wstring labelVal = BookmarkMgr::getBookmarkEntryDisplayName(bmEnt); + + if (searchState) { + std::string freqStr = frequencyToStr(bmEnt->frequency); + std::string bwStr = frequencyToStr(bmEnt->bandwidth); + + std::wstring fullText = labelVal + + L" " + bmEnt->userLabel + + 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()); + + if (!isKeywordMatch(fullText, searchKeywords)) { + continue; + } + } + + TreeViewItem* tvi = new TreeViewItem(); + tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK; + tvi->bookmarkEnt = bmEnt; + tvi->groupName = gn_i; + + wxTreeItemId itm = m_treeView->AppendItem(groupItem, labelVal); + m_treeView->SetItemData(itm, tvi); + if (prevSel != nullptr && prevSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK && prevSel->bookmarkEnt == bmEnt && groupExpanded) { + bmSelFound = itm; + } + if (nextEnt == bmEnt) { + bmSelFound = itm; + nextEnt = nullptr; + } + } + + if (groupExpanded) { + m_treeView->Expand(groupItem); + } + } + + + return bmSelFound; +} + + +void BookmarkView::doUpdateActiveList() { + std::vector &demods = wxGetApp().getDemodMgr().getDemodulators(); + +// DemodulatorInstance *activeDemodulator = wxGetApp().getDemodMgr().getActiveDemodulator(); + DemodulatorInstance *lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator(); + + TreeViewItem *prevSel = itemToTVI(m_treeView->GetSelection()); + + // Actives + m_treeView->DeleteChildren(activeBranch); + + bool activeExpandState = expandState["active"]; + bool searchState = (searchKeywords.size() != 0); + + wxTreeItemId selItem = nullptr; + for (auto demod_i : demods) { + wxString activeLabel = BookmarkMgr::getActiveDisplayName(demod_i); + + if (searchState) { + std::string freqStr = frequencyToStr(demod_i->getFrequency()); + std::string bwStr = frequencyToStr(demod_i->getBandwidth()); + std::string mtype = demod_i->getDemodulatorType(); + + 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()); + + if (!isKeywordMatch(fullText, searchKeywords)) { + continue; + } + } + + TreeViewItem* tvi = new TreeViewItem(); + tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE; + tvi->demod = demod_i; + + wxTreeItemId itm = m_treeView->AppendItem(activeBranch,activeLabel); + m_treeView->SetItemData(itm, tvi); + + if (nextDemod != nullptr && nextDemod == demod_i) { + selItem = itm; + nextDemod = nullptr; + } else if (!selItem && activeExpandState && lastActiveDemodulator && lastActiveDemodulator == demod_i) { + selItem = itm; + } + } + + bool rangeExpandState = searchState?false:expandState["range"]; + + BookmarkRangeList bmRanges = wxGetApp().getBookmarkMgr().getRanges(); + m_treeView->DeleteChildren(rangeBranch); + + for (auto &re_i: bmRanges) { + TreeViewItem* tvi = new TreeViewItem(); + tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE; + tvi->rangeEnt = re_i; + + std::wstring labelVal = re_i->label; + + if (labelVal == "") { + std::string wstr = frequencyToStr(re_i->startFreq) + " - " + frequencyToStr(re_i->endFreq); + labelVal = std::wstring(wstr.begin(),wstr.end()); + } + + wxTreeItemId itm = m_treeView->AppendItem(rangeBranch, labelVal); + m_treeView->SetItemData(itm, tvi); + + if (nextRange == re_i) { + selItem = itm; + nextRange = nullptr; + } else if (!selItem && rangeExpandState && prevSel && prevSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE && prevSel->rangeEnt == re_i) { + selItem = itm; + } + } + + + bool recentExpandState = searchState || expandState["recent"]; + + // Recents + BookmarkList bmRecents = wxGetApp().getBookmarkMgr().getRecents(); + m_treeView->DeleteChildren(recentBranch); + + for (auto &bmr_i: bmRecents) { + TreeViewItem* tvi = new TreeViewItem(); + tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT; + tvi->bookmarkEnt = bmr_i; + + 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 (searchKeywords.size()) { + + std::string freqStr = frequencyToStr(bmr_i->frequency); + std::string bwStr = frequencyToStr(bmr_i->bandwidth); + + std::wstring fullText = labelVal + + L" " + bmr_i->userLabel + + 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()); + + if (!isKeywordMatch(fullText, searchKeywords)) { + continue; + } + } + + wxTreeItemId itm = m_treeView->AppendItem(recentBranch, labelVal); + m_treeView->SetItemData(itm, tvi); + + if (nextEnt == bmr_i) { + selItem = itm; + nextEnt = nullptr; + } else if (!selItem && recentExpandState && prevSel && prevSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT && prevSel->bookmarkEnt == bmr_i) { + selItem = itm; + } + } + + if (activeExpandState) { + m_treeView->Expand(activeBranch); + } else { + m_treeView->Collapse(activeBranch); + } + if (recentExpandState) { + m_treeView->Expand(recentBranch); + } else { + m_treeView->Collapse(recentBranch); + } + if (rangeExpandState) { + m_treeView->Expand(rangeBranch); + } else { + m_treeView->Collapse(rangeBranch); + } + + if (selItem != nullptr) { + m_treeView->SelectItem(selItem); + } +} + + +void BookmarkView::onTreeBeginLabelEdit( wxTreeEvent& event ) { + TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(event.GetItem())); + + if (!tvi) { + event.Veto(); + return; + } + + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE || + tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT || + tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK || + tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP || + tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) + { + event.Allow(); + editingLabel = true; + } else { + event.Veto(); + } +} + + +void BookmarkView::onTreeEndLabelEdit( wxTreeEvent& event ) { + wxTreeItemId itm = event.GetItem(); + TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(itm)); + + std::wstring newText = m_treeView->GetEditControl()->GetValue().ToStdWstring(); + + editingLabel = false; + + if (!tvi) { + return; + } + + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + tvi->demod->setDemodulatorUserLabel(newText); + wxGetApp().getBookmarkMgr().updateActiveList(); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { + tvi->bookmarkEnt->label = newText; + tvi->bookmarkEnt->node->child("user_label")->element()->set(newText); + wxGetApp().getBookmarkMgr().updateActiveList(); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + tvi->bookmarkEnt->label = newText; + tvi->bookmarkEnt->node->child("user_label")->element()->set(newText); + wxGetApp().getBookmarkMgr().updateBookmarks(); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { + std::string newGroup = m_treeView->GetEditControl()->GetValue().ToStdString(); + wxGetApp().getBookmarkMgr().renameGroup(tvi->groupName, newGroup); + wxGetApp().getBookmarkMgr().updateBookmarks(); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { + std::wstring newName = m_treeView->GetEditControl()->GetValue().ToStdWstring(); + if (newName.length() != 0) { + tvi->rangeEnt->label = newName; + wxGetApp().getBookmarkMgr().updateActiveList(); + } + } +} + + +void BookmarkView::onTreeActivate( wxTreeEvent& event ) { + wxTreeItemId itm = event.GetItem(); + TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(itm)); + + if (tvi) { + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + if (!tvi->demod->isActive()) { + + wxGetApp().getDemodMgr().setActiveDemodulator(tvi->demod,true); + wxGetApp().getDemodMgr().setActiveDemodulator(tvi->demod,false); + wxGetApp().setFrequency(tvi->demod->getFrequency()); + nextDemod = tvi->demod; + } + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { + wxGetApp().getBookmarkMgr().removeRecent(tvi->bookmarkEnt); + activateBookmark(tvi->bookmarkEnt); + nextEnt = tvi->bookmarkEnt; + wxGetApp().getBookmarkMgr().updateActiveList(); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + activateBookmark(tvi->bookmarkEnt); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { + activateRange(tvi->rangeEnt); + } + } +} + + +void BookmarkView::onTreeCollapse( wxTreeEvent& event ) { + bool searchState = (searchKeywords.size() != 0); + + if (searchState) { + event.Skip(); + return; + } + + if (event.GetItem() == activeBranch) { + expandState["active"] = false; + } else if (event.GetItem() == bookmarkBranch) { + expandState["bookmark"] = false; + } else if (event.GetItem() == recentBranch) { + expandState["recent"] = false; + } else if (event.GetItem() == rangeBranch) { + expandState["range"] = false; + } else { + TreeViewItem *tvi = itemToTVI(event.GetItem()); + + if (tvi != nullptr) { + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { + wxGetApp().getBookmarkMgr().setExpandState(tvi->groupName,false); + } + } + + } +} + + +void BookmarkView::onTreeExpanded( wxTreeEvent& event ) { + bool searchState = (searchKeywords.size() != 0); + + if (searchState) { + event.Skip(); + return; + } + + if (event.GetItem() == activeBranch) { + expandState["active"] = true; + } else if (event.GetItem() == bookmarkBranch) { + expandState["bookmark"] = true; + } else if (event.GetItem() == recentBranch) { + expandState["recent"] = true; + } else if (event.GetItem() == rangeBranch) { + expandState["range"] = true; + } else { + TreeViewItem *tvi = itemToTVI(event.GetItem()); + + if (tvi != nullptr) { + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { + wxGetApp().getBookmarkMgr().setExpandState(tvi->groupName,true); + } + } + + } +} + + +void BookmarkView::onTreeItemMenu( wxTreeEvent& event ) { + if (m_treeView->GetSelection() == bookmarkBranch) { + wxMenu menu; + menu.Append(wxCONTEXT_ADD_GROUP_ID, BOOKMARK_VIEW_STR_ADD_GROUP); + menu.Connect(wxCONTEXT_ADD_GROUP_ID, wxEVT_MENU, (wxObjectEventFunction)&BookmarkView::onMenuItem, nullptr, this); + PopupMenu(&menu); + } +} + + +void BookmarkView::onMenuItem(wxCommandEvent& event) { + if (event.GetId() == wxCONTEXT_ADD_GROUP_ID) { + onAddGroup(event); + } +} + + +bool BookmarkView::isMouseInView() { + if (editingLabel) { + return true; + } + if (m_labelText->HasFocus()) { + return true; + } + return mouseInView.load(); +} + + +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(); + + m_bandwidthLabel->Hide(); + m_bandwidthVal->Hide(); + + m_modulationVal->Hide(); + m_modulationLabel->Hide(); + + m_labelText->Hide(); + m_labelLabel->Hide(); + + m_propPanel->Hide(); + m_buttonPanel->Hide(); +} + + +void BookmarkView::showProps() { + m_propPanel->Show(); + m_propPanel->GetSizer()->Layout(); +} + + +void BookmarkView::clearButtons() { + m_buttonPanel->Hide(); + m_buttonPanel->DestroyChildren(); + bookmarkChoice = nullptr; +} + +void BookmarkView::showButtons() { + m_buttonPanel->Show(); + m_buttonPanel->GetSizer()->Layout(); +} + +void BookmarkView::refreshLayout() { + GetSizer()->Layout(); + Update(); + Refresh(); +} + + +wxButton *BookmarkView::makeButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler) { + wxButton *nButton = new wxButton( m_buttonPanel, wxID_ANY, labelVal); + nButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, handler, nullptr, this); + nButton->SetBackgroundColour(ThemeMgr::mgr.currentTheme->generalBackground); + return nButton; +} + + +wxButton *BookmarkView::addButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler) { + wxButton *nButton = makeButton(parent, labelVal, handler); + parent->GetSizer()->Add( nButton, 0, wxEXPAND); + return nButton; +} + + +void BookmarkView::doBookmarkActive(std::string group, DemodulatorInstance *demod) { + wxGetApp().getBookmarkMgr().addBookmark(group, demod); + wxGetApp().getBookmarkMgr().updateBookmarks(); +} + + +void BookmarkView::doBookmarkRecent(std::string group, BookmarkEntry *be) { + wxGetApp().getBookmarkMgr().removeRecent(be); + wxGetApp().getBookmarkMgr().addBookmark(group, be); + nextEnt = be; + wxGetApp().getBookmarkMgr().updateBookmarks(); + bookmarkSelection(be); +} + + +void BookmarkView::doMoveBookmark(BookmarkEntry *be, std::string group) { + wxGetApp().getBookmarkMgr().moveBookmark(be, group); + nextEnt = be; + wxGetApp().getBookmarkMgr().updateBookmarks(); + bookmarkSelection(be); +} + + +void BookmarkView::doRemoveActive(DemodulatorInstance *demod) { + wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, true); + wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, false); + wxGetApp().removeDemodulator(demod); + wxGetApp().getDemodMgr().deleteThread(demod); +} + + +void BookmarkView::doRemoveRecent(BookmarkEntry *be) { + wxGetApp().getBookmarkMgr().removeRecent(be); + wxGetApp().getBookmarkMgr().updateActiveList(); +} + +void BookmarkView::doClearRecents() { + wxGetApp().getBookmarkMgr().clearRecents(); + wxGetApp().getBookmarkMgr().updateActiveList(); +} + + +void BookmarkView::updateBookmarkChoices() { + if (!bookmarkChoices.empty()) { + bookmarkChoices.erase(bookmarkChoices.begin(),bookmarkChoices.end()); + } + TreeViewItem *activeSel = itemToTVI(m_treeView->GetSelection()); + + bookmarkChoices.push_back(((activeSel != nullptr && activeSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK))?BOOKMARK_VIEW_CHOICE_MOVE:BOOKMARK_VIEW_CHOICE_DEFAULT); + wxGetApp().getBookmarkMgr().getGroups(bookmarkChoices); + bookmarkChoices.push_back(BOOKMARK_VIEW_CHOICE_NEW); +} + +void BookmarkView::addBookmarkChoice(wxWindow *parent) { + updateBookmarkChoices(); + bookmarkChoice = new wxChoice(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, bookmarkChoices, wxALL|wxEXPAND, wxDefaultValidator, "Bookmark"); + bookmarkChoice->Connect( wxEVT_COMMAND_CHOICE_SELECTED, (wxObjectEventFunction)&BookmarkView::onBookmarkChoice, nullptr, this); + parent->GetSizer()->Add(bookmarkChoice, 0, wxALL | wxEXPAND); +} + + +void BookmarkView::onBookmarkChoice( wxCommandEvent &event ) { + + TreeViewItem *tvi = itemToTVI(m_treeView->GetSelection()); + + int numSel = bookmarkChoice->GetCount(); + int sel = bookmarkChoice->GetSelection(); + + if (sel == 0) { + return; + } + + wxString stringVal = ""; + + if (sel == (numSel-1)) { + stringVal = wxGetTextFromUser(BOOKMARK_VIEW_STR_ADD_GROUP_DESC, BOOKMARK_VIEW_STR_ADD_GROUP, ""); + } else { + stringVal = bookmarkChoices[sel]; + } + + if (stringVal == "") { + return; + } + + if (tvi != nullptr) { + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + doBookmarkActive(stringVal.ToStdString(), tvi->demod); + } + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { + doBookmarkRecent(stringVal.ToStdString(), tvi->bookmarkEnt); + } + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + doMoveBookmark(tvi->bookmarkEnt, stringVal.ToStdString()); + } + } +} + + +void BookmarkView::activeSelection(DemodulatorInstance *dsel) { + + m_frequencyVal->SetLabelText(frequencyToStr(dsel->getFrequency())); + m_bandwidthVal->SetLabelText(frequencyToStr(dsel->getBandwidth())); + m_modulationVal->SetLabelText(dsel->getDemodulatorType()); + m_labelText->SetValue(dsel->getDemodulatorUserLabel()); + + hideProps(); + + m_frequencyVal->Show(); + m_frequencyLabel->Show(); + + m_bandwidthVal->Show(); + m_bandwidthLabel->Show(); + + m_modulationVal->Show(); + m_modulationLabel->Show(); + + m_labelText->Show(); + m_labelLabel->Show(); + + clearButtons(); + + addBookmarkChoice(m_buttonPanel); + addButton(m_buttonPanel, "Remove Active", wxCommandEventHandler( BookmarkView::onRemoveActive )); + + showProps(); + showButtons(); + refreshLayout(); +} + + +void BookmarkView::activateBookmark(BookmarkEntry *bmEnt) { + DemodulatorInstance *newDemod = wxGetApp().getDemodMgr().loadInstance(bmEnt->node); + + nextDemod = newDemod; + + wxTreeItemId selItem = m_treeView->GetSelection(); + if (selItem) { + m_treeView->SelectItem(selItem, false); + } + + long long freq = newDemod->getFrequency(); + long long currentFreq = wxGetApp().getFrequency(); + long long currentRate = wxGetApp().getSampleRate(); + + if ( ( abs(freq - currentFreq) > currentRate / 2 ) || ( abs( currentFreq - freq) > currentRate / 2 ) ) { + wxGetApp().setFrequency(freq); + } + + newDemod->run(); + newDemod->setActive(true); + wxGetApp().bindDemodulator(newDemod); + + doUpdateActiveList(); +} + + +void BookmarkView::activateRange(BookmarkRangeEntry *rangeEnt) { + wxGetApp().setFrequency(rangeEnt->freq); + wxGetApp().getAppFrame()->setViewState(rangeEnt->startFreq + (rangeEnt->endFreq - rangeEnt->startFreq) / 2, rangeEnt->endFreq - rangeEnt->startFreq); +} + + +void BookmarkView::bookmarkSelection(BookmarkEntry *bmSel) { + + m_frequencyVal->SetLabelText(frequencyToStr(bmSel->frequency)); + m_bandwidthVal->SetLabelText(frequencyToStr(bmSel->bandwidth)); + m_modulationVal->SetLabelText(bmSel->type); + m_labelText->SetValue(bmSel->label); + + hideProps(); + + m_frequencyVal->Show(); + m_frequencyLabel->Show(); + + m_bandwidthVal->Show(); + m_bandwidthLabel->Show(); + + m_modulationVal->Show(); + m_modulationLabel->Show(); + + m_labelText->Show(); + m_labelLabel->Show(); + + clearButtons(); + + addBookmarkChoice(m_buttonPanel); + addButton(m_buttonPanel, "Activate Bookmark", wxCommandEventHandler( BookmarkView::onActivateBookmark )); + addButton(m_buttonPanel, "Remove Bookmark", wxCommandEventHandler( BookmarkView::onRemoveBookmark )); + + showProps(); + showButtons(); + refreshLayout(); +} + + +void BookmarkView::recentSelection(BookmarkEntry *bmSel) { + + m_frequencyVal->SetLabelText(frequencyToStr(bmSel->frequency)); + m_bandwidthVal->SetLabelText(frequencyToStr(bmSel->bandwidth)); + m_modulationVal->SetLabelText(bmSel->type); + m_labelText->SetValue(bmSel->label); + + hideProps(); + + m_frequencyVal->Show(); + m_frequencyLabel->Show(); + + m_bandwidthVal->Show(); + m_bandwidthLabel->Show(); + + m_modulationVal->Show(); + m_modulationLabel->Show(); + + m_labelText->Show(); + m_labelLabel->Show(); + + clearButtons(); + + addBookmarkChoice(m_buttonPanel); + addButton(m_buttonPanel, "Activate Recent", wxCommandEventHandler( BookmarkView::onActivateRecent )); + addButton(m_buttonPanel, "Remove Recent", wxCommandEventHandler( BookmarkView::onRemoveRecent )); + + showProps(); + showButtons(); + refreshLayout(); +} + +void BookmarkView::groupSelection(std::string groupName) { + + clearButtons(); + + hideProps(); + + // m_labelText->SetValue(groupSel); + + // m_labelText->Show(); + // m_labelLabel->Show(); + + addButton(m_buttonPanel, "Remove Group", wxCommandEventHandler( BookmarkView::onRemoveGroup )); + addButton(m_buttonPanel, BOOKMARK_VIEW_STR_RENAME_GROUP, wxCommandEventHandler( BookmarkView::onRenameGroup )); + + // showProps(); + + showButtons(); + refreshLayout(); +} + + +void BookmarkView::rangeSelection(BookmarkRangeEntry *re) { + + clearButtons(); + + hideProps(); + + m_labelText->SetValue(re->label); + + m_labelText->Show(); + m_labelLabel->Show(); + + m_frequencyVal->Show(); + m_frequencyLabel->Show(); + + std::string strFreq = frequencyToStr(re->startFreq) + "-" + frequencyToStr(re->endFreq); + + m_frequencyVal->SetLabelText(std::wstring(strFreq.begin(),strFreq.end())); + + showProps(); + + addButton(m_buttonPanel, "Go to Range", wxCommandEventHandler( BookmarkView::onActivateRange )); + addButton(m_buttonPanel, "Remove Range", wxCommandEventHandler( BookmarkView::onRemoveRange )); + + showButtons(); + refreshLayout(); +} + + +void BookmarkView::bookmarkBranchSelection() { + + clearButtons(); + hideProps(); + + addButton(m_buttonPanel, BOOKMARK_VIEW_STR_ADD_GROUP, wxCommandEventHandler( BookmarkView::onAddGroup )); + + showButtons(); + refreshLayout(); +} + + +void BookmarkView::recentBranchSelection() { + clearButtons(); + hideProps(); + + addButton(m_buttonPanel, BOOKMARK_VIEW_STR_CLEAR_RECENT, wxCommandEventHandler( BookmarkView::onClearRecents )); + + showButtons(); + refreshLayout(); + + this->Layout(); +} + + +void BookmarkView::rangeBranchSelection() { + clearButtons(); + hideProps(); + + m_labelText->SetValue(wxT("")); + m_labelText->Show(); + m_labelLabel->Show(); + + showProps(); + + addButton(m_buttonPanel, "Add Active Range", wxCommandEventHandler( BookmarkView::onAddRange )); + + showButtons(); + refreshLayout(); + + this->Layout(); +} + + +void BookmarkView::activeBranchSelection() { + hideProps(); + this->Layout(); +} + + +void BookmarkView::onTreeSelect( wxTreeEvent& event ) { + wxTreeItemId itm = event.GetItem(); + TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(itm)); + + if (!tvi) { + + if (itm == bookmarkBranch) { + bookmarkBranchSelection(); + } else if (itm == activeBranch) { + activeBranchSelection(); + } else if (itm == recentBranch) { + recentBranchSelection(); + } else if (itm == rangeBranch) { + rangeBranchSelection(); + } else { + hideProps(); + this->Layout(); + } + + return; + } + + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + activeSelection(tvi->demod); + if (tvi->demod->isActive()) { + wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, true); + wxGetApp().getDemodMgr().setActiveDemodulator(tvi->demod, false); + } + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { + recentSelection(tvi->bookmarkEnt); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + bookmarkSelection(tvi->bookmarkEnt); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { + groupSelection(tvi->groupName); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { + rangeSelection(tvi->rangeEnt); + } else { + hideProps(); + this->Layout(); + } +} + + +void BookmarkView::onTreeSelectChanging( wxTreeEvent& event ) { + event.Skip(); +} + + +void BookmarkView::onLabelText( wxCommandEvent& event ) { + std::wstring newLabel = m_labelText->GetValue().ToStdWstring(); + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel != nullptr) { + if (curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + curSel->demod->setDemodulatorUserLabel(newLabel); + wxGetApp().getBookmarkMgr().updateActiveList(); + } else if (curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + curSel->bookmarkEnt->label = m_labelText->GetValue().ToStdWstring(); + curSel->bookmarkEnt->node->child("user_label")->element()->set(newLabel); + wxGetApp().getBookmarkMgr().updateBookmarks(); + } else if (curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { + curSel->bookmarkEnt->label = m_labelText->GetValue().ToStdWstring(); + curSel->bookmarkEnt->node->child("user_label")->element()->set(newLabel); + wxGetApp().getBookmarkMgr().updateActiveList(); + } else if (curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { + curSel->rangeEnt->label = m_labelText->GetValue().ToStdWstring(); + wxGetApp().getBookmarkMgr().updateActiveList(); + } + } + + // else if (groupSel != "") { +// std::string newGroupName = m_labelText->GetValue().ToStdString(); +// wxGetApp().getBookmarkMgr().renameGroup(groupSel, newGroupName); +// groupSel = newGroupName; +// wxGetApp().getBookmarkMgr().updateBookmarks(); +// } +} + + +void BookmarkView::onDoubleClickFreq( wxMouseEvent& event ) { + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, true); + wxGetApp().getDemodMgr().setActiveDemodulator(curSel->demod, false); + wxGetApp().showFrequencyInput(FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_DEFAULT); + } +} + + +void BookmarkView::onDoubleClickBandwidth( wxMouseEvent& event ) { + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, true); + wxGetApp().getDemodMgr().setActiveDemodulator(curSel->demod, false); + wxGetApp().showFrequencyInput(FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_BANDWIDTH); + } +} + + +void BookmarkView::onRemoveActive( wxCommandEvent& event ) { + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + if (editingLabel) { + return; + } + doRemoveActive(curSel->demod); + m_treeView->Delete(m_treeView->GetSelection()); + } +} + + +void BookmarkView::onRemoveBookmark( wxCommandEvent& event ) { + if (editingLabel) { + return; + } + + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + ActionDialog::showDialog(new ActionDialogRemoveBookmark(curSel->bookmarkEnt)); + } +} + + +void BookmarkView::onActivateBookmark( wxCommandEvent& event ) { + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + activateBookmark(curSel->bookmarkEnt); + } +} + + +void BookmarkView::onActivateRecent( wxCommandEvent& event ) { + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { + wxGetApp().getBookmarkMgr().removeRecent(curSel->bookmarkEnt); + activateBookmark(curSel->bookmarkEnt); + m_treeView->Delete(m_treeView->GetSelection()); + wxGetApp().getBookmarkMgr().updateActiveList(); + } +} + + +void BookmarkView::onRemoveRecent ( wxCommandEvent& event ) { + if (editingLabel) { + return; + } + + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { + wxGetApp().getBookmarkMgr().removeRecent(curSel->bookmarkEnt); + m_treeView->Delete(m_treeView->GetSelection()); + wxGetApp().getBookmarkMgr().updateActiveList(); + } +} + +void BookmarkView::onClearRecents ( wxCommandEvent& event ) { + if (editingLabel) { + return; + } + doClearRecents(); +} + + +void BookmarkView::onAddGroup( wxCommandEvent& event ) { + wxString stringVal = wxGetTextFromUser(BOOKMARK_VIEW_STR_ADD_GROUP_DESC, BOOKMARK_VIEW_STR_ADD_GROUP, ""); + if (stringVal.ToStdString() != "") { + wxGetApp().getBookmarkMgr().addGroup(stringVal.ToStdString()); + wxGetApp().getBookmarkMgr().updateBookmarks(); + } +} + + +void BookmarkView::onRemoveGroup( wxCommandEvent& event ) { + if (editingLabel) { + return; + } + + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { + ActionDialog::showDialog(new ActionDialogRemoveGroup(curSel->groupName)); + } +} + + +void BookmarkView::onRenameGroup( wxCommandEvent& event ) { + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (!curSel || curSel->type != TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { + return; + } + + wxString stringVal = ""; + stringVal = wxGetTextFromUser(BOOKMARK_VIEW_STR_RENAME_GROUP, "New Group Name", curSel->groupName); + + std::string newGroupName = stringVal.Trim().ToStdString(); + + if (newGroupName != "") { + wxGetApp().getBookmarkMgr().renameGroup(curSel->groupName, newGroupName); + wxGetApp().getBookmarkMgr().updateBookmarks(); + } +} + + +void BookmarkView::onAddRange( wxCommandEvent& event ) { + BookmarkRangeEntry *re = new BookmarkRangeEntry; + re->freq = wxGetApp().getFrequency(); + re->startFreq = wxGetApp().getAppFrame()->getViewCenterFreq() - (wxGetApp().getAppFrame()->getViewBandwidth()/2); + re->endFreq = wxGetApp().getAppFrame()->getViewCenterFreq() + (wxGetApp().getAppFrame()->getViewBandwidth()/2); + re->label = m_labelText->GetValue(); + wxGetApp().getBookmarkMgr().addRange(re); + wxGetApp().getBookmarkMgr().updateActiveList(); +} + + +void BookmarkView::onRemoveRange( wxCommandEvent& event ) { + if (editingLabel) { + return; + } + + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { + ActionDialog::showDialog(new ActionDialogRemoveRange(curSel->rangeEnt)); + } +} + + +void BookmarkView::onRenameRange( wxCommandEvent& event ) { + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (!curSel || curSel->type != TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { + return; + } + + wxString stringVal = ""; + stringVal = wxGetTextFromUser(BOOKMARK_VIEW_STR_RENAME_GROUP, "New Group Name", curSel->groupName); + + std::string newGroupName = stringVal.Trim().ToStdString(); + + if (newGroupName != "") { + wxGetApp().getBookmarkMgr().renameGroup(curSel->groupName, newGroupName); + wxGetApp().getBookmarkMgr().updateBookmarks(); + } +} + +void BookmarkView::onActivateRange( wxCommandEvent& event ) { + TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); + + if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { + activateRange(curSel->rangeEnt); + } +} + + +void BookmarkView::onTreeBeginDrag( wxTreeEvent& event ) { + TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(event.GetItem())); + + dragItem = nullptr; + dragItemId = nullptr; + + SetCursor(wxCURSOR_CROSS); + + if (!tvi) { + event.Veto(); + return; + } + + bool bAllow = false; + std::wstring dragItemName; + + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + bAllow = true; + dragItemName = BookmarkMgr::getActiveDisplayName(tvi->demod); + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT || tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + bAllow = true; + dragItemName = BookmarkMgr::getBookmarkEntryDisplayName(tvi->bookmarkEnt); + } + + if (bAllow) { + wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground); + wxColour textColor(ThemeMgr::mgr.currentTheme->text); + + m_treeView->SetBackgroundColour(textColor); + m_treeView->SetForegroundColour(bgColor); +// m_treeView->SetToolTip("Dragging " + dragItemName); + + dragItem = tvi; + dragItemId = event.GetItem(); + + visualDragItem = new BookmarkViewVisualDragItem(dragItemName); + + event.Allow(); + } else { + event.Veto(); + } +} + + +void BookmarkView::onTreeEndDrag( wxTreeEvent& event ) { + + wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground); + wxColour textColor(ThemeMgr::mgr.currentTheme->text); + + m_treeView->SetBackgroundColour(bgColor); + m_treeView->SetForegroundColour(textColor); + m_treeView->UnsetToolTip(); + + SetCursor(wxCURSOR_ARROW); + + if (visualDragItem != nullptr) { + visualDragItem->Destroy(); + delete visualDragItem; + visualDragItem = nullptr; + } + + if (!event.GetItem()) { + event.Veto(); + return; + } + + TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(event.GetItem())); + + if (!tvi) { + if (event.GetItem() == bookmarkBranch) { + if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { + doBookmarkActive(BOOKMARK_VIEW_STR_UNNAMED, dragItem->demod); + } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { + doBookmarkRecent(BOOKMARK_VIEW_STR_UNNAMED, dragItem->bookmarkEnt); + } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + doMoveBookmark(dragItem->bookmarkEnt, BOOKMARK_VIEW_STR_UNNAMED); + } + } + return; + } + + if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { + if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { // Active -> Group Item + doBookmarkActive(tvi->groupName, dragItem->demod); + } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { // Recent -> Group Item + doBookmarkRecent(tvi->groupName, dragItem->bookmarkEnt); + m_treeView->Delete(dragItemId); + } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { // Bookmark -> Group Item + doMoveBookmark(dragItem->bookmarkEnt, tvi->groupName); + } + } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { + if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { // Active -> Same Group + doBookmarkActive(tvi->groupName, dragItem->demod); + } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { // Recent -> Same Group + doBookmarkRecent(tvi->groupName, dragItem->bookmarkEnt); + m_treeView->Delete(dragItemId); + } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { // Bookmark -> Same Group + doMoveBookmark(dragItem->bookmarkEnt, tvi->groupName); + } + } +} + + +void BookmarkView::onTreeItemGetTooltip( wxTreeEvent& event ) { + + event.Skip(); +} + + +void BookmarkView::onEnterWindow( wxMouseEvent& event ) { + mouseInView.store(true); +} + + +void BookmarkView::onLeaveWindow( wxMouseEvent& event ) { + mouseInView.store(false); +} + +void BookmarkView::onMotion( wxMouseEvent& event ) { + wxPoint pos = ClientToScreen(event.GetPosition()); + + pos += wxPoint(30,-10); + + if (visualDragItem != nullptr) { + visualDragItem->SetPosition(pos); + } + + event.Skip(); +} + +TreeViewItem *BookmarkView::itemToTVI(wxTreeItemId item) { + TreeViewItem* tvi = nullptr; + + if (item != nullptr) { + tvi = dynamic_cast(m_treeView->GetItemData(item)); + } + + return tvi; +} + +void BookmarkView::onSearchTextFocus( wxMouseEvent& event ) { + if (!m_searchText->IsEmpty()) { + if (m_searchText->GetValue() == L"Search..") { + m_searchText->SetValue(L""); + } + } +} + + +void BookmarkView::onSearchText( wxCommandEvent& event ) { + wstring searchText = m_searchText->GetValue().Trim().Lower().ToStdWstring(); + + if (!searchKeywords.empty()) { + searchKeywords.erase(searchKeywords.begin(),searchKeywords.end()); + } + + if (searchText.length() != 0) { + std::wstringstream searchTextLo(searchText); + wstring tmp; + + 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'; + } + + } + } + + if (searchKeywords.size() != 0 && !m_clearSearchButton->IsShown()) { + m_clearSearchButton->Show(); + refreshLayout(); + } else if (searchKeywords.size() == 0 && m_clearSearchButton->IsShown()) { + m_clearSearchButton->Hide(); + refreshLayout(); + } + + wxGetApp().getBookmarkMgr().updateActiveList(); + wxGetApp().getBookmarkMgr().updateBookmarks(); +} + + +void BookmarkView::onClearSearch( wxCommandEvent& event ) { + m_clearSearchButton->Hide(); + m_searchText->SetValue(L"Search.."); + m_treeView->SetFocus(); + if (!searchKeywords.empty()) { + searchKeywords.erase(searchKeywords.begin(),searchKeywords.end()); + } + wxGetApp().getBookmarkMgr().updateActiveList(); + wxGetApp().getBookmarkMgr().updateBookmarks(); + 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 new file mode 100644 index 0000000..1307633 --- /dev/null +++ b/src/forms/Bookmark/BookmarkView.h @@ -0,0 +1,169 @@ +#pragma once + +#include "wx/choice.h" +#include "wx/dialog.h" + +#include "BookmarkPanel.h" +#include "BookmarkMgr.h" + + +class TreeViewItem : public wxTreeItemData { +public: + enum TreeViewItemType { + TREEVIEW_ITEM_TYPE_GROUP, + TREEVIEW_ITEM_TYPE_ACTIVE, + TREEVIEW_ITEM_TYPE_RECENT, + TREEVIEW_ITEM_TYPE_BOOKMARK, + TREEVIEW_ITEM_TYPE_RANGE + }; + + TreeViewItem() { + bookmarkEnt = nullptr; + demod = nullptr; + rangeEnt = nullptr; + }; + + TreeViewItemType type; + BookmarkEntry *bookmarkEnt; + BookmarkRangeEntry *rangeEnt; + DemodulatorInstance *demod; + std::string groupName; +}; + + +class BookmarkViewVisualDragItem : public wxDialog { +public: + BookmarkViewVisualDragItem(wxString labelValue = L"Popup"); +}; + + + +class BookmarkView : public BookmarkPanel { +public: + BookmarkView( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxTAB_TRAVERSAL ); + + void updateActiveList(); + void updateBookmarks(); + bool isKeywordMatch(std::wstring str, std::vector &keywords); + void updateBookmarks(std::string group); + + wxTreeItemId refreshBookmarks(); + void updateTheme(); + 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); + void bookmarkSelection(BookmarkEntry *bmSel); + void rangeSelection(BookmarkRangeEntry *re); + void activateBookmark(BookmarkEntry *bmEnt); + void activateRange(BookmarkRangeEntry *rangeEnt); + void recentSelection(BookmarkEntry *bmSel); + void groupSelection(std::string groupName); + void bookmarkBranchSelection(); + void recentBranchSelection(); + void rangeBranchSelection(); + void activeBranchSelection(); + + void hideProps(); + void showProps(); + + void onUpdateTimer( wxTimerEvent& event ); + + void doUpdateActiveList(); + + void onTreeBeginLabelEdit( wxTreeEvent& event ); + void onTreeEndLabelEdit( wxTreeEvent& event ); + void onTreeActivate( wxTreeEvent& event ); + void onTreeCollapse( wxTreeEvent& event ); + void onTreeExpanded( wxTreeEvent& event ); + void onTreeItemMenu( wxTreeEvent& event ); + void onTreeSelect( wxTreeEvent& event ); + void onTreeSelectChanging( wxTreeEvent& event ); + void onLabelText( wxCommandEvent& event ); + void onDoubleClickFreq( wxMouseEvent& event ); + void onDoubleClickBandwidth( wxMouseEvent& event ); + void onTreeBeginDrag( wxTreeEvent& event ); + void onTreeEndDrag( wxTreeEvent& event ); + void onTreeItemGetTooltip( wxTreeEvent& event ); + void onEnterWindow( wxMouseEvent& event ); + void onLeaveWindow( wxMouseEvent& event ); + void onMotion( wxMouseEvent& event ); + + void onSearchTextFocus( wxMouseEvent& event ); + void onSearchText( wxCommandEvent& event ); + void onClearSearch( wxCommandEvent& event ); + + void clearButtons(); + void showButtons(); + void refreshLayout(); + + wxButton *makeButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler); + wxButton *addButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler); + + void doBookmarkActive(std::string group, DemodulatorInstance *demod); + void doBookmarkRecent(std::string group, BookmarkEntry *be); + void doMoveBookmark(BookmarkEntry *be, std::string group); + void doRemoveActive(DemodulatorInstance *demod); + void doRemoveRecent(BookmarkEntry *be); + void doClearRecents(); + + void updateBookmarkChoices(); + void addBookmarkChoice(wxWindow *parent); + void onBookmarkChoice( wxCommandEvent &event ); + + void onRemoveActive( wxCommandEvent& event ); + void onRemoveBookmark( wxCommandEvent& event ); + + void onActivateBookmark( wxCommandEvent& event ); + void onActivateRecent( wxCommandEvent& event ); + void onRemoveRecent ( wxCommandEvent& event ); + void onClearRecents ( wxCommandEvent& event ); + + void onAddGroup( wxCommandEvent& event ); + void onRemoveGroup( wxCommandEvent& event ); + void onRenameGroup( wxCommandEvent& event ); + + void onAddRange( wxCommandEvent& event ); + void onRemoveRange( wxCommandEvent& event ); + void onRenameRange( wxCommandEvent& event ); + void onActivateRange( wxCommandEvent& event ); + + TreeViewItem *itemToTVI(wxTreeItemId item); + + std::atomic_bool mouseInView; + + wxTreeItemId rootBranch, activeBranch, bookmarkBranch, recentBranch, rangeBranch; + + std::map expandState; + + TreeViewItem *dragItem; + wxTreeItemId dragItemId; + BookmarkViewVisualDragItem *visualDragItem; + + bool editingLabel; + + // Bookmarks + std::atomic_bool doUpdateBookmarks; + std::set< std::string > doUpdateBookmarkGroup; + BookmarkNames groupNames; + std::map groups; + wxArrayString bookmarkChoices; + wxChoice *bookmarkChoice; + + // Active + std::atomic_bool doUpdateActive; + + // Focus + BookmarkEntry *nextEnt; + BookmarkRangeEntry *nextRange; + DemodulatorInstance *nextDemod; + + // Search + std::vector searchKeywords; +}; diff --git a/src/forms/Dialog/ActionDialog.cpp b/src/forms/Dialog/ActionDialog.cpp new file mode 100644 index 0000000..acb4b43 --- /dev/null +++ b/src/forms/Dialog/ActionDialog.cpp @@ -0,0 +1,61 @@ +#include "ActionDialog.h" + + +ActionDialog *ActionDialog::activeDialog = nullptr; + +ActionDialog::ActionDialog( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) + : ActionDialogBase(parent, id, title, pos, size, style) { +} + + +ActionDialog::~ActionDialog() { + +} + +void ActionDialog::showDialog(ActionDialog *dlg) { + if (activeDialog) { // rejected + delete dlg; + return; + } + activeDialog = dlg; + dlg->Layout(); + dlg->Fit(); + dlg->ShowModal(); +} + +ActionDialog *ActionDialog::getActiveDialog() { + return activeDialog; +} + + +void ActionDialog::setActiveDialog(ActionDialog *dlg) { + activeDialog = dlg; +} + + +void ActionDialog::onClickCancel( wxCommandEvent& event ) { + ActionDialog *dlg = activeDialog; + ActionDialog::setActiveDialog(nullptr); + dlg->EndModal(0); + doClickCancel(); + delete dlg; +} + + +void ActionDialog::onClickOK( wxCommandEvent& event ) { + ActionDialog *dlg = activeDialog; + ActionDialog::setActiveDialog(nullptr); + dlg->EndModal(0); + doClickOK(); + delete dlg; +} + + +void ActionDialog::doClickCancel() { + +} + + +void ActionDialog::doClickOK() { + +} diff --git a/src/forms/Dialog/ActionDialog.h b/src/forms/Dialog/ActionDialog.h new file mode 100644 index 0000000..698479f --- /dev/null +++ b/src/forms/Dialog/ActionDialog.h @@ -0,0 +1,21 @@ +#include "ActionDialogBase.h" +#include + +class ActionDialog : public ActionDialogBase { +public: + ActionDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("QuestionTitle"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~ActionDialog(); + + void onClickCancel( wxCommandEvent& event ); + void onClickOK( wxCommandEvent& event ); + + virtual void doClickCancel(); + virtual void doClickOK(); + + static ActionDialog *getActiveDialog(); + static void setActiveDialog(ActionDialog *dlg); + static void showDialog(ActionDialog *dlg); + +private: + static ActionDialog *activeDialog; +}; diff --git a/src/forms/Dialog/ActionDialogBase.cpp b/src/forms/Dialog/ActionDialogBase.cpp new file mode 100644 index 0000000..aaf21d3 --- /dev/null +++ b/src/forms/Dialog/ActionDialogBase.cpp @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Aug 23 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "ActionDialogBase.h" + +/////////////////////////////////////////////////////////////////////////// + +ActionDialogBase::ActionDialogBase( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* mainSizer; + mainSizer = new wxBoxSizer( wxVERTICAL ); + + m_questionText = new wxStaticText( this, wxID_ANY, wxT("Question"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE ); + m_questionText->Wrap( -1 ); + mainSizer->Add( m_questionText, 1, wxALL|wxEXPAND, 5 ); + + wxBoxSizer* buttonSizer; + buttonSizer = new wxBoxSizer( wxHORIZONTAL ); + + m_cancelButton = new wxButton( this, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0 ); + buttonSizer->Add( m_cancelButton, 1, wxALL|wxEXPAND, 5 ); + + m_okButton = new wxButton( this, wxID_ANY, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0 ); + buttonSizer->Add( m_okButton, 1, wxALL|wxEXPAND, 5 ); + + + mainSizer->Add( buttonSizer, 1, wxEXPAND, 5 ); + + + this->SetSizer( mainSizer ); + this->Layout(); + mainSizer->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + m_cancelButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActionDialogBase::onClickCancel ), NULL, this ); + m_okButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActionDialogBase::onClickOK ), NULL, this ); +} + +ActionDialogBase::~ActionDialogBase() +{ + // Disconnect Events + m_cancelButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActionDialogBase::onClickCancel ), NULL, this ); + m_okButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActionDialogBase::onClickOK ), NULL, this ); + +} diff --git a/src/forms/Dialog/ActionDialogBase.fbp b/src/forms/Dialog/ActionDialogBase.fbp new file mode 100644 index 0000000..8dc234d --- /dev/null +++ b/src/forms/Dialog/ActionDialogBase.fbp @@ -0,0 +1,369 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + ActionDialogBase + 1000 + none + 0 + ActionDialogBase + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + ActionDialogBase + + + wxDEFAULT_DIALOG_STYLE + + QuestionTitle + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mainSizer + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Question + + 0 + + + 0 + + 1 + m_questionText + 1 + + + protected + 1 + + Resizable + 1 + + wxALIGN_CENTRE + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + + buttonSizer + wxHORIZONTAL + none + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Cancel + + 0 + + + 0 + + 1 + m_cancelButton + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + onClickCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + OK + + 0 + + + 0 + + 1 + m_okButton + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + onClickOK + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/forms/Dialog/ActionDialogBase.h b/src/forms/Dialog/ActionDialogBase.h new file mode 100644 index 0000000..72b0b85 --- /dev/null +++ b/src/forms/Dialog/ActionDialogBase.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Aug 23 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __ACTIONDIALOGBASE_H__ +#define __ACTIONDIALOGBASE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class ActionDialogBase +/////////////////////////////////////////////////////////////////////////////// +class ActionDialogBase : public wxDialog +{ + private: + + protected: + wxStaticText* m_questionText; + wxButton* m_cancelButton; + wxButton* m_okButton; + + // Virtual event handlers, overide them in your derived class + virtual void onClickCancel( wxCommandEvent& event ) { event.Skip(); } + virtual void onClickOK( wxCommandEvent& event ) { event.Skip(); } + + + public: + + ActionDialogBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("QuestionTitle"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~ActionDialogBase(); + +}; + +#endif //__ACTIONDIALOGBASE_H__ diff --git a/src/forms/SDRDevices/SDRDevices.cpp b/src/forms/SDRDevices/SDRDevices.cpp index b069547..f1554ab 100644 --- a/src/forms/SDRDevices/SDRDevices.cpp +++ b/src/forms/SDRDevices/SDRDevices.cpp @@ -8,7 +8,7 @@ #include "CubicSDR.h" -SDRDevicesDialog::SDRDevicesDialog( wxWindow* parent ): devFrame( parent ) { +SDRDevicesDialog::SDRDevicesDialog( wxWindow* parent ): devFrame( parent, wxID_ANY, wxT(CUBICSDR_INSTALL_NAME " :: SDR Devices")) { refresh = true; failed = false; m_refreshButton->Disable(); diff --git a/src/panel/SpectrumPanel.cpp b/src/panel/SpectrumPanel.cpp index ec14b18..e5ab943 100644 --- a/src/panel/SpectrumPanel.cpp +++ b/src/panel/SpectrumPanel.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "CubicSDR.h" #include "ColorTheme.h" #include "CubicSDRDefs.h" @@ -13,6 +14,7 @@ SpectrumPanel::SpectrumPanel() { floorValue = 0; ceilValue = 1; showDb = false; + useDbOfs = false; fftSize = DEFAULT_FFT_SIZE; bandwidth = DEFAULT_DEMOD_BW; freq = 0; @@ -86,6 +88,14 @@ bool SpectrumPanel::getShowDb() { return showDb; } +void SpectrumPanel::setUseDBOffset(bool useOfs) { + this->useDbOfs = useOfs; +} + +bool SpectrumPanel::getUseDBOffset() { + return useDbOfs; +} + void SpectrumPanel::setPoints(std::vector &points) { this->points.assign(points.begin(), points.end()); @@ -272,11 +282,11 @@ void SpectrumPanel::drawPanelContents() { if (showDb) { float dbPanelWidth = (1.0 / viewWidth)*88.0 * GLFont::getScaleFactor(); float dbPanelHeight = (1.0/viewHeight)*14.0 * GLFont::getScaleFactor(); - + float dbOfs = useDbOfs?wxGetApp().getConfig()->getDBOffset():0; std::stringstream ssLabel(""); if (getCeilValue() != getFloorValue() && fftSize) { - ssLabel << std::fixed << std::setprecision(1) << (20.0 * log10(2.0*(getCeilValue())/(double)fftSize)) << "dB"; + ssLabel << std::fixed << std::setprecision(1) << (dbOfs + 20.0 * log10(2.0*(getCeilValue())/(double)fftSize)) << "dB"; } dbPanelCeil.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT); dbPanelCeil.setSize(dbPanelWidth, dbPanelHeight); @@ -285,7 +295,7 @@ void SpectrumPanel::drawPanelContents() { ssLabel.str(""); if (getCeilValue() != getFloorValue() && fftSize) { - ssLabel << (20.0 * log10(2.0*(getFloorValue())/(double)fftSize)) << "dB"; + ssLabel << (dbOfs + 20.0 * log10(2.0*(getFloorValue())/(double)fftSize)) << "dB"; } dbPanelFloor.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT); diff --git a/src/panel/SpectrumPanel.h b/src/panel/SpectrumPanel.h index 5cdb3c6..3885adf 100644 --- a/src/panel/SpectrumPanel.h +++ b/src/panel/SpectrumPanel.h @@ -29,7 +29,10 @@ public: void setShowDb(bool showDb); bool getShowDb(); - + + void setUseDBOffset(bool useOfs); + bool getUseDBOffset(); + protected: void drawPanelContents(); @@ -43,5 +46,5 @@ private: GLTextPanel dbPanelCeil; GLTextPanel dbPanelFloor; - bool showDb; -}; \ No newline at end of file + bool showDb, useDbOfs; +}; diff --git a/src/util/DataTree.cpp b/src/util/DataTree.cpp index 3c1d1ce..850e257 100755 --- a/src/util/DataTree.cpp +++ b/src/util/DataTree.cpp @@ -41,6 +41,14 @@ using namespace std; DataElement::DataElement() : data_type(DATA_NULL), data_size(0), unit_size(0), data_val(NULL) { } +DataElement::DataElement(DataElement &cloneFrom) : data_type(cloneFrom.getDataType()), unit_size(cloneFrom.getUnitSize()) { + data_val = NULL; + data_init(cloneFrom.getDataSize()); + if (data_size) { + memcpy(data_val, cloneFrom.getDataPointer(), data_size); + } +} + DataElement::~DataElement() { if (data_val) { delete[] data_val; @@ -427,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) { @@ -482,6 +495,22 @@ 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; + data_elem = new DataElement(cloneFrom); +} + DataNode::~DataNode() { while (children.size()) { DataNode *del = children.back(); @@ -510,6 +539,34 @@ DataNode *DataNode::newChild(const char *name_in) { return children.back(); } +DataNode *DataNode::newChild(const char *name_in, DataNode *otherNode) { + children.push_back(otherNode); + childmap[name_in].push_back(children.back()); + + children.back()->setParentNode(*this); + + return children.back(); +} + +DataNode *DataNode::newChildCloneFrom(const char *name_in, DataNode *cloneFrom) { + DataNode *cloneNode = new DataNode(name_in, *cloneFrom->element()); + + children.push_back(cloneNode); + 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); + } + + cloneFrom->rewind(); + + return children.back(); +} + + DataNode *DataNode::child(const char *name_in, int index) { DataNode *child_ret; @@ -565,6 +622,7 @@ DataNode *DataNode::getNext(const char *name_in) { void DataNode::rewind() { ptr = 0; + childmap_ptr.erase(childmap_ptr.begin(),childmap_ptr.end()); } void DataNode::rewind(const char *name_in) { @@ -1342,6 +1400,38 @@ long DataTree::getSerializedSize(DataElement &de_node_names, bool debug) /* get return total_size; } +void DataNode::rewindAll() { + stack dn_stack; + + /* start at the root */ + dn_stack.push(this); + + while (!dn_stack.empty()) { + dn_stack.top()->rewind(); + + /* if it has children, traverse into them */ + if (dn_stack.top()->hasAnother()) { + dn_stack.push(dn_stack.top()->getNext()); + dn_stack.top()->rewind(); + } else { + /* no more children, back out until we have children, then add next child to the top */ + while (!dn_stack.empty()) { + if (!dn_stack.top()->hasAnother()) { + dn_stack.top()->rewind(); + dn_stack.pop(); + } else + break; + } + + if (!dn_stack.empty()) { + dn_stack.push(dn_stack.top()->getNext()); + dn_stack.top()->rewind(); + } + } + } + +} + void DataNode::findAll(const char *name_in, vector &node_list_out) { stack dn_stack; diff --git a/src/util/DataTree.h b/src/util/DataTree.h index 5baa6e5..f2f3d28 100755 --- a/src/util/DataTree.h +++ b/src/util/DataTree.h @@ -128,6 +128,7 @@ private: public: DataElement(); + DataElement(DataElement &cloneFrom); ~DataElement(); int getDataType(); @@ -235,8 +236,10 @@ private: public: DataNode(); DataNode(const char *name_in); - - ~DataNode(); + DataNode(const char *name_in, DataElement &cloneFrom); + DataNode(const char *name_in, DataNode &cloneFrom); + + ~DataNode(); void setName(const char *name_in); string &getName() { return node_name; } @@ -250,6 +253,8 @@ public: DataElement *element(); /* DataElement at this node */ DataNode *newChild(const char *name_in); + DataNode *newChild(const char *name_in, DataNode *otherNode); + DataNode *newChildCloneFrom(const char *name_in, DataNode *cloneFrom); DataNode *child(const char *name_in, int index = 0); DataNode *child(int index); @@ -260,7 +265,8 @@ public: DataNode *getNext(); /* get next child */ void rewind(const char *name_in); /* rewind specific */ void rewind(); /* rewind generic */ - + void rewindAll(); + void findAll(const char *name_in, vector &node_list_out); // operator string () { string s; element()->get(s); return s; } @@ -287,6 +293,7 @@ public: operator vector () { vector v; element()->get(v); return v; } const string &operator= (const string &s) { element()->set(s); return s; } + const wstring &operator= (const wstring &s) { element()->set(s); return s; } char operator= (char i) { element()->set(i); return i; } unsigned char operator= (unsigned char i) { element()->set(i); return i; } @@ -316,7 +323,6 @@ public: bool operator() () { return hasAnother(); } DataNode *operator ^(const char *name_in) { return newChild(name_in); } - }; diff --git a/src/visual/ColorTheme.h b/src/visual/ColorTheme.h index 21cebed..aac98a8 100644 --- a/src/visual/ColorTheme.h +++ b/src/visual/ColorTheme.h @@ -8,6 +8,7 @@ #include #include #include +#include #define COLOR_THEME_DEFAULT 0 #define COLOR_THEME_BW 1 @@ -41,6 +42,14 @@ public: } RGBA4f operator*(float v) { return RGBA4f(r*v, g*v, b*v); } + + operator wxColour() { + return wxColour( + (unsigned char) std::min((r * 255.0), 255.0), + (unsigned char) std::min((g * 255.0), 255.0), + (unsigned char) std::min((b * 255.0), 255.0)); + + } }; diff --git a/src/visual/ImagePanel.cpp b/src/visual/ImagePanel.cpp new file mode 100644 index 0000000..b67e62e --- /dev/null +++ b/src/visual/ImagePanel.cpp @@ -0,0 +1,50 @@ +#include "ImagePanel.h" + +BEGIN_EVENT_TABLE(ImagePanel, wxPanel) +EVT_PAINT(ImagePanel::paintEvent) +END_EVENT_TABLE() + + +ImagePanel::ImagePanel(wxPanel * parent, wxString file, wxBitmapType format) : + wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE) { + image.LoadFile(file, format); +} + +void ImagePanel::paintEvent(wxPaintEvent & evt) { + wxPaintDC dc(this); + render(dc); +} + + +void ImagePanel::paintNow() { + wxClientDC dc(this); + render(dc); +} + + +void ImagePanel::render(wxDC& dc) { + + double imagew = image.GetWidth(); + double imageh = image.GetHeight(); + + wxSize destSize = dc.GetSize(); + + double destw = destSize.GetWidth(); + double desth = destSize.GetHeight(); + + double sf = 1.0, wf, hf; + + wf = destw / imagew; + hf = desth / imageh; + + sf = (wf < hf)?wf:hf; + + double resulth = imageh * sf; + double resultw = imagew * sf; + + dc.SetUserScale(sf, sf); + dc.DrawBitmap( image, (destw/2 - resultw/2)/sf, (desth/2 - resulth/2)/sf, false ); +} + + + \ No newline at end of file diff --git a/src/visual/ImagePanel.h b/src/visual/ImagePanel.h new file mode 100644 index 0000000..681e0d7 --- /dev/null +++ b/src/visual/ImagePanel.h @@ -0,0 +1,16 @@ +#include +#include + +class ImagePanel : public wxPanel { + wxBitmap image; + +public: + ImagePanel(wxPanel* parent, wxString file, wxBitmapType format); + + void paintEvent(wxPaintEvent & evt); + void paintNow(); + + void render(wxDC& dc); + + DECLARE_EVENT_TABLE() +}; diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 6594b6e..e41099d 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -177,6 +177,14 @@ bool SpectrumCanvas::getShowDb() { return spectrumPanel.getShowDb(); } +void SpectrumCanvas::setUseDBOfs(bool showDb) { + spectrumPanel.setUseDBOffset(showDb); +} + +bool SpectrumCanvas::getUseDBOfs() { + return spectrumPanel.getUseDBOffset(); +} + void SpectrumCanvas::setView(long long center_freq_in, int bandwidth_in) { bwChange += bandwidth_in-bandwidth; #define BW_RESET_TH 400000 @@ -296,7 +304,9 @@ void SpectrumCanvas::OnMouseRightReleased(wxMouseEvent& event) { wxGetApp().getSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold()); //make the peak hold act on the current dmod also, like a zoomed-in version. - wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold()); + if (wxGetApp().getDemodSpectrumProcessor()) { + wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold()); + } } mouseTracker.OnMouseRightReleased(event); } diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index b2cef75..a81415e 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -25,6 +25,9 @@ public: void setShowDb(bool showDb); bool getShowDb(); + void setUseDBOfs(bool showDb); + bool getUseDBOfs(); + void setView(long long center_freq_in, int bandwidth_in); void disableView();