[hamradio-commits] [cubicsdr] 01/01: New upstream version 0.2.0+git20170205+dfsg

Andreas E. Bombe aeb at moszumanska.debian.org
Fri Mar 17 23:04:25 UTC 2017


This is an automated email from the git hooks/post-receive script.

aeb pushed a commit to annotated tag upstream/0.2.0+git20170205+dfsg
in repository cubicsdr.

commit 061f54d33e45e08dd1c2cae8e0b50530c6cfa2ef
Author: Andreas Bombe <aeb at debian.org>
Date:   Mon Feb 6 15:17:29 2017 +0100

    New upstream version 0.2.0+git20170205+dfsg
---
 CMakeLists.txt                              |  762 +++++++------
 LICENSE                                     |   11 +-
 README.md                                   |    2 +-
 src/AppConfig.cpp                           |   95 +-
 src/AppConfig.h                             |   23 +-
 src/AppFrame.cpp                            |  767 +++++++++-----
 src/AppFrame.h                              |   53 +-
 src/BookmarkMgr.cpp                         |  524 +++++++++
 src/BookmarkMgr.h                           |  132 +++
 src/CubicSDR.cpp                            |  167 ++-
 src/CubicSDR.h                              |   21 +-
 src/CubicSDRDefs.h                          |   23 +-
 src/DemodLabelDialog.cpp                    |    5 +-
 src/DemodLabelDialog.h                      |    3 +
 src/FrequencyDialog.cpp                     |    3 +
 src/FrequencyDialog.h                       |    3 +
 src/IOThread.cpp                            |    9 +-
 src/IOThread.h                              |    9 +-
 src/ModemProperties.cpp                     |   31 +-
 src/ModemProperties.h                       |    3 +
 src/audio/AudioThread.cpp                   |    5 +-
 src/audio/AudioThread.h                     |    3 +
 src/demod/DemodDefs.h                       |    4 +
 src/demod/DemodulatorInstance.cpp           |   17 +-
 src/demod/DemodulatorInstance.h             |    3 +
 src/demod/DemodulatorMgr.cpp                |  147 ++-
 src/demod/DemodulatorMgr.h                  |   10 +
 src/demod/DemodulatorPreThread.cpp          |    3 +
 src/demod/DemodulatorPreThread.h            |    3 +
 src/demod/DemodulatorThread.cpp             |    3 +
 src/demod/DemodulatorThread.h               |    3 +
 src/demod/DemodulatorWorkerThread.cpp       |    3 +
 src/demod/DemodulatorWorkerThread.h         |    3 +
 src/forms/Bookmark/BookmarkPanel.cpp        |  136 +++
 src/forms/Bookmark/BookmarkPanel.fbp        | 1230 +++++++++++++++++++++
 src/forms/Bookmark/BookmarkPanel.h          |   83 ++
 src/forms/Bookmark/BookmarkView.cpp         | 1533 +++++++++++++++++++++++++++
 src/forms/Bookmark/BookmarkView.h           |  175 +++
 src/forms/Dialog/ActionDialog.cpp           |   61 ++
 src/forms/Dialog/ActionDialog.h             |   21 +
 src/forms/Dialog/ActionDialogBase.cpp       |   53 +
 src/forms/Dialog/ActionDialogBase.fbp       |  369 +++++++
 src/forms/Dialog/ActionDialogBase.h         |   50 +
 src/forms/DigitalConsole/DigitalConsole.cpp |    3 +
 src/forms/DigitalConsole/DigitalConsole.h   |    3 +
 src/forms/SDRDevices/SDRDeviceAdd.cpp       |    4 +-
 src/forms/SDRDevices/SDRDeviceAdd.h         |    3 +
 src/forms/SDRDevices/SDRDevices.cpp         |   27 +-
 src/forms/SDRDevices/SDRDevices.h           |    3 +
 src/modules/modem/Modem.cpp                 |    3 +
 src/modules/modem/Modem.h                   |    3 +
 src/modules/modem/ModemAnalog.cpp           |    9 +-
 src/modules/modem/ModemAnalog.h             |    3 +
 src/modules/modem/ModemDigital.cpp          |    5 +-
 src/modules/modem/ModemDigital.h            |    3 +
 src/modules/modem/analog/ModemAM.cpp        |    3 +
 src/modules/modem/analog/ModemAM.h          |    3 +
 src/modules/modem/analog/ModemDSB.cpp       |    3 +
 src/modules/modem/analog/ModemDSB.h         |    3 +
 src/modules/modem/analog/ModemFM.cpp        |    5 +-
 src/modules/modem/analog/ModemFM.h          |    3 +
 src/modules/modem/analog/ModemFMStereo.cpp  |   31 +-
 src/modules/modem/analog/ModemFMStereo.h    |    3 +
 src/modules/modem/analog/ModemIQ.cpp        |    3 +
 src/modules/modem/analog/ModemIQ.h          |    3 +
 src/modules/modem/analog/ModemLSB.cpp       |    9 +-
 src/modules/modem/analog/ModemLSB.h         |    3 +
 src/modules/modem/analog/ModemNBFM.cpp      |    5 +-
 src/modules/modem/analog/ModemNBFM.h        |    3 +
 src/modules/modem/analog/ModemUSB.cpp       |    9 +-
 src/modules/modem/analog/ModemUSB.h         |    3 +
 src/modules/modem/digital/ModemAPSK.cpp     |    3 +
 src/modules/modem/digital/ModemAPSK.h       |    3 +
 src/modules/modem/digital/ModemASK.cpp      |    3 +
 src/modules/modem/digital/ModemASK.h        |    3 +
 src/modules/modem/digital/ModemBPSK.cpp     |    3 +
 src/modules/modem/digital/ModemBPSK.h       |    3 +
 src/modules/modem/digital/ModemDPSK.cpp     |    3 +
 src/modules/modem/digital/ModemDPSK.h       |    3 +
 src/modules/modem/digital/ModemFSK.cpp      |   13 +-
 src/modules/modem/digital/ModemFSK.h        |    3 +
 src/modules/modem/digital/ModemGMSK.cpp     |    7 +-
 src/modules/modem/digital/ModemGMSK.h       |    3 +
 src/modules/modem/digital/ModemOOK.cpp      |    5 +-
 src/modules/modem/digital/ModemOOK.h        |    3 +
 src/modules/modem/digital/ModemPSK.cpp      |    3 +
 src/modules/modem/digital/ModemPSK.h        |    3 +
 src/modules/modem/digital/ModemQAM.cpp      |    3 +
 src/modules/modem/digital/ModemQAM.h        |    3 +
 src/modules/modem/digital/ModemQPSK.cpp     |    3 +
 src/modules/modem/digital/ModemQPSK.h       |    3 +
 src/modules/modem/digital/ModemSQAM.cpp     |    3 +
 src/modules/modem/digital/ModemSQAM.h       |    3 +
 src/modules/modem/digital/ModemST.cpp       |    3 +
 src/modules/modem/digital/ModemST.h         |    3 +
 src/panel/MeterPanel.cpp                    |    6 +-
 src/panel/MeterPanel.h                      |    3 +
 src/panel/ScopePanel.cpp                    |    3 +
 src/panel/ScopePanel.h                      |    3 +
 src/panel/SpectrumPanel.cpp                 |   21 +-
 src/panel/SpectrumPanel.h                   |   12 +-
 src/panel/WaterfallPanel.cpp                |    8 +
 src/panel/WaterfallPanel.h                  |    3 +
 src/process/FFTDataDistributor.cpp          |   71 +-
 src/process/FFTDataDistributor.h            |   15 +-
 src/process/FFTVisualDataThread.cpp         |   22 +-
 src/process/FFTVisualDataThread.h           |    3 +
 src/process/ScopeVisualProcessor.cpp        |    5 +-
 src/process/ScopeVisualProcessor.h          |    9 +-
 src/process/SpectrumVisualDataThread.cpp    |   10 +-
 src/process/SpectrumVisualDataThread.h      |    3 +
 src/process/SpectrumVisualProcessor.cpp     |    4 +
 src/process/SpectrumVisualProcessor.h       |    5 +-
 src/process/VisualProcessor.cpp             |    3 +
 src/process/VisualProcessor.h               |   63 +-
 src/rig/RigThread.cpp                       |    3 +
 src/rig/RigThread.h                         |    3 +
 src/sdr/SDRDeviceInfo.cpp                   |    3 +
 src/sdr/SDRDeviceInfo.h                     |    5 +-
 src/sdr/SDREnumerator.cpp                   |    3 +
 src/sdr/SDREnumerator.h                     |    3 +
 src/sdr/SDRPostThread.cpp                   |    3 +
 src/sdr/SDRPostThread.h                     |    9 +-
 src/sdr/SDRThread.cpp                       |  314 ------
 src/sdr/SDRThread.h                         |   73 --
 src/sdr/SoapySDRThread.cpp                  |   16 +-
 src/sdr/SoapySDRThread.h                    |    9 +-
 src/ui/GLPanel.cpp                          |    3 +
 src/ui/GLPanel.h                            |   11 +-
 src/ui/UITestCanvas.cpp                     |    5 +-
 src/ui/UITestCanvas.h                       |    5 +-
 src/ui/UITestContext.cpp                    |    5 +-
 src/ui/UITestContext.h                      |    3 +
 src/util/DataTree.cpp                       |   92 +-
 src/util/DataTree.h                         |   14 +-
 src/util/GLExt.cpp                          |    3 +
 src/util/GLExt.h                            |    3 +
 src/util/GLFont.cpp                         |    3 +
 src/util/GLFont.h                           |    7 +-
 src/util/Gradient.cpp                       |   12 +-
 src/util/Gradient.h                         |    3 +
 src/util/MouseTracker.cpp                   |    3 +
 src/util/MouseTracker.h                     |    3 +
 src/util/ThreadQueue.cpp                    |    3 +
 src/util/ThreadQueue.h                      |    3 +
 src/util/Timer.cpp                          |    4 +-
 src/util/Timer.h                            |    3 +
 src/visual/ColorTheme.cpp                   |    3 +
 src/visual/ColorTheme.h                     |   12 +
 src/visual/GainCanvas.cpp                   |   12 +-
 src/visual/GainCanvas.h                     |    5 +-
 src/visual/ImagePanel.cpp                   |   50 +
 src/visual/ImagePanel.h                     |   16 +
 src/visual/InteractiveCanvas.cpp            |   13 +-
 src/visual/InteractiveCanvas.h              |   16 +-
 src/visual/MeterCanvas.cpp                  |    5 +-
 src/visual/MeterCanvas.h                    |    5 +-
 src/visual/MeterContext.cpp                 |    3 +
 src/visual/MeterContext.h                   |    3 +
 src/visual/ModeSelectorCanvas.cpp           |    5 +-
 src/visual/ModeSelectorCanvas.h             |    5 +-
 src/visual/ModeSelectorContext.cpp          |    3 +
 src/visual/ModeSelectorContext.h            |    3 +
 src/visual/PrimaryGLContext.cpp             |    3 +
 src/visual/PrimaryGLContext.h               |    3 +
 src/visual/ScopeCanvas.cpp                  |   31 +-
 src/visual/ScopeCanvas.h                    |   15 +-
 src/visual/ScopeContext.cpp                 |    3 +
 src/visual/ScopeContext.h                   |    3 +
 src/visual/SpectrumCanvas.cpp               |   34 +-
 src/visual/SpectrumCanvas.h                 |   19 +-
 src/visual/TuningCanvas.cpp                 |    5 +-
 src/visual/TuningCanvas.h                   |    5 +-
 src/visual/TuningContext.cpp                |    3 +
 src/visual/TuningContext.h                  |    3 +
 src/visual/WaterfallCanvas.cpp              |   12 +-
 src/visual/WaterfallCanvas.h                |   20 +-
 177 files changed, 6759 insertions(+), 1276 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a817a1d..c034495 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,19 +6,69 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
 
 SET(CUBICSDR_VERSION_MAJOR "0")
 SET(CUBICSDR_VERSION_MINOR "2")
-SET(CUBICSDR_VERSION_PATCH "0")
-SET(CUBICSDR_VERSION_REL "")
-SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}${CUBICSDR_VERSION_REL}")
+SET(CUBICSDR_VERSION_PATCH "2")
+SET(CUBICSDR_VERSION_SUFFIX "-alpha")
+SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}${CUBICSDR_VERSION_SUFFIX}")
 
 SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}")
 SET(CPACK_PACKAGE_VERSION_MAJOR ${CUBICSDR_VERSION_MAJOR})
 SET(CPACK_PACKAGE_VERSION_MINOR ${CUBICSDR_VERSION_MINOR})
 SET(CPACK_PACKAGE_VERSION_PATCH ${CUBICSDR_VERSION_PATCH})
 
-SET (VERSION_SUFFIX "" CACHE STRING "Add custom version suffix to CubicSDR application title.")
+SET (CUSTOM_BUILD OFF CACHE BOOL "Enable custom build options")
+# Build options for custom deploys, optimization and debugging
+IF(CUSTOM_BUILD)
+    SET (CUBICSDR_BUILD_TITLE CACHE STRING "Custom Title")
+    # bundle flags
+    SET (CUBICSDR_INSTALL_NAME CACHE "CubicSDR" "Installation Name")
+    SET (CUBICSDR_INSTALL_TITLE CACHE "CubicSDR" "Installation Title")
+    SET (CUBICSDR_HEADER_IMAGE CACHE "" "Image file to display in header")
+    SET (CUBICSDR_HEADER_BG CACHE "000000" "Background Color (HEX) for header")
+    # feature flags
+    SET (CUBICSDR_ENABLE_VIEW_DEMOD ON CACHE BOOL "Enable Second Demodulator Spectrum/Waterfall view.")
+    SET (CUBICSDR_ENABLE_VIEW_SCOPE ON CACHE BOOL "Enable Demodulator Scope/Spectrum view.")
+    SET (CUBICSDR_MODEM_EXCLUDE CACHE "" "Comma-separated list of modems to exclude.")    
+
+    IF (NOT CUBICSDR_HEADER_IMAGE STREQUAL "")
+        SET(CUBICSDR_HAS_HEADER_IMAGE TRUE)
+        GET_FILENAME_COMPONENT(CUBICSDR_HEADER_IMAGE_FILE "${CUBICSDR_HEADER_IMAGE}" NAME)
+        GET_FILENAME_COMPONENT(CUBICSDR_HEADER_IMAGE_DIR "${CUBICSDR_HEADER_IMAGE}" PATH)
+        ADD_DEFINITIONS( 
+            -DCUBICSDR_HEADER_IMAGE="${CUBICSDR_HEADER_IMAGE_FILE}"
+            -DCUBICSDR_HEADER_BG="${CUBICSDR_HEADER_BG}"
+        )
+    ENDIF()
+
+    IF (NOT CUBICSDR_MODEM_EXCLUDE STREQUAL "")
+        ADD_DEFINITIONS( 
+            -DCUBICSDR_MODEM_EXCLUDE="${CUBICSDR_MODEM_EXCLUDE}"
+        )
+    ENDIF()
+ELSE()
+    SET (CUBICSDR_BUILD_TITLE "CubicSDR v${CUBICSDR_VERSION} by Charles J. Cliffe (@ccliffe)  ::  www.cubicsdr.com")
+    # bundle flags
+    SET (CUBICSDR_INSTALL_NAME "CubicSDR")
+    SET (CUBICSDR_INSTALL_TITLE "CubicSDR ${CUBICSDR_VERSION} Installer")
+    SET (CUBICSDR_HEADER_IMAGE "")
+    SET (CUBICSDR_HEADER_BG "")
+    # feature flags
+    SET (CUBICSDR_ENABLE_VIEW_DEMOD TRUE)
+    SET (CUBICSDR_ENABLE_VIEW_SCOPE TRUE)
+    SET (CUBICSDR_EXCLUDE_MODEM "")    
+ENDIF()
+
+IF(CUBICSDR_ENABLE_VIEW_DEMOD) 
+    ADD_DEFINITIONS( -DCUBICSDR_ENABLE_VIEW_DEMOD=1 )
+ENDIF()
+
+IF(CUBICSDR_ENABLE_VIEW_SCOPE) 
+    ADD_DEFINITIONS( -DCUBICSDR_ENABLE_VIEW_SCOPE=1 )
+ENDIF()
 
 ADD_DEFINITIONS(
-    -DCUBICSDR_VERSION="${CUBICSDR_VERSION}${VERSION_SUFFIX}"
+    -DCUBICSDR_INSTALL_NAME="${CUBICSDR_INSTALL_NAME}"
+    -DCUBICSDR_VERSION="${CUBICSDR_VERSION}"
+    -DCUBICSDR_BUILD_TITLE="${CUBICSDR_BUILD_TITLE}"
 )
 
 SET (ENABLE_DIGITAL_LAB OFF CACHE BOOL "Enable 'Digital Lab' testing features.")
@@ -26,16 +76,6 @@ IF(ENABLE_DIGITAL_LAB)
 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)")
-ELSE()
-	SET (ENABLE_LIQUID_EXPERIMENTAL ON CACHE BOOL "Enable experimental liquid-dsp features (requires latest liquid-dsp installed)")
-ENDIF()
-IF(ENABLE_LIQUID_EXPERIMENTAL)
-ADD_DEFINITIONS(
-    -DENABLE_LIQUID_EXPERIMENTAL=1
-)
-ENDIF()
 ENDIF()
 
 set(USE_HAMLIB OFF CACHE BOOL "Support hamlib for radio control functions.")
@@ -50,7 +90,7 @@ if (USE_HAMLIB)
     include_directories(${HAMLIB_INCLUDE_DIR})
     link_libraries(${HAMLIB_LIBRARY})
 
-	ADD_DEFINITIONS(-DUSE_HAMLIB)	    
+    ADD_DEFINITIONS(-DUSE_HAMLIB)        
 endif ()
 
 macro(configure_files srcDir destDir globStr)
@@ -103,16 +143,16 @@ SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NA
 SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME})
 
 IF (MSVC) 
-	include_directories ("${PROJECT_SOURCE_DIR}/external/wglext")
-	SET(LIQUID_INCLUDES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/include/" CACHE STRING "Liquid-DSP include directory")
-	SET(LIQUID_LIBRARIES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.lib" CACHE STRING "Liquid-DSP Library")
-	SET(LIQUID_DLL "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.dll" CACHE STRING "Liquid-DSP DLL")
-	SET(HAMLIB_DLLS "${PROJECT_SOURCE_DIR}/external/hamlib/${EX_PLATFORM}/libhamlib-2.dll;${PROJECT_SOURCE_DIR}/external/hamlib/${EX_PLATFORM}/libwinpthread-1.dll" CACHE STRING "HAMLIB DLLS")
+    include_directories ("${PROJECT_SOURCE_DIR}/external/wglext")
+    SET(LIQUID_INCLUDES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/include/" CACHE STRING "Liquid-DSP include directory")
+    SET(LIQUID_LIBRARIES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.lib" CACHE STRING "Liquid-DSP Library")
+    SET(LIQUID_DLL "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.dll" CACHE STRING "Liquid-DSP DLL")
+    SET(HAMLIB_DLLS "${PROJECT_SOURCE_DIR}/external/hamlib/${EX_PLATFORM}/libhamlib-2.dll;${PROJECT_SOURCE_DIR}/external/hamlib/${EX_PLATFORM}/libwinpthread-1.dll" CACHE STRING "HAMLIB DLLS")
 ELSE (MSVC)
-	ADD_DEFINITIONS(
-		-std=c++0x 
-		-pthread
-	)
+    ADD_DEFINITIONS(
+        -std=c++0x 
+        -pthread
+    )
 ENDIF(MSVC)
 
 find_package(OpenGL REQUIRED)
@@ -128,50 +168,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(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)
-
-	# 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(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")
+
+    # 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)    
   
-	SET(USE_MINGW_PATCH OFF CACHE BOOL "Add some missing functions when compiling against mingw liquid-dsp.")
-	IF (USE_MINGW_PATCH) 
-		SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} legacy_stdio_definitions.lib libgcc.a")
-		ADD_DEFINITIONS(
-			-DMINGW_PATCH=1
-		)
-		SET (GCC_LINKDIR "" CACHE STRING "")
-		IF (GCC_LINKDIR)
-			link_directories("${GCC_LINKDIR}")
-		ENDIF()
-	ENDIF()
+    SET(USE_MINGW_PATCH OFF CACHE BOOL "Add some missing functions when compiling against mingw liquid-dsp.")
+    IF (USE_MINGW_PATCH) 
+        SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} legacy_stdio_definitions.lib libgcc.a")
+        ADD_DEFINITIONS(
+            -DMINGW_PATCH=1
+        )
+        SET (GCC_LINKDIR "" CACHE STRING "")
+        IF (GCC_LINKDIR)
+            link_directories("${GCC_LINKDIR}")
+        ENDIF()
+    ENDIF()
 ENDIF (WIN32)
 
 IF (UNIX AND NOT APPLE)
@@ -240,22 +280,23 @@ ENDIF (APPLE)
 
 
 SET (cubicsdr_sources
-	src/CubicSDR.cpp
-	src/AppFrame.cpp
-	src/AppConfig.cpp
-	src/FrequencyDialog.cpp
+    src/CubicSDR.cpp
+    src/AppFrame.cpp
+    src/AppConfig.cpp
+    src/FrequencyDialog.cpp
     src/DemodLabelDialog.cpp
     src/IOThread.cpp
     src/ModemProperties.cpp
-	src/sdr/SDRDeviceInfo.cpp
-	src/sdr/SDRPostThread.cpp
-	src/sdr/SDREnumerator.cpp
-	src/sdr/SoapySDRThread.h
-	src/demod/DemodulatorPreThread.cpp
-	src/demod/DemodulatorThread.cpp
-	src/demod/DemodulatorWorkerThread.cpp
-	src/demod/DemodulatorInstance.cpp
-	src/demod/DemodulatorMgr.cpp
+    src/BookmarkMgr.cpp
+    src/sdr/SDRDeviceInfo.cpp
+    src/sdr/SDRPostThread.cpp
+    src/sdr/SDREnumerator.cpp
+    src/sdr/SoapySDRThread.h
+    src/demod/DemodulatorPreThread.cpp
+    src/demod/DemodulatorThread.cpp
+    src/demod/DemodulatorWorkerThread.cpp
+    src/demod/DemodulatorInstance.cpp
+    src/demod/DemodulatorMgr.cpp
     src/modules/modem/Modem.cpp
     src/modules/modem/ModemAnalog.cpp
     src/modules/modem/ModemDigital.cpp
@@ -267,50 +308,55 @@ SET (cubicsdr_sources
     src/modules/modem/analog/ModemIQ.cpp
     src/modules/modem/analog/ModemLSB.cpp
     src/modules/modem/analog/ModemUSB.cpp
-	src/audio/AudioThread.cpp
-	src/util/Gradient.cpp
-	src/util/Timer.cpp
-	src/util/MouseTracker.cpp
-	src/util/GLExt.cpp
-	src/util/GLFont.cpp
-	src/util/DataTree.cpp
+    src/audio/AudioThread.cpp
+    src/util/Gradient.cpp
+    src/util/Timer.cpp
+    src/util/MouseTracker.cpp
+    src/util/GLExt.cpp
+    src/util/GLFont.cpp
+    src/util/DataTree.cpp
     src/panel/ScopePanel.cpp
     src/panel/SpectrumPanel.cpp
     src/panel/WaterfallPanel.cpp
     src/panel/MeterPanel.cpp
     src/panel/MeterPanel.h
-	src/visual/ColorTheme.cpp
-	src/visual/PrimaryGLContext.cpp
-	src/visual/InteractiveCanvas.cpp
-	src/visual/MeterCanvas.cpp
-	src/visual/MeterContext.cpp
-	src/visual/TuningCanvas.cpp
-	src/visual/TuningContext.cpp
-	src/visual/ModeSelectorCanvas.cpp
-	src/visual/ModeSelectorContext.cpp
-	src/visual/ScopeCanvas.cpp
-	src/visual/ScopeContext.cpp
-	src/visual/SpectrumCanvas.cpp
-	src/visual/WaterfallCanvas.cpp
+    src/visual/ColorTheme.cpp
+    src/visual/PrimaryGLContext.cpp
+    src/visual/InteractiveCanvas.cpp
+    src/visual/MeterCanvas.cpp
+    src/visual/MeterContext.cpp
+    src/visual/TuningCanvas.cpp
+    src/visual/TuningContext.cpp
+    src/visual/ModeSelectorCanvas.cpp
+    src/visual/ModeSelectorContext.cpp
+    src/visual/ScopeCanvas.cpp
+    src/visual/ScopeContext.cpp
+    src/visual/SpectrumCanvas.cpp
+    src/visual/WaterfallCanvas.cpp
     src/visual/GainCanvas.cpp
-	src/process/VisualProcessor.cpp
-	src/process/ScopeVisualProcessor.cpp
-	src/process/SpectrumVisualProcessor.cpp
-	src/process/FFTVisualDataThread.cpp
-	src/process/FFTDataDistributor.cpp
+    src/visual/ImagePanel.cpp
+    src/process/VisualProcessor.cpp
+    src/process/ScopeVisualProcessor.cpp
+    src/process/SpectrumVisualProcessor.cpp
+    src/process/FFTVisualDataThread.cpp
+    src/process/FFTDataDistributor.cpp
     src/process/SpectrumVisualDataThread.cpp
-	src/ui/GLPanel.cpp
+    src/ui/GLPanel.cpp
     src/forms/SDRDevices/SDRDevices.cpp
     src/forms/SDRDevices/SDRDevicesForm.cpp
     src/forms/SDRDevices/SDRDeviceAdd.cpp
     src/forms/SDRDevices/SDRDeviceAddForm.cpp
-	external/rtaudio/RtAudio.cpp
-	external/lodepng/lodepng.cpp
-	external/tinyxml/tinyxml.cpp
-	external/tinyxml/tinystr.cpp
-	external/tinyxml/tinyxmlparser.cpp
-	external/tinyxml/tinyxmlerror.cpp
-	external/cubicvr2/math/cubic_math.cpp
+    src/forms/Bookmark/BookmarkPanel.cpp
+    src/forms/Bookmark/BookmarkView.cpp
+    src/forms/Dialog/ActionDialogBase.cpp
+    src/forms/Dialog/ActionDialog.cpp
+    external/rtaudio/RtAudio.cpp
+    external/lodepng/lodepng.cpp
+    external/tinyxml/tinyxml.cpp
+    external/tinyxml/tinystr.cpp
+    external/tinyxml/tinyxmlparser.cpp
+    external/tinyxml/tinyxmlerror.cpp
+    external/cubicvr2/math/cubic_math.cpp
 )
 
 IF(ENABLE_DIGITAL_LAB)
@@ -329,34 +375,30 @@ IF(ENABLE_DIGITAL_LAB)
         src/modules/modem/digital/ModemSQAM.cpp
         src/modules/modem/digital/ModemQAM.cpp
         src/modules/modem/digital/ModemQPSK.cpp
-    )
-	IF(ENABLE_LIQUID_EXPERIMENTAL)
-    SET (cubicsdr_sources
-        ${cubicsdr_sources}
         src/modules/modem/digital/ModemFSK.cpp
     )
-	ENDIF()
 ENDIF()
 
 SET (cubicsdr_headers
-	src/CubicSDRDefs.h
-	src/CubicSDR.h
-	src/AppFrame.h
-	src/AppConfig.h
-	src/FrequencyDialog.h
+    src/CubicSDRDefs.h
+    src/CubicSDR.h
+    src/AppFrame.h
+    src/AppConfig.h
+    src/FrequencyDialog.h
     src/DemodLabelDialog.h
     src/IOThread.h
     src/ModemProperties.h
-	src/sdr/SDRDeviceInfo.h
-	src/sdr/SDRPostThread.h
-	src/sdr/SDREnumerator.h
-	src/sdr/SoapySDRThread.cpp
-	src/demod/DemodulatorPreThread.h
-	src/demod/DemodulatorThread.h
-	src/demod/DemodulatorWorkerThread.h
-	src/demod/DemodulatorInstance.h
-	src/demod/DemodulatorMgr.h
-	src/demod/DemodDefs.h
+    src/BookmarkMgr.h
+    src/sdr/SDRDeviceInfo.h
+    src/sdr/SDRPostThread.h
+    src/sdr/SDREnumerator.h
+    src/sdr/SoapySDRThread.cpp
+    src/demod/DemodulatorPreThread.h
+    src/demod/DemodulatorThread.h
+    src/demod/DemodulatorWorkerThread.h
+    src/demod/DemodulatorInstance.h
+    src/demod/DemodulatorMgr.h
+    src/demod/DemodDefs.h
     src/modules/modem/Modem.h
     src/modules/modem/ModemAnalog.h
     src/modules/modem/ModemDigital.h
@@ -368,64 +410,69 @@ SET (cubicsdr_headers
     src/modules/modem/analog/ModemIQ.h
     src/modules/modem/analog/ModemLSB.h
     src/modules/modem/analog/ModemUSB.h
-	src/audio/AudioThread.h
-	src/util/Gradient.h
-	src/util/Timer.h
-	src/util/ThreadQueue.h
-	src/util/MouseTracker.h
-	src/util/GLExt.h
-	src/util/GLFont.h
-	src/util/DataTree.h
+    src/audio/AudioThread.h
+    src/util/Gradient.h
+    src/util/Timer.h
+    src/util/ThreadQueue.h
+    src/util/MouseTracker.h
+    src/util/GLExt.h
+    src/util/GLFont.h
+    src/util/DataTree.h
     src/panel/ScopePanel.h
     src/panel/SpectrumPanel.h
     src/panel/WaterfallPanel.h
-	src/visual/ColorTheme.h
-	src/visual/PrimaryGLContext.h
-	src/visual/InteractiveCanvas.h
-	src/visual/MeterCanvas.h
-	src/visual/MeterContext.h
-	src/visual/TuningCanvas.h
-	src/visual/TuningContext.h
-	src/visual/ModeSelectorCanvas.h
-	src/visual/ModeSelectorContext.h
-	src/visual/ScopeCanvas.h
-	src/visual/ScopeContext.h
-	src/visual/SpectrumCanvas.h
-	src/visual/WaterfallCanvas.h
+    src/visual/ColorTheme.h
+    src/visual/PrimaryGLContext.h
+    src/visual/InteractiveCanvas.h
+    src/visual/MeterCanvas.h
+    src/visual/MeterContext.h
+    src/visual/TuningCanvas.h
+    src/visual/TuningContext.h
+    src/visual/ModeSelectorCanvas.h
+    src/visual/ModeSelectorContext.h
+    src/visual/ScopeCanvas.h
+    src/visual/ScopeContext.h
+    src/visual/SpectrumCanvas.h
+    src/visual/WaterfallCanvas.h
     src/visual/GainCanvas.h
-	src/process/VisualProcessor.h
-	src/process/ScopeVisualProcessor.h
-	src/process/SpectrumVisualProcessor.h
-	src/process/FFTVisualDataThread.h
-	src/process/FFTDataDistributor.h
+    src/visual/ImagePanel.h
+    src/process/VisualProcessor.h
+    src/process/ScopeVisualProcessor.h
+    src/process/SpectrumVisualProcessor.h
+    src/process/FFTVisualDataThread.h
+    src/process/FFTDataDistributor.h
     src/process/SpectrumVisualDataThread.h
-	src/ui/GLPanel.h
-	src/ui/UITestCanvas.cpp
-	src/ui/UITestCanvas.h
-	src/ui/UITestContext.cpp
-	src/ui/UITestContext.h    
+    src/ui/GLPanel.h
+    src/ui/UITestCanvas.cpp
+    src/ui/UITestCanvas.h
+    src/ui/UITestContext.cpp
+    src/ui/UITestContext.h    
     src/forms/SDRDevices/SDRDevices.h
     src/forms/SDRDevices/SDRDevicesForm.h
     src/forms/SDRDevices/SDRDeviceAdd.h
     src/forms/SDRDevices/SDRDeviceAddForm.h
-	external/rtaudio/RtAudio.h
-	external/lodepng/lodepng.h
-	external/tinyxml/tinyxml.h
-	external/tinyxml/tinystr.h
-	external/cubicvr2/math/aabb.h
-	external/cubicvr2/math/cubic_math.h
-	external/cubicvr2/math/cubic_types.h
-	external/cubicvr2/math/frustum.h
-	external/cubicvr2/math/mat3.h
-	external/cubicvr2/math/mat4.h
-	external/cubicvr2/math/plane.h
-	external/cubicvr2/math/quaternion.h
-	external/cubicvr2/math/sphere.h
-	external/cubicvr2/math/transform.h
-	external/cubicvr2/math/triangle.h
-	external/cubicvr2/math/vec2.h
-	external/cubicvr2/math/vec3.h
-	external/cubicvr2/math/vec4.h
+    src/forms/Bookmark/BookmarkPanel.h
+    src/forms/Bookmark/BookmarkView.h
+    src/forms/Dialog/ActionDialogBase.h
+    src/forms/Dialog/ActionDialog.h
+    external/rtaudio/RtAudio.h
+    external/lodepng/lodepng.h
+    external/tinyxml/tinyxml.h
+    external/tinyxml/tinystr.h
+    external/cubicvr2/math/aabb.h
+    external/cubicvr2/math/cubic_math.h
+    external/cubicvr2/math/cubic_types.h
+    external/cubicvr2/math/frustum.h
+    external/cubicvr2/math/mat3.h
+    external/cubicvr2/math/mat4.h
+    external/cubicvr2/math/plane.h
+    external/cubicvr2/math/quaternion.h
+    external/cubicvr2/math/sphere.h
+    external/cubicvr2/math/transform.h
+    external/cubicvr2/math/triangle.h
+    external/cubicvr2/math/vec2.h
+    external/cubicvr2/math/vec3.h
+    external/cubicvr2/math/vec4.h
 )
 
 IF(ENABLE_DIGITAL_LAB)
@@ -444,13 +491,8 @@ SET (cubicsdr_headers
     src/modules/modem/digital/ModemSQAM.h
     src/modules/modem/digital/ModemQAM.h
     src/modules/modem/digital/ModemQPSK.h
+    src/modules/modem/digital/ModemFSK.h
 )    
-IF(ENABLE_LIQUID_EXPERIMENTAL)
-    SET (cubicsdr_sources
-        ${cubicsdr_sources}
-        src/modules/modem/digital/ModemFSK.h
-    )
-ENDIF()
 ENDIF()
 
 
@@ -495,6 +537,8 @@ set(REG_EXT "[^/]*([.]cpp|[.]c|[.]h|[.]hpp)$")
 
 SOURCE_GROUP("Base" REGULAR_EXPRESSION "src/${REG_EXT}")
 SOURCE_GROUP("Forms\\SDRDevices" REGULAR_EXPRESSION "src/forms/SDRDevices/${REG_EXT}")
+SOURCE_GROUP("Forms\\Bookmark" REGULAR_EXPRESSION "src/forms/Bookmark/${REG_EXT}")
+SOURCE_GROUP("Forms\\Dialog" REGULAR_EXPRESSION "src/forms/Dialog/${REG_EXT}")
 SOURCE_GROUP("SDR" REGULAR_EXPRESSION "src/sdr/${REG_EXT}")
 IF(USE_HAMLIB)
     SOURCE_GROUP("Rig" REGULAR_EXPRESSION "src/rig/${REG_EXT}")    
@@ -518,26 +562,28 @@ SOURCE_GROUP("_ext-TinyXML" REGULAR_EXPRESSION "external/tinyxml/.*${REG_EXT}")
 SOURCE_GROUP("_ext-CubicVR2" REGULAR_EXPRESSION "external/cubicvr2/.*${REG_EXT}")
 
 include_directories ( 
-	${PROJECT_SOURCE_DIR}/src/forms/SDRDevices
-	${PROJECT_SOURCE_DIR}/src/forms/DigitalConsole
-	${PROJECT_SOURCE_DIR}/src/sdr 
-	${PROJECT_SOURCE_DIR}/src/demod
-	${PROJECT_SOURCE_DIR}/src/modules
-	${PROJECT_SOURCE_DIR}/src/modules/modem
-	${PROJECT_SOURCE_DIR}/src/modules/modem/digital
-	${PROJECT_SOURCE_DIR}/src/modules/modem/analog
-	${PROJECT_SOURCE_DIR}/src/audio
-	${PROJECT_SOURCE_DIR}/src/util
-	${PROJECT_SOURCE_DIR}/src/panel
-	${PROJECT_SOURCE_DIR}/src/visual
-	${PROJECT_SOURCE_DIR}/src/process
-	${PROJECT_SOURCE_DIR}/src/ui
-	${PROJECT_SOURCE_DIR}/src/rig
-	${PROJECT_SOURCE_DIR}/src
-	${PROJECT_SOURCE_DIR}/external/rtaudio
-	${PROJECT_SOURCE_DIR}/external/lodepng 
-	${PROJECT_SOURCE_DIR}/external/tinyxml
-	${PROJECT_SOURCE_DIR}/external/cubicvr2/math
+    ${PROJECT_SOURCE_DIR}/src/forms/SDRDevices
+    ${PROJECT_SOURCE_DIR}/src/forms/DigitalConsole
+    ${PROJECT_SOURCE_DIR}/src/forms/Bookmark
+    ${PROJECT_SOURCE_DIR}/src/forms/Dialog
+    ${PROJECT_SOURCE_DIR}/src/sdr 
+    ${PROJECT_SOURCE_DIR}/src/demod
+    ${PROJECT_SOURCE_DIR}/src/modules
+    ${PROJECT_SOURCE_DIR}/src/modules/modem
+    ${PROJECT_SOURCE_DIR}/src/modules/modem/digital
+    ${PROJECT_SOURCE_DIR}/src/modules/modem/analog
+    ${PROJECT_SOURCE_DIR}/src/audio
+    ${PROJECT_SOURCE_DIR}/src/util
+    ${PROJECT_SOURCE_DIR}/src/panel
+    ${PROJECT_SOURCE_DIR}/src/visual
+    ${PROJECT_SOURCE_DIR}/src/process
+    ${PROJECT_SOURCE_DIR}/src/ui
+    ${PROJECT_SOURCE_DIR}/src/rig
+    ${PROJECT_SOURCE_DIR}/src
+    ${PROJECT_SOURCE_DIR}/external/rtaudio
+    ${PROJECT_SOURCE_DIR}/external/lodepng 
+    ${PROJECT_SOURCE_DIR}/external/tinyxml
+    ${PROJECT_SOURCE_DIR}/external/cubicvr2/math
 )
 
 set(RES_FILES "")
@@ -546,11 +592,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 "<CMAKE_RC_COMPILER> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>")
 endif(MINGW OR MSVC)
 
@@ -558,9 +604,12 @@ IF (NOT BUNDLE_APP)
     configure_files(${PROJECT_SOURCE_DIR}/font ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/fonts "*.fnt")
     configure_files(${PROJECT_SOURCE_DIR}/font ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/fonts "*.png")
     configure_files(${PROJECT_SOURCE_DIR}/icon ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} CubicSDR.ico)
-	IF(MSVC)	
-		configure_files(${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/ ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll")
-	ENDIF()
+    IF(MSVC)
+        configure_files(${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/ ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll")
+    ENDIF()
+    IF (CUBICSDR_HAS_HEADER_IMAGE)
+        configure_files(${CUBICSDR_HEADER_IMAGE_DIR} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} ${CUBICSDR_HEADER_IMAGE_FILE})
+    ENDIF()
     add_executable(CubicSDR ${cubicsdr_sources} ${cubicsdr_headers} ${RES_FILES})
     target_link_libraries(CubicSDR ${LIQUID_LIB} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${OTHER_LIBRARIES})
 ENDIF (NOT BUNDLE_APP)
@@ -575,6 +624,8 @@ IF (MSVC)
   set_target_properties(CubicSDR PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
   set_target_properties(CubicSDR PROPERTIES COMPILE_DEFINITIONS_MINSIZEREL "_WINDOWS")
   set(CMAKE_CREATE_WIN32_EXE "/SUBSYSTEM:WINDOWS /ENTRY:\"mainCRTStartup\"")
+  set_target_properties (CubicSDR PROPERTIES OUTPUT_NAME "${CUBICSDR_INSTALL_NAME}")
+
 ENDIF(MSVC)
 
 IF (APPLE)
@@ -596,20 +647,20 @@ IF (APPLE AND BUNDLE_APP)
     set(BUNDLE_MIR_SDR OFF CACHE BOOL "Bundle mir_sdr for personal use only -- do not distribute.")
     
     IF (BUNDLE_SOAPY_MODS)
-    	ADD_DEFINITIONS(
-    		-DBUNDLE_SOAPY_MODS=1
-    	)
-	    set(BUNDLED_MODS_ONLY OFF CACHE BOOL "Use bundled mods only")
-		IF (BUNDLED_MODS_ONLY)
-		ADD_DEFINITIONS(
-    		-DBUNDLED_MODS_ONLY=1
-    	)
-		ENDIF()
+        ADD_DEFINITIONS(
+            -DBUNDLE_SOAPY_MODS=1
+        )
+        set(BUNDLED_MODS_ONLY OFF CACHE BOOL "Use bundled mods only")
+        IF (BUNDLED_MODS_ONLY)
+        ADD_DEFINITIONS(
+            -DBUNDLED_MODS_ONLY=1
+        )
+        ENDIF()
     ENDIF()
     
     ADD_DEFINITIONS(
-		-std=c++0x 
-		-pthread
+        -std=c++0x 
+        -pthread
         -D_OSX_APP_
     )   
       
@@ -639,7 +690,7 @@ IF (APPLE AND BUNDLE_APP)
         MACOSX_BUNDLE_INFO_STRING "CubicSDR Open-Source Software-Defined Radio Application"
         MACOSX_BUNDLE_BUNDLE_NAME "CubicSDR"
         MACOSX_BUNDLE_BUNDLE_VERSION "${CUBICSDR_VERSION}"
-        MACOSX_BUNDLE_LONG_VERSION_STRING "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}.${CUBICSDR_VERSION_REL}"
+        MACOSX_BUNDLE_LONG_VERSION_STRING "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}${CUBICSDR_VERSION_SUFFIX}"
         MACOSX_BUNDLE_SHORT_VERSION_STRING "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}"
         MACOSX_BUNDLE_GUI_IDENTIFIER "com.cubicproductions.cubicsdr"
         MACOSX_BUNDLE_ICON_FILE "CubicSDR.icns"
@@ -651,9 +702,10 @@ IF (APPLE AND BUNDLE_APP)
     # SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
         
     IF (BUNDLE_SOAPY_MODS)
-
     message(STATUS "SOAPY_ROOT: ${SOAPY_SDR_ROOT}")
-    file(GLOB SOAPY_MODS ${SOAPY_SDR_ROOT}/lib/SoapySDR/modules/*.so)
+    SET(SOAPY_SDR_MOD_PATH "${SOAPY_SDR_ROOT}/lib/SoapySDR/modules/${SOAPY_SDR_ABI_VERSION}")
+    
+    file(GLOB SOAPY_MODS ${SOAPY_MOD_PATH}/*.so)
     
     FOREACH(SOAPY_MOD_FILE ${SOAPY_MODS})
             INSTALL( FILES "${SOAPY_MOD_FILE}"
@@ -734,25 +786,26 @@ IF (APPLE AND BUNDLE_APP)
 
    include(CPack)
 ENDIF()
+
 IF(APPLE AND NOT BUNDLE_APP)
     IF (NOT CMAKE_INSTALL_PREFIX)
         SET(CMAKE_INSTALL_PREFIX "/usr/")
     ENDIF()
-	ADD_DEFINITIONS(
-	    -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/"
-	)	
+    ADD_DEFINITIONS(
+        -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/"
+    )    
 
     set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro")
     set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro")
 
-	INSTALL(TARGETS CubicSDR DESTINATION bin)
+    INSTALL(TARGETS CubicSDR DESTINATION bin)
         install(FILES 
             ${PROJECT_SOURCE_DIR}/src/CubicSDR.png
-	    DESTINATION share/cubicsdr)
+        DESTINATION share/cubicsdr)
 
         install(FILES 
             ${CUBICSDR_FONTS}
-	    DESTINATION share/cubicsdr/fonts)
+        DESTINATION share/cubicsdr/fonts)
 
     CONFIGURE_FILE(
       "${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
@@ -768,181 +821,200 @@ IF (WIN32 AND NOT BUILD_INSTALLER)
     ADD_DEFINITIONS(
         -DRES_FOLDER="../share/cubicsdr/"
     )
-	
+    
     INSTALL(TARGETS CubicSDR DESTINATION bin)
-	INSTALL(FILES 		
-		${LIQUID_DLL}
-	DESTINATION bin)
+    INSTALL(FILES         
+        ${LIQUID_DLL}
+    DESTINATION bin)
 
-	IF(USE_HAMLIB)
-		FOREACH(HAMLIB_DLL ${HAMLIB_DLLS})
-			message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}")
+    IF(USE_HAMLIB)
+        FOREACH(HAMLIB_DLL ${HAMLIB_DLLS})
+            message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}")
             INSTALL( FILES "${HAMLIB_DLL}"
                 DESTINATION bin
             )
-		ENDFOREACH()
-	ENDIF()
+        ENDFOREACH()
+    ENDIF()
 
     INSTALL(FILES 
-		${PROJECT_SOURCE_DIR}/src/CubicSDR.png
-	 DESTINATION share/cubicsdr)
+        ${PROJECT_SOURCE_DIR}/src/CubicSDR.png
+     DESTINATION share/cubicsdr)
 
     INSTALL(FILES 
-		${CUBICSDR_FONTS}
-	 DESTINATION share/cubicsdr/fonts)
+        ${CUBICSDR_FONTS}
+     DESTINATION share/cubicsdr/fonts)
+
+    IF (CUBICSDR_HAS_HEADER_IMAGE)
+        INSTALL(FILES 
+        ${CUBICSDR_HEADER_IMAGE}
+     DESTINATION share/cubicsdr/)
+    ENDIF()
+
 ENDIF()
 
 IF (WIN32 AND BUILD_INSTALLER)
     set(BUNDLE_SOAPY_MODS OFF CACHE BOOL "Bundle local SoapySDR modules")
 
-	set(CPACK_GENERATOR NSIS)
-	set(CPACK_PACKAGE_NAME "CubicSDR")
-	set(CPACK_PACKAGE_VENDOR "cubicsdr.com")
-	set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "CubicSDR ${CUBICSDR_VERSION} Installer")
-	set(CPACK_PACKAGE_INSTALL_DIRECTORY "CubicSDR")
-	SET(CPACK_NSIS_INSTALLED_ICON_NAME "CubicSDR.ico")
-	SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") 
-	set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/icon\\\\NSIS_Header.bmp")
-	IF(EX_PLATFORM EQUAL 64)
-		SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
-		SET(CPACK_NSIS_PACKAGE_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY}")
-		SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION}")
-		set(CMAKE_CL_64 TRUE)	# This gets around a bug in the CPack installer name generation for MinGW 64-bit since 2.8
-	ELSE(EX_PLATFORM EQUAL 64)
-		SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
-		SET(CPACK_NSIS_PACKAGE_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} (x86)")
-		SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION} (x86)")
-		set(CMAKE_CL_64 FALSE)
-	ENDIF(EX_PLATFORM EQUAL 64)
-	
-	set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") 
-	install(TARGETS CubicSDR RUNTIME DESTINATION .)
-
-	install(FILES 
+    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 
         ${PROJECT_SOURCE_DIR}/icon/CubicSDR.ico
-		${LIQUID_DLL}
-		DESTINATION .)
+        ${LIQUID_DLL}
+        DESTINATION .)
 
-	install(FILES 
+    install(FILES 
         ${CUBICSDR_FONTS}
-		DESTINATION fonts)
-		
-	IF(USE_HAMLIB)
-		FOREACH(HAMLIB_DLL ${HAMLIB_DLLS})
-			message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}")
+        DESTINATION fonts)
+
+    IF (CUBICSDR_HAS_HEADER_IMAGE)
+        INSTALL(FILES 
+        ${CUBICSDR_HEADER_IMAGE}
+        DESTINATION .)
+    ENDIF()
+        
+    IF(USE_HAMLIB)
+        FOREACH(HAMLIB_DLL ${HAMLIB_DLLS})
+            message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}")
             INSTALL( FILES 
-				${HAMLIB_DLL}
-			DESTINATION .)
-		ENDFOREACH()
-	ENDIF()
-
-	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)
-
-		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)	
+                ${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${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)
+        file(GLOB MSVC32_DEPS ${PROJECT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/*.dll)
+        install(FILES ${MSVC32_DEPS} DESTINATION .)
+    ENDIF(MSVC AND EX_PLATFORM EQUAL 32)
+
+        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)    
 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 <cj at cubicproductions.com>")
-	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 <cj at cubicproductions.com>")
+    SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "CubicSDR Software Defined Radio application v${CUBICSDR_VERSION}")
+    SET(CPACK_DEBIAN_PACKAGE_SECTION "comm")
         SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
-	SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${EX_PLATFORM_NAME}")
+    SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${EX_PLATFORM_NAME}")
 
     IF (NOT CMAKE_INSTALL_PREFIX)
         SET(CMAKE_INSTALL_PREFIX "/usr/")
     ENDIF()
-	ADD_DEFINITIONS(
-	    -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/"
-	    -D_FORTIFY_SOURCE=2
-	)	
-    	
+    ADD_DEFINITIONS(
+        -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/"
+        -D_FORTIFY_SOURCE=2
+    )    
+        
     set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro")
     set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro")
 
-	CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/external/deb/deb_post.sh.in" 
-			"${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE)
+    CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/external/deb/deb_post.sh.in" 
+            "${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE)
 
-	CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in"
-			"${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE)
+    CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in"
+            "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE)
 
-	INSTALL(TARGETS CubicSDR DESTINATION bin)
+    INSTALL(TARGETS CubicSDR DESTINATION bin)
         install(FILES 
             ${PROJECT_SOURCE_DIR}/src/CubicSDR.png
-	    DESTINATION share/cubicsdr)
+        DESTINATION share/cubicsdr)
 
         install(FILES 
             ${CUBICSDR_FONTS}
-	    DESTINATION share/cubicsdr/fonts)
+        DESTINATION share/cubicsdr/fonts)
         
     INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" 
         DESTINATION share/applications)
         
-	INCLUDE(CPack)
+    INCLUDE(CPack)
 ENDIF()
 IF(UNIX AND NOT APPLE AND NOT BUILD_DEB)
     IF (NOT CMAKE_INSTALL_PREFIX)
         SET(CMAKE_INSTALL_PREFIX "/usr/")
     ENDIF()
-	ADD_DEFINITIONS(
-	    -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/"
-	)	
+    ADD_DEFINITIONS(
+        -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/"
+    )    
 
     set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro")
     set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro")
  
-	CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in"
-			"${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE)
+    CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in"
+            "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE)
 
-	INSTALL(TARGETS CubicSDR DESTINATION bin)
+    INSTALL(TARGETS CubicSDR DESTINATION bin)
  
     INSTALL(FILES 
             ${PROJECT_SOURCE_DIR}/src/CubicSDR.png
-		    DESTINATION share/cubicsdr)
+            DESTINATION share/cubicsdr)
 
     INSTALL(FILES 
             ${CUBICSDR_FONTS}
-		    DESTINATION share/cubicsdr/fonts)
+            DESTINATION share/cubicsdr/fonts)
+
+
+    IF (CUBICSDR_HAS_HEADER_IMAGE)
+        INSTALL(FILES 
+            ${CUBICSDR_HEADER_IMAGE}
+            DESTINATION share/cubicsdr)
+    ENDIF()
 
     INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop"
-        	DESTINATION share/applications)
+            DESTINATION share/applications)
         
     CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/external/deb/deb_post.sh.in" 
-			"${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE)
+            "${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE)
     CONFIGURE_FILE(
       "${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
       "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
diff --git a/LICENSE b/LICENSE
index 00eec89..d159169 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
-GNU GENERAL PUBLIC LICENSE
+                    GNU GENERAL PUBLIC LICENSE
                        Version 2, June 1991
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
@@ -290,8 +290,8 @@ to attach them to the start of each source file to most effectively
 convey the exclusion of warranty; and each file should have at least
 the "copyright" line and a pointer to where the full notice is found.
 
-    CubicSDR
-    Copyright (C) 2014  Charles J. Cliffe
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -329,7 +329,7 @@ necessary.  Here is a sample; alter the names:
   Yoyodyne, Inc., hereby disclaims all copyright interest in the program
   `Gnomovision' (which makes passes at compilers) written by James Hacker.
 
-  {signature of Ty Coon}, 1 April 1989
+  <signature of Ty Coon>, 1 April 1989
   Ty Coon, President of Vice
 
 This General Public License does not permit incorporating your program into
@@ -337,4 +337,3 @@ proprietary programs.  If your program is a subroutine library, you may
 consider it more useful to permit linking proprietary applications with the
 library.  If this is what you want to do, use the GNU Lesser General
 Public License instead of this License.
-
diff --git a/README.md b/README.md
index e931b94..579f298 100644
--- a/README.md
+++ b/README.md
@@ -48,4 +48,4 @@ Target Platforms:
 
 License:
 -------
-  - GPL
+  - GPL-2.0+
diff --git a/src/AppConfig.cpp b/src/AppConfig.cpp
index 39d2f4d..eea6539 100644
--- a/src/AppConfig.cpp
+++ b/src/AppConfig.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "AppConfig.h"
 #include "CubicSDR.h"
 
@@ -28,7 +31,6 @@ long long DeviceConfig::getOffset() {
     return offset.load();
 }
 
-
 void DeviceConfig::setSampleRate(long srate) {
     sampleRate.store(srate);
 }
@@ -82,7 +84,7 @@ void DeviceConfig::save(DataNode *node) {
     std::lock_guard < std::mutex > lock(busy_lock);
     *node->newChild("id") = deviceId;
     *node->newChild("name") = deviceName;
-    *node->newChild("ppm") = (int)ppm.load();
+    *node->newChild("ppm") = ppm.load();
     *node->newChild("offset") = offset.load();
     *node->newChild("sample_rate") = sampleRate.load();
     *node->newChild("agc_mode") = agcMode.load()?1:0;
@@ -289,7 +291,13 @@ AppConfig::AppConfig() : configName("") {
     centerFreq.store(100000000);
     waterfallLinesPerSec.store(DEFAULT_WATERFALL_LPS);
     spectrumAvgSpeed.store(0.65f);
+    dbOffset.store(0);
     modemPropsCollapsed.store(false);
+    mainSplit = -1;
+    visSplit = -1;
+    bookmarkSplit = 200;
+    bookmarksVisible.store(true);
+    
 #ifdef USE_HAMLIB
     rigEnabled.store(false);
     rigModel.store(1);
@@ -425,6 +433,14 @@ float AppConfig::getSpectrumAvgSpeed() {
     return spectrumAvgSpeed.load();
 }
 
+void AppConfig::setDBOffset(int offset) {
+    this->dbOffset.store(offset);
+}
+
+int AppConfig::getDBOffset() {
+    return dbOffset.load();
+}
+
 void AppConfig::setManualDevices(std::vector<SDRManualDef> manuals) {
     manualDevices = manuals;
 }
@@ -433,6 +449,39 @@ std::vector<SDRManualDef> 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;
 }
@@ -478,6 +527,12 @@ bool AppConfig::save() {
         *window_node->newChild("waterfall_lps") = waterfallLinesPerSec.load();
         *window_node->newChild("spectrum_avg") = spectrumAvgSpeed.load();
         *window_node->newChild("modemprops_collapsed") = modemPropsCollapsed.load();;
+        *window_node->newChild("db_offset") = dbOffset.load();
+
+        *window_node->newChild("main_split") = mainSplit.load();
+        *window_node->newChild("vis_split") = visSplit.load();
+        *window_node->newChild("bookmark_split") = bookmarkSplit.load();
+        *window_node->newChild("bookmark_visible") = bookmarksVisible.load();
     }
     
     DataNode *devices_node = cfg.rootNode()->newChild("devices");
@@ -556,12 +611,13 @@ bool AppConfig::load() {
     }
 
     if (cfg.rootNode()->hasAnother("window")) {
-        int x,y,w,h;
-        int max,tips,lpm,mpc;
+        int x = 0 ,y = 0 ,w = 0 ,h = 0;
+        int max = 0 ,tips = 0 ,lpm = 0 ,mpc = 0;
         
         DataNode *win_node = cfg.rootNode()->getNext("window");
         
         if (win_node->hasAnother("w") && win_node->hasAnother("h") && win_node->hasAnother("x") && win_node->hasAnother("y")) {
+
             win_node->getNext("x")->element()->get(x);
             win_node->getNext("y")->element()->get(y);
             win_node->getNext("w")->element()->get(w);
@@ -628,6 +684,37 @@ bool AppConfig::load() {
             win_node->getNext("modemprops_collapsed")->element()->get(mpc);
             modemPropsCollapsed.store(mpc?true:false);
         }
+        
+        if (win_node->hasAnother("db_offset")) {
+            DataNode *offset_node = win_node->getNext("db_offset");
+            int offsetValue = 0;
+            offset_node->element()->get(offsetValue);
+            setDBOffset(offsetValue);
+        }
+
+        if (win_node->hasAnother("main_split")) {
+            float gVal;
+            win_node->getNext("main_split")->element()->get(gVal);
+            mainSplit.store(gVal);
+        }
+        
+        if (win_node->hasAnother("vis_split")) {
+            float gVal;
+            win_node->getNext("vis_split")->element()->get(gVal);
+            visSplit.store(gVal);
+        }
+        
+        if (win_node->hasAnother("bookmark_split")) {
+            float gVal;
+            win_node->getNext("bookmark_split")->element()->get(gVal);
+            bookmarkSplit.store(gVal);
+        }
+
+        if (win_node->hasAnother("bookmark_visible")) {
+            int bVal;
+            win_node->getNext("bookmark_visible")->element()->get(bVal);
+            bookmarksVisible.store(bVal);
+        }
     }
     
     if (cfg.rootNode()->hasAnother("devices")) {
diff --git a/src/AppConfig.h b/src/AppConfig.h
index 96570aa..5d78e78 100644
--- a/src/AppConfig.h
+++ b/src/AppConfig.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <wx/stdpaths.h>
@@ -112,9 +115,25 @@ public:
     void setSpectrumAvgSpeed(float avgSpeed);
     float getSpectrumAvgSpeed();
     
+    void setDBOffset(int offset);
+    int getDBOffset();
+    
     void setManualDevices(std::vector<SDRManualDef> manuals);
     std::vector<SDRManualDef> 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);
@@ -157,8 +176,10 @@ private:
     std::atomic_llong snap;
     std::atomic_llong centerFreq;
     std::atomic_int waterfallLinesPerSec;
-    std::atomic<float> spectrumAvgSpeed;
+    std::atomic<float> spectrumAvgSpeed, mainSplit, visSplit, bookmarkSplit;
+    std::atomic_int dbOffset;
     std::vector<SDRManualDef> manualDevices;
+    std::atomic_bool bookmarksVisible;
 #if USE_HAMLIB
     std::atomic_int rigModel, rigRate;
     std::string rigPort;
diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp
index dff457b..599384a 100644
--- a/src/AppFrame.cpp
+++ b/src/AppFrame.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "AppFrame.h"
 
 #include "wx/wxprec.h"
@@ -20,6 +23,7 @@
 #include "DataTree.h"
 #include "ColorTheme.h"
 #include "DemodulatorMgr.h"
+#include "ImagePanel.h"
 
 #include <thread>
 
@@ -43,6 +47,10 @@ wxEND_EVENT_TABLE()
 #include "RigThread.h"
 #endif
 
+
+/* split a string by 'seperator' into a vector of string */
+std::vector<std::string> str_explode(const std::string &seperator, const std::string &in_str);
+
 #define APPFRAME_MODEMPROPS_MINSIZE 20
 #define APPFRAME_MODEMPROPS_MAXSIZE 240
 
@@ -54,21 +62,40 @@ AppFrame::AppFrame() :
 #endif
 
     wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
-    wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL);
     demodTray = new wxBoxSizer(wxHORIZONTAL);
     wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL);
     wxBoxSizer *demodTunerTray = new wxBoxSizer(wxHORIZONTAL);
 
-    int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
+    std::vector<int> attribList = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
     //wxGLAttributes attribList;
     //attribList.PlatformDefaults().RGBA().DoubleBuffer().EndList();
     //attribList.PlatformDefaults().MinRGBA(8, 8, 8, 8).DoubleBuffer().Depth(16).EndList();
 
     mainSplitter = new wxSplitterWindow( this, wxID_MAIN_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE );
-    mainSplitter->SetSashGravity(10.0/37.0);
+    mainSplitter->SetSashGravity(10.0f / 37.0f);
     mainSplitter->SetMinimumPaneSize(1);
+    
 
     wxPanel *demodPanel = new wxPanel(mainSplitter, wxID_ANY);
+
+#ifdef CUBICSDR_HEADER_IMAGE
+    wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath());
+    std::string headerPath = exePath.GetPath().ToStdString();
+    headerPath += filePathSeparator + std::string("" CUBICSDR_HEADER_IMAGE);
+    wxInitAllImageHandlers();
+
+    ImagePanel *imgPanel = new ImagePanel(demodPanel, headerPath, wxBITMAP_TYPE_ANY);
+
+    std::string headerBgColor = "" CUBICSDR_HEADER_BG;
+    if (headerBgColor != "") {
+        imgPanel->SetBackgroundColour(wxColour(headerBgColor));
+    }
+
+    imgPanel->SetBestFittingSize(wxSize(200, 0));
+
+    demodTray->Add(imgPanel, 0, wxEXPAND | wxALL, 0);
+    demodTray->AddSpacer(1);
+#endif
             
     gainCanvas = new GainCanvas(demodPanel, attribList);
     
@@ -77,16 +104,23 @@ AppFrame::AppFrame() :
     gainSpacerItem = demodTray->AddSpacer(1);
     gainSpacerItem->Show(false);
             
+    std::vector<std::string> modemList = { "FM", "FMS", "NBFM", "AM", "LSB", "USB", "DSB", "I/Q" };
+  
+#ifdef CUBICSDR_MODEM_EXCLUDE
+    std::string excludeListStr = "" CUBICSDR_MODEM_EXCLUDE;
+    std::vector<std::string> excludeList = str_explode(",",excludeListStr);
+    for (auto ex_i : excludeList) {
+        std::vector<std::string>::iterator found_i = std::find(modemList.begin(),modemList.end(),ex_i);
+        if (found_i != modemList.end()) {
+            modemList.erase(found_i);
+        }
+    }
+#endif
+            
     demodModeSelector = new ModeSelectorCanvas(demodPanel, attribList);
-    demodModeSelector->addChoice("FM");
-    demodModeSelector->addChoice("FMS");
-    demodModeSelector->addChoice("NBFM");
-    demodModeSelector->addChoice("AM");
-    demodModeSelector->addChoice("LSB");
-    demodModeSelector->addChoice("USB");
-    demodModeSelector->addChoice("DSB");
-    demodModeSelector->addChoice("I/Q");
-    demodModeSelector->setSelection("FM");
+    for (auto mt_i : modemList) {
+        demodModeSelector->addChoice(mt_i);
+    }
     demodModeSelector->setHelpTip("Choose modulation type: Frequency Modulation (Hotkey F), Amplitude Modulation (A) and Lower (L), Upper (U), Double Side-Band and more.");
     demodModeSelector->SetMinSize(wxSize(50,-1));
     demodModeSelector->SetMaxSize(wxSize(50,-1));
@@ -117,14 +151,20 @@ AppFrame::AppFrame() :
     modemProps->SetMinSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE,-1));
     modemProps->SetMaxSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE,-1));
 
-    modemProps->Hide();
+    ModemArgInfoList dummyInfo;
+    modemProps->initProperties(dummyInfo, nullptr);
+    modemProps->updateTheme();
+
     demodTray->Add(modemProps, 15, wxEXPAND | wxALL, 0);
 
 #ifndef __APPLE__
     demodTray->AddSpacer(1);
 #endif
-            
-    wxGetApp().getDemodSpectrumProcessor()->setup(1024);
+      
+#if CUBICSDR_ENABLE_VIEW_DEMOD
+    wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL);
+
+    wxGetApp().getDemodSpectrumProcessor()->setup(DEFAULT_DMOD_FFT_SIZE);
     demodSpectrumCanvas = new SpectrumCanvas(demodPanel, attribList);
     demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
     demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0);
@@ -133,7 +173,7 @@ AppFrame::AppFrame() :
     demodVisuals->AddSpacer(1);
 
     demodWaterfallCanvas = new WaterfallCanvas(demodPanel, attribList);
-    demodWaterfallCanvas->setup(1024, 128);
+    demodWaterfallCanvas->setup(DEFAULT_DMOD_FFT_SIZE, DEFAULT_DEMOD_WATERFALL_LINES_NB);
     demodWaterfallCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
     demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas);
     demodWaterfallCanvas->setMinBandwidth(8000);
@@ -141,13 +181,19 @@ AppFrame::AppFrame() :
     demodVisuals->Add(demodWaterfallCanvas, 6, wxEXPAND | wxALL, 0);
     wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodWaterfallCanvas->getVisualDataQueue());
     demodWaterfallCanvas->getVisualDataQueue()->set_max_num_items(3);
+    demodWaterfallCanvas->setLinesPerSecond((int)(DEFAULT_DEMOD_WATERFALL_LINES_NB / DEMOD_WATERFALL_DURATION_IN_SECONDS));
+
 
     demodVisuals->SetMinSize(wxSize(128,-1));
 
     demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0);
 
     demodTray->AddSpacer(1);
-
+#else
+    demodSpectrumCanvas = nullptr;
+    demodWaterfallCanvas = nullptr;
+#endif
+            
     demodSignalMeter = new MeterCanvas(demodPanel, attribList);
     demodSignalMeter->setMax(DEMOD_SIGNAL_MAX);
     demodSignalMeter->setMin(DEMOD_SIGNAL_MIN);
@@ -160,15 +206,19 @@ AppFrame::AppFrame() :
 
     demodTray->AddSpacer(1);
 
+#if CUBICSDR_ENABLE_VIEW_SCOPE
     scopeCanvas = new ScopeCanvas(demodPanel, attribList);
-    scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum.");
+    scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum, 'B' to toggle decibels display.");
     scopeCanvas->SetMinSize(wxSize(128,-1));
     demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0);
-    wxGetApp().getScopeProcessor()->setup(1024);
+    wxGetApp().getScopeProcessor()->setup(DEFAULT_SCOPE_FFT_SIZE);
     wxGetApp().getScopeProcessor()->attachOutput(scopeCanvas->getInputQueue());
 
     demodScopeTray->AddSpacer(1);
-
+#else
+    scopeCanvas = nullptr;
+#endif
+            
     deltaLockButton = new ModeSelectorCanvas(demodPanel, attribList);
     deltaLockButton->addChoice(1, "V");
     deltaLockButton->setPadding(-1,-1);
@@ -232,19 +282,23 @@ 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 );
 
     wxPanel *spectrumPanel = new wxPanel(mainVisSplitter, wxID_ANY);
     wxBoxSizer *spectrumSizer = new wxBoxSizer(wxHORIZONTAL);
 
-    wxGetApp().getSpectrumProcessor()->setup(2048);
+    wxGetApp().getSpectrumProcessor()->setup(DEFAULT_FFT_SIZE);
     spectrumCanvas = new SpectrumCanvas(spectrumPanel, attribList);
     spectrumCanvas->setShowDb(true);
+    spectrumCanvas->setUseDBOfs(true);
     spectrumCanvas->setScaleFactorEnabled(true);
     wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue());
            
@@ -284,7 +338,7 @@ AppFrame::AppFrame() :
     wxBoxSizer *wfSizer = new wxBoxSizer(wxHORIZONTAL);
            
     waterfallCanvas = new WaterfallCanvas(waterfallPanel, attribList);
-    waterfallCanvas->setup(2048, 512);
+    waterfallCanvas->setup(DEFAULT_FFT_SIZE, DEFAULT_MAIN_WATERFALL_LINES_NB);
 
     waterfallDataThread = new FFTVisualDataThread();
 
@@ -309,10 +363,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));
             
-    vbox->Add(mainSplitter, 1, wxEXPAND | wxALL, 0);
+    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);
@@ -375,6 +438,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?"));
@@ -467,6 +532,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);
 
@@ -585,13 +653,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
@@ -608,6 +690,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");
@@ -627,9 +710,13 @@ void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) {
     deviceChanged.store(true);
 }
 
+void AppFrame::notifyDeviceChanged() {
+    deviceChanged.store(true);
+}
+
 void AppFrame::updateDeviceParams() {
     
-    if (!deviceChanged.load()) {
+    if (!deviceChanged.load() || devInfo == nullptr) {
         return;
     }
     
@@ -649,6 +736,7 @@ void AppFrame::updateDeviceParams() {
 
     newSettingsMenu->AppendSeparator();
 
+    newSettingsMenu->Append(wxID_SET_DB_OFFSET, "Power Level Offset");
     newSettingsMenu->Append(wxID_SET_FREQ_OFFSET, "Frequency Offset");
 
     if (devInfo->hasCORR(SOAPY_SDR_RX, 0)) {
@@ -717,16 +805,35 @@ void AppFrame::updateDeviceParams() {
     menuBar->Replace(1, newSettingsMenu, wxT("&Settings"));
     settingsMenu = newSettingsMenu;
     
-    // Build sample rate menu
+    // Build/Rebuild the sample rate menu :
     sampleRates = devInfo->getSampleRates(SOAPY_SDR_RX, 0);
-    sampleRateMenuItems.erase(sampleRateMenuItems.begin(),sampleRateMenuItems.end());
+    sampleRateMenuItems.clear();
     
     wxMenu *newSampleRateMenu = new wxMenu;
     int ofs = 0;
+    
+    //Current sample rate, try to keep it as is.
     long sampleRate = wxGetApp().getSampleRate();
+   
+    long minRate = sampleRates.front();
+    long maxRate = sampleRates.back();
+
+    //If it is beyond limits, make device choose a reasonable value
+    if (sampleRate < minRate || sampleRate > maxRate) {
+        sampleRate = devInfo->getSampleRateNear(SOAPY_SDR_RX, 0, sampleRate);
+    }
+
+    //Check if a manual entry was previously set: if so, check its value is still within the limits of the device. If not so, reset it.
+    if (manualSampleRate > 0 && 
+        (manualSampleRate < minRate || manualSampleRate > maxRate)) {
+        manualSampleRate = -1;
+    }
+
     bool checked = false;
     for (vector<long>::iterator i = sampleRates.begin(); i != sampleRates.end(); i++) {
+
         sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_BASE+ofs, frequencyToStr(*i));
+        
         if (sampleRate == (*i)) {
             sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs]->Check(true);
             checked = true;
@@ -734,10 +841,26 @@ void AppFrame::updateDeviceParams() {
         ofs++;
     }
     
-    sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual Entry")); 
+    //Add a manual sample value radio button, but disabled by default in case the user 
+    //never ever uses manual entry.
+    if (manualSampleRate <= 0) {
+        sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual :  N/A"));
+        sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(false);
+    }
+    else {
+        sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual :  ") + frequencyToStr(manualSampleRate));
+        sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true);
+    }
+
+    //We apply the current sample rate after all 
     if (!checked) {
         sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true);
     }
+
+    //Append a normal button (NOT a radio-button) for manual entry dialog at the end
+    newSampleRateMenu->AppendSeparator();
+    sampleRateMenuItems[wxID_BANDWIDTH_MANUAL_DIALOG] = newSampleRateMenu->Append(wxID_BANDWIDTH_MANUAL_DIALOG, wxT("Manual Entry..."));
+
    
     menuBar->Replace(2, newSampleRateMenu, wxT("Sample &Rate"));
     sampleRateMenu = newSampleRateMenu;
@@ -807,6 +930,71 @@ void AppFrame::disableRig() {
 }
 #endif
 
+bool AppFrame::actionOnMenuDisplay(wxCommandEvent& event) {
+
+    //by default, is managed.
+    bool bManaged = true;
+
+    if (event.GetId() == wxID_THEME_DEFAULT) {
+        ThemeMgr::mgr.setTheme(COLOR_THEME_DEFAULT);
+    }
+    else if (event.GetId() == wxID_THEME_SHARP) {
+        ThemeMgr::mgr.setTheme(COLOR_THEME_SHARP);
+    }
+    else if (event.GetId() == wxID_THEME_BW) {
+        ThemeMgr::mgr.setTheme(COLOR_THEME_BW);
+    }
+    else if (event.GetId() == wxID_THEME_RAD) {
+        ThemeMgr::mgr.setTheme(COLOR_THEME_RAD);
+    }
+    else if (event.GetId() == wxID_THEME_TOUCH) {
+        ThemeMgr::mgr.setTheme(COLOR_THEME_TOUCH);
+    }
+    else if (event.GetId() == wxID_THEME_HD) {
+        ThemeMgr::mgr.setTheme(COLOR_THEME_HD);
+    }
+    else if (event.GetId() == wxID_THEME_RADAR) {
+        ThemeMgr::mgr.setTheme(COLOR_THEME_RADAR);
+    }
+    //Display : font sizes
+    else if (event.GetId() == wxID_DISPLAY_BASE) {
+        GLFont::setScale(GLFont::GLFONT_SCALE_NORMAL);
+    }
+    else if (event.GetId() == wxID_DISPLAY_BASE + 1) {
+        GLFont::setScale(GLFont::GLFONT_SCALE_MEDIUM);
+    }
+    else if (event.GetId() == wxID_DISPLAY_BASE + 2) {
+        GLFont::setScale(GLFont::GLFONT_SCALE_LARGE);
+    }
+    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();
+        }
+    }
+    else {
+        bManaged = false;
+    }
+
+    //update theme choice in children elements:  
+    if (event.GetId() >= wxID_THEME_DEFAULT && event.GetId() <= wxID_THEME_RADAR) {
+       
+        gainCanvas->setThemeColors();
+        modemProps->updateTheme();
+        bookmarkView->updateTheme();
+    }
+
+    //force all windows refresh
+    if (bManaged) {
+        Refresh();
+    }
+
+    return bManaged;
+}
 
 void AppFrame::OnMenu(wxCommandEvent& event) {
 
@@ -839,17 +1027,6 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
         lowPerfMode = lowPerfMenuItem->IsChecked();
         wxGetApp().getConfig()->setLowPerfMode(lowPerfMode);
 
-//        long srate = wxGetApp().getSampleRate();
-//        if (srate > CHANNELIZER_RATE_MAX && lowPerfMode) {
-//            if (wxGetApp().getSpectrumProcessor()->getFFTSize() != 1024) {
-//                setMainWaterfallFFTSize(1024);
-//            }
-//        } else if (srate > CHANNELIZER_RATE_MAX) {
-//            if (wxGetApp().getSpectrumProcessor()->getFFTSize() != 2048) {
-//                setMainWaterfallFFTSize(2048);
-//            }
-//        }
-
     } else if (event.GetId() == wxID_SET_TIPS ) {
         if (wxGetApp().getConfig()->getShowTips()) {
             wxGetApp().getConfig()->setShowTips(false);
@@ -864,6 +1041,12 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
         if (ofs != -1) {
             wxGetApp().setOffset(ofs);
         }
+    } else if (event.GetId() == wxID_SET_DB_OFFSET) {
+        long ofs = wxGetNumberFromUser("Shift the displayed RF power level by this amount.\ni.e. -30 for -30 dB", "Decibels (dB)",
+                                       "Power Level Offset", wxGetApp().getConfig()->getDBOffset(), -1000, 1000, this);
+        if (ofs != -1) {
+            wxGetApp().getConfig()->setDBOffset(ofs);
+        }
     } else if (event.GetId() == wxID_AGC_CONTROL) {
         if (wxGetApp().getDevice() == NULL) {
             agcMenuItem->Check(true);
@@ -930,45 +1113,23 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
         waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS));
         wxGetApp().getSpectrumProcessor()->setFFTAverageRate(0.65f);
         spectrumAvgMeter->setLevel(0.65f);
-        demodModeSelector->Refresh();
-        demodTuner->Refresh();
+ 
         SetTitle(CUBICSDR_TITLE);
         currentSessionFile = "";
-    } else if (event.GetId() == wxID_CLOSE || event.GetId() == wxID_EXIT) {
-        Close(false);
-    } else if (event.GetId() == wxID_THEME_DEFAULT) {
-        ThemeMgr::mgr.setTheme(COLOR_THEME_DEFAULT);
-    } else if (event.GetId() == wxID_THEME_SHARP) {
-        ThemeMgr::mgr.setTheme(COLOR_THEME_SHARP);
-    } else if (event.GetId() == wxID_THEME_BW) {
-        ThemeMgr::mgr.setTheme(COLOR_THEME_BW);
-    } else if (event.GetId() == wxID_THEME_RAD) {
-        ThemeMgr::mgr.setTheme(COLOR_THEME_RAD);
-    } else if (event.GetId() == wxID_THEME_TOUCH) {
-        ThemeMgr::mgr.setTheme(COLOR_THEME_TOUCH);
-    } else if (event.GetId() == wxID_THEME_HD) {
-        ThemeMgr::mgr.setTheme(COLOR_THEME_HD);
-    } else if (event.GetId() == wxID_THEME_RADAR) {
-        ThemeMgr::mgr.setTheme(COLOR_THEME_RADAR);
-    }
-    //Display : font sizes
-    else if (event.GetId() == wxID_DISPLAY_BASE) {
-        GLFont::setScale(GLFont::GLFONT_SCALE_NORMAL);
-        //force all windows refresh
-        Refresh();
-    }
-    else if (event.GetId() == wxID_DISPLAY_BASE + 1) {
-        GLFont::setScale(GLFont::GLFONT_SCALE_MEDIUM);
+        bookmarkSplitter->Unsplit(bookmarkView);
+        bookmarkSplitter->SplitVertically( bookmarkView, mainVisSplitter, wxGetApp().getConfig()->getBookmarkSplit() );
+        hideBookmarksItem->Check(false);
         //force all windows refresh
         Refresh();
+
+    } else if (event.GetId() == wxID_CLOSE || event.GetId() == wxID_EXIT) {
+        Close(false);
     }
-    else if (event.GetId() == wxID_DISPLAY_BASE + 2) {
-        GLFont::setScale(GLFont::GLFONT_SCALE_LARGE);
-        //force all windows refresh
-        Refresh();
+    else if (actionOnMenuDisplay(event)) {
+        //done in actionOnMenuDisplay
+        return;
     }
-
-    if (event.GetId() >= wxID_SETTINGS_BASE && event.GetId() < settingsIdMax) {
+    else if (event.GetId() >= wxID_SETTINGS_BASE && event.GetId() < settingsIdMax) {
         int setIdx = event.GetId()-wxID_SETTINGS_BASE;
         int menuIdx = 0;
         for (std::vector<SoapySDR::ArgInfo>::iterator arg_i = settingArgs.begin(); arg_i != settingArgs.end(); arg_i++) {
@@ -1019,50 +1180,53 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
         }
     }
     
-    if (event.GetId() >= wxID_THEME_DEFAULT && event.GetId() <= wxID_THEME_RADAR) {
-    	demodTuner->Refresh();
-    	demodModeSelector->Refresh();
-        waterfallSpeedMeter->Refresh();
-        spectrumAvgMeter->Refresh();
-        gainCanvas->setThemeColors();
-        modemProps->updateTheme();
+   
+
+    if (event.GetId() == wxID_BANDWIDTH_MANUAL) {
+        wxGetApp().setSampleRate(manualSampleRate);
     }
+    else if (event.GetId() == wxID_BANDWIDTH_MANUAL_DIALOG) {
+
+        int rateHigh = 0, rateLow = 0;
+
+        SDRDeviceInfo *dev = wxGetApp().getDevice();
+        if (dev != nullptr) {
 
-    switch (event.GetId()) {
-        case wxID_BANDWIDTH_MANUAL:
-            int rateHigh, rateLow;
-            
-            SDRDeviceInfo *dev = wxGetApp().getDevice();
-            if (dev == NULL) {
-                break;
-            }
-            
             std::vector<long> sampleRates = dev->getSampleRates(SOAPY_SDR_RX, 0);
-            
-            rateLow = 2000000;
-            rateHigh = 30000000;
-            
+
+            //default
+            rateLow = MANUAL_SAMPLE_RATE_MIN;
+            rateHigh = MANUAL_SAMPLE_RATE_MAX;
+
             if (sampleRates.size()) {
-                rateLow = sampleRates[0];
-                rateHigh = sampleRates[sampleRates.size()-1];
+                rateLow = sampleRates.front();
+                rateHigh = sampleRates.back();
             }
 
             long bw = wxGetNumberFromUser("\n" + dev->getName() + "\n\n  "
-                                          + "min: " + std::to_string(rateLow) + " Hz"
-                                          + ", max: " + std::to_string(rateHigh) + " Hz\n",
-                                          "Sample Rate in Hz",
-                                          "Manual Sample Rate Entry",
-                                          wxGetApp().getSampleRate(),
-                                          rateLow,
-                                          rateHigh,
-                                          this);
+                + "min: " + std::to_string(rateLow) + " Hz"
+                + ", max: " + std::to_string(rateHigh) + " Hz\n",
+                "Sample Rate in Hz",
+                "Manual Sample Rate Entry",
+                //If a manual sample rate has already been input, recall this one.
+                manualSampleRate > 0? manualSampleRate :wxGetApp().getSampleRate(),
+                rateLow,
+                rateHigh,
+                this);
+
             if (bw != -1) {
-                wxGetApp().setSampleRate(bw);
+
+                manualSampleRate = bw;
+                sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true);
+                
+                sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->SetItemLabel(wxT("Manual :  ") + frequencyToStr(manualSampleRate));
+                sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true);
+                wxGetApp().setSampleRate(manualSampleRate);
             }
-            break;
+        }
     }
-    
-    if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE + (int)sampleRates.size()) {
+    else if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE + (int)sampleRates.size()) {
+
         wxGetApp().setSampleRate(sampleRates[event.GetId()-wxID_BANDWIDTH_BASE]);
     }
     
@@ -1209,12 +1373,26 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
 void AppFrame::OnClose(wxCloseEvent& event) {
     wxGetApp().closeDeviceSelector();
 
-    wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodSpectrumCanvas->getVisualDataQueue());
-    wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodWaterfallCanvas->getVisualDataQueue());
+    if (wxGetApp().getDemodSpectrumProcessor()) {
+        wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodSpectrumCanvas->getVisualDataQueue());
+        wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodWaterfallCanvas->getVisualDataQueue());
+    }
     wxGetApp().getSpectrumProcessor()->removeOutput(spectrumCanvas->getVisualDataQueue());
 
+    if (saveDisabled) {
+        event.Skip();
+        return;
+    }
+    
+#ifdef __APPLE__
+    if (this->GetPosition().y > 0) {
+        wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize());
+        wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized());
+    }
+#else
     wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize());
     wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized());
+#endif
     wxGetApp().getConfig()->setTheme(ThemeMgr::mgr.getTheme());
     wxGetApp().getConfig()->setFontScale(GLFont::getScale());
     wxGetApp().getConfig()->setSnap(wxGetApp().getFrequencySnap());
@@ -1223,6 +1401,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);
@@ -1234,6 +1416,7 @@ void AppFrame::OnClose(wxCloseEvent& event) {
     wxGetApp().getConfig()->setRigFollowModem(rigFollowModemMenuItem->IsChecked());
 #endif
     wxGetApp().getConfig()->save();
+    wxGetApp().getBookmarkMgr().saveToFile("bookmarks.xml");
     event.Skip();
 }
 
@@ -1241,6 +1424,7 @@ void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) {
     new AppFrame();
 }
 
+
 void AppFrame::OnThread(wxCommandEvent& event) {
     event.Skip();
 }
@@ -1281,7 +1465,9 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
             demodGainMeter->setInputValue(demod->getGain());
             wxGetApp().getDemodMgr().setLastGain(demod->getGain());
             int outputDevice = demod->getOutputDevice();
-            scopeCanvas->setDeviceName(outputDevices[outputDevice].name);
+            if (scopeCanvas) {
+                scopeCanvas->setDeviceName(outputDevices[outputDevice].name);
+            }
 //            outputDeviceMenuItems[outputDevice]->Check(true);
             std::string dType = demod->getDemodulatorType();
             demodModeSelector->setSelection(dType);
@@ -1293,7 +1479,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
             modemPropertiesUpdated.store(true);
             demodTuner->setHalfBand(dType=="USB" || dType=="LSB");
         }
-        if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) {
+        if (!demodWaterfallCanvas || demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) {
             long long centerFreq = demod->getFrequency();
             unsigned int demodBw = (unsigned int) ceil((float) demod->getBandwidth() * 2.25);
 
@@ -1314,7 +1500,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
                 demodBw = 20000;
             }
 
-            if (centerFreq != demodWaterfallCanvas->getCenterFrequency()) {
+            if (demodWaterfallCanvas && centerFreq != demodWaterfallCanvas->getCenterFrequency()) {
                 demodWaterfallCanvas->setCenterFrequency(centerFreq);
                 demodSpectrumCanvas->setCenterFrequency(centerFreq);
             }
@@ -1400,8 +1586,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
                 }
             }
             
-            demodWaterfallCanvas->setBandwidth(demodBw);
-            demodSpectrumCanvas->setBandwidth(demodBw);
+            if (demodWaterfallCanvas) {
+                demodWaterfallCanvas->setBandwidth(demodBw);
+                demodSpectrumCanvas->setBandwidth(demodBw);
+            }
         }
 
         demodSignalMeter->setLevel(demod->getSignalLevel());
@@ -1457,14 +1645,18 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
             demodGainMeter->setLevel(demodGainMeter->getInputValue());
         }
 
-        if (wxGetApp().getFrequency() != demodWaterfallCanvas->getCenterFrequency()) {
+        if (demodWaterfallCanvas && wxGetApp().getFrequency() != demodWaterfallCanvas->getCenterFrequency()) {
             demodWaterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
-            demodSpectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
+            if (demodSpectrumCanvas) {
+                demodSpectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
+            }
         }
+
         if (spectrumCanvas->getViewState() && abs(wxGetApp().getFrequency()-spectrumCanvas->getCenterFrequency()) > (wxGetApp().getSampleRate()/2)) {
             spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
             waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
         }
+
         if (demodMuteButton->modeChanged()) {
             int muteMode = demodMuteButton->getSelection();
             if (muteMode == -1) {
@@ -1476,15 +1668,16 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
         }
     }
 
-    scopeCanvas->setPPMMode(demodTuner->isAltDown());
-    
-    scopeCanvas->setShowDb(spectrumCanvas->getShowDb());
-    wxGetApp().getScopeProcessor()->setScopeEnabled(scopeCanvas->scopeVisible());
-    wxGetApp().getScopeProcessor()->setSpectrumEnabled(scopeCanvas->spectrumVisible());
-    wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0));
+    if (scopeCanvas) {
+        scopeCanvas->setPPMMode(demodTuner->isAltDown());
+        
+        wxGetApp().getScopeProcessor()->setScopeEnabled(scopeCanvas->scopeVisible());
+        wxGetApp().getScopeProcessor()->setSpectrumEnabled(scopeCanvas->spectrumVisible());
+        wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0));
+        
+        wxGetApp().getScopeProcessor()->run();
+    }
     
-    wxGetApp().getScopeProcessor()->run();
-
     SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor();
 
     if (spectrumAvgMeter->inputChanged()) {
@@ -1502,9 +1695,11 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
     }
     
     SpectrumVisualProcessor *dproc = wxGetApp().getDemodSpectrumProcessor();
-    
-    dproc->setView(demodWaterfallCanvas->getViewState(), demodWaterfallCanvas->getCenterFrequency(),demodWaterfallCanvas->getBandwidth());
 
+    if (dproc) {
+        dproc->setView(demodWaterfallCanvas->getViewState(), demodWaterfallCanvas->getCenterFrequency(),demodWaterfallCanvas->getBandwidth());
+    }
+    
     SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor();
     
     if (waterfallSpeedMeter->inputChanged()) {
@@ -1536,13 +1731,15 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
             ModemDigitalOutputConsole *outp = (ModemDigitalOutputConsole *)demod->getOutput();
             if (!outp->getDialog()) {
                 outp->setTitle(demod->getDemodulatorType() + ": " + frequencyToStr(demod->getFrequency()));
-                outp->setDialog(new DigitalConsole(this, outp));
+                outp->setDialog(new DigitalConsole(this, outp)) ;
             }
             demod->showOutput();
         }
 #endif
-    } else if (!demod) {
-        modemProps->Hide();
+    } else if (!demod && modemPropertiesUpdated.load()) {
+        ModemArgInfoList dummyInfo;
+        modemProps->initProperties(dummyInfo, nullptr);
+        modemProps->updateTheme();
         demodTray->Layout();
     }
     
@@ -1563,7 +1760,9 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
         wxGetApp().getSpectrumProcessor()->setPeakHold(peakHoldMode == 1);
 
         //make the peak hold act on the current dmod also, like a zoomed-in version.
-        wxGetApp().getDemodSpectrumProcessor()->setPeakHold(peakHoldMode == 1);
+        if (wxGetApp().getDemodSpectrumProcessor()) {
+            wxGetApp().getDemodSpectrumProcessor()->setPeakHold(peakHoldMode == 1);
+        }
         peakHoldButton->clearModeChanged();
     }
     
@@ -1577,7 +1776,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
 #endif
     
 #ifdef _WIN32
-    if (scopeCanvas->HasFocus()) {
+    if (scopeCanvas && scopeCanvas->HasFocus()) {
         waterfallCanvas->SetFocus();
     }
 #endif
@@ -1633,7 +1832,9 @@ void AppFrame::OnUnSplit(wxSplitterEvent& event)
 void AppFrame::saveSession(std::string fileName) {
     DataTree s("cubicsdr_session");
     DataNode *header = s.rootNode()->newChild("header");
-    *header->newChild("version") = std::string(CUBICSDR_VERSION);
+    //save as wstring to prevent problems 
+    header->newChild("version")->element()->set(wxString(CUBICSDR_VERSION).ToStdWstring());
+    
     *header->newChild("center_freq") = wxGetApp().getFrequency();
     *header->newChild("sample_rate") = wxGetApp().getSampleRate();
     
@@ -1647,35 +1848,10 @@ void AppFrame::saveSession(std::string fileName) {
     DataNode *demods = s.rootNode()->newChild("demodulators");
 
     std::vector<DemodulatorInstance *> &instances = wxGetApp().getDemodMgr().getDemodulators();
-    std::vector<DemodulatorInstance *>::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
@@ -1711,23 +1887,71 @@ bool AppFrame::loadSession(std::string fileName) {
         DataNode *header = l.rootNode()->getNext("header");
 
         if (header->hasAnother("version")) {
-        std::string version(*header->getNext("version"));
-//            std::cout << "Loading " << version << " session file" << std::endl;
+              //"Force" the retreiving of the value as string, even if its look like a number internally ! (ex: "0.2.0")
+              DataNode *versionNode = header->getNext("version");
+              std::wstring version;
+              try {
+                  versionNode->element()->get(version);
+
+                  std::cout << "Loading session file version: '" << version << "'..." << std::endl;
+              }
+              catch (DataTypeMismatchException* e) {
+                  //this is for managing the old session format NOT encoded as std:wstring,
+                  //force current version
+                  std::cout << "Warning while Loading session file version, probably old format :'" << e->what() << "' please consider re-saving the current session..." << std::endl;
+                  version = wxString(CUBICSDR_VERSION).ToStdWstring();
+              }    
         }
 
         if (header->hasAnother("sample_rate")) {
-            int sample_rate = *header->getNext("sample_rate");
-            
+
+            long sample_rate = *header->getNext("sample_rate");
+
             SDRDeviceInfo *dev = wxGetApp().getSDRThread()->getDevice();
             if (dev) {
-                // Try for a reasonable default sample rate.
-                sample_rate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, sample_rate);
+                //retreive the available sample rates. A valid previously chosen manual
+                //value is constrained within these limits. If it doesn't behave, lets the device choose
+                //for us.
+                long minRate = MANUAL_SAMPLE_RATE_MIN;
+                long maxRate = MANUAL_SAMPLE_RATE_MAX;
+
+                std::vector<long> sampleRates = dev->getSampleRates(SOAPY_SDR_RX, 0);
+
+                if (sampleRates.size()) {
+                    minRate = sampleRates.front();
+                    maxRate = sampleRates.back();
+                }
+
+                //If it is beyond limits, make device choose a reasonable value
+                if (sample_rate < minRate || sample_rate > maxRate) {
+                    sample_rate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, sample_rate);
+                }
+               
+                //scan the available sample rates and see if it matches a predifined one
+                int menuIndex = -1;
+                for (auto discreteRate : sampleRates) {
+                    if (discreteRate == sample_rate) {
+                        menuIndex++;
+                        //activate Bandwidth Menu entry matching this predefined sample_rate.
+                        sampleRateMenuItems[wxID_BANDWIDTH_BASE + menuIndex]->Check(true);
+                        break;
+                    }
+                } //end for
+                //this is a manual entry
+                if (menuIndex == -1) {
+                    manualSampleRate = sample_rate;
+                    sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true);
+                    // Apply the manual value, activate the menu entry
+                    
+                    sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->SetItemLabel(wxString("Manual Entry :  ") + frequencyToStr(sample_rate));
+                    sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true);
+                }
+                //update applied value
                 wxGetApp().setSampleRate(sample_rate);
                 deviceChanged.store(true);
             } else {
                 wxGetApp().setSampleRate(sample_rate);
             }
-            
         }
 
         DemodulatorInstance *loadedActiveDemod = nullptr;
@@ -1737,7 +1961,6 @@ bool AppFrame::loadSession(std::string fileName) {
             
         DataNode *demodulators = l.rootNode()->getNext("demodulators");
 
-        int numDemodulators = 0;
         std::vector<DemodulatorInstance *> demodsLoaded;
         
         while (demodulators->hasAnother("demodulator")) {
@@ -1747,122 +1970,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;
-            
-            std::string type = "FM";
-            
-
-            DataNode *demodTypeNode = demod->hasAnother("type")?demod->getNext("type"):nullptr;
+            newDemod = wxGetApp().getDemodMgr().loadInstance(demod);
             
-            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<int, RtAudio::DeviceInfo>::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()) {
@@ -1908,6 +2024,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;
 }
 
@@ -1928,7 +2046,9 @@ void AppFrame::setMainWaterfallFFTSize(int fftSize) {
 }
 
 void AppFrame::setScopeDeviceName(std::string deviceName) {
-    scopeCanvas->setDeviceName(deviceName);
+    if (scopeCanvas) {
+        scopeCanvas->setDeviceName(deviceName);
+    }
 }
 
 
@@ -1940,12 +2060,20 @@ void AppFrame::refreshGainUI() {
 bool AppFrame::isUserDemodBusy() {
     return (modemProps && modemProps->isMouseInView())
         || (waterfallCanvas->isMouseInView() && waterfallCanvas->isMouseDown())
-        || (demodWaterfallCanvas->isMouseInView() && demodWaterfallCanvas->isMouseDown())
+        || (demodWaterfallCanvas && demodWaterfallCanvas->isMouseInView() && demodWaterfallCanvas->isMouseDown())
         || (wxGetApp().getDemodMgr().getLastActiveDemodulator() &&
             wxGetApp().getDemodMgr().getActiveDemodulator() &&
             wxGetApp().getDemodMgr().getLastActiveDemodulator() != wxGetApp().getDemodMgr().getActiveDemodulator());
 }
 
+BookmarkView *AppFrame::getBookmarkView() {
+    return bookmarkView;
+}
+
+void AppFrame::disableSave(bool state) {
+    saveDisabled = state;
+}
+
 
 #ifdef _WIN32
 bool AppFrame::canFocus() {
@@ -2005,6 +2133,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();
     
@@ -2089,12 +2221,21 @@ int AppFrame::OnGlobalKeyDown(wxKeyEvent &event) {
             break;
     }
     
+    //Re-dispatch the key events if the mouse cursor is within a given
+    //widget region, effectively activating its specific key shortcuts,
+    //which else are overriden by this global key handler.
     if (demodTuner->getMouseTracker()->mouseInView()) {
         demodTuner->OnKeyDown(event);
     } else if (waterfallCanvas->getMouseTracker()->mouseInView()) {
         waterfallCanvas->OnKeyDown(event);
     }
-    
+    else if (spectrumCanvas->getMouseTracker()->mouseInView()) {
+        spectrumCanvas->OnKeyDown(event);
+    }
+    else if (scopeCanvas->getMouseTracker()->mouseInView()) {
+        scopeCanvas->OnKeyDown(event);
+    }
+
     return 1;
 }
 
@@ -2106,6 +2247,10 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) {
         return -1;
     }
 
+    if (bookmarkView && bookmarkView->isMouseInView()) {
+        return -1;
+    }
+
     if (event.ControlDown()) {
         return 1;
     }
@@ -2178,7 +2323,9 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) {
             break;
         case 'P':
             wxGetApp().getSpectrumProcessor()->setPeakHold(!wxGetApp().getSpectrumProcessor()->getPeakHold());
-            wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold());
+            if (wxGetApp().getDemodSpectrumProcessor()) {
+                wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold());
+            }
             peakHoldButton->setSelection(wxGetApp().getSpectrumProcessor()->getPeakHold()?1:0);
             peakHoldButton->clearModeChanged();
             break;
@@ -2196,13 +2343,22 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) {
         default:
             break;
     }
-    
+
+    //Re-dispatch the key events if the mouse cursor is within a given
+    //widget region, effectively activating its specific key shortcuts,
+    //which else are overriden by this global key handler.
     if (demodTuner->getMouseTracker()->mouseInView()) {
         demodTuner->OnKeyUp(event);
-    } else if (waterfallCanvas->getMouseTracker()->mouseInView()) {
+    }
+    else if (waterfallCanvas->getMouseTracker()->mouseInView()) {
         waterfallCanvas->OnKeyUp(event);
     }
-    
+    else if (spectrumCanvas->getMouseTracker()->mouseInView()) {
+        spectrumCanvas->OnKeyUp(event);
+    }
+    else if (scopeCanvas->getMouseTracker()->mouseInView()) {
+        scopeCanvas->OnKeyUp(event);
+    }
     
     // TODO: Catch key-ups outside of original target
 
@@ -2229,3 +2385,42 @@ void AppFrame::setViewState(long long center_freq) {
     spectrumCanvas->disableView();
     waterfallCanvas->disableView();
 }
+
+
+long long AppFrame::getViewCenterFreq() {
+    return waterfallCanvas->getCenterFrequency();
+
+}
+
+
+int AppFrame::getViewBandwidth() {
+    return waterfallCanvas->getBandwidth();
+}
+
+
+/* split a string by 'seperator' into a vector of string */
+std::vector<std::string> str_explode(const std::string &seperator, const std::string &in_str)
+{
+    std::vector<std::string> vect_out;
+    
+    int i = 0, j = 0;
+    int seperator_len = seperator.length();
+    int str_len = in_str.length();
+    
+    while(i < str_len)
+    {
+        j = in_str.find_first_of(seperator,i);
+        
+        if (j == std::string::npos && i < str_len)  j = str_len;
+        
+        if (j == std::string::npos) break;
+        
+        vect_out.push_back(in_str.substr(i,j-i));
+        
+        i = j;
+        
+        i+=seperator_len;
+    }
+    
+    return vect_out;
+}
diff --git a/src/AppFrame.h b/src/AppFrame.h
index 4c2acee..a790810 100644
--- a/src/AppFrame.h
+++ b/src/AppFrame.h
@@ -1,9 +1,14 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <wx/frame.h>
 #include <wx/panel.h>
 #include <wx/splitter.h>
 #include <wx/sizer.h>
+#include <wx/bitmap.h>
+#include <wx/statbmp.h>
 
 #include "PrimaryGLContext.h"
 
@@ -19,6 +24,7 @@
 #include "ModemProperties.h"
 //#include "UITestCanvas.h"
 #include "FrequencyDialog.h"
+#include "BookmarkView.h"
 
 #include <map>
 
@@ -32,9 +38,11 @@
 #define wxID_AGC_CONTROL 2009
 #define wxID_SDR_START_STOP 2010
 #define wxID_LOW_PERF 2011
+#define wxID_SET_DB_OFFSET 2012
 
 #define wxID_MAIN_SPLITTER 2050
 #define wxID_VIS_SPLITTER 2051
+#define wxID_BM_SPLITTER 2052
 
 #define wxID_THEME_DEFAULT 2100
 #define wxID_THEME_SHARP 2101
@@ -44,7 +52,10 @@
 #define wxID_THEME_HD 2105
 #define wxID_THEME_RADAR 2106
 
+#define wxID_DISPLAY_BOOKMARKS 2107
+
 #define wxID_BANDWIDTH_BASE 2150
+#define wxID_BANDWIDTH_MANUAL_DIALOG 2199
 #define wxID_BANDWIDTH_MANUAL 2200
 
 #define wxID_DISPLAY_BASE 2250
@@ -73,6 +84,7 @@ class AppFrame: public wxFrame {
 public:
     AppFrame();
     ~AppFrame();
+
     void OnThread(wxCommandEvent& event);
     void OnEventInput(wxThreadEvent& event);
     void initDeviceParams(SDRDeviceInfo *devInfo);
@@ -100,9 +112,20 @@ 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);
+
+    //call this in case the main UI is not 
+    //the origin of device changes / sample rate by operator,
+    //and must be notified back to update its UI elements
+    //(ex: SDR Devices dialog changing the configuration)
+    void notifyDeviceChanged();
+    
 #ifdef _WIN32
 	bool canFocus();
 #endif
@@ -115,6 +138,10 @@ private:
     void OnDoubleClickSash(wxSplitterEvent& event);
     void OnUnSplit(wxSplitterEvent& event);
    
+    //manage Display menu actions, return true if the event has been
+    //treated.
+    bool actionOnMenuDisplay(wxCommandEvent& event);
+
     ScopeCanvas *scopeCanvas;
     SpectrumCanvas *spectrumCanvas;
     WaterfallCanvas *waterfallCanvas;
@@ -133,8 +160,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;
 
@@ -147,16 +175,17 @@ private:
     std::map<int, wxMenuItem *> directSamplingMenuItems;
     wxMenuBar *menuBar;
     
-    wxMenu *sampleRateMenu;
-    wxMenu *displayMenu;
-    wxMenuItem *agcMenuItem;
-    wxMenuItem *iqSwapMenuItem;
-    wxMenuItem *lowPerfMenuItem;
-    wxMenu *settingsMenu;
+    wxMenu *sampleRateMenu = nullptr;
+    wxMenu *displayMenu = nullptr;
+    wxMenuItem *agcMenuItem = nullptr;
+    wxMenuItem *iqSwapMenuItem = nullptr;
+    wxMenuItem *lowPerfMenuItem = nullptr;
+    wxMenu *settingsMenu = nullptr;
     
     SoapySDR::ArgInfoList settingArgs;
     int settingsIdMax;
     std::vector<long> sampleRates;
+    long manualSampleRate = -1;
     
     std::string currentSessionFile;
     
@@ -171,7 +200,10 @@ private:
 	wxMenuItem *showTipMenuItem;
 
     bool lowPerfMode;
-    
+
+    wxMenuItem *hideBookmarksItem;
+    bool saveDisabled;
+
 #ifdef USE_HAMLIB
     void enableRig();
     void disableRig();
@@ -184,6 +216,7 @@ private:
     wxMenuItem *rigCenterLockMenuItem;
     wxMenuItem *rigFollowModemMenuItem;
     wxMenuItem *sdrIFMenuItem;
+    
     std::map<int, wxMenuItem *> rigSerialMenuItems;
     std::map<int, wxMenuItem *> rigModelMenuItems;
     int rigModel;
diff --git a/src/BookmarkMgr.cpp b/src/BookmarkMgr.cpp
new file mode 100644
index 0000000..ae2034c
--- /dev/null
+++ b/src/BookmarkMgr.cpp
@@ -0,0 +1,524 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#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..c230b4b
--- /dev/null
+++ b/src/BookmarkMgr.h
@@ -0,0 +1,132 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#pragma once
+
+#include <wx/arrstr.h>
+
+#include <vector>
+#include <set>
+
+#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<BookmarkEntry *,BookmarkEntry *,bool>
+{
+    bool operator()(const BookmarkEntry *a, BookmarkEntry *b) const
+    {
+        return a->frequency < b->frequency;
+    }
+};
+
+
+struct BookmarkRangeEntryCompare : public std::binary_function<BookmarkRangeEntry *,BookmarkRangeEntry *,bool>
+{
+    bool operator()(const BookmarkRangeEntry *a, BookmarkRangeEntry *b) const
+    {
+        return a->freq < b->freq;
+    }
+};
+
+typedef std::vector<BookmarkEntry *> BookmarkList;
+typedef std::vector<BookmarkRangeEntry *> BookmarkRangeList;
+typedef std::map<std::string, BookmarkList > BookmarkMap;
+typedef std::map<std::string, bool > BookmarkMapSorted;
+typedef std::vector<std::string> BookmarkNames;
+typedef std::map<std::string, bool> 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 11195f9..a2aa0e8 100644
--- a/src/CubicSDR.cpp
+++ b/src/CubicSDR.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #define OPENGL
 
 #include "CubicSDRDefs.h"
@@ -27,6 +30,9 @@ IMPLEMENT_APP(CubicSDR)
 #include <fstream>
 #include <clocale>
 
+#include "ActionDialog.h"
+
+
 //#ifdef ENABLE_DIGITAL_LAB
 //// console output buffer for windows
 //#ifdef _WINDOWS
@@ -133,8 +139,65 @@ long long strToFrequency(std::string freqStr) {
 }
 
 
-CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE),agcMode(false)
-       {
+
+class ActionDialogBookmarkCatastophe : public ActionDialog {
+public:
+    ActionDialogBookmarkCatastophe() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Last-Loaded Backup Failure :( :( :(")) {
+        m_questionText->SetLabelText(wxT("All attempts to recover bookmarks have failed. \nWould you like to exit without touching any more save files?\nClick OK to exit without saving; or Cancel to continue anyways."));
+    }
+    
+    void doClickOK() {
+        wxGetApp().getAppFrame()->disableSave(true);
+        wxGetApp().getAppFrame()->Close(false);
+    }
+};
+
+
+
+class ActionDialogBookmarkBackupLoadFailed : public ActionDialog {
+public:
+    ActionDialogBookmarkBackupLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Backup Load Failure :( :(")) {
+        m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks backup file. \nWould you like to attempt to load the last succssfully loaded bookmarks file?"));
+    }
+    
+    void doClickOK() {
+        if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) {
+            if (wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.lastloaded",false)) {
+                wxGetApp().getBookmarkMgr().updateBookmarks();
+                wxGetApp().getBookmarkMgr().updateActiveList();
+            } else {
+                ActionDialog::showDialog(new ActionDialogBookmarkCatastophe());
+            }
+        }
+    }
+};
+
+
+class ActionDialogBookmarkLoadFailed : public ActionDialog {
+public:
+    ActionDialogBookmarkLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Load Failure :(")) {
+        m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks file. \nWould you like to attempt to load the backup file?"));
+    }
+    
+    void doClickOK() {
+        bool loadOk = false;
+        if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) {
+            loadOk = wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.backup",false);
+        }
+        if (loadOk) {
+            wxGetApp().getBookmarkMgr().updateBookmarks();
+            wxGetApp().getBookmarkMgr().updateActiveList();
+        } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) {
+            ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed());
+        } else {
+            ActionDialog::showDialog(new ActionDialogBookmarkCatastophe());
+        }
+    }
+};
+
+
+CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), agcMode(false)
+{
         sampleRateInitialized.store(false);
         agcMode.store(true);
         soloMode.store(false);
@@ -207,9 +270,7 @@ bool CubicSDR::OnInit() {
     Modem::addModemFactory(ModemASK::factory, "ASK", 200000);
     Modem::addModemFactory(ModemBPSK::factory, "BPSK", 200000);
     Modem::addModemFactory(ModemDPSK::factory, "DPSK", 200000);
-#if ENABLE_LIQUID_EXPERIMENTAL
     Modem::addModemFactory(ModemFSK::factory, "FSK", 19200);
-#endif
     Modem::addModemFactory(ModemGMSK::factory, "GMSK", 19200);
     Modem::addModemFactory(ModemOOK::factory, "OOK", 200000);
     Modem::addModemFactory(ModemPSK::factory, "PSK", 200000);
@@ -228,26 +289,16 @@ bool CubicSDR::OnInit() {
 
     // Visual Data
     spectrumVisualThread = new SpectrumVisualDataThread();
-    demodVisualThread = new SpectrumVisualDataThread();
     
     pipeIQVisualData = new DemodulatorThreadInputQueue();
     pipeIQVisualData->set_max_num_items(1);
-
-    pipeDemodIQVisualData = new DemodulatorThreadInputQueue();
-    pipeDemodIQVisualData->set_max_num_items(1);
     
     pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
     pipeWaterfallIQVisualData->set_max_num_items(128);
     
-    getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData);
     getSpectrumProcessor()->setInput(pipeIQVisualData);
     getSpectrumProcessor()->setHideDC(true);
     
-    pipeAudioVisualData = new DemodulatorThreadOutputQueue();
-    pipeAudioVisualData->set_max_num_items(1);
-    
-    scopeProcessor.setInput(pipeAudioVisualData);
-    
     // I/Q Data
     pipeSDRIQData = new SDRThreadIQDataQueue();
     pipeSDRIQData->set_max_num_items(100);
@@ -260,11 +311,42 @@ bool CubicSDR::OnInit() {
 
     sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
     sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
-    sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData);
+     
+#if CUBICSDR_ENABLE_VIEW_SCOPE
+    pipeAudioVisualData = new DemodulatorThreadOutputQueue();
+    pipeAudioVisualData->set_max_num_items(1);
     
-    t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread);
+    scopeProcessor.setInput(pipeAudioVisualData);
+#else
+    pipeAudioVisualData = nullptr;
+#endif
+    
+#if CUBICSDR_ENABLE_VIEW_DEMOD
+    demodVisualThread = new SpectrumVisualDataThread();
+    pipeDemodIQVisualData = new DemodulatorThreadInputQueue();
+    pipeDemodIQVisualData->set_max_num_items(1);
+    
+    if (getDemodSpectrumProcessor()) {
+        getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData);
+    }
+    sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData);
+#else
+    demodVisualThread = nullptr;
+    pipeDemodIQVisualData = nullptr;
+    t_DemodVisual = nullptr;
+#endif
+
+    // Now that input/output queue plumbing is completely done, we can
+    //safely starts all the threads:
     t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread);
-    t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread);
+
+    if (demodVisualThread != nullptr) {
+        t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread);
+    }
+
+    //Start SDRPostThread last.
+    t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread);
+    
 
     sdrEnum = new SDREnumerator();
     
@@ -283,6 +365,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;
 }
 
@@ -313,16 +408,20 @@ int CubicSDR::OnExit() {
    
     std::cout << "Terminating Visual Processor threads.." << std::endl;
     spectrumVisualThread->terminate();
-    demodVisualThread->terminate();
-
+    if (demodVisualThread) {
+        demodVisualThread->terminate();
+    }
+    
     //Wait nicely
     sdrPostThread->isTerminated(1000);
     spectrumVisualThread->isTerminated(1000);
-    demodVisualThread->isTerminated(1000);
+    if (demodVisualThread) {
+        demodVisualThread->isTerminated(1000);
+    }
 
     //Then join the thread themselves
     t_PostSDR->join();
-    t_DemodVisual->join();
+    if (t_DemodVisual) t_DemodVisual->join();
     t_SpectrumVisual->join();
 
     //Now only we can delete
@@ -491,7 +590,9 @@ void CubicSDR::setFrequency(long long freq) {
     getSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold());
 
     //make the peak hold act on the current dmod also, like a zoomed-in version.
-    getDemodSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold());
+    if (getDemodSpectrumProcessor()) {
+        getDemodSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold());
+    }
 }
 
 long long CubicSDR::getOffset() {
@@ -534,15 +635,15 @@ void CubicSDR::setSampleRate(long long rate_in) {
     setFrequency(frequency);
 
     if (rate_in <= CHANNELIZER_RATE_MAX / 8) {
-        appframe->setMainWaterfallFFTSize(512);
+        appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE / 4);
         appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false);
         spectrumVisualThread->getProcessor()->setHideDC(false);
     } else if (rate_in <= CHANNELIZER_RATE_MAX) {
-        appframe->setMainWaterfallFFTSize(1024);
+        appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE / 2);
         appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false);
         spectrumVisualThread->getProcessor()->setHideDC(false);
     } else if (rate_in > CHANNELIZER_RATE_MAX) {
-        appframe->setMainWaterfallFFTSize(2048);
+        appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE);
         appframe->getWaterfallDataThread()->getProcessor()->setHideDC(true);
         spectrumVisualThread->getProcessor()->setHideDC(true);
     }
@@ -652,7 +753,11 @@ SpectrumVisualProcessor *CubicSDR::getSpectrumProcessor() {
 }
 
 SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() {
-    return demodVisualThread->getProcessor();
+    if (demodVisualThread) {
+        return demodVisualThread->getProcessor();
+    } else {
+        return nullptr;
+    }
 }
 
 DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() {
@@ -671,6 +776,10 @@ DemodulatorMgr &CubicSDR::getDemodMgr() {
     return demodMgr;
 }
 
+BookmarkMgr &CubicSDR::getBookmarkMgr() {
+    return bookmarkMgr;
+}
+
 SDRPostThread *CubicSDR::getSDRPostThread() {
     return sdrPostThread;
 }
@@ -704,6 +813,7 @@ void CubicSDR::removeDemodulator(DemodulatorInstance *demod) {
     }
     demod->setActive(false);
     sdrPostThread->removeDemodulator(demod);
+    wxGetApp().getAppFrame()->notifyUpdateModemProperties();
 }
 
 std::vector<SDRDeviceInfo*>* CubicSDR::getDevices() {
@@ -749,6 +859,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:
@@ -806,6 +917,10 @@ bool CubicSDR::areDevicesReady() {
     return devicesReady.load();
 }
 
+void CubicSDR::notifyMainUIOfDeviceChange() {
+    appframe->notifyDeviceChanged();
+}
+
 bool CubicSDR::areDevicesEnumerating() {
     return !sdrEnum->isTerminated();
 }
diff --git a/src/CubicSDR.h b/src/CubicSDR.h
index 07b28cb..33bfa31 100644
--- a/src/CubicSDR.h
+++ b/src/CubicSDR.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 //WX_GL_CORE_PROFILE 1
@@ -10,12 +13,8 @@
 #include "PrimaryGLContext.h"
 
 #include "ThreadQueue.h"
-#ifdef USE_RTL_SDR
-    #include "SDRThread.h"
-#else
-    #include "SoapySDRThread.h"
-    #include "SDREnumerator.h"
-#endif
+#include "SoapySDRThread.h"
+#include "SDREnumerator.h"
 #include "SDRPostThread.h"
 #include "AudioThread.h"
 #include "DemodulatorMgr.h"
@@ -23,6 +22,7 @@
 #include "AppFrame.h"
 #include "FrequencyDialog.h"
 #include "DemodLabelDialog.h"
+#include "BookmarkMgr.h"
 
 #include "ScopeVisualProcessor.h"
 #include "SpectrumVisualProcessor.h"
@@ -44,9 +44,7 @@
 #include "ModemASK.h"
 #include "ModemBPSK.h"
 #include "ModemDPSK.h"
-#if ENABLE_LIQUID_EXPERIMENTAL
 #include "ModemFSK.h"
-#endif
 #include "ModemGMSK.h"
 #include "ModemOOK.h"
 #include "ModemPSK.h"
@@ -93,6 +91,9 @@ public:
 
     void setOffset(long long ofs);
     long long getOffset();
+    
+    void setDBOffset(int ofs);
+    int getDBOffset();
 
     void setSampleRate(long long rate_in);
     long long getSampleRate();
@@ -111,6 +112,7 @@ public:
     DemodulatorThreadInputQueue* getWaterfallVisualQueue();
     DemodulatorThreadInputQueue* getActiveDemodVisualQueue();
     DemodulatorMgr &getDemodMgr();
+    BookmarkMgr &getBookmarkMgr();
 
     SDRPostThread *getSDRPostThread();
     SDRThread *getSDRThread();
@@ -136,6 +138,8 @@ public:
     bool areDevicesEnumerating();
     bool areModulesMissing();
     std::string getNotification();
+
+    void notifyMainUIOfDeviceChange();
     
     void addRemote(std::string remoteAddr);
     void removeRemote(std::string remoteAddr);
@@ -179,6 +183,7 @@ private:
     std::vector<SDRDeviceInfo *> *devs = nullptr;
 
     DemodulatorMgr demodMgr;
+    BookmarkMgr bookmarkMgr;
 
     std::atomic_llong frequency;
     std::atomic_llong offset;
diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h
index 210126c..e5cc4ca 100644
--- a/src/CubicSDRDefs.h
+++ b/src/CubicSDRDefs.h
@@ -1,6 +1,9 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
-#define CUBICSDR_TITLE "CubicSDR v" CUBICSDR_VERSION " by Charles J. Cliffe (@ccliffe)  ::  www.cubicsdr.com"
+#define CUBICSDR_TITLE "" CUBICSDR_BUILD_TITLE
 
 #ifndef __BYTE_ORDER
     #ifdef _WIN32
@@ -28,13 +31,31 @@ const char filePathSeparator =
 #define BUF_SIZE (16384*6)
 
 #define DEFAULT_SAMPLE_RATE 2500000
+
+//
 #define DEFAULT_FFT_SIZE 2048
+#define DEFAULT_DMOD_FFT_SIZE (DEFAULT_FFT_SIZE / 2)
+#define DEFAULT_SCOPE_FFT_SIZE (DEFAULT_FFT_SIZE / 2)
+
+//Both must be a power of 2 to prevent terrible OpenGL performance.
+//TODO: Make the waterfall resolutions an option.
+#define DEFAULT_MAIN_WATERFALL_LINES_NB 512 // 1024
+#define DEFAULT_DEMOD_WATERFALL_LINES_NB 256
 
 #define DEFAULT_DEMOD_TYPE "FM"
 #define DEFAULT_DEMOD_BW 200000
 
 #define DEFAULT_WATERFALL_LPS 30
 
+//Dmod waterfall lines per second is adjusted 
+//so that the whole demod waterfall show DEMOD_WATERFALL_DURATION_IN_SECONDS
+//seconds.
+#define DEMOD_WATERFALL_DURATION_IN_SECONDS 4.0
+
 #define CHANNELIZER_RATE_MAX 500000
 
+#define MANUAL_SAMPLE_RATE_MIN 2000000 // 2MHz
+#define MANUAL_SAMPLE_RATE_MAX 200000000 // 200MHz (We are 2017+ after all)
 
+//Represents the amount of time to process in the FFT distributor. 
+#define FFT_DISTRIBUTOR_BUFFER_IN_SECONDS 0.250
diff --git a/src/DemodLabelDialog.cpp b/src/DemodLabelDialog.cpp
index d5a7e50..251c356 100644
--- a/src/DemodLabelDialog.cpp
+++ b/src/DemodLabelDialog.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "DemodLabelDialog.h"
 
 #include "wx/clipbrd.h"
@@ -57,7 +60,7 @@ void DemodLabelDialog::OnChar(wxKeyEvent& event) {
         else {
             activeDemod->setDemodulatorUserLabel(L"");
         }
-
+        wxGetApp().getBookmarkMgr().updateActiveList();
         Close();
         break;
     case WXK_ESCAPE:
diff --git a/src/DemodLabelDialog.h b/src/DemodLabelDialog.h
index a10d34b..cee9cff 100644
--- a/src/DemodLabelDialog.h
+++ b/src/DemodLabelDialog.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/dialog.h"
diff --git a/src/FrequencyDialog.cpp b/src/FrequencyDialog.cpp
index 3882455..f16ad3b 100644
--- a/src/FrequencyDialog.cpp
+++ b/src/FrequencyDialog.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "FrequencyDialog.h"
 
 #include "wx/clipbrd.h"
diff --git a/src/FrequencyDialog.h b/src/FrequencyDialog.h
index 6353c39..259de20 100644
--- a/src/FrequencyDialog.h
+++ b/src/FrequencyDialog.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/dialog.h"
diff --git a/src/IOThread.cpp b/src/IOThread.cpp
index a27c71c..029fe73 100644
--- a/src/IOThread.cpp
+++ b/src/IOThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "IOThread.h"
 #include <typeinfo>
 
@@ -69,7 +72,7 @@ void IOThread::terminate() {
 };
 
 void IOThread::onBindOutput(std::string /* name */, ThreadQueueBase* /* threadQueue */) {
-    
+   
 };
 
 void IOThread::onBindInput(std::string /* name */, ThreadQueueBase* /* threadQueue */) {
@@ -77,20 +80,24 @@ void IOThread::onBindInput(std::string /* name */, ThreadQueueBase* /* threadQue
 };
 
 void IOThread::setInputQueue(std::string qname, ThreadQueueBase *threadQueue) {
+    std::lock_guard < std::mutex > lock(m_queue_bindings_mutex);
     input_queues[qname] = threadQueue;
     this->onBindInput(qname, threadQueue);
 };
 
 ThreadQueueBase *IOThread::getInputQueue(std::string qname) {
+    std::lock_guard < std::mutex > lock(m_queue_bindings_mutex);
     return input_queues[qname];
 };
 
 void IOThread::setOutputQueue(std::string qname, ThreadQueueBase *threadQueue) {
+    std::lock_guard < std::mutex > lock(m_queue_bindings_mutex);
     output_queues[qname] = threadQueue;
     this->onBindOutput(qname, threadQueue);
 };
 
 ThreadQueueBase *IOThread::getOutputQueue(std::string qname) {
+    std::lock_guard < std::mutex > lock(m_queue_bindings_mutex);
     return output_queues[qname];
 };
 
diff --git a/src/IOThread.h b/src/IOThread.h
index 2fe8504..9750366 100644
--- a/src/IOThread.h
+++ b/src/IOThread.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <mutex>
@@ -218,7 +221,10 @@ public:
 protected:
     std::map<std::string, ThreadQueueBase *, map_string_less> input_queues;
     std::map<std::string, ThreadQueueBase *, map_string_less> output_queues;
-    
+
+    //this protects against concurrent changes in input/output bindings: get/set/Input/OutPutQueue
+    mutable std::mutex m_queue_bindings_mutex;
+
     //true when a termination is ordered
     std::atomic_bool stopping;
     Timer gTimer;
@@ -227,4 +233,5 @@ private:
     //true when the thread has really ended, i.e run() from threadMain() has returned.
     std::atomic_bool terminated;
 
+   
 };
diff --git a/src/ModemProperties.cpp b/src/ModemProperties.cpp
index be3e6ba..9984bdc 100644
--- a/src/ModemProperties.cpp
+++ b/src/ModemProperties.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemProperties.h"
 #include "CubicSDR.h"
 
@@ -30,26 +33,10 @@ void ModemProperties::OnShow(wxShowEvent & /* event */) {
 }
 
 void ModemProperties::updateTheme() {
-    wxColour bgColor(
-                 (unsigned char) (ThemeMgr::mgr.currentTheme->generalBackground.r * 255.0),
-                 (unsigned char) (ThemeMgr::mgr.currentTheme->generalBackground.g * 255.0),
-                 (unsigned char) (ThemeMgr::mgr.currentTheme->generalBackground.b * 255.0));
-
-    wxColour textColor(
-                       (unsigned char) (ThemeMgr::mgr.currentTheme->text.r * 255.0),
-                       (unsigned char) (ThemeMgr::mgr.currentTheme->text.g * 255.0),
-                       (unsigned char) (ThemeMgr::mgr.currentTheme->text.b * 255.0));
-    
-    wxColour btn(
-                       (unsigned char) (ThemeMgr::mgr.currentTheme->button.r * 255.0),
-                       (unsigned char) (ThemeMgr::mgr.currentTheme->button.g * 255.0),
-                       (unsigned char) (ThemeMgr::mgr.currentTheme->button.b * 255.0));
-
-    wxColour btnHl(
-                 (unsigned char) (ThemeMgr::mgr.currentTheme->buttonHighlight.r * 255.0),
-                 (unsigned char) (ThemeMgr::mgr.currentTheme->buttonHighlight.g * 255.0),
-                 (unsigned char) (ThemeMgr::mgr.currentTheme->buttonHighlight.b * 255.0));
-
+    wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground);
+    wxColour textColor(ThemeMgr::mgr.currentTheme->text);
+    wxColour btn(ThemeMgr::mgr.currentTheme->button);
+    wxColour btnHl(ThemeMgr::mgr.currentTheme->buttonHighlight);
 
     m_propertyGrid->SetEmptySpaceColour(bgColor);
     m_propertyGrid->SetCellBackgroundColour(bgColor);
@@ -122,10 +109,8 @@ void ModemProperties::initProperties(ModemArgInfoList newArgs, DemodulatorInstan
     m_propertyGrid->Clear();
 
     if (!demodInstance) {
-        Hide();
+        m_propertyGrid->Append(new wxPropertyCategory("Modem Settings"));
         return;
-    } else {
-        Show();
     }
     
     m_propertyGrid->Append(new wxPropertyCategory(demodInstance->getDemodulatorType() + " Settings"));
diff --git a/src/ModemProperties.h b/src/ModemProperties.h
index bfebcde..268e366 100644
--- a/src/ModemProperties.h
+++ b/src/ModemProperties.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <wx/panel.h>
diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp
index f1c2038..bb8b6d0 100644
--- a/src/audio/AudioThread.cpp
+++ b/src/audio/AudioThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "AudioThread.h"
 #include "CubicSDRDefs.h"
 #include <vector>
@@ -257,7 +260,7 @@ void AudioThread::enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs) {
             std::cout << "\t\t32-bit float normalized between plus/minus 1.0." << std::endl;
         }
         if (nFormats & RTAUDIO_FLOAT64) {
-            std::cout << "\t\t32-bit float normalized between plus/minus 1.0." << std::endl;
+            std::cout << "\t\t64-bit float normalized between plus/minus 1.0." << std::endl;
         }
 
         std::vector<unsigned int>::iterator srate;
diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h
index 65388c7..8eff8be 100644
--- a/src/audio/AudioThread.h
+++ b/src/audio/AudioThread.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <queue>
diff --git a/src/demod/DemodDefs.h b/src/demod/DemodDefs.h
index dab36b3..043ad41 100644
--- a/src/demod/DemodDefs.h
+++ b/src/demod/DemodDefs.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "ThreadQueue.h"
@@ -56,6 +59,7 @@ class ModemKit;
 class DemodulatorThreadPostIQData: public ReferenceCounter {
 public:
     std::vector<liquid_float_complex> data;
+
     long long sampleRate;
     std::string modemName;
     std::string modemType;
diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp
index bf3d788..61f4432 100644
--- a/src/demod/DemodulatorInstance.cpp
+++ b/src/demod/DemodulatorInstance.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "DemodulatorInstance.h"
 #include "CubicSDR.h"
 
@@ -80,6 +83,8 @@ DemodulatorInstance::~DemodulatorInstance() {
     delete pipeIQDemodData;
     delete threadQueueControl;
     delete pipeAudioData;
+    
+    wxGetApp().getBookmarkMgr().updateActiveList();
 }
 
 void DemodulatorInstance::setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue) {
@@ -118,6 +123,7 @@ void DemodulatorInstance::run() {
 
     active = true;
 
+    wxGetApp().getBookmarkMgr().updateActiveList();
 }
 
 void DemodulatorInstance::updateLabel(long long freq) {
@@ -125,6 +131,7 @@ void DemodulatorInstance::updateLabel(long long freq) {
     newLabel.precision(3);
     newLabel << std::fixed << ((long double) freq / 1000000.0);
     setLabel(newLabel.str());
+    wxGetApp().getBookmarkMgr().updateActiveList();
 }
 
 void DemodulatorInstance::terminate() {
@@ -227,6 +234,8 @@ void DemodulatorInstance::setActive(bool state) {
         tracking = false;
     }
     active = state;
+    
+    wxGetApp().getBookmarkMgr().updateActiveList();
 }
 
 void DemodulatorInstance::squelchAuto() {
@@ -331,7 +340,9 @@ void DemodulatorInstance::setDemodulatorType(std::string demod_type_in) {
             outp->setTitle(getDemodulatorType() + ": " + frequencyToStr(getFrequency()));
         }
 #endif
-}
+    }
+    
+    wxGetApp().getBookmarkMgr().updateActiveList();
 }
 
 std::string DemodulatorInstance::getDemodulatorType() {
@@ -391,6 +402,10 @@ void DemodulatorInstance::setFrequency(long long freq) {
         wxGetApp().getRigThread()->setFrequency(freq,true);
     }
 #endif
+    
+    if (this->isActive()) {
+        wxGetApp().getBookmarkMgr().updateActiveList();
+    }
 }
 
 long long DemodulatorInstance::getFrequency() {
diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h
index 554ed48..749c6ec 100644
--- a/src/demod/DemodulatorInstance.h
+++ b/src/demod/DemodulatorInstance.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <vector>
diff --git a/src/demod/DemodulatorMgr.cpp b/src/demod/DemodulatorMgr.cpp
index 61be65c..2a75d34 100644
--- a/src/demod/DemodulatorMgr.cpp
+++ b/src/demod/DemodulatorMgr.cpp
@@ -1,15 +1,22 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include <DemodulatorMgr.h>
 #include <sstream>
 #include <algorithm>
-#include "CubicSDR.h"
 #include <string>
 #include <sstream>
 #include <algorithm>
 
+#include "DemodulatorMgr.h"
+#include "CubicSDR.h"
+
 #if USE_HAMLIB
 #include "RigThread.h"
 #endif
 
+#include "DataTree.h"
+
 bool demodFreqCompare (DemodulatorInstance *i, DemodulatorInstance *j) { return (i->getFrequency()<j->getFrequency()); }
 bool inactiveCompare (DemodulatorInstance *i, DemodulatorInstance *j) { return (i->isActive()<j->isActive()); }
 
@@ -130,6 +137,8 @@ DemodulatorInstance *DemodulatorMgr::getFirstDemodulator() {
 
 void DemodulatorMgr::deleteThread(DemodulatorInstance *demod) {
     std::lock_guard < std::recursive_mutex > lock(demods_busy);
+
+    wxGetApp().getBookmarkMgr().addRecent(demod);
     
     std::vector<DemodulatorInstance *>::iterator i;
 
@@ -215,6 +224,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 +378,138 @@ ModemSettings DemodulatorMgr::getLastModemSettings(std::string modemType) {
 void DemodulatorMgr::setLastModemSettings(std::string modemType, ModemSettings settings) {
     lastModemSettings[modemType] = settings;
 }
+
+void DemodulatorMgr::setOutputDevices(std::map<int,RtAudio::DeviceInfo> 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<int, RtAudio::DeviceInfo>::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..41b0945 100644
--- a/src/demod/DemodulatorMgr.h
+++ b/src/demod/DemodulatorMgr.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <vector>
@@ -6,6 +9,8 @@
 
 #include "DemodulatorInstance.h"
 
+class DataNode;
+
 class DemodulatorMgr {
 public:
     DemodulatorMgr();
@@ -54,6 +59,10 @@ public:
 
     void updateLastState();
     
+    void setOutputDevices(std::map<int,RtAudio::DeviceInfo> devs);
+    void saveInstance(DataNode *node, DemodulatorInstance *inst);
+    DemodulatorInstance *loadInstance(DataNode *node);
+    
 private:
     
     void garbageCollect();
@@ -79,4 +88,5 @@ private:
     std::recursive_mutex demods_busy;
     
     std::map<std::string, ModemSettings> lastModemSettings;
+    std::map<int,RtAudio::DeviceInfo> outputDevices;
 };
diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp
index e4286fe..e4319a4 100644
--- a/src/demod/DemodulatorPreThread.cpp
+++ b/src/demod/DemodulatorPreThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "CubicSDRDefs.h"
 #include <vector>
 
diff --git a/src/demod/DemodulatorPreThread.h b/src/demod/DemodulatorPreThread.h
index e20bf68..31e496a 100644
--- a/src/demod/DemodulatorPreThread.h
+++ b/src/demod/DemodulatorPreThread.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <queue>
diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp
index 259c128..16fa619 100644
--- a/src/demod/DemodulatorThread.cpp
+++ b/src/demod/DemodulatorThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "CubicSDRDefs.h"
 #include "DemodulatorThread.h"
 #include "DemodulatorInstance.h"
diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h
index 10124a4..5709bb9 100644
--- a/src/demod/DemodulatorThread.h
+++ b/src/demod/DemodulatorThread.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <queue>
diff --git a/src/demod/DemodulatorWorkerThread.cpp b/src/demod/DemodulatorWorkerThread.cpp
index 35efd62..2c5c896 100644
--- a/src/demod/DemodulatorWorkerThread.cpp
+++ b/src/demod/DemodulatorWorkerThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "DemodulatorWorkerThread.h"
 #include "CubicSDRDefs.h"
 #include "CubicSDR.h"
diff --git a/src/demod/DemodulatorWorkerThread.h b/src/demod/DemodulatorWorkerThread.h
index 140046a..e0b4065 100644
--- a/src/demod/DemodulatorWorkerThread.h
+++ b/src/demod/DemodulatorWorkerThread.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <queue>
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 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<wxFormBuilder_Project>
+    <FileVersion major="1" minor="13" />
+    <object class="Project" expanded="1">
+        <property name="class_decoration"></property>
+        <property name="code_generation">C++</property>
+        <property name="disconnect_events">1</property>
+        <property name="disconnect_mode">source_name</property>
+        <property name="disconnect_php_events">0</property>
+        <property name="disconnect_python_events">0</property>
+        <property name="embedded_files_path">res</property>
+        <property name="encoding">UTF-8</property>
+        <property name="event_generation">connect</property>
+        <property name="file">BookmarkPanel</property>
+        <property name="first_id">1000</property>
+        <property name="help_provider">none</property>
+        <property name="internationalize">0</property>
+        <property name="name">BookmarkPanel</property>
+        <property name="namespace"></property>
+        <property name="path">.</property>
+        <property name="precompiled_header"></property>
+        <property name="relative_path">1</property>
+        <property name="skip_lua_events">1</property>
+        <property name="skip_php_events">1</property>
+        <property name="skip_python_events">1</property>
+        <property name="ui_table">UI</property>
+        <property name="use_enum">0</property>
+        <property name="use_microsoft_bom">0</property>
+        <object class="Panel" expanded="1">
+            <property name="aui_managed">0</property>
+            <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
+            <property name="bg"></property>
+            <property name="context_help"></property>
+            <property name="context_menu">1</property>
+            <property name="enabled">1</property>
+            <property name="event_handler">impl_virtual</property>
+            <property name="fg"></property>
+            <property name="font"></property>
+            <property name="hidden">0</property>
+            <property name="id">wxID_ANY</property>
+            <property name="maximum_size"></property>
+            <property name="minimum_size"></property>
+            <property name="name">BookmarkPanel</property>
+            <property name="pos"></property>
+            <property name="size">169,471</property>
+            <property name="subclass"></property>
+            <property name="tooltip"></property>
+            <property name="window_extra_style"></property>
+            <property name="window_name"></property>
+            <property name="window_style">wxTAB_TRAVERSAL</property>
+            <event name="OnAuiFindManager"></event>
+            <event name="OnAuiPaneButton"></event>
+            <event name="OnAuiPaneClose"></event>
+            <event name="OnAuiPaneMaximize"></event>
+            <event name="OnAuiPaneRestore"></event>
+            <event name="OnAuiRender"></event>
+            <event name="OnChar"></event>
+            <event name="OnEnterWindow">onEnterWindow</event>
+            <event name="OnEraseBackground"></event>
+            <event name="OnInitDialog"></event>
+            <event name="OnKeyDown"></event>
+            <event name="OnKeyUp"></event>
+            <event name="OnKillFocus"></event>
+            <event name="OnLeaveWindow">onLeaveWindow</event>
+            <event name="OnLeftDClick"></event>
+            <event name="OnLeftDown"></event>
+            <event name="OnLeftUp"></event>
+            <event name="OnMiddleDClick"></event>
+            <event name="OnMiddleDown"></event>
+            <event name="OnMiddleUp"></event>
+            <event name="OnMotion">onMotion</event>
+            <event name="OnMouseEvents"></event>
+            <event name="OnMouseWheel"></event>
+            <event name="OnPaint"></event>
+            <event name="OnRightDClick"></event>
+            <event name="OnRightDown"></event>
+            <event name="OnRightUp"></event>
+            <event name="OnSetFocus"></event>
+            <event name="OnSize"></event>
+            <event name="OnUpdateUI"></event>
+            <object class="wxBoxSizer" expanded="1">
+                <property name="minimum_size"></property>
+                <property name="name">bSizer1</property>
+                <property name="orient">wxVERTICAL</property>
+                <property name="permission">none</property>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">5</property>
+                    <property name="flag">wxALL|wxEXPAND</property>
+                    <property name="proportion">0</property>
+                    <object class="wxTextCtrl" expanded="1">
+                        <property name="BottomDockable">1</property>
+                        <property name="LeftDockable">1</property>
+                        <property name="RightDockable">1</property>
+                        <property name="TopDockable">1</property>
+                        <property name="aui_layer"></property>
+                        <property name="aui_name"></property>
+                        <property name="aui_position"></property>
+                        <property name="aui_row"></property>
+                        <property name="best_size"></property>
+                        <property name="bg"></property>
+                        <property name="caption"></property>
+                        <property name="caption_visible">1</property>
+                        <property name="center_pane">0</property>
+                        <property name="close_button">1</property>
+                        <property name="context_help"></property>
+                        <property name="context_menu">1</property>
+                        <property name="default_pane">0</property>
+                        <property name="dock">Dock</property>
+                        <property name="dock_fixed">0</property>
+                        <property name="docking">Left</property>
+                        <property name="enabled">1</property>
+                        <property name="fg"></property>
+                        <property name="floatable">1</property>
+                        <property name="font"></property>
+                        <property name="gripper">0</property>
+                        <property name="hidden">0</property>
+                        <property name="id">wxID_ANY</property>
+                        <property name="max_size"></property>
+                        <property name="maximize_button">0</property>
+                        <property name="maximum_size"></property>
+                        <property name="maxlength"></property>
+                        <property name="min_size"></property>
+                        <property name="minimize_button">0</property>
+                        <property name="minimum_size"></property>
+                        <property name="moveable">1</property>
+                        <property name="name">m_searchText</property>
+                        <property name="pane_border">1</property>
+                        <property name="pane_position"></property>
+                        <property name="pane_size"></property>
+                        <property name="permission">protected</property>
+                        <property name="pin_button">1</property>
+                        <property name="pos"></property>
+                        <property name="resize">Resizable</property>
+                        <property name="show">1</property>
+                        <property name="size"></property>
+                        <property name="style">wxTE_PROCESS_ENTER</property>
+                        <property name="subclass"></property>
+                        <property name="toolbar_pane">0</property>
+                        <property name="tooltip"></property>
+                        <property name="validator_data_type"></property>
+                        <property name="validator_style">wxFILTER_NONE</property>
+                        <property name="validator_type">wxDefaultValidator</property>
+                        <property name="validator_variable"></property>
+                        <property name="value">Search..</property>
+                        <property name="window_extra_style"></property>
+                        <property name="window_name"></property>
+                        <property name="window_style"></property>
+                        <event name="OnChar"></event>
+                        <event name="OnEnterWindow"></event>
+                        <event name="OnEraseBackground"></event>
+                        <event name="OnKeyDown"></event>
+                        <event name="OnKeyUp"></event>
+                        <event name="OnKillFocus"></event>
+                        <event name="OnLeaveWindow"></event>
+                        <event name="OnLeftDClick"></event>
+                        <event name="OnLeftDown">onSearchTextFocus</event>
+                        <event name="OnLeftUp"></event>
+                        <event name="OnMiddleDClick"></event>
+                        <event name="OnMiddleDown"></event>
+                        <event name="OnMiddleUp"></event>
+                        <event name="OnMotion"></event>
+                        <event name="OnMouseEvents"></event>
+                        <event name="OnMouseWheel"></event>
+                        <event name="OnPaint"></event>
+                        <event name="OnRightDClick"></event>
+                        <event name="OnRightDown"></event>
+                        <event name="OnRightUp"></event>
+                        <event name="OnSetFocus"></event>
+                        <event name="OnSize"></event>
+                        <event name="OnText">onSearchText</event>
+                        <event name="OnTextEnter"></event>
+                        <event name="OnTextMaxLen"></event>
+                        <event name="OnTextURL"></event>
+                        <event name="OnUpdateUI"></event>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">5</property>
+                    <property name="flag">wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT</property>
+                    <property name="proportion">0</property>
+                    <object class="wxButton" expanded="1">
+                        <property name="BottomDockable">1</property>
+                        <property name="LeftDockable">1</property>
+                        <property name="RightDockable">1</property>
+                        <property name="TopDockable">1</property>
+                        <property name="aui_layer"></property>
+                        <property name="aui_name"></property>
+                        <property name="aui_position"></property>
+                        <property name="aui_row"></property>
+                        <property name="best_size"></property>
+                        <property name="bg"></property>
+                        <property name="caption"></property>
+                        <property name="caption_visible">1</property>
+                        <property name="center_pane">0</property>
+                        <property name="close_button">1</property>
+                        <property name="context_help"></property>
+                        <property name="context_menu">1</property>
+                        <property name="default">0</property>
+                        <property name="default_pane">0</property>
+                        <property name="dock">Dock</property>
+                        <property name="dock_fixed">0</property>
+                        <property name="docking">Left</property>
+                        <property name="enabled">1</property>
+                        <property name="fg"></property>
+                        <property name="floatable">1</property>
+                        <property name="font"></property>
+                        <property name="gripper">0</property>
+                        <property name="hidden">0</property>
+                        <property name="id">wxID_ANY</property>
+                        <property name="label">Clear Search</property>
+                        <property name="max_size"></property>
+                        <property name="maximize_button">0</property>
+                        <property name="maximum_size"></property>
+                        <property name="min_size"></property>
+                        <property name="minimize_button">0</property>
+                        <property name="minimum_size"></property>
+                        <property name="moveable">1</property>
+                        <property name="name">m_clearSearchButton</property>
+                        <property name="pane_border">1</property>
+                        <property name="pane_position"></property>
+                        <property name="pane_size"></property>
+                        <property name="permission">protected</property>
+                        <property name="pin_button">1</property>
+                        <property name="pos"></property>
+                        <property name="resize">Resizable</property>
+                        <property name="show">1</property>
+                        <property name="size"></property>
+                        <property name="style"></property>
+                        <property name="subclass"></property>
+                        <property name="toolbar_pane">0</property>
+                        <property name="tooltip"></property>
+                        <property name="validator_data_type"></property>
+                        <property name="validator_style">wxFILTER_NONE</property>
+                        <property name="validator_type">wxDefaultValidator</property>
+                        <property name="validator_variable"></property>
+                        <property name="window_extra_style"></property>
+                        <property name="window_name"></property>
+                        <property name="window_style"></property>
+                        <event name="OnButtonClick">onClearSearch</event>
+                        <event name="OnChar"></event>
+                        <event name="OnEnterWindow"></event>
+                        <event name="OnEraseBackground"></event>
+                        <event name="OnKeyDown"></event>
+                        <event name="OnKeyUp"></event>
+                        <event name="OnKillFocus"></event>
+                        <event name="OnLeaveWindow"></event>
+                        <event name="OnLeftDClick"></event>
+                        <event name="OnLeftDown"></event>
+                        <event name="OnLeftUp"></event>
+                        <event name="OnMiddleDClick"></event>
+                        <event name="OnMiddleDown"></event>
+                        <event name="OnMiddleUp"></event>
+                        <event name="OnMotion"></event>
+                        <event name="OnMouseEvents"></event>
+                        <event name="OnMouseWheel"></event>
+                        <event name="OnPaint"></event>
+                        <event name="OnRightDClick"></event>
+                        <event name="OnRightDown"></event>
+                        <event name="OnRightUp"></event>
+                        <event name="OnSetFocus"></event>
+                        <event name="OnSize"></event>
+                        <event name="OnUpdateUI"></event>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">5</property>
+                    <property name="flag">wxEXPAND</property>
+                    <property name="proportion">1</property>
+                    <object class="wxTreeCtrl" expanded="1">
+                        <property name="BottomDockable">1</property>
+                        <property name="LeftDockable">1</property>
+                        <property name="RightDockable">1</property>
+                        <property name="TopDockable">1</property>
+                        <property name="aui_layer"></property>
+                        <property name="aui_name"></property>
+                        <property name="aui_position"></property>
+                        <property name="aui_row"></property>
+                        <property name="best_size"></property>
+                        <property name="bg"></property>
+                        <property name="caption"></property>
+                        <property name="caption_visible">1</property>
+                        <property name="center_pane">0</property>
+                        <property name="close_button">1</property>
+                        <property name="context_help"></property>
+                        <property name="context_menu">0</property>
+                        <property name="default_pane">0</property>
+                        <property name="dock">Dock</property>
+                        <property name="dock_fixed">0</property>
+                        <property name="docking">Left</property>
+                        <property name="enabled">1</property>
+                        <property name="fg"></property>
+                        <property name="floatable">1</property>
+                        <property name="font"></property>
+                        <property name="gripper">0</property>
+                        <property name="hidden">0</property>
+                        <property name="id">wxID_ANY</property>
+                        <property name="max_size"></property>
+                        <property name="maximize_button">0</property>
+                        <property name="maximum_size"></property>
+                        <property name="min_size"></property>
+                        <property name="minimize_button">0</property>
+                        <property name="minimum_size"></property>
+                        <property name="moveable">1</property>
+                        <property name="name">m_treeView</property>
+                        <property name="pane_border">1</property>
+                        <property name="pane_position"></property>
+                        <property name="pane_size"></property>
+                        <property name="permission">protected</property>
+                        <property name="pin_button">1</property>
+                        <property name="pos"></property>
+                        <property name="resize">Resizable</property>
+                        <property name="show">1</property>
+                        <property name="size"></property>
+                        <property name="style">wxTR_DEFAULT_STYLE|wxTR_EDIT_LABELS|wxTR_HAS_VARIABLE_ROW_HEIGHT|wxTR_HIDE_ROOT|wxTR_SINGLE</property>
+                        <property name="subclass"></property>
+                        <property name="toolbar_pane">0</property>
+                        <property name="tooltip"></property>
+                        <property name="window_extra_style"></property>
+                        <property name="window_name"></property>
+                        <property name="window_style"></property>
+                        <event name="OnChar"></event>
+                        <event name="OnEnterWindow"></event>
+                        <event name="OnEraseBackground"></event>
+                        <event name="OnKeyDown"></event>
+                        <event name="OnKeyUp"></event>
+                        <event name="OnKillFocus"></event>
+                        <event name="OnLeaveWindow"></event>
+                        <event name="OnLeftDClick"></event>
+                        <event name="OnLeftDown"></event>
+                        <event name="OnLeftUp"></event>
+                        <event name="OnMiddleDClick"></event>
+                        <event name="OnMiddleDown"></event>
+                        <event name="OnMiddleUp"></event>
+                        <event name="OnMotion">onMotion</event>
+                        <event name="OnMouseEvents"></event>
+                        <event name="OnMouseWheel"></event>
+                        <event name="OnPaint"></event>
+                        <event name="OnRightDClick"></event>
+                        <event name="OnRightDown"></event>
+                        <event name="OnRightUp"></event>
+                        <event name="OnSetFocus"></event>
+                        <event name="OnSize"></event>
+                        <event name="OnTreeBeginDrag">onTreeBeginDrag</event>
+                        <event name="OnTreeBeginLabelEdit">onTreeBeginLabelEdit</event>
+                        <event name="OnTreeBeginRDrag"></event>
+                        <event name="OnTreeDeleteItem"></event>
+                        <event name="OnTreeEndDrag">onTreeEndDrag</event>
+                        <event name="OnTreeEndLabelEdit">onTreeEndLabelEdit</event>
+                        <event name="OnTreeGetInfo"></event>
+                        <event name="OnTreeItemActivated">onTreeActivate</event>
+                        <event name="OnTreeItemCollapsed">onTreeCollapse</event>
+                        <event name="OnTreeItemCollapsing"></event>
+                        <event name="OnTreeItemExpanded">onTreeExpanded</event>
+                        <event name="OnTreeItemExpanding"></event>
+                        <event name="OnTreeItemGetTooltip">onTreeItemGetTooltip</event>
+                        <event name="OnTreeItemMenu">onTreeItemMenu</event>
+                        <event name="OnTreeItemMiddleClick"></event>
+                        <event name="OnTreeItemRightClick"></event>
+                        <event name="OnTreeKeyDown"></event>
+                        <event name="OnTreeSelChanged">onTreeSelect</event>
+                        <event name="OnTreeSelChanging">onTreeSelectChanging</event>
+                        <event name="OnTreeSetInfo"></event>
+                        <event name="OnTreeStateImageClick"></event>
+                        <event name="OnUpdateUI"></event>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">5</property>
+                    <property name="flag">wxALL|wxBOTTOM|wxEXPAND|wxTOP</property>
+                    <property name="proportion">0</property>
+                    <object class="wxPanel" expanded="1">
+                        <property name="BottomDockable">1</property>
+                        <property name="LeftDockable">1</property>
+                        <property name="RightDockable">1</property>
+                        <property name="TopDockable">1</property>
+                        <property name="aui_layer"></property>
+                        <property name="aui_name"></property>
+                        <property name="aui_position"></property>
+                        <property name="aui_row"></property>
+                        <property name="best_size"></property>
+                        <property name="bg"></property>
+                        <property name="caption"></property>
+                        <property name="caption_visible">1</property>
+                        <property name="center_pane">0</property>
+                        <property name="close_button">1</property>
+                        <property name="context_help"></property>
+                        <property name="context_menu">1</property>
+                        <property name="default_pane">0</property>
+                        <property name="dock">Dock</property>
+                        <property name="dock_fixed">0</property>
+                        <property name="docking">Left</property>
+                        <property name="enabled">1</property>
+                        <property name="fg"></property>
+                        <property name="floatable">1</property>
+                        <property name="font"></property>
+                        <property name="gripper">0</property>
+                        <property name="hidden">0</property>
+                        <property name="id">wxID_ANY</property>
+                        <property name="max_size"></property>
+                        <property name="maximize_button">0</property>
+                        <property name="maximum_size"></property>
+                        <property name="min_size"></property>
+                        <property name="minimize_button">0</property>
+                        <property name="minimum_size"></property>
+                        <property name="moveable">1</property>
+                        <property name="name">m_propPanel</property>
+                        <property name="pane_border">1</property>
+                        <property name="pane_position"></property>
+                        <property name="pane_size"></property>
+                        <property name="permission">protected</property>
+                        <property name="pin_button">1</property>
+                        <property name="pos"></property>
+                        <property name="resize">Resizable</property>
+                        <property name="show">1</property>
+                        <property name="size"></property>
+                        <property name="subclass"></property>
+                        <property name="toolbar_pane">0</property>
+                        <property name="tooltip"></property>
+                        <property name="window_extra_style"></property>
+                        <property name="window_name"></property>
+                        <property name="window_style">wxTAB_TRAVERSAL</property>
+                        <event name="OnChar"></event>
+                        <event name="OnEnterWindow"></event>
+                        <event name="OnEraseBackground"></event>
+                        <event name="OnKeyDown"></event>
+                        <event name="OnKeyUp"></event>
+                        <event name="OnKillFocus"></event>
+                        <event name="OnLeaveWindow"></event>
+                        <event name="OnLeftDClick"></event>
+                        <event name="OnLeftDown"></event>
+                        <event name="OnLeftUp"></event>
+                        <event name="OnMiddleDClick"></event>
+                        <event name="OnMiddleDown"></event>
+                        <event name="OnMiddleUp"></event>
+                        <event name="OnMotion"></event>
+                        <event name="OnMouseEvents"></event>
+                        <event name="OnMouseWheel"></event>
+                        <event name="OnPaint"></event>
+                        <event name="OnRightDClick"></event>
+                        <event name="OnRightDown"></event>
+                        <event name="OnRightUp"></event>
+                        <event name="OnSetFocus"></event>
+                        <event name="OnSize"></event>
+                        <event name="OnUpdateUI"></event>
+                        <object class="wxFlexGridSizer" expanded="1">
+                            <property name="cols">2</property>
+                            <property name="flexible_direction">wxBOTH</property>
+                            <property name="growablecols">1</property>
+                            <property name="growablerows"></property>
+                            <property name="hgap">0</property>
+                            <property name="minimum_size"></property>
+                            <property name="name">fgPropSizer</property>
+                            <property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
+                            <property name="permission">none</property>
+                            <property name="rows">0</property>
+                            <property name="vgap">0</property>
+                            <object class="sizeritem" expanded="0">
+                                <property name="border">5</property>
+                                <property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT</property>
+                                <property name="proportion">0</property>
+                                <object class="wxStaticText" expanded="0">
+                                    <property name="BottomDockable">1</property>
+                                    <property name="LeftDockable">1</property>
+                                    <property name="RightDockable">1</property>
+                                    <property name="TopDockable">1</property>
+                                    <property name="aui_layer"></property>
+                                    <property name="aui_name"></property>
+                                    <property name="aui_position"></property>
+                                    <property name="aui_row"></property>
+                                    <property name="best_size"></property>
+                                    <property name="bg"></property>
+                                    <property name="caption"></property>
+                                    <property name="caption_visible">1</property>
+                                    <property name="center_pane">0</property>
+                                    <property name="close_button">1</property>
+                                    <property name="context_help"></property>
+                                    <property name="context_menu">1</property>
+                                    <property name="default_pane">0</property>
+                                    <property name="dock">Dock</property>
+                                    <property name="dock_fixed">0</property>
+                                    <property name="docking">Left</property>
+                                    <property name="enabled">1</property>
+                                    <property name="fg"></property>
+                                    <property name="floatable">1</property>
+                                    <property name="font"></property>
+                                    <property name="gripper">0</property>
+                                    <property name="hidden">0</property>
+                                    <property name="id">wxID_ANY</property>
+                                    <property name="label">Label</property>
+                                    <property name="max_size"></property>
+                                    <property name="maximize_button">0</property>
+                                    <property name="maximum_size"></property>
+                                    <property name="min_size"></property>
+                                    <property name="minimize_button">0</property>
+                                    <property name="minimum_size"></property>
+                                    <property name="moveable">1</property>
+                                    <property name="name">m_labelLabel</property>
+                                    <property name="pane_border">1</property>
+                                    <property name="pane_position"></property>
+                                    <property name="pane_size"></property>
+                                    <property name="permission">protected</property>
+                                    <property name="pin_button">1</property>
+                                    <property name="pos"></property>
+                                    <property name="resize">Resizable</property>
+                                    <property name="show">1</property>
+                                    <property name="size"></property>
+                                    <property name="style"></property>
+                                    <property name="subclass"></property>
+                                    <property name="toolbar_pane">0</property>
+                                    <property name="tooltip"></property>
+                                    <property name="window_extra_style"></property>
+                                    <property name="window_name"></property>
+                                    <property name="window_style"></property>
+                                    <property name="wrap">-1</property>
+                                    <event name="OnChar"></event>
+                                    <event name="OnEnterWindow"></event>
+                                    <event name="OnEraseBackground"></event>
+                                    <event name="OnKeyDown"></event>
+                                    <event name="OnKeyUp"></event>
+                                    <event name="OnKillFocus"></event>
+                                    <event name="OnLeaveWindow"></event>
+                                    <event name="OnLeftDClick"></event>
+                                    <event name="OnLeftDown"></event>
+                                    <event name="OnLeftUp"></event>
+                                    <event name="OnMiddleDClick"></event>
+                                    <event name="OnMiddleDown"></event>
+                                    <event name="OnMiddleUp"></event>
+                                    <event name="OnMotion"></event>
+                                    <event name="OnMouseEvents"></event>
+                                    <event name="OnMouseWheel"></event>
+                                    <event name="OnPaint"></event>
+                                    <event name="OnRightDClick"></event>
+                                    <event name="OnRightDown"></event>
+                                    <event name="OnRightUp"></event>
+                                    <event name="OnSetFocus"></event>
+                                    <event name="OnSize"></event>
+                                    <event name="OnUpdateUI"></event>
+                                </object>
+                            </object>
+                            <object class="sizeritem" expanded="0">
+                                <property name="border">5</property>
+                                <property name="flag">wxALL|wxEXPAND</property>
+                                <property name="proportion">0</property>
+                                <object class="wxTextCtrl" expanded="0">
+                                    <property name="BottomDockable">1</property>
+                                    <property name="LeftDockable">1</property>
+                                    <property name="RightDockable">1</property>
+                                    <property name="TopDockable">1</property>
+                                    <property name="aui_layer"></property>
+                                    <property name="aui_name"></property>
+                                    <property name="aui_position"></property>
+                                    <property name="aui_row"></property>
+                                    <property name="best_size"></property>
+                                    <property name="bg"></property>
+                                    <property name="caption"></property>
+                                    <property name="caption_visible">1</property>
+                                    <property name="center_pane">0</property>
+                                    <property name="close_button">1</property>
+                                    <property name="context_help"></property>
+                                    <property name="context_menu">1</property>
+                                    <property name="default_pane">0</property>
+                                    <property name="dock">Dock</property>
+                                    <property name="dock_fixed">0</property>
+                                    <property name="docking">Left</property>
+                                    <property name="enabled">1</property>
+                                    <property name="fg"></property>
+                                    <property name="floatable">1</property>
+                                    <property name="font"></property>
+                                    <property name="gripper">0</property>
+                                    <property name="hidden">0</property>
+                                    <property name="id">wxID_ANY</property>
+                                    <property name="max_size"></property>
+                                    <property name="maximize_button">0</property>
+                                    <property name="maximum_size"></property>
+                                    <property name="maxlength"></property>
+                                    <property name="min_size"></property>
+                                    <property name="minimize_button">0</property>
+                                    <property name="minimum_size"></property>
+                                    <property name="moveable">1</property>
+                                    <property name="name">m_labelText</property>
+                                    <property name="pane_border">1</property>
+                                    <property name="pane_position"></property>
+                                    <property name="pane_size"></property>
+                                    <property name="permission">protected</property>
+                                    <property name="pin_button">1</property>
+                                    <property name="pos"></property>
+                                    <property name="resize">Resizable</property>
+                                    <property name="show">1</property>
+                                    <property name="size"></property>
+                                    <property name="style">wxTE_PROCESS_ENTER</property>
+                                    <property name="subclass"></property>
+                                    <property name="toolbar_pane">0</property>
+                                    <property name="tooltip"></property>
+                                    <property name="validator_data_type"></property>
+                                    <property name="validator_style">wxFILTER_NONE</property>
+                                    <property name="validator_type">wxDefaultValidator</property>
+                                    <property name="validator_variable"></property>
+                                    <property name="value"></property>
+                                    <property name="window_extra_style"></property>
+                                    <property name="window_name"></property>
+                                    <property name="window_style"></property>
+                                    <event name="OnChar"></event>
+                                    <event name="OnEnterWindow"></event>
+                                    <event name="OnEraseBackground"></event>
+                                    <event name="OnKeyDown"></event>
+                                    <event name="OnKeyUp"></event>
+                                    <event name="OnKillFocus"></event>
+                                    <event name="OnLeaveWindow"></event>
+                                    <event name="OnLeftDClick"></event>
+                                    <event name="OnLeftDown"></event>
+                                    <event name="OnLeftUp"></event>
+                                    <event name="OnMiddleDClick"></event>
+                                    <event name="OnMiddleDown"></event>
+                                    <event name="OnMiddleUp"></event>
+                                    <event name="OnMotion"></event>
+                                    <event name="OnMouseEvents"></event>
+                                    <event name="OnMouseWheel"></event>
+                                    <event name="OnPaint"></event>
+                                    <event name="OnRightDClick"></event>
+                                    <event name="OnRightDown"></event>
+                                    <event name="OnRightUp"></event>
+                                    <event name="OnSetFocus"></event>
+                                    <event name="OnSize"></event>
+                                    <event name="OnText"></event>
+                                    <event name="OnTextEnter">onLabelText</event>
+                                    <event name="OnTextMaxLen"></event>
+                                    <event name="OnTextURL"></event>
+                                    <event name="OnUpdateUI"></event>
+                                </object>
+                            </object>
+                            <object class="sizeritem" expanded="0">
+                                <property name="border">5</property>
+                                <property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT</property>
+                                <property name="proportion">0</property>
+                                <object class="wxStaticText" expanded="0">
+                                    <property name="BottomDockable">1</property>
+                                    <property name="LeftDockable">1</property>
+                                    <property name="RightDockable">1</property>
+                                    <property name="TopDockable">1</property>
+                                    <property name="aui_layer"></property>
+                                    <property name="aui_name"></property>
+                                    <property name="aui_position"></property>
+                                    <property name="aui_row"></property>
+                                    <property name="best_size"></property>
+                                    <property name="bg"></property>
+                                    <property name="caption"></property>
+                                    <property name="caption_visible">1</property>
+                                    <property name="center_pane">0</property>
+                                    <property name="close_button">1</property>
+                                    <property name="context_help"></property>
+                                    <property name="context_menu">1</property>
+                                    <property name="default_pane">0</property>
+                                    <property name="dock">Dock</property>
+                                    <property name="dock_fixed">0</property>
+                                    <property name="docking">Left</property>
+                                    <property name="enabled">1</property>
+                                    <property name="fg"></property>
+                                    <property name="floatable">1</property>
+                                    <property name="font"></property>
+                                    <property name="gripper">0</property>
+                                    <property name="hidden">0</property>
+                                    <property name="id">wxID_ANY</property>
+                                    <property name="label">Freq</property>
+                                    <property name="max_size"></property>
+                                    <property name="maximize_button">0</property>
+                                    <property name="maximum_size"></property>
+                                    <property name="min_size"></property>
+                                    <property name="minimize_button">0</property>
+                                    <property name="minimum_size"></property>
+                                    <property name="moveable">1</property>
+                                    <property name="name">m_frequencyLabel</property>
+                                    <property name="pane_border">1</property>
+                                    <property name="pane_position"></property>
+                                    <property name="pane_size"></property>
+                                    <property name="permission">protected</property>
+                                    <property name="pin_button">1</property>
+                                    <property name="pos"></property>
+                                    <property name="resize">Resizable</property>
+                                    <property name="show">1</property>
+                                    <property name="size"></property>
+                                    <property name="style"></property>
+                                    <property name="subclass"></property>
+                                    <property name="toolbar_pane">0</property>
+                                    <property name="tooltip"></property>
+                                    <property name="window_extra_style"></property>
+                                    <property name="window_name"></property>
+                                    <property name="window_style"></property>
+                                    <property name="wrap">-1</property>
+                                    <event name="OnChar"></event>
+                                    <event name="OnEnterWindow"></event>
+                                    <event name="OnEraseBackground"></event>
+                                    <event name="OnKeyDown"></event>
+                                    <event name="OnKeyUp"></event>
+                                    <event name="OnKillFocus"></event>
+                                    <event name="OnLeaveWindow"></event>
+                                    <event name="OnLeftDClick"></event>
+                                    <event name="OnLeftDown"></event>
+                                    <event name="OnLeftUp"></event>
+                                    <event name="OnMiddleDClick"></event>
+                                    <event name="OnMiddleDown"></event>
+                                    <event name="OnMiddleUp"></event>
+                                    <event name="OnMotion"></event>
+                                    <event name="OnMouseEvents"></event>
+                                    <event name="OnMouseWheel"></event>
+                                    <event name="OnPaint"></event>
+                                    <event name="OnRightDClick"></event>
+                                    <event name="OnRightDown"></event>
+                                    <event name="OnRightUp"></event>
+                                    <event name="OnSetFocus"></event>
+                                    <event name="OnSize"></event>
+                                    <event name="OnUpdateUI"></event>
+                                </object>
+                            </object>
+                            <object class="sizeritem" expanded="0">
+                                <property name="border">5</property>
+                                <property name="flag">wxALL</property>
+                                <property name="proportion">0</property>
+                                <object class="wxStaticText" expanded="0">
+                                    <property name="BottomDockable">1</property>
+                                    <property name="LeftDockable">1</property>
+                                    <property name="RightDockable">1</property>
+                                    <property name="TopDockable">1</property>
+                                    <property name="aui_layer"></property>
+                                    <property name="aui_name"></property>
+                                    <property name="aui_position"></property>
+                                    <property name="aui_row"></property>
+                                    <property name="best_size"></property>
+                                    <property name="bg"></property>
+                                    <property name="caption"></property>
+                                    <property name="caption_visible">1</property>
+                                    <property name="center_pane">0</property>
+                                    <property name="close_button">1</property>
+                                    <property name="context_help"></property>
+                                    <property name="context_menu">1</property>
+                                    <property name="default_pane">0</property>
+                                    <property name="dock">Dock</property>
+                                    <property name="dock_fixed">0</property>
+                                    <property name="docking">Left</property>
+                                    <property name="enabled">1</property>
+                                    <property name="fg"></property>
+                                    <property name="floatable">1</property>
+                                    <property name="font"></property>
+                                    <property name="gripper">0</property>
+                                    <property name="hidden">0</property>
+                                    <property name="id">wxID_ANY</property>
+                                    <property name="label">FrequencyVal</property>
+                                    <property name="max_size"></property>
+                                    <property name="maximize_button">0</property>
+                                    <property name="maximum_size"></property>
+                                    <property name="min_size"></property>
+                                    <property name="minimize_button">0</property>
+                                    <property name="minimum_size"></property>
+                                    <property name="moveable">1</property>
+                                    <property name="name">m_frequencyVal</property>
+                                    <property name="pane_border">1</property>
+                                    <property name="pane_position"></property>
+                                    <property name="pane_size"></property>
+                                    <property name="permission">protected</property>
+                                    <property name="pin_button">1</property>
+                                    <property name="pos"></property>
+                                    <property name="resize">Resizable</property>
+                                    <property name="show">1</property>
+                                    <property name="size"></property>
+                                    <property name="style"></property>
+                                    <property name="subclass"></property>
+                                    <property name="toolbar_pane">0</property>
+                                    <property name="tooltip"></property>
+                                    <property name="window_extra_style"></property>
+                                    <property name="window_name"></property>
+                                    <property name="window_style"></property>
+                                    <property name="wrap">-1</property>
+                                    <event name="OnChar"></event>
+                                    <event name="OnEnterWindow"></event>
+                                    <event name="OnEraseBackground"></event>
+                                    <event name="OnKeyDown"></event>
+                                    <event name="OnKeyUp"></event>
+                                    <event name="OnKillFocus"></event>
+                                    <event name="OnLeaveWindow"></event>
+                                    <event name="OnLeftDClick">onDoubleClickFreq</event>
+                                    <event name="OnLeftDown"></event>
+                                    <event name="OnLeftUp"></event>
+                                    <event name="OnMiddleDClick"></event>
+                                    <event name="OnMiddleDown"></event>
+                                    <event name="OnMiddleUp"></event>
+                                    <event name="OnMotion"></event>
+                                    <event name="OnMouseEvents"></event>
+                                    <event name="OnMouseWheel"></event>
+                                    <event name="OnPaint"></event>
+                                    <event name="OnRightDClick"></event>
+                                    <event name="OnRightDown"></event>
+                                    <event name="OnRightUp"></event>
+                                    <event name="OnSetFocus"></event>
+                                    <event name="OnSize"></event>
+                                    <event name="OnUpdateUI"></event>
+                                </object>
+                            </object>
+                            <object class="sizeritem" expanded="0">
+                                <property name="border">5</property>
+                                <property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT</property>
+                                <property name="proportion">0</property>
+                                <object class="wxStaticText" expanded="0">
+                                    <property name="BottomDockable">1</property>
+                                    <property name="LeftDockable">1</property>
+                                    <property name="RightDockable">1</property>
+                                    <property name="TopDockable">1</property>
+                                    <property name="aui_layer"></property>
+                                    <property name="aui_name"></property>
+                                    <property name="aui_position"></property>
+                                    <property name="aui_row"></property>
+                                    <property name="best_size"></property>
+                                    <property name="bg"></property>
+                                    <property name="caption"></property>
+                                    <property name="caption_visible">1</property>
+                                    <property name="center_pane">0</property>
+                                    <property name="close_button">1</property>
+                                    <property name="context_help"></property>
+                                    <property name="context_menu">1</property>
+                                    <property name="default_pane">0</property>
+                                    <property name="dock">Dock</property>
+                                    <property name="dock_fixed">0</property>
+                                    <property name="docking">Left</property>
+                                    <property name="enabled">1</property>
+                                    <property name="fg"></property>
+                                    <property name="floatable">1</property>
+                                    <property name="font"></property>
+                                    <property name="gripper">0</property>
+                                    <property name="hidden">0</property>
+                                    <property name="id">wxID_ANY</property>
+                                    <property name="label">BW</property>
+                                    <property name="max_size"></property>
+                                    <property name="maximize_button">0</property>
+                                    <property name="maximum_size"></property>
+                                    <property name="min_size"></property>
+                                    <property name="minimize_button">0</property>
+                                    <property name="minimum_size"></property>
+                                    <property name="moveable">1</property>
+                                    <property name="name">m_bandwidthLabel</property>
+                                    <property name="pane_border">1</property>
+                                    <property name="pane_position"></property>
+                                    <property name="pane_size"></property>
+                                    <property name="permission">protected</property>
+                                    <property name="pin_button">1</property>
+                                    <property name="pos"></property>
+                                    <property name="resize">Resizable</property>
+                                    <property name="show">1</property>
+                                    <property name="size"></property>
+                                    <property name="style"></property>
+                                    <property name="subclass"></property>
+                                    <property name="toolbar_pane">0</property>
+                                    <property name="tooltip"></property>
+                                    <property name="window_extra_style"></property>
+                                    <property name="window_name"></property>
+                                    <property name="window_style"></property>
+                                    <property name="wrap">-1</property>
+                                    <event name="OnChar"></event>
+                                    <event name="OnEnterWindow"></event>
+                                    <event name="OnEraseBackground"></event>
+                                    <event name="OnKeyDown"></event>
+                                    <event name="OnKeyUp"></event>
+                                    <event name="OnKillFocus"></event>
+                                    <event name="OnLeaveWindow"></event>
+                                    <event name="OnLeftDClick"></event>
+                                    <event name="OnLeftDown"></event>
+                                    <event name="OnLeftUp"></event>
+                                    <event name="OnMiddleDClick"></event>
+                                    <event name="OnMiddleDown"></event>
+                                    <event name="OnMiddleUp"></event>
+                                    <event name="OnMotion"></event>
+                                    <event name="OnMouseEvents"></event>
+                                    <event name="OnMouseWheel"></event>
+                                    <event name="OnPaint"></event>
+                                    <event name="OnRightDClick"></event>
+                                    <event name="OnRightDown"></event>
+                                    <event name="OnRightUp"></event>
+                                    <event name="OnSetFocus"></event>
+                                    <event name="OnSize"></event>
+                                    <event name="OnUpdateUI"></event>
+                                </object>
+                            </object>
+                            <object class="sizeritem" expanded="0">
+                                <property name="border">5</property>
+                                <property name="flag">wxALL</property>
+                                <property name="proportion">0</property>
+                                <object class="wxStaticText" expanded="0">
+                                    <property name="BottomDockable">1</property>
+                                    <property name="LeftDockable">1</property>
+                                    <property name="RightDockable">1</property>
+                                    <property name="TopDockable">1</property>
+                                    <property name="aui_layer"></property>
+                                    <property name="aui_name"></property>
+                                    <property name="aui_position"></property>
+                                    <property name="aui_row"></property>
+                                    <property name="best_size"></property>
+                                    <property name="bg"></property>
+                                    <property name="caption"></property>
+                                    <property name="caption_visible">1</property>
+                                    <property name="center_pane">0</property>
+                                    <property name="close_button">1</property>
+                                    <property name="context_help"></property>
+                                    <property name="context_menu">1</property>
+                                    <property name="default_pane">0</property>
+                                    <property name="dock">Dock</property>
+                                    <property name="dock_fixed">0</property>
+                                    <property name="docking">Left</property>
+                                    <property name="enabled">1</property>
+                                    <property name="fg"></property>
+                                    <property name="floatable">1</property>
+                                    <property name="font"></property>
+                                    <property name="gripper">0</property>
+                                    <property name="hidden">0</property>
+                                    <property name="id">wxID_ANY</property>
+                                    <property name="label">BandwidthVal</property>
+                                    <property name="max_size"></property>
+                                    <property name="maximize_button">0</property>
+                                    <property name="maximum_size"></property>
+                                    <property name="min_size"></property>
+                                    <property name="minimize_button">0</property>
+                                    <property name="minimum_size"></property>
+                                    <property name="moveable">1</property>
+                                    <property name="name">m_bandwidthVal</property>
+                                    <property name="pane_border">1</property>
+                                    <property name="pane_position"></property>
+                                    <property name="pane_size"></property>
+                                    <property name="permission">protected</property>
+                                    <property name="pin_button">1</property>
+                                    <property name="pos"></property>
+                                    <property name="resize">Resizable</property>
+                                    <property name="show">1</property>
+                                    <property name="size"></property>
+                                    <property name="style"></property>
+                                    <property name="subclass"></property>
+                                    <property name="toolbar_pane">0</property>
+                                    <property name="tooltip"></property>
+                                    <property name="window_extra_style"></property>
+                                    <property name="window_name"></property>
+                                    <property name="window_style"></property>
+                                    <property name="wrap">-1</property>
+                                    <event name="OnChar"></event>
+                                    <event name="OnEnterWindow"></event>
+                                    <event name="OnEraseBackground"></event>
+                                    <event name="OnKeyDown"></event>
+                                    <event name="OnKeyUp"></event>
+                                    <event name="OnKillFocus"></event>
+                                    <event name="OnLeaveWindow"></event>
+                                    <event name="OnLeftDClick">onDoubleClickBandwidth</event>
+                                    <event name="OnLeftDown"></event>
+                                    <event name="OnLeftUp"></event>
+                                    <event name="OnMiddleDClick"></event>
+                                    <event name="OnMiddleDown"></event>
+                                    <event name="OnMiddleUp"></event>
+                                    <event name="OnMotion"></event>
+                                    <event name="OnMouseEvents"></event>
+                                    <event name="OnMouseWheel"></event>
+                                    <event name="OnPaint"></event>
+                                    <event name="OnRightDClick"></event>
+                                    <event name="OnRightDown"></event>
+                                    <event name="OnRightUp"></event>
+                                    <event name="OnSetFocus"></event>
+                                    <event name="OnSize"></event>
+                                    <event name="OnUpdateUI"></event>
+                                </object>
+                            </object>
+                            <object class="sizeritem" expanded="0">
+                                <property name="border">5</property>
+                                <property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT</property>
+                                <property name="proportion">0</property>
+                                <object class="wxStaticText" expanded="0">
+                                    <property name="BottomDockable">1</property>
+                                    <property name="LeftDockable">1</property>
+                                    <property name="RightDockable">1</property>
+                                    <property name="TopDockable">1</property>
+                                    <property name="aui_layer"></property>
+                                    <property name="aui_name"></property>
+                                    <property name="aui_position"></property>
+                                    <property name="aui_row"></property>
+                                    <property name="best_size"></property>
+                                    <property name="bg"></property>
+                                    <property name="caption"></property>
+                                    <property name="caption_visible">1</property>
+                                    <property name="center_pane">0</property>
+                                    <property name="close_button">1</property>
+                                    <property name="context_help"></property>
+                                    <property name="context_menu">1</property>
+                                    <property name="default_pane">0</property>
+                                    <property name="dock">Dock</property>
+                                    <property name="dock_fixed">0</property>
+                                    <property name="docking">Left</property>
+                                    <property name="enabled">1</property>
+                                    <property name="fg"></property>
+                                    <property name="floatable">1</property>
+                                    <property name="font"></property>
+                                    <property name="gripper">0</property>
+                                    <property name="hidden">0</property>
+                                    <property name="id">wxID_ANY</property>
+                                    <property name="label">Type</property>
+                                    <property name="max_size"></property>
+                                    <property name="maximize_button">0</property>
+                                    <property name="maximum_size"></property>
+                                    <property name="min_size"></property>
+                                    <property name="minimize_button">0</property>
+                                    <property name="minimum_size"></property>
+                                    <property name="moveable">1</property>
+                                    <property name="name">m_modulationLabel</property>
+                                    <property name="pane_border">1</property>
+                                    <property name="pane_position"></property>
+                                    <property name="pane_size"></property>
+                                    <property name="permission">protected</property>
+                                    <property name="pin_button">1</property>
+                                    <property name="pos"></property>
+                                    <property name="resize">Resizable</property>
+                                    <property name="show">1</property>
+                                    <property name="size"></property>
+                                    <property name="style"></property>
+                                    <property name="subclass"></property>
+                                    <property name="toolbar_pane">0</property>
+                                    <property name="tooltip"></property>
+                                    <property name="window_extra_style"></property>
+                                    <property name="window_name"></property>
+                                    <property name="window_style"></property>
+                                    <property name="wrap">-1</property>
+                                    <event name="OnChar"></event>
+                                    <event name="OnEnterWindow"></event>
+                                    <event name="OnEraseBackground"></event>
+                                    <event name="OnKeyDown"></event>
+                                    <event name="OnKeyUp"></event>
+                                    <event name="OnKillFocus"></event>
+                                    <event name="OnLeaveWindow"></event>
+                                    <event name="OnLeftDClick"></event>
+                                    <event name="OnLeftDown"></event>
+                                    <event name="OnLeftUp"></event>
+                                    <event name="OnMiddleDClick"></event>
+                                    <event name="OnMiddleDown"></event>
+                                    <event name="OnMiddleUp"></event>
+                                    <event name="OnMotion"></event>
+                                    <event name="OnMouseEvents"></event>
+                                    <event name="OnMouseWheel"></event>
+                                    <event name="OnPaint"></event>
+                                    <event name="OnRightDClick"></event>
+                                    <event name="OnRightDown"></event>
+                                    <event name="OnRightUp"></event>
+                                    <event name="OnSetFocus"></event>
+                                    <event name="OnSize"></event>
+                                    <event name="OnUpdateUI"></event>
+                                </object>
+                            </object>
+                            <object class="sizeritem" expanded="0">
+                                <property name="border">5</property>
+                                <property name="flag">wxALL</property>
+                                <property name="proportion">0</property>
+                                <object class="wxStaticText" expanded="0">
+                                    <property name="BottomDockable">1</property>
+                                    <property name="LeftDockable">1</property>
+                                    <property name="RightDockable">1</property>
+                                    <property name="TopDockable">1</property>
+                                    <property name="aui_layer"></property>
+                                    <property name="aui_name"></property>
+                                    <property name="aui_position"></property>
+                                    <property name="aui_row"></property>
+                                    <property name="best_size"></property>
+                                    <property name="bg"></property>
+                                    <property name="caption"></property>
+                                    <property name="caption_visible">1</property>
+                                    <property name="center_pane">0</property>
+                                    <property name="close_button">1</property>
+                                    <property name="context_help"></property>
+                                    <property name="context_menu">1</property>
+                                    <property name="default_pane">0</property>
+                                    <property name="dock">Dock</property>
+                                    <property name="dock_fixed">0</property>
+                                    <property name="docking">Left</property>
+                                    <property name="enabled">1</property>
+                                    <property name="fg"></property>
+                                    <property name="floatable">1</property>
+                                    <property name="font"></property>
+                                    <property name="gripper">0</property>
+                                    <property name="hidden">0</property>
+                                    <property name="id">wxID_ANY</property>
+                                    <property name="label">TypeVal</property>
+                                    <property name="max_size"></property>
+                                    <property name="maximize_button">0</property>
+                                    <property name="maximum_size"></property>
+                                    <property name="min_size"></property>
+                                    <property name="minimize_button">0</property>
+                                    <property name="minimum_size"></property>
+                                    <property name="moveable">1</property>
+                                    <property name="name">m_modulationVal</property>
+                                    <property name="pane_border">1</property>
+                                    <property name="pane_position"></property>
+                                    <property name="pane_size"></property>
+                                    <property name="permission">protected</property>
+                                    <property name="pin_button">1</property>
+                                    <property name="pos"></property>
+                                    <property name="resize">Resizable</property>
+                                    <property name="show">1</property>
+                                    <property name="size"></property>
+                                    <property name="style"></property>
+                                    <property name="subclass"></property>
+                                    <property name="toolbar_pane">0</property>
+                                    <property name="tooltip"></property>
+                                    <property name="window_extra_style"></property>
+                                    <property name="window_name"></property>
+                                    <property name="window_style"></property>
+                                    <property name="wrap">-1</property>
+                                    <event name="OnChar"></event>
+                                    <event name="OnEnterWindow"></event>
+                                    <event name="OnEraseBackground"></event>
+                                    <event name="OnKeyDown"></event>
+                                    <event name="OnKeyUp"></event>
+                                    <event name="OnKillFocus"></event>
+                                    <event name="OnLeaveWindow"></event>
+                                    <event name="OnLeftDClick"></event>
+                                    <event name="OnLeftDown"></event>
+                                    <event name="OnLeftUp"></event>
+                                    <event name="OnMiddleDClick"></event>
+                                    <event name="OnMiddleDown"></event>
+                                    <event name="OnMiddleUp"></event>
+                                    <event name="OnMotion"></event>
+                                    <event name="OnMouseEvents"></event>
+                                    <event name="OnMouseWheel"></event>
+                                    <event name="OnPaint"></event>
+                                    <event name="OnRightDClick"></event>
+                                    <event name="OnRightDown"></event>
+                                    <event name="OnRightUp"></event>
+                                    <event name="OnSetFocus"></event>
+                                    <event name="OnSize"></event>
+                                    <event name="OnUpdateUI"></event>
+                                </object>
+                            </object>
+                        </object>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">5</property>
+                    <property name="flag">wxALL|wxEXPAND</property>
+                    <property name="proportion">0</property>
+                    <object class="wxPanel" expanded="1">
+                        <property name="BottomDockable">1</property>
+                        <property name="LeftDockable">1</property>
+                        <property name="RightDockable">1</property>
+                        <property name="TopDockable">1</property>
+                        <property name="aui_layer"></property>
+                        <property name="aui_name"></property>
+                        <property name="aui_position"></property>
+                        <property name="aui_row"></property>
+                        <property name="best_size"></property>
+                        <property name="bg"></property>
+                        <property name="caption"></property>
+                        <property name="caption_visible">1</property>
+                        <property name="center_pane">0</property>
+                        <property name="close_button">1</property>
+                        <property name="context_help"></property>
+                        <property name="context_menu">1</property>
+                        <property name="default_pane">0</property>
+                        <property name="dock">Dock</property>
+                        <property name="dock_fixed">0</property>
+                        <property name="docking">Left</property>
+                        <property name="enabled">1</property>
+                        <property name="fg"></property>
+                        <property name="floatable">1</property>
+                        <property name="font"></property>
+                        <property name="gripper">0</property>
+                        <property name="hidden">0</property>
+                        <property name="id">wxID_ANY</property>
+                        <property name="max_size"></property>
+                        <property name="maximize_button">0</property>
+                        <property name="maximum_size"></property>
+                        <property name="min_size"></property>
+                        <property name="minimize_button">0</property>
+                        <property name="minimum_size"></property>
+                        <property name="moveable">1</property>
+                        <property name="name">m_buttonPanel</property>
+                        <property name="pane_border">1</property>
+                        <property name="pane_position"></property>
+                        <property name="pane_size"></property>
+                        <property name="permission">protected</property>
+                        <property name="pin_button">1</property>
+                        <property name="pos"></property>
+                        <property name="resize">Resizable</property>
+                        <property name="show">1</property>
+                        <property name="size"></property>
+                        <property name="subclass"></property>
+                        <property name="toolbar_pane">0</property>
+                        <property name="tooltip"></property>
+                        <property name="window_extra_style"></property>
+                        <property name="window_name"></property>
+                        <property name="window_style">wxTAB_TRAVERSAL</property>
+                        <event name="OnChar"></event>
+                        <event name="OnEnterWindow"></event>
+                        <event name="OnEraseBackground"></event>
+                        <event name="OnKeyDown"></event>
+                        <event name="OnKeyUp"></event>
+                        <event name="OnKillFocus"></event>
+                        <event name="OnLeaveWindow"></event>
+                        <event name="OnLeftDClick"></event>
+                        <event name="OnLeftDown"></event>
+                        <event name="OnLeftUp"></event>
+                        <event name="OnMiddleDClick"></event>
+                        <event name="OnMiddleDown"></event>
+                        <event name="OnMiddleUp"></event>
+                        <event name="OnMotion"></event>
+                        <event name="OnMouseEvents"></event>
+                        <event name="OnMouseWheel"></event>
+                        <event name="OnPaint"></event>
+                        <event name="OnRightDClick"></event>
+                        <event name="OnRightDown"></event>
+                        <event name="OnRightUp"></event>
+                        <event name="OnSetFocus"></event>
+                        <event name="OnSize"></event>
+                        <event name="OnUpdateUI"></event>
+                        <object class="wxBoxSizer" expanded="1">
+                            <property name="minimum_size"></property>
+                            <property name="name">m_buttonPanelSizer</property>
+                            <property name="orient">wxVERTICAL</property>
+                            <property name="permission">none</property>
+                        </object>
+                    </object>
+                </object>
+            </object>
+            <object class="wxTimer" expanded="1">
+                <property name="enabled">0</property>
+                <property name="id">wxID_ANY</property>
+                <property name="name">m_updateTimer</property>
+                <property name="oneshot">0</property>
+                <property name="period">500</property>
+                <property name="permission">protected</property>
+                <event name="OnTimer">onUpdateTimer</event>
+            </object>
+        </object>
+    </object>
+</wxFormBuilder_Project>
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 <wx/artprov.h>
+#include <wx/xrc/xmlres.h>
+#include <wx/string.h>
+#include <wx/textctrl.h>
+#include <wx/gdicmn.h>
+#include <wx/font.h>
+#include <wx/colour.h>
+#include <wx/settings.h>
+#include <wx/button.h>
+#include <wx/treectrl.h>
+#include <wx/stattext.h>
+#include <wx/sizer.h>
+#include <wx/panel.h>
+#include <wx/timer.h>
+
+///////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+/// 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..5347f06
--- /dev/null
+++ b/src/forms/Bookmark/BookmarkView.cpp
@@ -0,0 +1,1533 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <wx/menu.h>
+#include <wx/textdlg.h>
+
+#include <algorithm>
+#include <wchar.h>
+
+#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;
+};
+
+
+class ActionDialogUpdateRange : public ActionDialog {
+public:
+    ActionDialogUpdateRange( BookmarkRangeEntry *rangeEnt ) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Update 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 update the range\n '" + name + L"' to the active range?");
+    }
+    
+    void doClickOK() {
+        BookmarkRangeEntry *ue = BookmarkView::makeActiveRangeEntry();
+
+        subject->freq = ue->freq;
+        subject->startFreq = ue->startFreq;
+        subject->endFreq = ue->endFreq;
+        
+        delete ue;
+        
+        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_propPanel->SetForegroundColour(textColor);
+
+    m_buttonPanel->SetBackgroundColour(bgColor);
+    m_buttonPanel->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<std::wstring> &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<std::string, bool> 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<DemodulatorInstance *> &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<TreeViewItem*>(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<TreeViewItem*>(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<TreeViewItem*>(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);
+    
+    wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground);
+    wxColour textColor(ThemeMgr::mgr.currentTheme->text);
+
+    nButton->SetBackgroundColour(bgColor);
+    nButton->SetForegroundColour(textColor);
+    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, "Update Range", wxCommandEventHandler( BookmarkView::onUpdateRange ))->SetToolTip("Update range by setting it to the active range.");
+    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<TreeViewItem*>(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 = BookmarkView::makeActiveRangeEntry();
+    
+    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::onUpdateRange( wxCommandEvent& /* event */ ) {
+    TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection());
+    
+    if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) {
+        ActionDialog::showDialog(new ActionDialogUpdateRange(curSel->rangeEnt));
+    }
+}
+
+void BookmarkView::onTreeBeginDrag( wxTreeEvent& event ) {
+    TreeViewItem* tvi = dynamic_cast<TreeViewItem*>(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<TreeViewItem*>(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<TreeViewItem*>(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));
+}
+
+
+BookmarkRangeEntry *BookmarkView::makeActiveRangeEntry() {
+    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);
+    
+    return re;
+}
diff --git a/src/forms/Bookmark/BookmarkView.h b/src/forms/Bookmark/BookmarkView.h
new file mode 100644
index 0000000..9e7e261
--- /dev/null
+++ b/src/forms/Bookmark/BookmarkView.h
@@ -0,0 +1,175 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#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<std::wstring> &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();
+    static BookmarkRangeEntry *makeActiveRangeEntry();
+
+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 );
+    void onUpdateRange( wxCommandEvent& event );
+
+
+    TreeViewItem *itemToTVI(wxTreeItemId item);
+    
+    std::atomic_bool mouseInView;
+    
+    wxTreeItemId rootBranch, activeBranch, bookmarkBranch, recentBranch, rangeBranch;
+    
+    std::map<std::string, bool> expandState;
+    
+    TreeViewItem *dragItem;
+    wxTreeItemId dragItemId;
+    BookmarkViewVisualDragItem *visualDragItem;
+    
+    bool editingLabel;
+    
+    // Bookmarks
+    std::atomic_bool doUpdateBookmarks;
+    std::set< std::string > doUpdateBookmarkGroup;
+    BookmarkNames groupNames;
+    std::map<std::string, wxTreeItemId> groups;
+    wxArrayString bookmarkChoices;
+    wxChoice *bookmarkChoice;
+    
+    // Active
+    std::atomic_bool doUpdateActive;
+    
+    // Focus
+    BookmarkEntry *nextEnt;
+    BookmarkRangeEntry *nextRange;
+    DemodulatorInstance *nextDemod;
+    
+    // Search
+    std::vector<std::wstring> 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 <mutex>
+
+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 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<wxFormBuilder_Project>
+    <FileVersion major="1" minor="13" />
+    <object class="Project" expanded="1">
+        <property name="class_decoration"></property>
+        <property name="code_generation">C++</property>
+        <property name="disconnect_events">1</property>
+        <property name="disconnect_mode">source_name</property>
+        <property name="disconnect_php_events">0</property>
+        <property name="disconnect_python_events">0</property>
+        <property name="embedded_files_path">res</property>
+        <property name="encoding">UTF-8</property>
+        <property name="event_generation">connect</property>
+        <property name="file">ActionDialogBase</property>
+        <property name="first_id">1000</property>
+        <property name="help_provider">none</property>
+        <property name="internationalize">0</property>
+        <property name="name">ActionDialogBase</property>
+        <property name="namespace"></property>
+        <property name="path">.</property>
+        <property name="precompiled_header"></property>
+        <property name="relative_path">1</property>
+        <property name="skip_lua_events">1</property>
+        <property name="skip_php_events">1</property>
+        <property name="skip_python_events">1</property>
+        <property name="ui_table">UI</property>
+        <property name="use_enum">0</property>
+        <property name="use_microsoft_bom">0</property>
+        <object class="Dialog" expanded="1">
+            <property name="aui_managed">0</property>
+            <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
+            <property name="bg"></property>
+            <property name="center">wxBOTH</property>
+            <property name="context_help"></property>
+            <property name="context_menu">1</property>
+            <property name="enabled">1</property>
+            <property name="event_handler">impl_virtual</property>
+            <property name="extra_style"></property>
+            <property name="fg"></property>
+            <property name="font"></property>
+            <property name="hidden">0</property>
+            <property name="id">wxID_ANY</property>
+            <property name="maximum_size"></property>
+            <property name="minimum_size"></property>
+            <property name="name">ActionDialogBase</property>
+            <property name="pos"></property>
+            <property name="size"></property>
+            <property name="style">wxDEFAULT_DIALOG_STYLE</property>
+            <property name="subclass"></property>
+            <property name="title">QuestionTitle</property>
+            <property name="tooltip"></property>
+            <property name="window_extra_style"></property>
+            <property name="window_name"></property>
+            <property name="window_style"></property>
+            <event name="OnActivate"></event>
+            <event name="OnActivateApp"></event>
+            <event name="OnAuiFindManager"></event>
+            <event name="OnAuiPaneButton"></event>
+            <event name="OnAuiPaneClose"></event>
+            <event name="OnAuiPaneMaximize"></event>
+            <event name="OnAuiPaneRestore"></event>
+            <event name="OnAuiRender"></event>
+            <event name="OnChar"></event>
+            <event name="OnClose"></event>
+            <event name="OnEnterWindow"></event>
+            <event name="OnEraseBackground"></event>
+            <event name="OnHibernate"></event>
+            <event name="OnIconize"></event>
+            <event name="OnIdle"></event>
+            <event name="OnInitDialog"></event>
+            <event name="OnKeyDown"></event>
+            <event name="OnKeyUp"></event>
+            <event name="OnKillFocus"></event>
+            <event name="OnLeaveWindow"></event>
+            <event name="OnLeftDClick"></event>
+            <event name="OnLeftDown"></event>
+            <event name="OnLeftUp"></event>
+            <event name="OnMiddleDClick"></event>
+            <event name="OnMiddleDown"></event>
+            <event name="OnMiddleUp"></event>
+            <event name="OnMotion"></event>
+            <event name="OnMouseEvents"></event>
+            <event name="OnMouseWheel"></event>
+            <event name="OnPaint"></event>
+            <event name="OnRightDClick"></event>
+            <event name="OnRightDown"></event>
+            <event name="OnRightUp"></event>
+            <event name="OnSetFocus"></event>
+            <event name="OnSize"></event>
+            <event name="OnUpdateUI"></event>
+            <object class="wxBoxSizer" expanded="1">
+                <property name="minimum_size"></property>
+                <property name="name">mainSizer</property>
+                <property name="orient">wxVERTICAL</property>
+                <property name="permission">none</property>
+                <object class="sizeritem" expanded="0">
+                    <property name="border">5</property>
+                    <property name="flag">wxALL|wxEXPAND</property>
+                    <property name="proportion">1</property>
+                    <object class="wxStaticText" expanded="0">
+                        <property name="BottomDockable">1</property>
+                        <property name="LeftDockable">1</property>
+                        <property name="RightDockable">1</property>
+                        <property name="TopDockable">1</property>
+                        <property name="aui_layer"></property>
+                        <property name="aui_name"></property>
+                        <property name="aui_position"></property>
+                        <property name="aui_row"></property>
+                        <property name="best_size"></property>
+                        <property name="bg"></property>
+                        <property name="caption"></property>
+                        <property name="caption_visible">1</property>
+                        <property name="center_pane">0</property>
+                        <property name="close_button">1</property>
+                        <property name="context_help"></property>
+                        <property name="context_menu">1</property>
+                        <property name="default_pane">0</property>
+                        <property name="dock">Dock</property>
+                        <property name="dock_fixed">0</property>
+                        <property name="docking">Left</property>
+                        <property name="enabled">1</property>
+                        <property name="fg"></property>
+                        <property name="floatable">1</property>
+                        <property name="font"></property>
+                        <property name="gripper">0</property>
+                        <property name="hidden">0</property>
+                        <property name="id">wxID_ANY</property>
+                        <property name="label">Question</property>
+                        <property name="max_size"></property>
+                        <property name="maximize_button">0</property>
+                        <property name="maximum_size"></property>
+                        <property name="min_size"></property>
+                        <property name="minimize_button">0</property>
+                        <property name="minimum_size"></property>
+                        <property name="moveable">1</property>
+                        <property name="name">m_questionText</property>
+                        <property name="pane_border">1</property>
+                        <property name="pane_position"></property>
+                        <property name="pane_size"></property>
+                        <property name="permission">protected</property>
+                        <property name="pin_button">1</property>
+                        <property name="pos"></property>
+                        <property name="resize">Resizable</property>
+                        <property name="show">1</property>
+                        <property name="size"></property>
+                        <property name="style">wxALIGN_CENTRE</property>
+                        <property name="subclass"></property>
+                        <property name="toolbar_pane">0</property>
+                        <property name="tooltip"></property>
+                        <property name="window_extra_style"></property>
+                        <property name="window_name"></property>
+                        <property name="window_style"></property>
+                        <property name="wrap">-1</property>
+                        <event name="OnChar"></event>
+                        <event name="OnEnterWindow"></event>
+                        <event name="OnEraseBackground"></event>
+                        <event name="OnKeyDown"></event>
+                        <event name="OnKeyUp"></event>
+                        <event name="OnKillFocus"></event>
+                        <event name="OnLeaveWindow"></event>
+                        <event name="OnLeftDClick"></event>
+                        <event name="OnLeftDown"></event>
+                        <event name="OnLeftUp"></event>
+                        <event name="OnMiddleDClick"></event>
+                        <event name="OnMiddleDown"></event>
+                        <event name="OnMiddleUp"></event>
+                        <event name="OnMotion"></event>
+                        <event name="OnMouseEvents"></event>
+                        <event name="OnMouseWheel"></event>
+                        <event name="OnPaint"></event>
+                        <event name="OnRightDClick"></event>
+                        <event name="OnRightDown"></event>
+                        <event name="OnRightUp"></event>
+                        <event name="OnSetFocus"></event>
+                        <event name="OnSize"></event>
+                        <event name="OnUpdateUI"></event>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">5</property>
+                    <property name="flag">wxEXPAND</property>
+                    <property name="proportion">1</property>
+                    <object class="wxBoxSizer" expanded="1">
+                        <property name="minimum_size"></property>
+                        <property name="name">buttonSizer</property>
+                        <property name="orient">wxHORIZONTAL</property>
+                        <property name="permission">none</property>
+                        <object class="sizeritem" expanded="0">
+                            <property name="border">5</property>
+                            <property name="flag">wxALL|wxEXPAND</property>
+                            <property name="proportion">1</property>
+                            <object class="wxButton" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Cancel</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_cancelButton</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass"></property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <event name="OnButtonClick">onClickCancel</event>
+                                <event name="OnChar"></event>
+                                <event name="OnEnterWindow"></event>
+                                <event name="OnEraseBackground"></event>
+                                <event name="OnKeyDown"></event>
+                                <event name="OnKeyUp"></event>
+                                <event name="OnKillFocus"></event>
+                                <event name="OnLeaveWindow"></event>
+                                <event name="OnLeftDClick"></event>
+                                <event name="OnLeftDown"></event>
+                                <event name="OnLeftUp"></event>
+                                <event name="OnMiddleDClick"></event>
+                                <event name="OnMiddleDown"></event>
+                                <event name="OnMiddleUp"></event>
+                                <event name="OnMotion"></event>
+                                <event name="OnMouseEvents"></event>
+                                <event name="OnMouseWheel"></event>
+                                <event name="OnPaint"></event>
+                                <event name="OnRightDClick"></event>
+                                <event name="OnRightDown"></event>
+                                <event name="OnRightUp"></event>
+                                <event name="OnSetFocus"></event>
+                                <event name="OnSize"></event>
+                                <event name="OnUpdateUI"></event>
+                            </object>
+                        </object>
+                        <object class="sizeritem" expanded="0">
+                            <property name="border">5</property>
+                            <property name="flag">wxALL|wxEXPAND</property>
+                            <property name="proportion">1</property>
+                            <object class="wxButton" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">OK</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_okButton</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass"></property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <event name="OnButtonClick">onClickOK</event>
+                                <event name="OnChar"></event>
+                                <event name="OnEnterWindow"></event>
+                                <event name="OnEraseBackground"></event>
+                                <event name="OnKeyDown"></event>
+                                <event name="OnKeyUp"></event>
+                                <event name="OnKillFocus"></event>
+                                <event name="OnLeaveWindow"></event>
+                                <event name="OnLeftDClick"></event>
+                                <event name="OnLeftDown"></event>
+                                <event name="OnLeftUp"></event>
+                                <event name="OnMiddleDClick"></event>
+                                <event name="OnMiddleDown"></event>
+                                <event name="OnMiddleUp"></event>
+                                <event name="OnMotion"></event>
+                                <event name="OnMouseEvents"></event>
+                                <event name="OnMouseWheel"></event>
+                                <event name="OnPaint"></event>
+                                <event name="OnRightDClick"></event>
+                                <event name="OnRightDown"></event>
+                                <event name="OnRightUp"></event>
+                                <event name="OnSetFocus"></event>
+                                <event name="OnSize"></event>
+                                <event name="OnUpdateUI"></event>
+                            </object>
+                        </object>
+                    </object>
+                </object>
+            </object>
+        </object>
+    </object>
+</wxFormBuilder_Project>
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 <wx/artprov.h>
+#include <wx/xrc/xmlres.h>
+#include <wx/string.h>
+#include <wx/stattext.h>
+#include <wx/gdicmn.h>
+#include <wx/font.h>
+#include <wx/colour.h>
+#include <wx/settings.h>
+#include <wx/button.h>
+#include <wx/sizer.h>
+#include <wx/dialog.h>
+
+///////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+/// Class ActionDialogBase
+///////////////////////////////////////////////////////////////////////////////
+class ActionDialogBase : public wxDialog 
+{
+	private:
+	
+	protected:
+		wxStaticText* m_questionText;
+		wxButton* m_cancelButton;
+		wxButton* m_okButton;
+		
+		// Virtual event handlers, overide them in your derived class
+		virtual void onClickCancel( wxCommandEvent& event ) { event.Skip(); }
+		virtual void onClickOK( wxCommandEvent& event ) { event.Skip(); }
+		
+	
+	public:
+		
+		ActionDialogBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("QuestionTitle"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); 
+		~ActionDialogBase();
+	
+};
+
+#endif //__ACTIONDIALOGBASE_H__
diff --git a/src/forms/DigitalConsole/DigitalConsole.cpp b/src/forms/DigitalConsole/DigitalConsole.cpp
index f472ca1..8238335 100644
--- a/src/forms/DigitalConsole/DigitalConsole.cpp
+++ b/src/forms/DigitalConsole/DigitalConsole.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "DigitalConsole.h"
 #include "CubicSDR.h"
 #include <iomanip>
diff --git a/src/forms/DigitalConsole/DigitalConsole.h b/src/forms/DigitalConsole/DigitalConsole.h
index 5d5321d..a7ba9d6 100644
--- a/src/forms/DigitalConsole/DigitalConsole.h
+++ b/src/forms/DigitalConsole/DigitalConsole.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <map>
diff --git a/src/forms/SDRDevices/SDRDeviceAdd.cpp b/src/forms/SDRDevices/SDRDeviceAdd.cpp
index e7bcd39..00b2ce2 100644
--- a/src/forms/SDRDevices/SDRDeviceAdd.cpp
+++ b/src/forms/SDRDevices/SDRDeviceAdd.cpp
@@ -1,5 +1,7 @@
-#include "SDRDeviceAdd.h"
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
 
+#include "SDRDeviceAdd.h"
 #include "SDREnumerator.h"
 
 SDRDeviceAddDialog::SDRDeviceAddDialog( wxWindow* parent ): SDRDeviceAddForm( parent ) {
diff --git a/src/forms/SDRDevices/SDRDeviceAdd.h b/src/forms/SDRDevices/SDRDeviceAdd.h
index 649d130..36fe0e7 100644
--- a/src/forms/SDRDevices/SDRDeviceAdd.h
+++ b/src/forms/SDRDevices/SDRDeviceAdd.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "SDRDeviceAddForm.h"
diff --git a/src/forms/SDRDevices/SDRDevices.cpp b/src/forms/SDRDevices/SDRDevices.cpp
index df81fb8..e0bd51e 100644
--- a/src/forms/SDRDevices/SDRDevices.cpp
+++ b/src/forms/SDRDevices/SDRDevices.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "SDRDevices.h"
 
 #include <wx/textdlg.h>
@@ -5,7 +8,11 @@
 
 #include "CubicSDR.h"
 
-SDRDevicesDialog::SDRDevicesDialog( wxWindow* parent ): devFrame( parent ) {
+#ifdef __linux__
+#include "CubicSDR.xpm"
+#endif
+
+SDRDevicesDialog::SDRDevicesDialog( wxWindow* parent ): devFrame( parent, wxID_ANY, wxT(CUBICSDR_INSTALL_NAME " :: SDR Devices")) {
     refresh = true;
     failed = false;
     m_refreshButton->Disable();
@@ -17,6 +24,13 @@ SDRDevicesDialog::SDRDevicesDialog( wxWindow* parent ): devFrame( parent ) {
     removeId = nullptr;
     devAddDialog = nullptr;
     dev = nullptr;
+
+#ifdef __linux__
+    SetIcon(wxICON(cubicsdr));
+#elif _WIN32
+    SetIcon(wxICON(frame_icon));
+#endif
+
 }
 
 void SDRDevicesDialog::OnClose( wxCloseEvent& /* event */) {
@@ -113,8 +127,8 @@ void SDRDevicesDialog::refreshDeviceProperties() {
         devSettings["name"] = m_propertyGrid->Append( new wxStringProperty("Name", wxPG_LABEL, devConfig->getDeviceName()) );
         devSettings["offset"] = m_propertyGrid->Append( new wxIntProperty("Offset (Hz)", wxPG_LABEL, devConfig->getOffset()) );
         
-        int currentSampleRate = wxGetApp().getSampleRate();
-        int deviceSampleRate = devConfig->getSampleRate();
+        long currentSampleRate = wxGetApp().getSampleRate();
+        long deviceSampleRate = devConfig->getSampleRate();
         
         if (!deviceSampleRate) {
             deviceSampleRate = selDev->getSampleRateNear(SOAPY_SDR_RX, 0, currentSampleRate);
@@ -315,7 +329,7 @@ void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) {
         wxGetApp().setDeviceArgs(settingArgs);
         wxGetApp().setStreamArgs(streamArgs);
         wxGetApp().setDevice(dev,0);
-                
+        wxGetApp().notifyMainUIOfDeviceChange();
         Close();
     }
     event.Skip();
@@ -435,13 +449,14 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) {
         DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId());
         
         std::string strRate = deviceArgs["sample_rate"].options[event.GetPropertyValue().GetInteger()];
-        int srate = 0;
+        long srate = 0;
         try {
-            srate = std::stoi(strRate);
+            srate = std::stol(strRate);
             devConfig->setSampleRate(srate);
             
             if (dev->isActive() || !wxGetApp().getDevice()) {
                 wxGetApp().setSampleRate(srate);
+                wxGetApp().notifyMainUIOfDeviceChange();
             }            
         } catch (std::invalid_argument e) {
             // nop
diff --git a/src/forms/SDRDevices/SDRDevices.h b/src/forms/SDRDevices/SDRDevices.h
index 199abbe..cbf2e18 100644
--- a/src/forms/SDRDevices/SDRDevices.h
+++ b/src/forms/SDRDevices/SDRDevices.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <map>
diff --git a/src/modules/modem/Modem.cpp b/src/modules/modem/Modem.cpp
index da782ee..daaf00f 100644
--- a/src/modules/modem/Modem.cpp
+++ b/src/modules/modem/Modem.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "Modem.h"
 #include "CubicSDR.h"
 
diff --git a/src/modules/modem/Modem.h b/src/modules/modem/Modem.h
index 423c65b..a22166d 100644
--- a/src/modules/modem/Modem.h
+++ b/src/modules/modem/Modem.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "liquid/liquid.h"
diff --git a/src/modules/modem/ModemAnalog.cpp b/src/modules/modem/ModemAnalog.cpp
index 14f0b3c..02d65c8 100644
--- a/src/modules/modem/ModemAnalog.cpp
+++ b/src/modules/modem/ModemAnalog.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemAnalog.h"
 
 ModemAnalog::ModemAnalog() : Modem(), aOutputCeil(1), aOutputCeilMA(1), aOutputCeilMAA(1) {
@@ -12,7 +15,7 @@ int ModemAnalog::checkSampleRate(long long sampleRate, int /* audioSampleRate */
     if (sampleRate < MIN_BANDWIDTH) {
         return MIN_BANDWIDTH;
     }
-    return sampleRate;
+    return (int)sampleRate;
 }
 
 ModemKit *ModemAnalog::buildKit(long long sampleRate, int audioSampleRate) {
@@ -24,7 +27,7 @@ ModemKit *ModemAnalog::buildKit(long long sampleRate, int audioSampleRate) {
     akit->sampleRate = sampleRate;
     akit->audioSampleRate = audioSampleRate;
     akit->audioResampleRatio = double(audioSampleRate) / double(sampleRate);
-    akit->audioResampler = msresamp_rrrf_create(akit->audioResampleRatio, As);
+    akit->audioResampler = msresamp_rrrf_create((float)akit->audioResampleRatio, As);
     
     return akit;
 }
@@ -82,7 +85,7 @@ void ModemAnalog::buildAudioOutput(ModemKitAnalog *akit, AudioThreadInput *audio
         }
     }
     
-    msresamp_rrrf_execute(akit->audioResampler, &demodOutputData[0], demodOutputData.size(), &resampledOutputData[0], &numAudioWritten);
+    msresamp_rrrf_execute(akit->audioResampler, &demodOutputData[0], (int)demodOutputData.size(), &resampledOutputData[0], &numAudioWritten);
     
     audioOut->channels = 1;
     audioOut->sampleRate = akit->audioSampleRate;
diff --git a/src/modules/modem/ModemAnalog.h b/src/modules/modem/ModemAnalog.h
index be19159..153c3c9 100644
--- a/src/modules/modem/ModemAnalog.h
+++ b/src/modules/modem/ModemAnalog.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "Modem.h"
 
diff --git a/src/modules/modem/ModemDigital.cpp b/src/modules/modem/ModemDigital.cpp
index 3723d15..401d97f 100644
--- a/src/modules/modem/ModemDigital.cpp
+++ b/src/modules/modem/ModemDigital.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemDigital.h"
 
 
@@ -23,7 +26,7 @@ int ModemDigital::checkSampleRate(long long sampleRate, int /* audioSampleRate *
     if (sampleRate < MIN_BANDWIDTH) {
         return MIN_BANDWIDTH;
     }
-    return sampleRate;
+    return (int)sampleRate;
 }
 
 ModemKit *ModemDigital::buildKit(long long sampleRate, int audioSampleRate) {
diff --git a/src/modules/modem/ModemDigital.h b/src/modules/modem/ModemDigital.h
index e1991c1..05722e8 100644
--- a/src/modules/modem/ModemDigital.h
+++ b/src/modules/modem/ModemDigital.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "Modem.h"
 #include <map>
diff --git a/src/modules/modem/analog/ModemAM.cpp b/src/modules/modem/analog/ModemAM.cpp
index ab0aac0..03da7e8 100644
--- a/src/modules/modem/analog/ModemAM.cpp
+++ b/src/modules/modem/analog/ModemAM.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemAM.h"
 
 ModemAM::ModemAM() : ModemAnalog() {
diff --git a/src/modules/modem/analog/ModemAM.h b/src/modules/modem/analog/ModemAM.h
index 9da1ca2..af165b9 100644
--- a/src/modules/modem/analog/ModemAM.h
+++ b/src/modules/modem/analog/ModemAM.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "Modem.h"
 #include "ModemAnalog.h"
diff --git a/src/modules/modem/analog/ModemDSB.cpp b/src/modules/modem/analog/ModemDSB.cpp
index 2cfe35c..37f5b26 100644
--- a/src/modules/modem/analog/ModemDSB.cpp
+++ b/src/modules/modem/analog/ModemDSB.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemDSB.h"
 
 ModemDSB::ModemDSB() : ModemAnalog() {
diff --git a/src/modules/modem/analog/ModemDSB.h b/src/modules/modem/analog/ModemDSB.h
index 4a80784..2645d13 100644
--- a/src/modules/modem/analog/ModemDSB.h
+++ b/src/modules/modem/analog/ModemDSB.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "Modem.h"
 #include "ModemAnalog.h"
diff --git a/src/modules/modem/analog/ModemFM.cpp b/src/modules/modem/analog/ModemFM.cpp
index 933e6cf..da969b8 100644
--- a/src/modules/modem/analog/ModemFM.cpp
+++ b/src/modules/modem/analog/ModemFM.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemFM.h"
 
 ModemFM::ModemFM() : ModemAnalog() {
@@ -30,7 +33,7 @@ void ModemFM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *au
         return;
     }
     
-    freqdem_demodulate_block(demodFM, &input->data[0], bufSize, &demodOutputData[0]);
+    freqdem_demodulate_block(demodFM, &input->data[0], (int)bufSize, &demodOutputData[0]);
 
     buildAudioOutput(fmkit, audioOut, false);
 }
diff --git a/src/modules/modem/analog/ModemFM.h b/src/modules/modem/analog/ModemFM.h
index a62e729..2e08a1a 100644
--- a/src/modules/modem/analog/ModemFM.h
+++ b/src/modules/modem/analog/ModemFM.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "Modem.h"
 #include "ModemAnalog.h"
diff --git a/src/modules/modem/analog/ModemFMStereo.cpp b/src/modules/modem/analog/ModemFMStereo.cpp
index f98fa21..e84c49b 100644
--- a/src/modules/modem/analog/ModemFMStereo.cpp
+++ b/src/modules/modem/analog/ModemFMStereo.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemFMStereo.h"
 
 ModemFMStereo::ModemFMStereo() {
@@ -27,7 +30,7 @@ int ModemFMStereo::checkSampleRate(long long sampleRate, int /* audioSampleRate
     } else if (sampleRate < 1500) {
         return 1500;
     } else {
-        return sampleRate;
+        return (int)sampleRate;
     }
 }
 
@@ -92,13 +95,13 @@ ModemKit *ModemFMStereo::buildKit(long long sampleRate, int audioSampleRate) {
    
     float As = 60.0f;         // stop-band attenuation [dB]
     
-    kit->audioResampler = msresamp_rrrf_create(kit->audioResampleRatio, As);
-    kit->stereoResampler = msresamp_rrrf_create(kit->audioResampleRatio, As);
+    kit->audioResampler = msresamp_rrrf_create((float)kit->audioResampleRatio, As);
+    kit->stereoResampler = msresamp_rrrf_create((float)kit->audioResampleRatio, As);
     
     // Stereo filters / shifters
-    double firStereoCutoff = 16000.0 / double(audioSampleRate);
+    float firStereoCutoff = 16000.0f / float(audioSampleRate);
     // filter transition
-    float ft = 1000.0f / double(audioSampleRate);
+    float ft = 1000.0f / float(audioSampleRate);
     // fractional timing offset
     float mu = 0.0f;
     
@@ -139,13 +142,13 @@ ModemKit *ModemFMStereo::buildKit(long long sampleRate, int audioSampleRate) {
     kit->demph = _demph;
     
     if (_demph) {
-        float f = (1.0f / (2.0f * M_PI * double(_demph) * 1e-6));
-        float t = 1.0f / (2.0f * M_PI * f);
-        t = 1.0f / (2.0f * float(audioSampleRate) * tan(1.0f / (2.0f * float(audioSampleRate) * t)));
+        double f = (1.0 / (2.0 * M_PI * double(_demph) * 1e-6));
+        double t = 1.0 / (2.0 * M_PI * f);
+        t = 1.0 / (2.0 * double(audioSampleRate) * tan(1.0 / (2.0 * double(audioSampleRate) * t)));
         
-        float tb = (1.0f + 2.0f * t * float(audioSampleRate));
-        float b_demph[2] = { 1.0f / tb, 1.0f / tb };
-        float a_demph[2] = { 1.0f, (1.0f - 2.0f * t * float(audioSampleRate)) / tb };
+        double tb = (1.0 + 2.0 * t * double(audioSampleRate));
+        float b_demph[2] = { (float)(1.0 / tb), (float)(1.0 / tb) };
+        float a_demph[2] = { 1.0, (float)((1.0 - 2.0 * t * double(audioSampleRate)) / tb) };
         
         kit->iirDemphL = iirfilt_rrrf_create(b_demph, 2, a_demph, 2);
         kit->iirDemphR = iirfilt_rrrf_create(b_demph, 2, a_demph, 2);
@@ -188,7 +191,7 @@ void ModemFMStereo::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInp
     
     size_t audio_out_size = (size_t)ceil((double) (bufSize) * audio_resample_ratio) + 512;
     
-    freqdem_demodulate_block(demodFM, &input->data[0], bufSize, &demodOutputData[0]);
+    freqdem_demodulate_block(demodFM, &input->data[0], (int)bufSize, &demodOutputData[0]);
     
     if (resampledOutputData.size() != audio_out_size) {
         if (resampledOutputData.capacity() < audio_out_size) {
@@ -199,7 +202,7 @@ void ModemFMStereo::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInp
     
     unsigned int numAudioWritten;
     
-    msresamp_rrrf_execute(fmkit->audioResampler, &demodOutputData[0], bufSize, &resampledOutputData[0], &numAudioWritten);
+    msresamp_rrrf_execute(fmkit->audioResampler, &demodOutputData[0], (int)bufSize, &resampledOutputData[0], &numAudioWritten);
     
     if (demodStereoData.size() != bufSize) {
         if (demodStereoData.capacity() < bufSize) {
@@ -249,7 +252,7 @@ void ModemFMStereo::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInp
         resampledStereoData.resize(audio_out_size);
     }
     
-    msresamp_rrrf_execute(fmkit->stereoResampler, &demodStereoData[0], bufSize, &resampledStereoData[0], &numAudioWritten);
+    msresamp_rrrf_execute(fmkit->stereoResampler, &demodStereoData[0], (int)bufSize, &resampledStereoData[0], &numAudioWritten);
     
     audioOut->channels = 2;
     if (audioOut->data.capacity() < (numAudioWritten * 2)) {
diff --git a/src/modules/modem/analog/ModemFMStereo.h b/src/modules/modem/analog/ModemFMStereo.h
index ccba7dd..1c8bd85 100644
--- a/src/modules/modem/analog/ModemFMStereo.h
+++ b/src/modules/modem/analog/ModemFMStereo.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "Modem.h"
 
diff --git a/src/modules/modem/analog/ModemIQ.cpp b/src/modules/modem/analog/ModemIQ.cpp
index 78bfddd..9fa3ec7 100644
--- a/src/modules/modem/analog/ModemIQ.cpp
+++ b/src/modules/modem/analog/ModemIQ.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemIQ.h"
 
 ModemIQ::ModemIQ() {
diff --git a/src/modules/modem/analog/ModemIQ.h b/src/modules/modem/analog/ModemIQ.h
index e3d8901..30a551b 100644
--- a/src/modules/modem/analog/ModemIQ.h
+++ b/src/modules/modem/analog/ModemIQ.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "Modem.h"
 
diff --git a/src/modules/modem/analog/ModemLSB.cpp b/src/modules/modem/analog/ModemLSB.cpp
index 2d704cf..140a90a 100644
--- a/src/modules/modem/analog/ModemLSB.cpp
+++ b/src/modules/modem/analog/ModemLSB.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemLSB.h"
 
 ModemLSB::ModemLSB() : ModemAnalog() {
@@ -9,7 +12,7 @@ ModemLSB::ModemLSB() : ModemAnalog() {
     ssbFilt = iirfilt_crcf_create_lowpass(6, 0.25);
 #endif
 	ssbShift = nco_crcf_create(LIQUID_NCO);
-    nco_crcf_set_frequency(ssbShift,  (2.0 * M_PI) * 0.25);
+    nco_crcf_set_frequency(ssbShift,  (float)((2.0 * M_PI) * 0.25));
     c2rFilt = firhilbf_create(5, 90.0);
     useSignalOutput(true);
 }
@@ -38,9 +41,9 @@ int ModemLSB::checkSampleRate(long long sampleRate, int /* audioSampleRate */) {
         return MIN_BANDWIDTH;
     }
     if (sampleRate % 2 == 0) {
-        return sampleRate;
+        return (int)sampleRate;
     }
-    return sampleRate+1;
+    return (int)(sampleRate+1);
 }
 
 int ModemLSB::getDefaultSampleRate() {
diff --git a/src/modules/modem/analog/ModemLSB.h b/src/modules/modem/analog/ModemLSB.h
index e787dda..8834313 100644
--- a/src/modules/modem/analog/ModemLSB.h
+++ b/src/modules/modem/analog/ModemLSB.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemAnalog.h"
 
diff --git a/src/modules/modem/analog/ModemNBFM.cpp b/src/modules/modem/analog/ModemNBFM.cpp
index 5dc77a6..fd1b255 100644
--- a/src/modules/modem/analog/ModemNBFM.cpp
+++ b/src/modules/modem/analog/ModemNBFM.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemNBFM.h"
 
 ModemNBFM::ModemNBFM() : ModemAnalog() {
@@ -30,7 +33,7 @@ void ModemNBFM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *
         return;
     }
     
-    freqdem_demodulate_block(demodFM, &input->data[0], bufSize, &demodOutputData[0]);
+    freqdem_demodulate_block(demodFM, &input->data[0], (unsigned int)bufSize, &demodOutputData[0]);
 
     buildAudioOutput(fmkit, audioOut, false);
 }
diff --git a/src/modules/modem/analog/ModemNBFM.h b/src/modules/modem/analog/ModemNBFM.h
index 7d15cdb..52d34ad 100644
--- a/src/modules/modem/analog/ModemNBFM.h
+++ b/src/modules/modem/analog/ModemNBFM.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "Modem.h"
 #include "ModemAnalog.h"
diff --git a/src/modules/modem/analog/ModemUSB.cpp b/src/modules/modem/analog/ModemUSB.cpp
index 534974e..f221f2c 100644
--- a/src/modules/modem/analog/ModemUSB.cpp
+++ b/src/modules/modem/analog/ModemUSB.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemUSB.h"
 
 ModemUSB::ModemUSB() : ModemAnalog() {
@@ -9,7 +12,7 @@ ModemUSB::ModemUSB() : ModemAnalog() {
 	ssbFilt = iirfilt_crcf_create_lowpass(6, 0.25);
 #endif
 	ssbShift = nco_crcf_create(LIQUID_NCO);
-    nco_crcf_set_frequency(ssbShift,  (2.0f * M_PI) * 0.25f);
+    nco_crcf_set_frequency(ssbShift,  (float)((2.0 * M_PI) * 0.25));
     c2rFilt = firhilbf_create(5, 90.0);
     useSignalOutput(true);
 }
@@ -38,9 +41,9 @@ int ModemUSB::checkSampleRate(long long sampleRate, int /* audioSampleRate */) {
         return MIN_BANDWIDTH;
     }
     if (sampleRate % 2 == 0) {
-        return sampleRate;
+        return (int)sampleRate;
     }
-    return sampleRate+1;
+    return (int)(sampleRate+1);
 }
 
 int ModemUSB::getDefaultSampleRate() {
diff --git a/src/modules/modem/analog/ModemUSB.h b/src/modules/modem/analog/ModemUSB.h
index f9dec65..e7ae61d 100644
--- a/src/modules/modem/analog/ModemUSB.h
+++ b/src/modules/modem/analog/ModemUSB.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemAnalog.h"
 
diff --git a/src/modules/modem/digital/ModemAPSK.cpp b/src/modules/modem/digital/ModemAPSK.cpp
index d67bd3a..2533e91 100644
--- a/src/modules/modem/digital/ModemAPSK.cpp
+++ b/src/modules/modem/digital/ModemAPSK.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemAPSK.h"
 
 ModemAPSK::ModemAPSK() : ModemDigital() {
diff --git a/src/modules/modem/digital/ModemAPSK.h b/src/modules/modem/digital/ModemAPSK.h
index e3dabb5..4ebf379 100644
--- a/src/modules/modem/digital/ModemAPSK.h
+++ b/src/modules/modem/digital/ModemAPSK.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/modules/modem/digital/ModemASK.cpp b/src/modules/modem/digital/ModemASK.cpp
index b1e87c6..766eec9 100644
--- a/src/modules/modem/digital/ModemASK.cpp
+++ b/src/modules/modem/digital/ModemASK.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemASK.h"
 
 ModemASK::ModemASK() : ModemDigital()  {
diff --git a/src/modules/modem/digital/ModemASK.h b/src/modules/modem/digital/ModemASK.h
index a672d14..af9f1d1 100644
--- a/src/modules/modem/digital/ModemASK.h
+++ b/src/modules/modem/digital/ModemASK.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/modules/modem/digital/ModemBPSK.cpp b/src/modules/modem/digital/ModemBPSK.cpp
index 59cb4f1..eef810c 100644
--- a/src/modules/modem/digital/ModemBPSK.cpp
+++ b/src/modules/modem/digital/ModemBPSK.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemBPSK.h"
 
 ModemBPSK::ModemBPSK() : ModemDigital()  {
diff --git a/src/modules/modem/digital/ModemBPSK.h b/src/modules/modem/digital/ModemBPSK.h
index f986ca4..70719c3 100644
--- a/src/modules/modem/digital/ModemBPSK.h
+++ b/src/modules/modem/digital/ModemBPSK.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/modules/modem/digital/ModemDPSK.cpp b/src/modules/modem/digital/ModemDPSK.cpp
index 4927183..f8dbbaa 100644
--- a/src/modules/modem/digital/ModemDPSK.cpp
+++ b/src/modules/modem/digital/ModemDPSK.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemDPSK.h"
 
 ModemDPSK::ModemDPSK() : ModemDigital() {
diff --git a/src/modules/modem/digital/ModemDPSK.h b/src/modules/modem/digital/ModemDPSK.h
index ffeb5e3..dbb24a0 100644
--- a/src/modules/modem/digital/ModemDPSK.h
+++ b/src/modules/modem/digital/ModemDPSK.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/modules/modem/digital/ModemFSK.cpp b/src/modules/modem/digital/ModemFSK.cpp
index 8db47f8..d152a8e 100644
--- a/src/modules/modem/digital/ModemFSK.cpp
+++ b/src/modules/modem/digital/ModemFSK.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemFSK.h"
 #include <iomanip>
 
@@ -5,7 +8,7 @@ ModemFSK::ModemFSK() : ModemDigital()  {
     // DMR defaults?
     bps = 1;
     sps = 9600;
-    bw = 0.45;
+    bw = 0.45f;
     outStream << std::hex;
 }
 
@@ -14,12 +17,12 @@ ModemBase *ModemFSK::factory() {
 }
 
 int ModemFSK::checkSampleRate(long long sampleRate, int audioSampleRate) {
-    float minSps = pow(2.0,bps);
-    float nextSps = (float(sampleRate) / float(sps));
+    double minSps = pow(2.0,bps);
+    double nextSps = (double(sampleRate) / double(sps));
     if (nextSps < minSps) {
         return 2 * bps * sps;
     } else {
-        return sampleRate;
+        return (int)sampleRate;
     }
 }
 
@@ -98,7 +101,7 @@ std::string ModemFSK::readSetting(std::string setting) {
 ModemKit *ModemFSK::buildKit(long long sampleRate, int audioSampleRate) {
     ModemKitFSK *dkit = new ModemKitFSK;
     dkit->m           = bps;
-    dkit->k           = sampleRate / sps;
+    dkit->k           = (unsigned int)(sampleRate / sps);
     dkit->bw          = bw;
 
     dkit->demodFSK = fskdem_create(dkit->m, dkit->k, dkit->bw);
diff --git a/src/modules/modem/digital/ModemFSK.h b/src/modules/modem/digital/ModemFSK.h
index e8cbcce..6f8846e 100644
--- a/src/modules/modem/digital/ModemFSK.h
+++ b/src/modules/modem/digital/ModemFSK.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 #include <sstream>
diff --git a/src/modules/modem/digital/ModemGMSK.cpp b/src/modules/modem/digital/ModemGMSK.cpp
index e7ec132..c1a79fb 100644
--- a/src/modules/modem/digital/ModemGMSK.cpp
+++ b/src/modules/modem/digital/ModemGMSK.cpp
@@ -1,10 +1,13 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemGMSK.h"
 #include <iomanip>
 
 ModemGMSK::ModemGMSK() : ModemDigital()  {
     _sps = 4;
     _fdelay = 3;
-    _ebf = 0.3;
+    _ebf = 0.3f;
     outStream << std::hex;
 }
 
@@ -24,7 +27,7 @@ int ModemGMSK::checkSampleRate(long long sampleRate, int audioSampleRate) {
     if (sampleRate < MIN_BANDWIDTH) {
         return MIN_BANDWIDTH;
     }
-    return sampleRate;
+    return (int)sampleRate;
 }
 
 int ModemGMSK::getDefaultSampleRate() {
diff --git a/src/modules/modem/digital/ModemGMSK.h b/src/modules/modem/digital/ModemGMSK.h
index 714670b..6b82c0d 100644
--- a/src/modules/modem/digital/ModemGMSK.h
+++ b/src/modules/modem/digital/ModemGMSK.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 #include <sstream>
diff --git a/src/modules/modem/digital/ModemOOK.cpp b/src/modules/modem/digital/ModemOOK.cpp
index 36f90ed..fce95a6 100644
--- a/src/modules/modem/digital/ModemOOK.cpp
+++ b/src/modules/modem/digital/ModemOOK.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemOOK.h"
 
 ModemOOK::ModemOOK() : ModemDigital()  {
@@ -20,7 +23,7 @@ int ModemOOK::checkSampleRate(long long sampleRate, int audioSampleRate) {
     if (sampleRate < 100) {
         return 100;
     }
-    return sampleRate;
+    return (int)sampleRate;
 }
 
 void ModemOOK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) {
diff --git a/src/modules/modem/digital/ModemOOK.h b/src/modules/modem/digital/ModemOOK.h
index d4396e3..03b5a4b 100644
--- a/src/modules/modem/digital/ModemOOK.h
+++ b/src/modules/modem/digital/ModemOOK.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/modules/modem/digital/ModemPSK.cpp b/src/modules/modem/digital/ModemPSK.cpp
index c1e66bc..05ac291 100644
--- a/src/modules/modem/digital/ModemPSK.cpp
+++ b/src/modules/modem/digital/ModemPSK.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemPSK.h"
 
 ModemPSK::ModemPSK() : ModemDigital()  {
diff --git a/src/modules/modem/digital/ModemPSK.h b/src/modules/modem/digital/ModemPSK.h
index 7732342..5f9880a 100644
--- a/src/modules/modem/digital/ModemPSK.h
+++ b/src/modules/modem/digital/ModemPSK.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/modules/modem/digital/ModemQAM.cpp b/src/modules/modem/digital/ModemQAM.cpp
index 84bd203..e2c500d 100644
--- a/src/modules/modem/digital/ModemQAM.cpp
+++ b/src/modules/modem/digital/ModemQAM.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemQAM.h"
 
 ModemQAM::ModemQAM() : ModemDigital()  {
diff --git a/src/modules/modem/digital/ModemQAM.h b/src/modules/modem/digital/ModemQAM.h
index 21da247..4260596 100644
--- a/src/modules/modem/digital/ModemQAM.h
+++ b/src/modules/modem/digital/ModemQAM.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/modules/modem/digital/ModemQPSK.cpp b/src/modules/modem/digital/ModemQPSK.cpp
index 6164af5..2b35994 100644
--- a/src/modules/modem/digital/ModemQPSK.cpp
+++ b/src/modules/modem/digital/ModemQPSK.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemQPSK.h"
 
 ModemQPSK::ModemQPSK() : ModemDigital()  {
diff --git a/src/modules/modem/digital/ModemQPSK.h b/src/modules/modem/digital/ModemQPSK.h
index b532e38..c510a54 100644
--- a/src/modules/modem/digital/ModemQPSK.h
+++ b/src/modules/modem/digital/ModemQPSK.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/modules/modem/digital/ModemSQAM.cpp b/src/modules/modem/digital/ModemSQAM.cpp
index fee4b2f..20dc445 100644
--- a/src/modules/modem/digital/ModemSQAM.cpp
+++ b/src/modules/modem/digital/ModemSQAM.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemSQAM.h"
 
 ModemSQAM::ModemSQAM() : ModemDigital()  {
diff --git a/src/modules/modem/digital/ModemSQAM.h b/src/modules/modem/digital/ModemSQAM.h
index 46db613..fcd2b92 100644
--- a/src/modules/modem/digital/ModemSQAM.h
+++ b/src/modules/modem/digital/ModemSQAM.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/modules/modem/digital/ModemST.cpp b/src/modules/modem/digital/ModemST.cpp
index 993cd03..409a1a7 100644
--- a/src/modules/modem/digital/ModemST.cpp
+++ b/src/modules/modem/digital/ModemST.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModemST.h"
 
 ModemST::ModemST() : ModemDigital()  {
diff --git a/src/modules/modem/digital/ModemST.h b/src/modules/modem/digital/ModemST.h
index 76b62ea..cf74e69 100644
--- a/src/modules/modem/digital/ModemST.h
+++ b/src/modules/modem/digital/ModemST.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 #include "ModemDigital.h"
 
diff --git a/src/panel/MeterPanel.cpp b/src/panel/MeterPanel.cpp
index 54cbe12..c3d191f 100644
--- a/src/panel/MeterPanel.cpp
+++ b/src/panel/MeterPanel.cpp
@@ -1,3 +1,5 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
 
 #include "MeterPanel.h"
 #include "ColorTheme.h"
@@ -40,14 +42,14 @@ MeterPanel::MeterPanel(std::string name, float low, float high, float current) {
     
     addChild(&bgPanel);
     
-    labelPanel.setSize(1.0, 0.1);
+    labelPanel.setSize(1.0f, 0.1f);
     labelPanel.setPosition(0.0, 1.0);
     labelPanel.setText(name,GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER, true);
     labelPanel.setFill(GLPanel::GLPANEL_FILL_NONE);
     
     addChild(&labelPanel);
     
-    valuePanel.setSize(1.0, 0.1);
+    valuePanel.setSize(1.0f, 0.1f);
     valuePanel.setPosition(0.0, -1.0);
     
     setValueLabel(std::to_string(int(current)));
diff --git a/src/panel/MeterPanel.h b/src/panel/MeterPanel.h
index 75781d7..21b4c58 100644
--- a/src/panel/MeterPanel.h
+++ b/src/panel/MeterPanel.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "GLPanel.h"
diff --git a/src/panel/ScopePanel.cpp b/src/panel/ScopePanel.cpp
index f83e00c..bde35cd 100644
--- a/src/panel/ScopePanel.cpp
+++ b/src/panel/ScopePanel.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ScopePanel.h"
 #include "ColorTheme.h"
 
diff --git a/src/panel/ScopePanel.h b/src/panel/ScopePanel.h
index 2ce9224..1829203 100644
--- a/src/panel/ScopePanel.h
+++ b/src/panel/ScopePanel.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "GLPanel.h"
diff --git a/src/panel/SpectrumPanel.cpp b/src/panel/SpectrumPanel.cpp
index 60dfcd3..780f442 100644
--- a/src/panel/SpectrumPanel.cpp
+++ b/src/panel/SpectrumPanel.cpp
@@ -1,15 +1,20 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "SpectrumPanel.h"
 
 #include <sstream>
 #include <iostream>
 #include <iomanip>
+#include "CubicSDR.h"
 #include "ColorTheme.h"
 #include "CubicSDRDefs.h"
 
 SpectrumPanel::SpectrumPanel() {
     floorValue = 0;
     ceilValue = 1;
-    showDb = false;
+    showDb = true;
+    useDbOfs = true;
     fftSize = DEFAULT_FFT_SIZE;
     bandwidth = DEFAULT_DEMOD_BW;
     freq = 0;
@@ -83,6 +88,14 @@ bool SpectrumPanel::getShowDb() {
     return showDb;
 }
 
+void SpectrumPanel::setUseDBOffset(bool useOfs) {
+    this->useDbOfs = useOfs;
+}
+
+bool SpectrumPanel::getUseDBOffset() {
+    return useDbOfs;
+}
+
 
 void SpectrumPanel::setPoints(std::vector<float> &points) {
     this->points.assign(points.begin(), points.end());
@@ -269,11 +282,11 @@ void SpectrumPanel::drawPanelContents() {
     if (showDb) {
         float dbPanelWidth = (1.0 / viewWidth)*88.0 * GLFont::getScaleFactor();
         float dbPanelHeight = (1.0/viewHeight)*14.0 * GLFont::getScaleFactor();
-        
+        float dbOfs = useDbOfs?wxGetApp().getConfig()->getDBOffset():0;
         
         std::stringstream ssLabel("");
         if (getCeilValue() != getFloorValue() && fftSize) {
-            ssLabel << std::fixed << std::setprecision(1) << (20.0 * log10(2.0*(getCeilValue())/(double)fftSize)) << "dB";
+            ssLabel << std::fixed << std::setprecision(1) << (dbOfs + 20.0 * log10(2.0*(getCeilValue())/(double)fftSize)) << "dB";
         }
         dbPanelCeil.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT);
         dbPanelCeil.setSize(dbPanelWidth, dbPanelHeight);
@@ -282,7 +295,7 @@ void SpectrumPanel::drawPanelContents() {
         
         ssLabel.str("");
         if (getCeilValue() != getFloorValue() && fftSize) {
-            ssLabel <<  (20.0 * log10(2.0*(getFloorValue())/(double)fftSize)) << "dB";
+            ssLabel <<  (dbOfs + 20.0 * log10(2.0*(getFloorValue())/(double)fftSize)) << "dB";
         }
 
         dbPanelFloor.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT);
diff --git a/src/panel/SpectrumPanel.h b/src/panel/SpectrumPanel.h
index 39aa3fe..3885adf 100644
--- a/src/panel/SpectrumPanel.h
+++ b/src/panel/SpectrumPanel.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "GLPanel.h"
@@ -26,7 +29,10 @@ public:
 
     void setShowDb(bool showDb);
     bool getShowDb();
-    
+
+    void setUseDBOffset(bool useOfs);
+    bool getUseDBOffset();
+
 protected:
     void drawPanelContents();
 
@@ -40,5 +46,5 @@ private:
     
     GLTextPanel dbPanelCeil;
     GLTextPanel dbPanelFloor;
-    bool showDb;
-};
\ No newline at end of file
+    bool showDb, useDbOfs;
+};
diff --git a/src/panel/WaterfallPanel.cpp b/src/panel/WaterfallPanel.cpp
index eab8a7b..20b6fa1 100644
--- a/src/panel/WaterfallPanel.cpp
+++ b/src/panel/WaterfallPanel.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "WaterfallPanel.h"
 
 WaterfallPanel::WaterfallPanel() : GLPanel(), fft_size(0), waterfall_lines(0), waterfall_slice(NULL), activeTheme(NULL) {
@@ -36,6 +39,7 @@ void WaterfallPanel::refreshTheme() {
 void WaterfallPanel::setPoints(std::vector<float> &points) {
     size_t halfPts = points.size()/2;
     if (halfPts == fft_size) {
+       
         for (unsigned int i = 0; i < fft_size; i++) {
             this->points[i] = points[i*2+1];
         }
@@ -99,6 +103,10 @@ void WaterfallPanel::update() {
         
         unsigned char *waterfall_tex;
         
+        //Creates 2x 2D textures into card memory.
+        //of size half_fft_size * waterfall_lines, which can be BIG.
+        //The limit of the size of Waterfall is the size of the maximum supported 2D texture 
+        //by the graphic card. (half_fft_size * waterfall_lines, i.e DEFAULT_DEMOD_WATERFALL_LINES_NB * DEFAULT_FFT_SIZE/2)
         waterfall_tex = new unsigned char[half_fft_size * waterfall_lines];
         memset(waterfall_tex, 0, half_fft_size * waterfall_lines);
         
diff --git a/src/panel/WaterfallPanel.h b/src/panel/WaterfallPanel.h
index 451358a..32170d0 100644
--- a/src/panel/WaterfallPanel.h
+++ b/src/panel/WaterfallPanel.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "GLPanel.h"
diff --git a/src/process/FFTDataDistributor.cpp b/src/process/FFTDataDistributor.cpp
index 140a50f..7a3c66d 100644
--- a/src/process/FFTDataDistributor.cpp
+++ b/src/process/FFTDataDistributor.cpp
@@ -1,11 +1,16 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "FFTDataDistributor.h"
+#include <algorithm>
 
 FFTDataDistributor::FFTDataDistributor() : outputBuffers("FFTDataDistributorBuffers"), fftSize(DEFAULT_FFT_SIZE), linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0) {
-    bufferedItems = 0;
+
 }
 
-void FFTDataDistributor::setFFTSize(unsigned int fftSize) {
-	this->fftSize = fftSize;
+void FFTDataDistributor::setFFTSize(unsigned int size) {
+	 
+    fftSize.store(size);
 }
 
 void FFTDataDistributor::setLinesPerSecond(unsigned int lines) {
@@ -26,25 +31,50 @@ void FFTDataDistributor::process() {
 		input->pop(inp);
 
 		if (inp) {
+            //Settings have changed, set new values and dump all previous samples stored in inputBuffer: 
 			if (inputBuffer.sampleRate != inp->sampleRate || inputBuffer.frequency != inp->frequency) {
-                
-                bufferMax = inp->sampleRate / 4;
+
+                //bufferMax must be at least fftSize (+ margin), else the waterfall get frozen, because no longer updated.
+                bufferMax = std::max((size_t)(inp->sampleRate * FFT_DISTRIBUTOR_BUFFER_IN_SECONDS), (size_t)(1.2 * fftSize.load()));
+
 //                std::cout << "Buffer Max: " << bufferMax << std::endl;
                 bufferOffset = 0;
-                
+                bufferedItems = 0;
 				inputBuffer.sampleRate = inp->sampleRate;
 				inputBuffer.frequency = inp->frequency;
                 inputBuffer.data.resize(bufferMax);
 			}
+
+            //adjust (bufferMax ; inputBuffer.data) in case of FFT size change only.
+            if (bufferMax < (size_t)(1.2 * fftSize.load())) {
+                bufferMax = (size_t)(1.2 * fftSize.load());
+                inputBuffer.data.resize(bufferMax);
+            }
+
+            size_t nbSamplesToAdd = inp->data.size();
+
+            //No room left in inputBuffer.data to accept inp->data.size() more samples.
+            //so make room by sliding left of bufferOffset, which is fine because 
+            //those samples has already been processed.
             if ((bufferOffset + bufferedItems + inp->data.size()) > bufferMax) {
                 memmove(&inputBuffer.data[0], &inputBuffer.data[bufferOffset], bufferedItems*sizeof(liquid_float_complex));
                 bufferOffset = 0;
-            } else {
-                memcpy(&inputBuffer.data[bufferOffset+bufferedItems],&inp->data[0],inp->data.size()*sizeof(liquid_float_complex));
-                bufferedItems += inp->data.size();
+                //if there are too much samples, we may even overflow !
+                //as a fallback strategy, drop the last incomming new samples not fitting in inputBuffer.data.
+                if (bufferedItems + inp->data.size() > bufferMax) {
+                    //clamp nbSamplesToAdd
+                    nbSamplesToAdd = bufferMax - bufferedItems;
+                    std::cout << "FFTDataDistributor::process() incoming samples overflow, dropping the last " << (inp->data.size() - nbSamplesToAdd) << " input samples..." << std::endl;
+                }
             }
+            
+            //store nbSamplesToAdd incoming samples. 
+            memcpy(&inputBuffer.data[bufferOffset+bufferedItems],&inp->data[0], nbSamplesToAdd *sizeof(liquid_float_complex));
+            bufferedItems += nbSamplesToAdd;
+            //
 			inp->decRefCount();
 		} else {
+            //empty inp, wait for another.
 			continue;
 		}
 
@@ -53,28 +83,32 @@ void FFTDataDistributor::process() {
 		// number of lines in input
 		double inputLines = (double)bufferedItems / (double)fftSize;
 
-		// ratio required to achieve the desired rate
+		// ratio required to achieve the desired rate:
+        // it means we can achieive 'lineRateStep' times the target linesPerSecond.
+        // < 1 means we cannot reach it by lack of samples.
 		double lineRateStep = ((double)linesPerSecond * inputTime)/(double)inputLines;
 
+        //we have enough samples to FFT at least one 'line' of 'fftSize' frequencies for display:
 		if (bufferedItems >= fftSize) {
-			int numProcessed = 0;
-
+			size_t numProcessed = 0;
 			if (lineRateAccum + (lineRateStep * ((double)bufferedItems/(double)fftSize)) < 1.0) {
 				// move along, nothing to see here..
 				lineRateAccum += (lineRateStep * ((double)bufferedItems/(double)fftSize));
 				numProcessed = bufferedItems;
 			} else {
-				for (unsigned int i = 0, iMax = bufferedItems; i < iMax; i += fftSize) {
+				for (size_t i = 0, iMax = bufferedItems; i < iMax; i += fftSize) {
 					if ((i + fftSize) > iMax) {
 						break;
 					}
 					lineRateAccum += lineRateStep;
 
 					if (lineRateAccum >= 1.0) {
+                        //each i represents a FFT computation
 						DemodulatorThreadIQData *outp = outputBuffers.getBuffer();
 						outp->frequency = inputBuffer.frequency;
 						outp->sampleRate = inputBuffer.sampleRate;
-						outp->data.assign(inputBuffer.data.begin()+bufferOffset+i,inputBuffer.data.begin()+bufferOffset+i+fftSize);
+						outp->data.assign(inputBuffer.data.begin()+bufferOffset+i,
+                                          inputBuffer.data.begin()+bufferOffset+i+ fftSize);
 						distribute(outp);
 
 						while (lineRateAccum >= 1.0) {
@@ -83,16 +117,19 @@ void FFTDataDistributor::process() {
 					}
 
 					numProcessed += fftSize;
-				}
+				} //end for 
 			}
+            //advance bufferOffset read pointer, 
+            //reduce size of bufferedItems.
 			if (numProcessed) {
                 bufferedItems -= numProcessed;
                 bufferOffset += numProcessed;
             }
+            //clamp to zero the number of remaining items.
             if (bufferedItems <= 0) {
                 bufferedItems = 0;
                 bufferOffset = 0;
             }
-		}
-	}
+		} //end if bufferedItems >= fftSize
+	} //en while
 }
diff --git a/src/process/FFTDataDistributor.h b/src/process/FFTDataDistributor.h
index da58828..9f60288 100644
--- a/src/process/FFTDataDistributor.h
+++ b/src/process/FFTDataDistributor.h
@@ -1,24 +1,31 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "VisualProcessor.h"
 #include "DemodDefs.h"
 #include <cmath>
 #include <cstring>
+#include <atomic>
 
 class FFTDataDistributor : public VisualProcessor<DemodulatorThreadIQData, DemodulatorThreadIQData> {
 public:
     FFTDataDistributor();
-    void setFFTSize(unsigned int fftSize);
+    void setFFTSize(unsigned int size);
     void setLinesPerSecond(unsigned int lines);
     unsigned int getLinesPerSecond();
 
 protected:
-    void process();
+    virtual void process();
     
     DemodulatorThreadIQData inputBuffer, tempBuffer;
     ReBuffer<DemodulatorThreadIQData> outputBuffers;
-    unsigned int fftSize;
+    std::atomic<unsigned int> fftSize;
+   
     unsigned int linesPerSecond;
     double lineRateAccum;
-    size_t bufferMax, bufferOffset, bufferedItems;
+    size_t bufferMax = 0;
+    size_t bufferOffset = 0;
+    size_t bufferedItems = 0;
 };
diff --git a/src/process/FFTVisualDataThread.cpp b/src/process/FFTVisualDataThread.cpp
index 8eb9186..44e303d 100644
--- a/src/process/FFTVisualDataThread.cpp
+++ b/src/process/FFTVisualDataThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "FFTVisualDataThread.h"
 #include "CubicSDR.h"
 
@@ -27,10 +30,18 @@ void FFTVisualDataThread::run() {
     DemodulatorThreadInputQueue *pipeIQDataIn = static_cast<DemodulatorThreadInputQueue *>(getInputQueue("IQDataInput"));
     SpectrumVisualDataQueue *pipeFFTDataOut = static_cast<SpectrumVisualDataQueue *>(getOutputQueue("FFTDataOutput"));
     
-    fftQueue.set_max_num_items(100);
+
+    fftQueue.set_max_num_items(100); 
     pipeFFTDataOut->set_max_num_items(100);
+
+    //FFT distributor plumbing:
+    // IQDataInput push samples to process to FFT Data distributor. 
     fftDistrib.setInput(pipeIQDataIn);
+
+    //The FFT distributor has actually 1 output only, so it doesn't distribute at all :) 
     fftDistrib.attachOutput(&fftQueue);
+    
+    //FFT Distributor output is ==> SpectrumVisualProcessor input.
     wproc.setInput(&fftQueue);
     wproc.attachOutput(pipeFFTDataOut);
     wproc.setup(DEFAULT_FFT_SIZE);
@@ -39,7 +50,9 @@ void FFTVisualDataThread::run() {
     
     while(!stopping) {
         
-        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        //this if fed by FFTDataDistributor which has a buffer of FFT_DISTRIBUTOR_BUFFER_IN_SECONDS
+        //so sleep for << FFT_DISTRIBUTOR_BUFFER_IN_SECONDS not to be overflown
+        std::this_thread::sleep_for(std::chrono::milliseconds((int)(FFT_DISTRIBUTOR_BUFFER_IN_SECONDS * 1000.0 / 25.0)));
 //        std::this_thread::yield();
         
         int fftSize = wproc.getDesiredInputSize();
@@ -56,8 +69,11 @@ void FFTVisualDataThread::run() {
             lpsChanged.store(false);
         }
         
+        //Make FFT Distributor process IQ samples
+        //and package them into ready-to-FFT sample sets (representing 1 line) by wproc
         fftDistrib.run();
-        
+      
+        // Make wproc do a FFT of each of the sample sets provided by fftDistrib: 
         while (!wproc.isInputEmpty()) {
             wproc.run();
         }
diff --git a/src/process/FFTVisualDataThread.h b/src/process/FFTVisualDataThread.h
index bb16be4..55ff408 100644
--- a/src/process/FFTVisualDataThread.h
+++ b/src/process/FFTVisualDataThread.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "IOThread.h"
diff --git a/src/process/ScopeVisualProcessor.cpp b/src/process/ScopeVisualProcessor.cpp
index e5d57fc..5b83b07 100644
--- a/src/process/ScopeVisualProcessor.cpp
+++ b/src/process/ScopeVisualProcessor.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ScopeVisualProcessor.h"
 #include <cstring>
 #include <string>
@@ -8,7 +11,7 @@ ScopeVisualProcessor::ScopeVisualProcessor(): outputBuffers("ScopeVisualProcesso
     fft_average_rate = 0.65f;
 	fft_ceil_ma = fft_ceil_maa = 0;
 	fft_floor_ma = fft_floor_maa = 0;
-    maxScopeSamples = 1024;
+    maxScopeSamples = DEFAULT_DMOD_FFT_SIZE;
     fftPlan = nullptr;
 }
 
diff --git a/src/process/ScopeVisualProcessor.h b/src/process/ScopeVisualProcessor.h
index ed85354..de0732c 100644
--- a/src/process/ScopeVisualProcessor.h
+++ b/src/process/ScopeVisualProcessor.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "VisualProcessor.h"
@@ -7,7 +10,7 @@
 class ScopeRenderData: public ReferenceCounter {
 public:
 	std::vector<float> waveform_points;
-    ScopePanel::ScopeMode mode;
+    ScopePanel::ScopeMode mode = ScopePanel::SCOPE_MODE_Y;
     int inputRate;
     int sampleRate;
 	int channels;
@@ -26,7 +29,7 @@ public:
     void setScopeEnabled(bool scopeEnable);
     void setSpectrumEnabled(bool spectrumEnable);
 protected:
-    void process();
+    virtual void process();
     ReBuffer<ScopeRenderData> outputBuffers;
 
     std::atomic_bool scopeEnabled;
@@ -36,7 +39,7 @@ protected:
     std::vector<liquid_float_complex> fftOutput;
     fftplan fftPlan;
     
-    unsigned int fftSize;
+    unsigned int fftSize = 0;
     int desiredInputSize;
     unsigned int maxScopeSamples;
     
diff --git a/src/process/SpectrumVisualDataThread.cpp b/src/process/SpectrumVisualDataThread.cpp
index 0c8981a..d29796f 100644
--- a/src/process/SpectrumVisualDataThread.cpp
+++ b/src/process/SpectrumVisualDataThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "SpectrumVisualDataThread.h"
 #include "CubicSDR.h"
 
@@ -13,11 +16,12 @@ SpectrumVisualProcessor *SpectrumVisualDataThread::getProcessor() {
 }
 
 void SpectrumVisualDataThread::run() {
-//    std::cout << "Spectrum visual data thread started." << std::endl;
     
     while(!stopping) {
-        std::this_thread::sleep_for(std::chrono::milliseconds(10));
-//        std::this_thread::yield();
+        //this if fed by FFTDataDistributor which has a buffer of FFT_DISTRIBUTOR_BUFFER_IN_SECONDS
+        //so sleep for << FFT_DISTRIBUTOR_BUFFER_IN_SECONDS not to be overflown
+        std::this_thread::sleep_for(std::chrono::milliseconds((int)(FFT_DISTRIBUTOR_BUFFER_IN_SECONDS * 1000.0 / 25.0)));
+
         sproc.run();
     }
     
diff --git a/src/process/SpectrumVisualDataThread.h b/src/process/SpectrumVisualDataThread.h
index 0987193..c6efc6c 100644
--- a/src/process/SpectrumVisualDataThread.h
+++ b/src/process/SpectrumVisualDataThread.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "IOThread.h"
diff --git a/src/process/SpectrumVisualProcessor.cpp b/src/process/SpectrumVisualProcessor.cpp
index ad80374..7b02892 100644
--- a/src/process/SpectrumVisualProcessor.cpp
+++ b/src/process/SpectrumVisualProcessor.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "SpectrumVisualProcessor.h"
 #include "CubicSDR.h"
 
@@ -26,6 +29,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : outputBuffers("SpectrumVisu
     
     fft_ceil_ma = fft_ceil_maa = 100.0;
     fft_floor_ma = fft_floor_maa = 0.0;
+    fft_floor_peak = 0.0;
     desiredInputSize.store(0);
     fft_average_rate = 0.65f;
     scaleFactor.store(1.0);
diff --git a/src/process/SpectrumVisualProcessor.h b/src/process/SpectrumVisualProcessor.h
index 80a5d05..44467c2 100644
--- a/src/process/SpectrumVisualProcessor.h
+++ b/src/process/SpectrumVisualProcessor.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "VisualProcessor.h"
@@ -50,7 +53,7 @@ public:
     float getScaleFactor();
     
 protected:
-    void process();
+    virtual void process();
     
     ReBuffer<SpectrumVisualData> outputBuffers;
     std::atomic_bool is_view;
diff --git a/src/process/VisualProcessor.cpp b/src/process/VisualProcessor.cpp
index c19fe88..34c1b37 100644
--- a/src/process/VisualProcessor.cpp
+++ b/src/process/VisualProcessor.cpp
@@ -1 +1,4 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "VisualProcessor.h"
diff --git a/src/process/VisualProcessor.h b/src/process/VisualProcessor.h
index 2b3bf2c..bd41d40 100644
--- a/src/process/VisualProcessor.h
+++ b/src/process/VisualProcessor.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "CubicSDRDefs.h"
@@ -40,19 +43,22 @@ public:
         return false;
     }
 
+    //Set a (new) 'input' queue for incoming data.
     void setInput(ThreadQueue<InputDataType *> *vis_in) {
         std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
         input = vis_in;
         
     }
     
+    //Add a vis_out queue where to consumed 'input' data will be
+    //dispatched by distribute(). 
     void attachOutput(ThreadQueue<OutputDataType *> *vis_out) {
         // attach an output queue
         std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
         outputs.push_back(vis_out);
-       
     }
     
+    //reverse of attachOutput(), removed an existing attached vis_out.
     void removeOutput(ThreadQueue<OutputDataType *> *vis_out) {
         // remove an output queue
         std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
@@ -61,9 +67,9 @@ public:
         if (i != outputs.end()) {
             outputs.erase(i);
         }
-      
     }
     
+    //Call process() repeateadly until all available 'input' data is consumed.
     void run() {
         
         std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
@@ -75,37 +81,51 @@ public:
     }
     
 protected:
-    virtual void process() {
-        // process inputs to output
-        // distribute(output);
-    }
-
-    void distribute(OutputDataType *output) {
-        // distribute outputs
+    // derived class must implement a  process() interface
+    //where typically 'input' data is consummed, procerssed, and then dispatched
+    //with distribute() to all 'outputs'.
+    virtual void process() = 0;
+
+    //To be used by derived classes implementing 
+    //process() : will dispatch 'item' into as many 
+    //available outputs, previously set by attachOutput().
+    void distribute(OutputDataType *item) {
+      
         std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
-
-        output->setRefCount(outputs.size());
+        //We will try to distribute 'output' among all 'outputs',
+        //so 'output' will a-priori be shared among all 'outputs' so set its ref count to this 
+        //amount.
+        item->setRefCount((int)outputs.size());
         for (outputs_i = outputs.begin(); outputs_i != outputs.end(); outputs_i++) {
-
-        	if (!(*outputs_i)->push(output)) {
-        		output->decRefCount();
+            //if 'output' failed to be given to an outputs_i, dec its ref count accordingly.
+        	if (!(*outputs_i)->push(item)) {
+                item->decRefCount();
         	} 
         }
+
+        // Now 'item' refcount matches the times 'item' has been successfully distributed,
+        //i.e shared among the outputs.
     }
 
-    ThreadQueue<InputDataType *> *input;
+    //the incoming data queue 
+    ThreadQueue<InputDataType *> *input = nullptr;
+    
+    //the n-outputs where to process()-ed data is distribute()-ed.
     std::vector<ThreadQueue<OutputDataType *> *> outputs;
-	typename std::vector<ThreadQueue<OutputDataType *> *>::iterator outputs_i;
+	
+    typename std::vector<ThreadQueue<OutputDataType *> *>::iterator outputs_i;
 
-    //protects input and outputs, must be recursive because ao reentrance
+    //protects input and outputs, must be recursive because of re-entrance
     std::recursive_mutex busy_update;
 };
 
-
+//Specialization much like VisualDataReDistributor, except 
+//the input (pointer) is directly re-dispatched
+//to outputs, so that all output indeed SHARE the same instance. 
 template<class OutputDataType = ReferenceCounter>
 class VisualDataDistributor : public VisualProcessor<OutputDataType, OutputDataType> {
 protected:
-    void process() {
+    virtual void process() {
         OutputDataType *inp;
         while (VisualProcessor<OutputDataType, OutputDataType>::input->try_pop(inp)) {
             
@@ -123,11 +143,12 @@ protected:
     }
 };
 
-
+//specialization class which process() take an input item and re-dispatch
+//A COPY to every outputs, without further processing. This is a 1-to-n dispatcher. 
 template<class OutputDataType = ReferenceCounter>
 class VisualDataReDistributor : public VisualProcessor<OutputDataType, OutputDataType> {
 protected:
-    void process() {
+    virtual void process() {
         OutputDataType *inp;
         while (VisualProcessor<OutputDataType, OutputDataType>::input->try_pop(inp)) {
             
diff --git a/src/rig/RigThread.cpp b/src/rig/RigThread.cpp
index e14d86d..7bdcf4b 100644
--- a/src/rig/RigThread.cpp
+++ b/src/rig/RigThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "RigThread.h"
 
 std::vector<const struct rig_caps *> RigThread::rigCaps;
diff --git a/src/rig/RigThread.h b/src/rig/RigThread.h
index 53b521f..c60b070 100644
--- a/src/rig/RigThread.h
+++ b/src/rig/RigThread.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "IOThread.h"
diff --git a/src/sdr/SDRDeviceInfo.cpp b/src/sdr/SDRDeviceInfo.cpp
index 1e326ed..e79b77d 100644
--- a/src/sdr/SDRDeviceInfo.cpp
+++ b/src/sdr/SDRDeviceInfo.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "SDRDeviceInfo.h"
 #include <cstdlib>
 #include <algorithm>
diff --git a/src/sdr/SDRDeviceInfo.h b/src/sdr/SDRDeviceInfo.h
index 77714de..cc903c1 100644
--- a/src/sdr/SDRDeviceInfo.h
+++ b/src/sdr/SDRDeviceInfo.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <string>
@@ -86,7 +89,7 @@ public:
     SDRRangeMap getGains(int direction, size_t channel);
 
 private:
-    int index;
+    int index = 0;
     std::string name, serial, product, manufacturer, tuner;
     std::string driver, hardware, manual_params;
     bool timestamps, available, remote, manual;
diff --git a/src/sdr/SDREnumerator.cpp b/src/sdr/SDREnumerator.cpp
index 116234b..a7680c0 100644
--- a/src/sdr/SDREnumerator.cpp
+++ b/src/sdr/SDREnumerator.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "SDREnumerator.h"
 #include "CubicSDRDefs.h"
 #include <vector>
diff --git a/src/sdr/SDREnumerator.h b/src/sdr/SDREnumerator.h
index 6511243..4bdd09f 100644
--- a/src/sdr/SDREnumerator.h
+++ b/src/sdr/SDREnumerator.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <atomic>
diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp
index 052969e..f7b1c18 100644
--- a/src/sdr/SDRPostThread.cpp
+++ b/src/sdr/SDRPostThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "SDRPostThread.h"
 #include "CubicSDRDefs.h"
 #include "CubicSDR.h"
diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h
index c27fba1..2f7ad3e 100644
--- a/src/sdr/SDRPostThread.h
+++ b/src/sdr/SDRPostThread.h
@@ -1,10 +1,9 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
-#if USE_RTL_SDR
-#include "SDRThread.h"
-#else
 #include "SoapySDRThread.h"
-#endif
 #include <algorithm>
 
 class SDRPostThread : public IOThread {
@@ -46,7 +45,7 @@ private:
     std::vector<liquid_float_complex> fpData;
     std::vector<liquid_float_complex> dataOut;
     std::vector<long long> chanCenters;
-    long long chanBw;
+    long long chanBw = 0;
     
     size_t nRunDemods;
     std::vector<DemodulatorInstance *> runDemods;
diff --git a/src/sdr/SDRThread.cpp b/src/sdr/SDRThread.cpp
deleted file mode 100644
index 5ee8080..0000000
--- a/src/sdr/SDRThread.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-#include "SDRThread.h"
-#include "CubicSDRDefs.h"
-#include <vector>
-#include "CubicSDR.h"
-
-SDRThread::SDRThread() : IOThread() {
-	offset.store(0);
-	deviceId.store(-1);
-    dev = NULL;
-    sampleRate.store(DEFAULT_SAMPLE_RATE);
-}
-
-SDRThread::~SDRThread() {
-    rtlsdr_close(dev);
-}
-
-int SDRThread::enumerate_rtl(std::vector<SDRDeviceInfo *> *devs) {
-
-    int first_available = -1;
-
-    char manufact[256], product[256], serial[256];
-
-    unsigned int rtl_count = rtlsdr_get_device_count();
-
-    std::cout << "RTL Devices: " << rtl_count << std::endl;
-
-    for (int i = 0; i < rtl_count; i++) {
-        std::string deviceName(rtlsdr_get_device_name(i));
-        std::string deviceManufacturer;
-        std::string deviceProduct;
-        std::string deviceTuner;
-        std::string deviceSerial;
-
-        bool deviceAvailable = false;
-        std::cout << "Device #" << i << ": " << deviceName << std::endl;
-        if (rtlsdr_get_device_usb_strings(i, manufact, product, serial) == 0) {
-            std::cout << "\tManufacturer: " << manufact << ", Product Name: " << product << ", Serial: " << serial << std::endl;
-
-            deviceSerial = serial;
-            deviceAvailable = true;
-            deviceProduct = product;
-            deviceManufacturer = manufact;
-
-            rtlsdr_dev_t *devTest = nullptr;
-            if(rtlsdr_open(&devTest, i) < 0)
-            {
-                std::cout << "\tFailed to open device " << i << std::endl;
-                continue;
-            }
-
-            std::cout << "\t Tuner type: ";
-            switch (rtlsdr_get_tuner_type(devTest)) {
-            case RTLSDR_TUNER_UNKNOWN:
-                deviceTuner = "Unknown";
-                break;
-            case RTLSDR_TUNER_E4000:
-                deviceTuner = "Elonics E4000";
-                break;
-            case RTLSDR_TUNER_FC0012:
-                deviceTuner = "Fitipower FC0012";
-                break;
-            case RTLSDR_TUNER_FC0013:
-                deviceTuner = "Fitipower FC0013";
-                break;
-            case RTLSDR_TUNER_FC2580:
-                deviceTuner = "Fitipower FC2580";
-                break;
-            case RTLSDR_TUNER_R820T:
-                deviceTuner = "Rafael Micro R820T";
-                break;
-            case RTLSDR_TUNER_R828D:
-                deviceTuner = "Rafael Micro R828D";
-                break;
-            }
-
-            std::cout << deviceTuner << std::endl;
-            /*
-             int num_gains = rtlsdr_get_tuner_gains(dev, NULL);
-
-             int *gains = (int *)malloc(sizeof(int) * num_gains);
-             rtlsdr_get_tuner_gains(dev, gains);
-
-             std::cout << "\t Valid gains: ";
-             for (int g = 0; g < num_gains; g++) {
-             if (g > 0) {
-             std::cout << ", ";
-             }
-             std::cout << ((float)gains[g]/10.0f);
-             }
-             std::cout << std::endl;
-
-             free(gains);
-             */
-
-            rtlsdr_close(devTest);
-            if (first_available == -1) {
-                first_available = i;
-            }
-
-        } else {
-            std::cout << "\tUnable to access device #" << i << " (in use?)" << std::endl;
-        }
-
-        if (devs != NULL) {
-            SDRDeviceInfo *devInfo = new SDRDeviceInfo();
-            devInfo->setName(deviceName);
-            devInfo->setAvailable(deviceAvailable);
-            devInfo->setProduct(deviceProduct);
-            devInfo->setSerial(deviceSerial);
-            devInfo->setManufacturer(deviceManufacturer);
-            devs->push_back(devInfo);
-        }
-    }
-
-    return first_available;
-
-}
-
-void SDRThread::run() {
-#ifdef __APPLE__
-    pthread_t tID = pthread_self();  // ID of this thread
-    int priority = sched_get_priority_max( SCHED_FIFO) - 1;
-    sched_param prio = { priority }; // scheduling priority of thread
-    pthread_setschedparam(tID, SCHED_FIFO, &prio);
-#endif
-
-    std::cout << "SDR thread initializing.." << std::endl;
-
-    std::vector<SDRDeviceInfo *> devs;
-    if (deviceId == -1) {
-        deviceId = enumerate_rtl(&devs);
-    } else {
-        enumerate_rtl(&devs);
-    }
-
-    if (deviceId == -1) {
-        std::cout << "No devices found.. SDR Thread exiting.." << std::endl;
-        return;
-    } else {
-        std::cout << "Using device #" << deviceId << std::endl;
-    }
-
-    DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devs[deviceId]->getDeviceId());
-    
-    signed char buf[BUF_SIZE];
-
-    long long frequency = wxGetApp().getConfig()->getCenterFreq();
-    int ppm = devConfig->getPPM();
-    int direct_sampling_mode = devConfig->getDirectSampling();;
-    int buf_size = BUF_SIZE;
-    offset.store(devConfig->getOffset());
-    wxGetApp().setSwapIQ(devConfig->getIQSwap());
-    
-    rtlsdr_open(&dev, deviceId);
-    rtlsdr_set_sample_rate(dev, sampleRate.load());
-    rtlsdr_set_center_freq(dev, frequency - offset.load());
-    rtlsdr_set_freq_correction(dev, ppm);
-    rtlsdr_set_agc_mode(dev, 1);
-    rtlsdr_set_offset_tuning(dev, 0);
-    rtlsdr_reset_buffer(dev);
-
-//    sampleRate = rtlsdr_get_sample_rate(dev);
-
-    std::cout << "Sample Rate is: " << sampleRate.load() << std::endl;
-
-    int n_read;
-    double seconds = 0.0;
-
-    std::cout << "SDR thread started.." << std::endl;
-
-    ReBuffer<SDRThreadIQData> buffers;
-
-    SDRThreadIQDataQueue* iqDataOutQueue = (SDRThreadIQDataQueue*) getOutputQueue("IQDataOutput");
-    SDRThreadCommandQueue* cmdQueue = (SDRThreadCommandQueue*) getInputQueue("SDRCommandQueue");
-
-    while (!terminated) {
-        if (!cmdQueue->empty()) {
-            bool freq_changed = false;
-            bool offset_changed = false;
-            bool rate_changed = false;
-            bool device_changed = false;
-            bool ppm_changed = false;
-            bool direct_sampling_changed = false;
-            long long new_freq = frequency;
-            long long new_offset = offset.load();
-            long long new_rate = sampleRate.load();
-            int new_device = deviceId;
-            int new_ppm = ppm;
-
-            while (!cmdQueue->empty()) {
-                SDRThreadCommand command;
-                cmdQueue->pop(command);
-
-                switch (command.cmd) {
-                case SDRThreadCommand::SDR_THREAD_CMD_TUNE:
-                    freq_changed = true;
-                    new_freq = command.llong_value;
-                    if (new_freq < sampleRate.load() / 2) {
-                        new_freq = sampleRate.load() / 2;
-                    }
-//                    std::cout << "Set frequency: " << new_freq << std::endl;
-                    break;
-                case SDRThreadCommand::SDR_THREAD_CMD_SET_OFFSET:
-                    offset_changed = true;
-                    new_offset = command.llong_value;
-                    std::cout << "Set offset: " << new_offset << std::endl;
-                    break;
-                case SDRThreadCommand::SDR_THREAD_CMD_SET_SAMPLERATE:
-                    rate_changed = true;
-                    new_rate = command.llong_value;
-                    if (new_rate <= 250000) {
-                        buf_size = BUF_SIZE/4;
-                    } else if (new_rate < 1500000) {
-                        buf_size = BUF_SIZE/2;
-                    } else {
-                        buf_size = BUF_SIZE;
-                    }
-                    std::cout << "Set sample rate: " << new_rate << std::endl;
-                    break;
-                case SDRThreadCommand::SDR_THREAD_CMD_SET_DEVICE:
-                    device_changed = true;
-                    new_device = (int) command.llong_value;
-                    std::cout << "Set device: " << new_device << std::endl;
-                    break;
-                case SDRThreadCommand::SDR_THREAD_CMD_SET_PPM:
-                    ppm_changed = true;
-                    new_ppm = (int) command.llong_value;
-                    //std::cout << "Set PPM: " << new_ppm << std::endl;
-                    break;
-                case SDRThreadCommand::SDR_THREAD_CMD_SET_DIRECT_SAMPLING:
-                    direct_sampling_mode = (int)command.llong_value;
-                    direct_sampling_changed = true;
-                    break;
-                default:
-                    break;
-                }
-            }
-
-            if (device_changed) {
-                rtlsdr_close(dev);
-                rtlsdr_open(&dev, new_device);
-                rtlsdr_set_sample_rate(dev, sampleRate.load());
-                rtlsdr_set_center_freq(dev, frequency - offset.load());
-                rtlsdr_set_freq_correction(dev, ppm);
-                rtlsdr_set_agc_mode(dev, 1);
-                rtlsdr_set_offset_tuning(dev, 0);
-                rtlsdr_set_direct_sampling(dev, direct_sampling_mode);
-                rtlsdr_reset_buffer(dev);
-            }
-            if (offset_changed) {
-                if (!freq_changed) {
-                    new_freq = frequency;
-                    freq_changed = true;
-                }
-                offset.store(new_offset);
-            }
-            if (rate_changed) {
-                rtlsdr_set_sample_rate(dev, new_rate);
-                rtlsdr_reset_buffer(dev);
-                sampleRate.store(rtlsdr_get_sample_rate(dev));
-            }
-            if (freq_changed) {
-                frequency = new_freq;
-                rtlsdr_set_center_freq(dev, frequency - offset.load());
-            }
-            if (ppm_changed) {
-                ppm = new_ppm;
-                rtlsdr_set_freq_correction(dev, ppm);
-            }
-            if (direct_sampling_changed) {
-                rtlsdr_set_direct_sampling(dev, direct_sampling_mode);
-            }
-        }
-
-        rtlsdr_read_sync(dev, buf, buf_size, &n_read);
-
-        SDRThreadIQData *dataOut = buffers.getBuffer();
-
-//        std::lock_guard < std::mutex > lock(dataOut->m_mutex);
-        dataOut->setRefCount(1);
-        dataOut->frequency = frequency;
-        dataOut->sampleRate = sampleRate.load();
-
-        if (dataOut->data.capacity() < n_read) {
-            dataOut->data.reserve(n_read);
-        }
-
-        if (dataOut->data.size() != n_read) {
-            dataOut->data.resize(n_read);
-        }
-
-        memcpy(&dataOut->data[0], buf, n_read);
-
-        double time_slice = (double) n_read / (double) sampleRate.load();
-        seconds += time_slice;
-
-        if (iqDataOutQueue != NULL) {
-            iqDataOutQueue->push(dataOut);
-        }
-    }
-
-//     buffers.purge();
-    
-    std::cout << "SDR thread done." << std::endl;
-}
-
-
-int SDRThread::getDeviceId() const {
-    return deviceId.load();
-}
-
-void SDRThread::setDeviceId(int deviceId) {
-    this->deviceId.store(deviceId);
-}
diff --git a/src/sdr/SDRThread.h b/src/sdr/SDRThread.h
deleted file mode 100644
index 4b3a8ac..0000000
--- a/src/sdr/SDRThread.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#pragma once
-
-#include <atomic>
-
-#include "rtl-sdr.h"
-
-#include "ThreadQueue.h"
-#include "DemodulatorMgr.h"
-#include "SDRDeviceInfo.h"
-
-class SDRThreadCommand {
-public:
-    enum SDRThreadCommandEnum {
-        SDR_THREAD_CMD_NULL, SDR_THREAD_CMD_TUNE, SDR_THREAD_CMD_SET_OFFSET, SDR_THREAD_CMD_SET_SAMPLERATE, SDR_THREAD_CMD_SET_PPM, SDR_THREAD_CMD_SET_DEVICE, SDR_THREAD_CMD_SET_DIRECT_SAMPLING
-    };
-
-    SDRThreadCommand() :
-            cmd(SDR_THREAD_CMD_NULL), llong_value(0) {
-
-    }
-
-    SDRThreadCommand(SDRThreadCommandEnum cmd) :
-            cmd(cmd), llong_value(0) {
-
-    }
-
-    SDRThreadCommandEnum cmd;
-    long long llong_value;
-};
-
-class SDRThreadIQData: public ReferenceCounter {
-public:
-    long long frequency;
-    long long sampleRate;
-    std::vector<unsigned char> data;
-
-    SDRThreadIQData() :
-            frequency(0), sampleRate(DEFAULT_SAMPLE_RATE) {
-
-    }
-
-    SDRThreadIQData(long long bandwidth, long long frequency, std::vector<signed char> *data) :
-            frequency(frequency), sampleRate(bandwidth) {
-
-    }
-
-    ~SDRThreadIQData() {
-
-    }
-};
-
-typedef ThreadQueue<SDRThreadCommand> SDRThreadCommandQueue;
-typedef ThreadQueue<SDRThreadIQData *> SDRThreadIQDataQueue;
-
-class SDRThread : public IOThread {
-public:
-    rtlsdr_dev_t *dev;
-
-    SDRThread();
-    ~SDRThread();
-
-    static int enumerate_rtl(std::vector<SDRDeviceInfo *> *devs);
-
-    void run();
-    
-    int getDeviceId() const;
-    void setDeviceId(int deviceId);
-
-protected:
-    std::atomic<uint32_t> sampleRate;
-    std::atomic_llong offset;
-    std::atomic_int deviceId;
-};
diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp
index 7a6b645..0c1e36f 100644
--- a/src/sdr/SoapySDRThread.cpp
+++ b/src/sdr/SoapySDRThread.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "SoapySDRThread.h"
 #include "CubicSDRDefs.h"
 #include <vector>
@@ -205,7 +208,12 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) {
         int n_stream_read = device->readStream(stream, buffs, mtElems, flags, timeNs);
 
         //if the n_stream_read <= 0, bail out from reading. 
-        if (n_stream_read <= 0) {
+        if (n_stream_read == 0) {
+             std::cout << "SDRThread::readStream(): read blocking..." << std::endl;
+             break;
+        }
+        else if (n_stream_read < 0) {
+            std::cout << "SDRThread::readStream(): read failed with code: " << n_stream_read << std::endl;
             break;
         }
 
@@ -223,7 +231,7 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) {
         } else {
             break;
         }
-    }
+    } //end while
     
     if (n_read > 0 && !stopping && !iqDataOutQueue->full()) {
         SDRThreadIQData *dataOut = buffers.getBuffer();
@@ -512,7 +520,7 @@ long long SDRThread::getOffset() {
     return offset.load();
 }
 
-void SDRThread::setSampleRate(int rate) {
+void SDRThread::setSampleRate(long rate) {
     sampleRate.store(rate);
     rate_changed = true;
     DeviceConfig *devConfig = deviceConfig.load();
@@ -521,7 +529,7 @@ void SDRThread::setSampleRate(int rate) {
     }
 //    std::cout << "Set sample rate: " << sampleRate.load() << std::endl;
 }
-int SDRThread::getSampleRate() {
+long SDRThread::getSampleRate() {
     return sampleRate.load();
 }
 
diff --git a/src/sdr/SoapySDRThread.h b/src/sdr/SoapySDRThread.h
index 1c79103..794b837 100644
--- a/src/sdr/SoapySDRThread.h
+++ b/src/sdr/SoapySDRThread.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <atomic>
@@ -67,8 +70,8 @@ public:
     void setOffset(long long ofs);
     long long getOffset();
     
-    void setSampleRate(int rate);
-    int getSampleRate();
+    void setSampleRate(long rate);
+    long getSampleRate();
 
     void setPPM(int ppm);
     int getPPM();
@@ -106,7 +109,7 @@ protected:
     std::map<std::string, std::string> settings;
     std::map<std::string, bool> settingChanged;
 
-    std::atomic<uint32_t> sampleRate;
+    std::atomic_llong sampleRate;
     std::atomic_llong frequency, offset, lock_freq;
     std::atomic_int ppm, numElems, mtuElems, numChannels;
     std::atomic_bool hasPPM, hasHardwareDC;
diff --git a/src/ui/GLPanel.cpp b/src/ui/GLPanel.cpp
index bf8af6d..4963d0b 100644
--- a/src/ui/GLPanel.cpp
+++ b/src/ui/GLPanel.cpp
@@ -1,4 +1,7 @@
 
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "GLPanel.h"
 #include "cubic_math.h"
 #include <algorithm>
diff --git a/src/ui/GLPanel.h b/src/ui/GLPanel.h
index ebdb646..94cd7a6 100644
--- a/src/ui/GLPanel.h
+++ b/src/ui/GLPanel.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <vector>
@@ -35,10 +38,10 @@ private:
 public:
     typedef enum GLPanelFillType { GLPANEL_FILL_NONE, GLPANEL_FILL_SOLID, GLPANEL_FILL_GRAD_X, GLPANEL_FILL_GRAD_Y, GLPANEL_FILL_GRAD_BAR_X, GLPANEL_FILL_GRAD_BAR_Y } GLPanelFillType;
     typedef enum GLPanelCoordinateSystem { GLPANEL_Y_DOWN_ZERO_ONE, GLPANEL_Y_UP_ZERO_ONE, GLPANEL_Y_UP, GLPANEL_Y_DOWN } GLPanelCoordinateSystem;
-    float pos[2];
-    float rot[3];
-    float size[2];
-    float view[2];
+    float pos[2] = {0.0f,0.0f};
+    float rot[3] = { 0.0f,0.0f,0.0f };
+    float size[2] = { 0.0f,0.0f };
+    float view[2] = { 0.0f,0.0f };
     GLPanelFillType fillType;
     GLPanelCoordinateSystem coord;
     float marginPx;
diff --git a/src/ui/UITestCanvas.cpp b/src/ui/UITestCanvas.cpp
index ba14f4a..9abe7df 100644
--- a/src/ui/UITestCanvas.cpp
+++ b/src/ui/UITestCanvas.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "UITestCanvas.h"
 
 #include "wx/wxprec.h"
@@ -24,7 +27,7 @@ EVT_LEAVE_WINDOW(UITestCanvas::OnMouseLeftWindow)
 EVT_ENTER_WINDOW(UITestCanvas::OnMouseEnterWindow)
 wxEND_EVENT_TABLE()
 
-UITestCanvas::UITestCanvas(wxWindow *parent, int *dispAttrs) :
+UITestCanvas::UITestCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
 InteractiveCanvas(parent, dispAttrs) {
     
     glContext = new UITestContext(this, &wxGetApp().GetContext(this));
diff --git a/src/ui/UITestCanvas.h b/src/ui/UITestCanvas.h
index a75481f..631366a 100644
--- a/src/ui/UITestCanvas.h
+++ b/src/ui/UITestCanvas.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
@@ -14,7 +17,7 @@
 
 class UITestCanvas: public InteractiveCanvas {
 public:
-    UITestCanvas(wxWindow *parent, int *dispAttrs);
+    UITestCanvas(wxWindow *parent, std::vector<int> dispAttrs);
     ~UITestCanvas();
     
 private:
diff --git a/src/ui/UITestContext.cpp b/src/ui/UITestContext.cpp
index 1f5696a..e8cdd17 100644
--- a/src/ui/UITestContext.cpp
+++ b/src/ui/UITestContext.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "UITestContext.h"
 #include "UITestCanvas.h"
 #include "ColorTheme.h"
@@ -42,7 +45,7 @@ PrimaryGLContext(canvas, sharedContext), testMeter("TEST",0,100,50) {
 //    testPanel.addChild(&testChildPanel);
 //    testPanel.addChild(&testChildPanel2);
 //    testPanel.addChild(&testChildPanel3);
-    testMeter.setSize(0.1,0.9);
+    testMeter.setSize(0.1f,0.9f);
     testPanel.addChild(&testMeter);
 }
 
diff --git a/src/ui/UITestContext.h b/src/ui/UITestContext.h
index 45dd3e3..62ce4fa 100644
--- a/src/ui/UITestContext.h
+++ b/src/ui/UITestContext.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "PrimaryGLContext.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<DataNode *> 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<DataNode *> &node_list_out) {
     stack<DataNode *> 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<DataNode *> &node_list_out);
     
 //    operator string () { string s; element()->get(s); return s; }
@@ -287,6 +293,7 @@ public:
     operator vector<long double> () { vector<long double> 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/util/GLExt.cpp b/src/util/GLExt.cpp
index efab5f0..191fbdb 100644
--- a/src/util/GLExt.cpp
+++ b/src/util/GLExt.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "GLExt.h"
 #include <cstring>
 #include <iostream>
diff --git a/src/util/GLExt.h b/src/util/GLExt.h
index 619a337..34a655d 100644
--- a/src/util/GLExt.h
+++ b/src/util/GLExt.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
diff --git a/src/util/GLFont.cpp b/src/util/GLFont.cpp
index ee20ef9..e3e7e5f 100644
--- a/src/util/GLFont.cpp
+++ b/src/util/GLFont.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "GLFont.h"
 
 #include <iostream>
diff --git a/src/util/GLFont.h b/src/util/GLFont.h
index 3feac86..ff2f50a 100644
--- a/src/util/GLFont.h
+++ b/src/util/GLFont.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <map>
@@ -15,8 +18,8 @@ public:
     GLFontStringCache();
     int drawlen;
     int vpx, vpy;
-    int pxHeight;
-    float msgWidth;
+    int pxHeight = 0;
+    float msgWidth = 0.0f;
     std::atomic_int gc;
     std::vector<float> gl_vertices;
     std::vector<float> gl_uv;
diff --git a/src/util/Gradient.cpp b/src/util/Gradient.cpp
index 7279296..906b8b8 100644
--- a/src/util/Gradient.cpp
+++ b/src/util/Gradient.cpp
@@ -1,4 +1,8 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "Gradient.h"
+#include <stddef.h>
 
 Gradient::Gradient() {
 
@@ -21,19 +25,19 @@ std::vector<float> &Gradient::getBlue() {
 }
 
 void Gradient::generate(unsigned int len) {
-    unsigned int chunk_size = len / (colors.size() - 1);
+    size_t chunk_size = len / (colors.size() - 1);
 
-    unsigned int p = 0;
+    size_t p = 0;
     r_val.resize(len);
     g_val.resize(len);
     b_val.resize(len);
 
-    for (unsigned int j = 0, jMax = colors.size() - 1; j < jMax; j++) {
+    for (size_t j = 0, jMax = colors.size() - 1; j < jMax; j++) {
         if ((chunk_size * (jMax)) < len && (j == jMax - 1)) {
             chunk_size += len - chunk_size * (jMax);
         }
 
-        for (unsigned int i = 0; i < chunk_size; i++) {
+        for (size_t i = 0; i < chunk_size; i++) {
             float idx = (float) (i) / (float) chunk_size;
 
             float r1 = colors[j].r;
diff --git a/src/util/Gradient.h b/src/util/Gradient.h
index 650daf3..1dac203 100644
--- a/src/util/Gradient.h
+++ b/src/util/Gradient.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <vector>
diff --git a/src/util/MouseTracker.cpp b/src/util/MouseTracker.cpp
index 00fb878..b0572f5 100644
--- a/src/util/MouseTracker.cpp
+++ b/src/util/MouseTracker.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "MouseTracker.h"
 #include <iostream>
 
diff --git a/src/util/MouseTracker.h b/src/util/MouseTracker.h
index e23455a..dfb4623 100644
--- a/src/util/MouseTracker.h
+++ b/src/util/MouseTracker.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/window.h"
diff --git a/src/util/ThreadQueue.cpp b/src/util/ThreadQueue.cpp
index bf390c9..3597da5 100644
--- a/src/util/ThreadQueue.cpp
+++ b/src/util/ThreadQueue.cpp
@@ -1 +1,4 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include <ThreadQueue.h>
\ No newline at end of file
diff --git a/src/util/ThreadQueue.h b/src/util/ThreadQueue.h
index 5d0c52f..8545e82 100644
--- a/src/util/ThreadQueue.h
+++ b/src/util/ThreadQueue.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once 
 
 /* Credit to Alfredo Pons / https://plus.google.com/109903449837592676231
diff --git a/src/util/Timer.cpp b/src/util/Timer.cpp
index aa449e7..d9d0b37 100644
--- a/src/util/Timer.cpp
+++ b/src/util/Timer.cpp
@@ -1,3 +1,5 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
 
 #include "Timer.h"
 
@@ -54,7 +56,7 @@ void Timer::unlock()
 
 	update();
 	
-	last_update = system_milliseconds-lock_rate;
+	last_update = system_milliseconds-(unsigned long)lock_rate;
 	
 	offset += msec_tmp-system_milliseconds;
 	
diff --git a/src/util/Timer.h b/src/util/Timer.h
index 18ab841..95d3d15 100644
--- a/src/util/Timer.h
+++ b/src/util/Timer.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #ifndef TIMER_H
 #define TIMER_H
 
diff --git a/src/visual/ColorTheme.cpp b/src/visual/ColorTheme.cpp
index 6180778..853b680 100644
--- a/src/visual/ColorTheme.cpp
+++ b/src/visual/ColorTheme.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ColorTheme.h"
 #include "CubicSDR.h"
 #include "CubicSDRDefs.h"
diff --git a/src/visual/ColorTheme.h b/src/visual/ColorTheme.h
index 6f35324..aac98a8 100644
--- a/src/visual/ColorTheme.h
+++ b/src/visual/ColorTheme.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "Gradient.h"
@@ -5,6 +8,7 @@
 #include <map>
 #include <vector>
 #include <string>
+#include <wx/colour.h>
 
 #define COLOR_THEME_DEFAULT 0
 #define COLOR_THEME_BW 1
@@ -38,6 +42,14 @@ public:
     }
     
     RGBA4f operator*(float v) { return RGBA4f(r*v, g*v, b*v); }
+    
+    operator wxColour() {
+        return wxColour(
+                        (unsigned char) std::min((r * 255.0), 255.0),
+                       (unsigned char) std::min((g * 255.0), 255.0),
+                       (unsigned char) std::min((b * 255.0), 255.0));
+
+    }
 
 };
 
diff --git a/src/visual/GainCanvas.cpp b/src/visual/GainCanvas.cpp
index 6f64193..56661de 100644
--- a/src/visual/GainCanvas.cpp
+++ b/src/visual/GainCanvas.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "GainCanvas.h"
 
 #include "wx/wxprec.h"
@@ -25,7 +28,7 @@ EVT_ENTER_WINDOW(GainCanvas::OnMouseEnterWindow)
 EVT_MOUSEWHEEL(GainCanvas::OnMouseWheelMoved)
 wxEND_EVENT_TABLE()
 
-GainCanvas::GainCanvas(wxWindow *parent, int *dispAttrs) :
+GainCanvas::GainCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
         InteractiveCanvas(parent, dispAttrs) {
 
     glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
@@ -167,6 +170,13 @@ void GainCanvas::setHelpTip(std::string tip) {
 
 void GainCanvas::updateGainUI() {
     SDRDeviceInfo *devInfo = wxGetApp().getDevice();
+
+    //possible if we 'Refresh Devices' then devInfo becomes null
+    //until a new device is selected.
+    if (devInfo == nullptr) {
+        return;
+    }
+
     DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devInfo->getDeviceId());
     
     gains = devInfo->getGains(SOAPY_SDR_RX, 0);
diff --git a/src/visual/GainCanvas.h b/src/visual/GainCanvas.h
index 469dafc..2e7a552 100644
--- a/src/visual/GainCanvas.h
+++ b/src/visual/GainCanvas.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
@@ -17,7 +20,7 @@
 
 class GainCanvas: public InteractiveCanvas {
 public:
-    GainCanvas(wxWindow *parent, int *dispAttrs);
+    GainCanvas(wxWindow *parent, std::vector<int> dispAttrs);
     ~GainCanvas();
 
     void setHelpTip(std::string tip);
diff --git a/src/visual/ImagePanel.cpp b/src/visual/ImagePanel.cpp
new file mode 100644
index 0000000..b67e62e
--- /dev/null
+++ b/src/visual/ImagePanel.cpp
@@ -0,0 +1,50 @@
+#include "ImagePanel.h"
+ 
+BEGIN_EVENT_TABLE(ImagePanel, wxPanel)
+EVT_PAINT(ImagePanel::paintEvent)
+END_EVENT_TABLE()
+ 
+
+ImagePanel::ImagePanel(wxPanel * parent, wxString file, wxBitmapType format) :
+	wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE) {
+	image.LoadFile(file, format);
+}
+
+void ImagePanel::paintEvent(wxPaintEvent & evt) {
+    wxPaintDC dc(this);
+    render(dc);
+}
+ 
+
+void ImagePanel::paintNow() {
+    wxClientDC dc(this);
+    render(dc);
+}
+ 
+
+void ImagePanel::render(wxDC&  dc) {
+
+	double imagew = image.GetWidth();
+	double imageh = image.GetHeight();
+
+	wxSize destSize = dc.GetSize();
+
+	double destw = destSize.GetWidth();
+	double desth = destSize.GetHeight();
+
+	double sf = 1.0, wf, hf;
+
+	wf = destw / imagew;
+	hf = desth / imageh;
+
+	sf = (wf < hf)?wf:hf;
+
+	double resulth = imageh * sf;
+	double resultw = imagew * sf;
+
+	dc.SetUserScale(sf, sf);
+    dc.DrawBitmap( image, (destw/2 - resultw/2)/sf, (desth/2 - resulth/2)/sf, false );
+}
+
+ 
+ 
\ No newline at end of file
diff --git a/src/visual/ImagePanel.h b/src/visual/ImagePanel.h
new file mode 100644
index 0000000..681e0d7
--- /dev/null
+++ b/src/visual/ImagePanel.h
@@ -0,0 +1,16 @@
+#include <wx/wx.h>
+#include <wx/sizer.h>
+ 
+class ImagePanel : public wxPanel {
+	wxBitmap image;
+
+public:
+	ImagePanel(wxPanel* parent, wxString file, wxBitmapType format);
+
+	void paintEvent(wxPaintEvent & evt);
+	void paintNow();
+
+	void render(wxDC& dc);
+
+	DECLARE_EVENT_TABLE()
+};
diff --git a/src/visual/InteractiveCanvas.cpp b/src/visual/InteractiveCanvas.cpp
index e91464a..b18a3ec 100644
--- a/src/visual/InteractiveCanvas.cpp
+++ b/src/visual/InteractiveCanvas.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "InteractiveCanvas.h"
 
 #include "wx/wxprec.h"
@@ -17,8 +20,8 @@
 
 #include <wx/numformatter.h>
 
-InteractiveCanvas::InteractiveCanvas(wxWindow *parent, int *dispAttrs) :
-        wxGLCanvas(parent, wxID_ANY, dispAttrs, wxDefaultPosition, wxDefaultSize,
+InteractiveCanvas::InteractiveCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
+        wxGLCanvas(parent, wxID_ANY, dispAttrs.data(), wxDefaultPosition, wxDefaultSize,
         wxFULL_REPAINT_ON_RESIZE), parent(parent), shiftDown(false), altDown(false), ctrlDown(false), centerFreq(0), bandwidth(0), lastBandwidth(0), isView(
         false) {
     mouseTracker.setTarget(this);
@@ -27,7 +30,7 @@ InteractiveCanvas::InteractiveCanvas(wxWindow *parent, int *dispAttrs) :
 InteractiveCanvas::~InteractiveCanvas() {
 }
 
-void InteractiveCanvas::setView(long long center_freq_in, int bandwidth_in) {
+void InteractiveCanvas::setView(long long center_freq_in, long long bandwidth_in) {
     isView = true;
     centerFreq = center_freq_in;
     bandwidth = bandwidth_in;
@@ -71,11 +74,11 @@ long long InteractiveCanvas::getCenterFrequency() {
     }
 }
 
-void InteractiveCanvas::setBandwidth(unsigned int bandwidth_in) {
+void InteractiveCanvas::setBandwidth(long long bandwidth_in) {
     bandwidth = bandwidth_in;
 }
 
-unsigned int InteractiveCanvas::getBandwidth() {
+long long InteractiveCanvas::getBandwidth() {
     if (isView) {
         return bandwidth;
     } else {
diff --git a/src/visual/InteractiveCanvas.h b/src/visual/InteractiveCanvas.h
index 1877e43..c255720 100644
--- a/src/visual/InteractiveCanvas.h
+++ b/src/visual/InteractiveCanvas.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
@@ -5,24 +8,25 @@
 
 #include "MouseTracker.h"
 #include <string>
+#include <vector>
 
 class InteractiveCanvas: public wxGLCanvas {
 public:
-    InteractiveCanvas(wxWindow *parent, int *dispAttrs);
+    InteractiveCanvas(wxWindow *parent, std::vector<int> dispAttrs);
     ~InteractiveCanvas();
 
     long long getFrequencyAt(float x);
     long long getFrequencyAt(float x, long long iqCenterFreq, long long iqBandwidth);
     
-    virtual void setView(long long center_freq_in, int bandwidth_in);
+    virtual void setView(long long center_freq_in, long long bandwidth_in);
     virtual void disableView();
     bool getViewState();
 
     void setCenterFrequency(long long center_freq_in);
     long long getCenterFrequency();
 
-    void setBandwidth(unsigned int bandwidth_in);
-    unsigned int getBandwidth();
+    void setBandwidth(long long bandwidth_in);
+    long long getBandwidth();
 
     MouseTracker *getMouseTracker();
     bool isMouseInView();
@@ -56,8 +60,8 @@ protected:
     bool ctrlDown;
 
     long long centerFreq;
-    unsigned int bandwidth;
-    unsigned int lastBandwidth;
+    long long bandwidth;
+    long long lastBandwidth;
 
     bool isView;
 	std::string lastToolTip;
diff --git a/src/visual/MeterCanvas.cpp b/src/visual/MeterCanvas.cpp
index c1bb2ad..d80b432 100644
--- a/src/visual/MeterCanvas.cpp
+++ b/src/visual/MeterCanvas.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "MeterCanvas.h"
 
 #include "wx/wxprec.h"
@@ -27,7 +30,7 @@ EVT_LEAVE_WINDOW(MeterCanvas::OnMouseLeftWindow)
 EVT_ENTER_WINDOW(MeterCanvas::OnMouseEnterWindow)
 wxEND_EVENT_TABLE()
 
-MeterCanvas::MeterCanvas(wxWindow *parent, int *dispAttrs) :
+MeterCanvas::MeterCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
         InteractiveCanvas(parent, dispAttrs), level(0), level_min(0), level_max(1), inputValue(0), userInputValue(0), showUserInput(true) {
 
     glContext = new MeterContext(this, &wxGetApp().GetContext(this));
diff --git a/src/visual/MeterCanvas.h b/src/visual/MeterCanvas.h
index 4b955ba..9134d6e 100644
--- a/src/visual/MeterCanvas.h
+++ b/src/visual/MeterCanvas.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
@@ -14,7 +17,7 @@
 
 class MeterCanvas: public InteractiveCanvas {
 public:
-    MeterCanvas(wxWindow *parent, int *dispAttrs);
+    MeterCanvas(wxWindow *parent, std::vector<int> dispAttrs);
     ~MeterCanvas();
 
     void setLevel(float level_in);
diff --git a/src/visual/MeterContext.cpp b/src/visual/MeterContext.cpp
index 8387e64..4c33332 100644
--- a/src/visual/MeterContext.cpp
+++ b/src/visual/MeterContext.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "MeterContext.h"
 #include "MeterCanvas.h"
 #include "ColorTheme.h"
diff --git a/src/visual/MeterContext.h b/src/visual/MeterContext.h
index 3fac2f2..789ebcd 100644
--- a/src/visual/MeterContext.h
+++ b/src/visual/MeterContext.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "PrimaryGLContext.h"
diff --git a/src/visual/ModeSelectorCanvas.cpp b/src/visual/ModeSelectorCanvas.cpp
index dcfdaa4..31c53ed 100644
--- a/src/visual/ModeSelectorCanvas.cpp
+++ b/src/visual/ModeSelectorCanvas.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModeSelectorCanvas.h"
 
 #include "wx/wxprec.h"
@@ -24,7 +27,7 @@ EVT_LEAVE_WINDOW(ModeSelectorCanvas::OnMouseLeftWindow)
 EVT_ENTER_WINDOW(ModeSelectorCanvas::OnMouseEnterWindow)
 wxEND_EVENT_TABLE()
 
-ModeSelectorCanvas::ModeSelectorCanvas(wxWindow *parent, int *dispAttrs) :
+ModeSelectorCanvas::ModeSelectorCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
 InteractiveCanvas(parent, dispAttrs), numChoices(0), currentSelection(-1), toggleMode(false), inputChanged(false), padX(4.0), padY(4.0), highlightOverride(false) {
 
     glContext = new ModeSelectorContext(this, &wxGetApp().GetContext(this));
diff --git a/src/visual/ModeSelectorCanvas.h b/src/visual/ModeSelectorCanvas.h
index 004dd4c..3a0d588 100644
--- a/src/visual/ModeSelectorCanvas.h
+++ b/src/visual/ModeSelectorCanvas.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
@@ -24,7 +27,7 @@ public:
 
 class ModeSelectorCanvas: public InteractiveCanvas {
 public:
-    ModeSelectorCanvas(wxWindow *parent, int *dispAttrs);
+    ModeSelectorCanvas(wxWindow *parent, std::vector<int> dispAttrs);
     ~ModeSelectorCanvas();
 
     int getHoveredSelection();
diff --git a/src/visual/ModeSelectorContext.cpp b/src/visual/ModeSelectorContext.cpp
index 44c793c..5ce3b08 100644
--- a/src/visual/ModeSelectorContext.cpp
+++ b/src/visual/ModeSelectorContext.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ModeSelectorContext.h"
 #include "ModeSelectorCanvas.h"
 #include "ColorTheme.h"
diff --git a/src/visual/ModeSelectorContext.h b/src/visual/ModeSelectorContext.h
index 2d84fa3..8daecc0 100644
--- a/src/visual/ModeSelectorContext.h
+++ b/src/visual/ModeSelectorContext.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "PrimaryGLContext.h"
diff --git a/src/visual/PrimaryGLContext.cpp b/src/visual/PrimaryGLContext.cpp
index 64c8963..cc58c77 100644
--- a/src/visual/PrimaryGLContext.cpp
+++ b/src/visual/PrimaryGLContext.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "PrimaryGLContext.h"
 
 #include "wx/wxprec.h"
diff --git a/src/visual/PrimaryGLContext.h b/src/visual/PrimaryGLContext.h
index 2f19460..f421def 100644
--- a/src/visual/PrimaryGLContext.h
+++ b/src/visual/PrimaryGLContext.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp
index 83e04e7..5371513 100644
--- a/src/visual/ScopeCanvas.cpp
+++ b/src/visual/ScopeCanvas.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ScopeCanvas.h"
 
 #include "wx/wxprec.h"
@@ -28,7 +31,7 @@ EVT_LEAVE_WINDOW(ScopeCanvas::OnMouseLeftWindow)
 EVT_ENTER_WINDOW(ScopeCanvas::OnMouseEnterWindow)
 wxEND_EVENT_TABLE()
 
-ScopeCanvas::ScopeCanvas(wxWindow *parent, int *dispAttrs) : InteractiveCanvas(parent, dispAttrs), ppmMode(false), ctr(0), ctrTarget(0), dragAccel(0), helpTip("") {
+ScopeCanvas::ScopeCanvas(wxWindow *parent, std::vector<int> dispAttrs) : InteractiveCanvas(parent, dispAttrs), ppmMode(false), ctr(0), ctrTarget(0), dragAccel(0), helpTip("") {
 
     glContext = new ScopeContext(this, &wxGetApp().GetContext(this));
     inputData.set_max_num_items(2);
@@ -42,7 +45,10 @@ ScopeCanvas::ScopeCanvas(wxWindow *parent, int *dispAttrs) : InteractiveCanvas(p
     parentPanel.setFill(GLPanel::GLPANEL_FILL_NONE);
     scopePanel.setSize(1.0,-1.0);
     spectrumPanel.setSize(1.0,-1.0);
-    spectrumPanel.setShowDb(true);
+    showDb = true;
+    spectrumPanel.setShowDb(showDb);
+    //dB offset is a RF value, has no meaning in audio, disable it.
+    spectrumPanel.setUseDBOffset(false);
 }
 
 ScopeCanvas::~ScopeCanvas() {
@@ -86,8 +92,8 @@ bool ScopeCanvas::getPPMMode() {
     return ppmMode;
 }
 
-void ScopeCanvas::setShowDb(bool showDb) {
-    this->showDb = showDb;
+void ScopeCanvas::setShowDb(bool show) {
+    this->showDb = show;
 }
 
 bool ScopeCanvas::getShowDb() {
@@ -272,3 +278,20 @@ void ScopeCanvas::setHelpTip(std::string tip) {
     helpTip = tip;
 }
 
+void ScopeCanvas::OnKeyDown(wxKeyEvent& event) {
+    InteractiveCanvas::OnKeyDown(event);
+
+    switch (event.GetKeyCode()) {
+
+    case 'B':
+        setShowDb(!getShowDb());
+        break;
+    default:
+        event.Skip();
+    }
+}
+
+void ScopeCanvas::OnKeyUp(wxKeyEvent& event) {
+    InteractiveCanvas::OnKeyUp(event);
+}
+
diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h
index 006f7f4..7511437 100644
--- a/src/visual/ScopeCanvas.h
+++ b/src/visual/ScopeCanvas.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
@@ -14,9 +17,19 @@
 
 class ScopeCanvas: public InteractiveCanvas {
 public:
-    ScopeCanvas(wxWindow *parent, int *dispAttrs);
+    ScopeCanvas(wxWindow *parent, std::vector<int> dispAttrs);
     ~ScopeCanvas();
 
+    //This is public because it is indeed forwarded from
+    //AppFrame::OnGlobalKeyDown, because global key handler intercepts 
+    //calls in all windows.
+    void OnKeyDown(wxKeyEvent& event);
+
+    //This is public because it is indeed forwarded from
+    //AppFrame::OnGlobalKeyUp, because global key handler intercepts 
+    //calls in all windows.
+    void OnKeyUp(wxKeyEvent& event);
+
     void setDeviceName(std::string device_name);
     void setPPMMode(bool ppmMode);
     bool getPPMMode();
diff --git a/src/visual/ScopeContext.cpp b/src/visual/ScopeContext.cpp
index a71f74f..0e86d9b 100644
--- a/src/visual/ScopeContext.cpp
+++ b/src/visual/ScopeContext.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "ScopeContext.h"
 
 #include "ScopeCanvas.h"
diff --git a/src/visual/ScopeContext.h b/src/visual/ScopeContext.h
index 873bde0..b4860d9 100644
--- a/src/visual/ScopeContext.h
+++ b/src/visual/ScopeContext.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "PrimaryGLContext.h"
diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp
index 8a31c85..742bb2c 100644
--- a/src/visual/SpectrumCanvas.cpp
+++ b/src/visual/SpectrumCanvas.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "SpectrumCanvas.h"
 
 #include "wx/wxprec.h"
@@ -29,7 +32,7 @@ EVT_RIGHT_DOWN(SpectrumCanvas::OnMouseRightDown)
 EVT_RIGHT_UP(SpectrumCanvas::OnMouseRightReleased)
 wxEND_EVENT_TABLE()
 
-SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *dispAttrs) :
+SpectrumCanvas::SpectrumCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
         InteractiveCanvas(parent, dispAttrs), waterfallCanvas(NULL) {
 
     glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
@@ -174,6 +177,14 @@ bool SpectrumCanvas::getShowDb() {
     return spectrumPanel.getShowDb();
 }
 
+void SpectrumCanvas::setUseDBOfs(bool showDb) {
+    spectrumPanel.setUseDBOffset(showDb);
+}
+
+bool SpectrumCanvas::getUseDBOfs() {
+    return spectrumPanel.getUseDBOffset();
+}
+
 void SpectrumCanvas::setView(long long center_freq_in, int bandwidth_in) {
     bwChange += bandwidth_in-bandwidth;
     #define BW_RESET_TH 400000
@@ -293,7 +304,26 @@ void SpectrumCanvas::OnMouseRightReleased(wxMouseEvent& event) {
         wxGetApp().getSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold());
 
         //make the peak hold act on the current dmod also, like a zoomed-in version.
-        wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold());
+        if (wxGetApp().getDemodSpectrumProcessor()) {
+            wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold());
+        }
     }
     mouseTracker.OnMouseRightReleased(event);
 }
+
+void SpectrumCanvas::OnKeyDown(wxKeyEvent& event) {
+    InteractiveCanvas::OnKeyDown(event);
+
+    switch (event.GetKeyCode()) {
+  
+    case 'B':
+       setShowDb(!getShowDb());
+       break;
+    default:
+        event.Skip();
+    }
+}
+
+void SpectrumCanvas::OnKeyUp(wxKeyEvent& event) {
+    InteractiveCanvas::OnKeyUp(event);
+}
diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h
index 85af252..68374aa 100644
--- a/src/visual/SpectrumCanvas.h
+++ b/src/visual/SpectrumCanvas.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include <vector>
@@ -13,15 +16,28 @@ class WaterfallCanvas;
 
 class SpectrumCanvas: public InteractiveCanvas {
 public:
-    SpectrumCanvas(wxWindow *parent, int *dispAttrs);
+    SpectrumCanvas(wxWindow *parent, std::vector<int> dispAttrs);
     ~SpectrumCanvas();
 
+    //This is public because it is indeed forwarded from
+    //AppFrame::OnGlobalKeyDown, because global key handler intercepts 
+    //calls in all windows.
+    void OnKeyDown(wxKeyEvent& event);
+
+    //This is public because it is indeed forwarded from
+    //AppFrame::OnGlobalKeyUp, because global key handler intercepts 
+    //calls in all windows.
+    void OnKeyUp(wxKeyEvent& event);
+
     void attachWaterfallCanvas(WaterfallCanvas *canvas_in);
     void moveCenterFrequency(long long freqChange);
 
     void setShowDb(bool showDb);
     bool getShowDb();
     
+    void setUseDBOfs(bool showDb);
+    bool getUseDBOfs();
+    
     void setView(long long center_freq_in, int bandwidth_in);
     void disableView();
 
@@ -44,6 +60,7 @@ private:
     void OnMouseRightDown(wxMouseEvent& event);
     void OnMouseRightReleased(wxMouseEvent& event);
 
+   
     void updateScaleFactor(float factor);
     
     PrimaryGLContext *glContext;
diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp
index 6d58520..9ee6b3e 100644
--- a/src/visual/TuningCanvas.cpp
+++ b/src/visual/TuningCanvas.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "TuningCanvas.h"
 
 #include "wx/wxprec.h"
@@ -30,7 +33,7 @@ EVT_MOUSEWHEEL(TuningCanvas::OnMouseWheelMoved)
 //EVT_KEY_UP(TuningCanvas::OnKeyUp)
 wxEND_EVENT_TABLE()
 
-TuningCanvas::TuningCanvas(wxWindow *parent, int *dispAttrs) :
+TuningCanvas::TuningCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
         InteractiveCanvas(parent, dispAttrs), dragAccum(0), uxDown(0), top(false), bottom(false), freq(-1), bw(-1), center(-1), halfBand(false) {
 
     glContext = new TuningContext(this, &wxGetApp().GetContext(this));
diff --git a/src/visual/TuningCanvas.h b/src/visual/TuningCanvas.h
index eae7083..708ddcf 100644
--- a/src/visual/TuningCanvas.h
+++ b/src/visual/TuningCanvas.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
@@ -17,7 +20,7 @@ public:
     enum ActiveState {
         TUNING_HOVER_NONE, TUNING_HOVER_FREQ, TUNING_HOVER_BW, TUNING_HOVER_PPM, TUNING_HOVER_CENTER
     };
-    TuningCanvas(wxWindow *parent, int *dispAttrs);
+    TuningCanvas(wxWindow *parent, std::vector<int> dispAttrs);
     ~TuningCanvas();
 
     void setHelpTip(std::string tip);
diff --git a/src/visual/TuningContext.cpp b/src/visual/TuningContext.cpp
index 58f49de..3c06161 100644
--- a/src/visual/TuningContext.cpp
+++ b/src/visual/TuningContext.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "TuningContext.h"
 #include "TuningCanvas.h"
 
diff --git a/src/visual/TuningContext.h b/src/visual/TuningContext.h
index 3bbec2f..5f37e50 100644
--- a/src/visual/TuningContext.h
+++ b/src/visual/TuningContext.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "PrimaryGLContext.h"
diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp
index 69fe127..b75ca92 100644
--- a/src/visual/WaterfallCanvas.cpp
+++ b/src/visual/WaterfallCanvas.cpp
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "WaterfallCanvas.h"
 
 #include "wx/wxprec.h"
@@ -34,12 +37,12 @@ EVT_ENTER_WINDOW(WaterfallCanvas::OnMouseEnterWindow)
 EVT_MOUSEWHEEL(WaterfallCanvas::OnMouseWheelMoved)
 wxEND_EVENT_TABLE()
 
-WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *dispAttrs) :
+WaterfallCanvas::WaterfallCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
         InteractiveCanvas(parent, dispAttrs), dragState(WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), new_fft_size(0), waterfall_lines(0),
         dragOfs(0), mouseZoom(1), zoom(1), freqMoving(false), freqMove(0.0), hoverAlpha(1.0) {
 
     glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
-    linesPerSecond = 30;
+    linesPerSecond = DEFAULT_WATERFALL_LPS;
     lpsIndex = 0;
     preBuf = false;
     SetCursor(wxCURSOR_CROSS);
@@ -437,11 +440,6 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
         wxGetApp().removeDemodulator(activeDemod);
         wxGetApp().getDemodMgr().deleteThread(activeDemod);
         break;
-    case 'B':
-        if (spectrumCanvas) {
-            spectrumCanvas->setShowDb(!spectrumCanvas->getShowDb());
-        }
-        break;
     case WXK_SPACE:
         wxGetApp().showFrequencyInput();
         break;
diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h
index aa53448..4f3ed31 100644
--- a/src/visual/WaterfallCanvas.h
+++ b/src/visual/WaterfallCanvas.h
@@ -1,3 +1,6 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
 #pragma once
 
 #include "wx/glcanvas.h"
@@ -18,7 +21,7 @@ public:
         WF_DRAG_NONE, WF_DRAG_BANDWIDTH_LEFT, WF_DRAG_BANDWIDTH_RIGHT, WF_DRAG_FREQUENCY, WF_DRAG_RANGE
     };
 
-    WaterfallCanvas(wxWindow *parent, int *dispAttrs);
+    WaterfallCanvas(wxWindow *parent, std::vector<int> dispAttrs);
     void setup(unsigned int fft_size_in, int waterfall_lines_in);
     void setFFTSize(unsigned int fft_size_in);
     ~WaterfallCanvas();
@@ -33,10 +36,21 @@ public:
     void setLinesPerSecond(int lps);
     void setMinBandwidth(int min);
 
+    //This is public because it is indeed forwarded from
+    //AppFrame::OnGlobalKeyDown, because global key handler intercepts 
+    //calls in all windows.
     void OnKeyDown(wxKeyEvent& event);
+
+    //This is public because it is indeed forwarded from
+    //AppFrame::OnGlobalKeyUp, because global key handler intercepts 
+    //calls in all windows.
     void OnKeyUp(wxKeyEvent& event);
+
+    //public because called by SpectrumCanvas.
     void OnMouseWheelMoved(wxMouseEvent& event);
     
+    
+    
 private:
     void OnPaint(wxPaintEvent& event);
     void OnIdle(wxIdleEvent &event);
@@ -55,8 +69,8 @@ private:
     
     std::vector<float> spectrum_points;
 
-    SpectrumCanvas *spectrumCanvas;
-    PrimaryGLContext *glContext;
+    SpectrumCanvas *spectrumCanvas = nullptr;
+    PrimaryGLContext *glContext = nullptr;
     WaterfallPanel waterfallPanel;
 
     DragState dragState;

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-hamradio/cubicsdr.git



More information about the pkg-hamradio-commits mailing list