diff --git a/CMakeLists.txt b/CMakeLists.txt index c04819a..d54217f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,8 @@ 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_SUFFIX "") +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}") @@ -77,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( @@ -100,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) @@ -153,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) @@ -178,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) @@ -290,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 @@ -317,51 +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/visual/ImagePanel.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) @@ -381,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 @@ -419,65 +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/visual/ImagePanel.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) @@ -547,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}") @@ -570,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 "") @@ -598,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) @@ -610,9 +624,9 @@ 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() @@ -653,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_ ) @@ -708,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}" @@ -796,21 +811,21 @@ 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" @@ -826,33 +841,33 @@ 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/) + ${CUBICSDR_HEADER_IMAGE} + DESTINATION share/cubicsdr/) ENDIF() ENDIF() @@ -860,167 +875,167 @@ 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_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 .) + 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) + DESTINATION fonts) IF (CUBICSDR_HAS_HEADER_IMAGE) INSTALL(FILES - ${CUBICSDR_HEADER_IMAGE} - DESTINATION .) + ${CUBICSDR_HEADER_IMAGE} + DESTINATION .) ENDIF() - - 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 .) - 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) + ${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 2772ddb..cc5ce84 100644 --- a/src/AppConfig.cpp +++ b/src/AppConfig.cpp @@ -290,6 +290,11 @@ AppConfig::AppConfig() : configName("") { 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); @@ -441,6 +446,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; } @@ -487,6 +525,11 @@ bool AppConfig::save() { *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"); @@ -644,6 +687,30 @@ bool AppConfig::load() { 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 f8d31e3..0eb8f3f 100644 --- a/src/AppConfig.h +++ b/src/AppConfig.h @@ -118,6 +118,19 @@ public: 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,12 +173,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 5f5a9da..cb6407a 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -69,8 +69,9 @@ 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); @@ -277,10 +278,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 ); @@ -355,10 +359,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); @@ -421,6 +434,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?")); @@ -513,6 +528,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); @@ -631,13 +649,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 @@ -654,6 +686,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"); @@ -987,6 +1020,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) { @@ -1019,6 +1055,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) { @@ -1079,6 +1123,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) { spectrumAvgMeter->Refresh(); gainCanvas->setThemeColors(); modemProps->updateTheme(); + bookmarkView->updateTheme(); } switch (event.GetId()) { @@ -1268,6 +1313,11 @@ void AppFrame::OnClose(wxCloseEvent& event) { } wxGetApp().getSpectrumProcessor()->removeOutput(spectrumCanvas->getVisualDataQueue()); + if (saveDisabled) { + event.Skip(); + return; + } + wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize()); wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized()); wxGetApp().getConfig()->setTheme(ThemeMgr::mgr.getTheme()); @@ -1278,6 +1328,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); @@ -1289,6 +1343,7 @@ void AppFrame::OnClose(wxCloseEvent& event) { wxGetApp().getConfig()->setRigFollowModem(rigFollowModemMenuItem->IsChecked()); #endif wxGetApp().getConfig()->save(); + wxGetApp().getBookmarkMgr().saveToFile("bookmarks.xml"); event.Skip(); } @@ -1713,35 +1768,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 @@ -1815,7 +1845,6 @@ bool AppFrame::loadSession(std::string fileName) { DataNode *demodulators = l.rootNode()->getNext("demodulators"); - int numDemodulators = 0; std::vector demodsLoaded; while (demodulators->hasAnother("demodulator")) { @@ -1825,122 +1854,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()) { @@ -1986,6 +1908,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; } @@ -2024,6 +1948,14 @@ bool AppFrame::isUserDemodBusy() { wxGetApp().getDemodMgr().getLastActiveDemodulator() != wxGetApp().getDemodMgr().getActiveDemodulator()); } +BookmarkView *AppFrame::getBookmarkView() { + return bookmarkView; +} + +void AppFrame::disableSave(bool state) { + saveDisabled = state; +} + #ifdef _WIN32 bool AppFrame::canFocus() { @@ -2083,6 +2015,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(); @@ -2184,6 +2120,10 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) { return -1; } + if (bookmarkView && bookmarkView->isMouseInView()) { + return -1; + } + if (event.ControlDown()) { return 1; } @@ -2309,6 +2249,17 @@ void AppFrame::setViewState(long long center_freq) { } +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) { diff --git a/src/AppFrame.h b/src/AppFrame.h index 31e431c..bb6f8b0 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -21,6 +21,7 @@ #include "ModemProperties.h" //#include "UITestCanvas.h" #include "FrequencyDialog.h" +#include "BookmarkView.h" #include @@ -38,6 +39,7 @@ #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 +49,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 @@ -104,9 +108,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 @@ -137,8 +146,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; @@ -188,6 +198,7 @@ private: wxMenuItem *rigCenterLockMenuItem; wxMenuItem *rigFollowModemMenuItem; wxMenuItem *sdrIFMenuItem; + wxMenuItem *hideBookmarksItem; std::map rigSerialMenuItems; std::map rigModelMenuItems; int rigModel; @@ -197,6 +208,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 19bd023..f5bebed 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -27,6 +27,9 @@ IMPLEMENT_APP(CubicSDR) #include #include +#include "ActionDialog.h" + + //#ifdef ENABLE_DIGITAL_LAB //// console output buffer for windows //#ifdef _WINDOWS @@ -133,8 +136,65 @@ long long strToFrequency(std::string freqStr) { } -CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE),agcMode(false) - { + +class ActionDialogBookmarkCatastophe : public ActionDialog { +public: + ActionDialogBookmarkCatastophe() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Last-Loaded Backup Failure :( :( :(")) { + m_questionText->SetLabelText(wxT("All attempts to recover bookmarks have failed. \nWould you like to exit without touching any more save files?\nClick OK to exit without saving; or Cancel to continue anyways.")); + } + + void doClickOK() { + wxGetApp().getAppFrame()->disableSave(true); + wxGetApp().getAppFrame()->Close(false); + } +}; + + + +class ActionDialogBookmarkBackupLoadFailed : public ActionDialog { +public: + ActionDialogBookmarkBackupLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Backup Load Failure :( :(")) { + m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks backup file. \nWould you like to attempt to load the last succssfully loaded bookmarks file?")); + } + + void doClickOK() { + if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { + if (wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.lastloaded",false)) { + wxGetApp().getBookmarkMgr().updateBookmarks(); + wxGetApp().getBookmarkMgr().updateActiveList(); + } else { + ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); + } + } + } +}; + + +class ActionDialogBookmarkLoadFailed : public ActionDialog { +public: + ActionDialogBookmarkLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Load Failure :(")) { + m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks file. \nWould you like to attempt to load the backup file?")); + } + + void doClickOK() { + bool loadOk = false; + if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) { + loadOk = wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.backup",false); + } + if (loadOk) { + wxGetApp().getBookmarkMgr().updateBookmarks(); + wxGetApp().getBookmarkMgr().updateActiveList(); + } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { + ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed()); + } else { + ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); + } + } +}; + + +CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), agcMode(false) +{ sampleRateInitialized.store(false); agcMode.store(true); soloMode.store(false); @@ -294,6 +354,19 @@ bool CubicSDR::OnInit() { // pthread_setschedparam(pthread_self(), main_policy, &main_param); //#endif + if (!wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml")) { + if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) { + ActionDialog::showDialog(new ActionDialogBookmarkLoadFailed()); + } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { + ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed()); + } else { + ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); + } + } else { + getBookmarkMgr().updateActiveList(); + getBookmarkMgr().updateBookmarks(); + } + return true; } @@ -692,6 +765,10 @@ DemodulatorMgr &CubicSDR::getDemodMgr() { return demodMgr; } +BookmarkMgr &CubicSDR::getBookmarkMgr() { + return bookmarkMgr; +} + SDRPostThread *CubicSDR::getSDRPostThread() { return sdrPostThread; } @@ -771,6 +848,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 c6fd209..0ee0b32 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -23,6 +23,7 @@ #include "AppFrame.h" #include "FrequencyDialog.h" #include "DemodLabelDialog.h" +#include "BookmarkMgr.h" #include "ScopeVisualProcessor.h" #include "SpectrumVisualProcessor.h" @@ -114,6 +115,7 @@ public: DemodulatorThreadInputQueue* getWaterfallVisualQueue(); DemodulatorThreadInputQueue* getActiveDemodVisualQueue(); DemodulatorMgr &getDemodMgr(); + BookmarkMgr &getBookmarkMgr(); SDRPostThread *getSDRPostThread(); SDRThread *getSDRThread(); @@ -182,6 +184,7 @@ private: std::vector *devs = nullptr; DemodulatorMgr demodMgr; + BookmarkMgr bookmarkMgr; std::atomic_llong frequency; std::atomic_llong offset; diff --git a/src/DemodLabelDialog.cpp b/src/DemodLabelDialog.cpp index d5a7e50..f002296 100644 --- a/src/DemodLabelDialog.cpp +++ b/src/DemodLabelDialog.cpp @@ -57,7 +57,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 fc58a8d..bce143a 100644 --- a/src/ModemProperties.cpp +++ b/src/ModemProperties.cpp @@ -30,26 +30,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); diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index bf3d788..f8955fe 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -80,6 +80,8 @@ DemodulatorInstance::~DemodulatorInstance() { delete pipeIQDemodData; delete threadQueueControl; delete pipeAudioData; + + wxGetApp().getBookmarkMgr().updateActiveList(); } void DemodulatorInstance::setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue) { @@ -118,6 +120,7 @@ void DemodulatorInstance::run() { active = true; + wxGetApp().getBookmarkMgr().updateActiveList(); } void DemodulatorInstance::updateLabel(long long freq) { @@ -125,6 +128,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() { @@ -227,6 +231,8 @@ void DemodulatorInstance::setActive(bool state) { tracking = false; } active = state; + + wxGetApp().getBookmarkMgr().updateActiveList(); } void DemodulatorInstance::squelchAuto() { @@ -331,7 +337,9 @@ void DemodulatorInstance::setDemodulatorType(std::string demod_type_in) { outp->setTitle(getDemodulatorType() + ": " + frequencyToStr(getFrequency())); } #endif -} + } + + wxGetApp().getBookmarkMgr().updateActiveList(); } std::string DemodulatorInstance::getDemodulatorType() { @@ -391,6 +399,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 61be65c..5801102 100644 --- a/src/demod/DemodulatorMgr.cpp +++ b/src/demod/DemodulatorMgr.cpp @@ -1,15 +1,17 @@ -#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()); } @@ -130,6 +132,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; @@ -215,6 +219,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(); @@ -368,3 +373,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 a993f84..348815e 100644 --- a/src/demod/DemodulatorMgr.h +++ b/src/demod/DemodulatorMgr.h @@ -6,6 +6,8 @@ #include "DemodulatorInstance.h" +class DataNode; + class DemodulatorMgr { public: DemodulatorMgr(); @@ -54,6 +56,10 @@ public: void updateLastState(); + void setOutputDevices(std::map devs); + void saveInstance(DataNode *node, DemodulatorInstance *inst); + DemodulatorInstance *loadInstance(DataNode *node); + private: void garbageCollect(); @@ -79,4 +85,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/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 6f35324..3e61d5b 100644 --- a/src/visual/ColorTheme.h +++ b/src/visual/ColorTheme.h @@ -5,6 +5,7 @@ #include #include #include +#include #define COLOR_THEME_DEFAULT 0 #define COLOR_THEME_BW 1 @@ -38,6 +39,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)); + + } };