diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ae0c0b671..ac35cc8b7d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -120,7 +120,9 @@ jobs: - restore_cache: keys: - macos-cache-v2-universal-{{checksum "ci/macos-universal-deps.sh"}}-{{checksum "ci/universal-build-macos.sh"}} + - run: ci/macos-sign-setup.sh - run: ci/universal-build-macos.sh + - run: ci/macos-sign-cleanup.sh - save_cache: key: macos-cache-v2-universal-{{checksum "ci/macos-universal-deps.sh"}}-{{checksum "ci/universal-build-macos.sh"}} paths: @@ -147,7 +149,9 @@ jobs: - restore_cache: keys: - macos-cache-v2-intel-{{checksum "ci/macos-universal-deps.sh"}}-{{checksum "ci/universal-build-macos.sh"}} + - run: ci/macos-sign-setup.sh - run: ci/universal-build-macos.sh + - run: ci/macos-sign-cleanup.sh - save_cache: key: macos-cache-v2-intel-{{checksum "ci/macos-universal-deps.sh"}}-{{checksum "ci/universal-build-macos.sh"}} paths: diff --git a/CMakeLists.txt b/CMakeLists.txt index f4c239a85c..a56d85b926 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -931,6 +931,7 @@ set(GUI_SRC ${GUI_SRC_DIR}/NMEALogWindow.cpp ${GUI_SRC_DIR}/ocpCursor.cpp ${GUI_SRC_DIR}/ocpn_app.cpp + ${GUI_SRC_DIR}/ocpn_plugin_gui.cpp ${GUI_SRC_DIR}/OCPN_AUIManager.cpp ${GUI_SRC_DIR}/ocpndc.cpp ${GUI_SRC_DIR}/ocpn_frame.cpp @@ -1874,6 +1875,7 @@ if (APPLE) set_target_properties( ${PACKAGE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/buildosx/Info.plist.in" + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES ) endif () diff --git a/buildosx/entitlements.plist b/buildosx/entitlements.plist new file mode 100644 index 0000000000..d161ccd944 --- /dev/null +++ b/buildosx/entitlements.plist @@ -0,0 +1,18 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-executable-page-protection + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + com.apple.security.get-task-allow + + + diff --git a/ci/circleci-build-android-corelib-armhf.sh b/ci/circleci-build-android-corelib-armhf.sh index 28d7f72150..be7b8a418b 100755 --- a/ci/circleci-build-android-corelib-armhf.sh +++ b/ci/circleci-build-android-corelib-armhf.sh @@ -27,6 +27,7 @@ cd $builddir && rm -rf * cmake \ -DCMAKE_BUILD_TYPE=Release \ -DOCPN_TARGET_TUPLE:STRING="Android-armhf;16;armhf" \ + -DOCPN_BUILD_SAMPLE=ON \ -Dtool_base="$HOME/android-sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/linux-x86_64"\ .. diff --git a/ci/generic-build-debian.sh b/ci/generic-build-debian.sh index 17467ebb48..30ba471c5e 100755 --- a/ci/generic-build-debian.sh +++ b/ci/generic-build-debian.sh @@ -69,6 +69,7 @@ else -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DOCPN_CI_BUILD:BOOL=ON \ -DOCPN_USE_BUNDLED_LIBS=OFF \ + -DOCPN_BUILD_SAMPLE=ON \ .. make -sj2 dbus-run-session make run-tests || : diff --git a/ci/generic-build-flatpak.sh b/ci/generic-build-flatpak.sh index 72988e4773..3d4354f21e 100755 --- a/ci/generic-build-flatpak.sh +++ b/ci/generic-build-flatpak.sh @@ -37,6 +37,13 @@ cd flatpak sed -i -e '/url:/s|\.\.|https://github.com/OpenCPN/OpenCPN.git|' \ -e "/BUILD_NUMBER/s/0/$BUILD_NUMBER/" \ org.opencpn.OpenCPN.yaml +ed org.opencpn.OpenCPN.yaml << EOF +/DOCPN_FLATPAK=ON/ +i + - -DOCPN_BUILD_SAMPLE=ON \\ +. +wq +EOF test -d ../build || mkdir ../build cd ../build diff --git a/ci/generic-build-macos.sh b/ci/generic-build-macos.sh index 59f6ecdd5b..8b763c63bc 100755 --- a/ci/generic-build-macos.sh +++ b/ci/generic-build-macos.sh @@ -60,6 +60,7 @@ cmake -DOCPN_CI_BUILD=$CI_BUILD \ -DCMAKE_INSTALL_PREFIX=/tmp/opencpn \ -DOCPN_RELEASE=0 \ -DOCPN_BUILD_TEST=ON \ + -DOCPN_BUILD_SAMPLE=ON \ .. # Compile OpenCPN diff --git a/ci/macos-sign-cleanup.sh b/ci/macos-sign-cleanup.sh new file mode 100755 index 0000000000..c8b84d0db0 --- /dev/null +++ b/ci/macos-sign-cleanup.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -x + +echo "Cleaning up the Apple developer account information keychain" + +if [ -z "${APPLE_DEVELOPER_ID}" ]; then + echo "Apple developer account not set up, exiting..." + exit 0 +fi + +KEYCHAIN_PATH="app-signing.keychain-db" + +# Clean up the keychain +security delete-keychain "${KEYCHAIN_PATH}" diff --git a/ci/macos-sign-setup.sh b/ci/macos-sign-setup.sh new file mode 100755 index 0000000000..7b0fecd1ad --- /dev/null +++ b/ci/macos-sign-setup.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +set -x + +echo "Importing Apple developer certificates" + +if [ -z "${APPLE_DEVELOPER_ID}" ]; then + echo "Apple developer account not set up, exiting..." + exit 0 +fi + +KEYCHAIN_PATH="app-signing.keychain-db" +APP_P12="app.p12" +INST_P12="inst.p12" + +# Store the certificates to the keychain +echo -n "${DEVELOPER_ID_APPLICATION_P12}" | base64 --decode -o "${APP_P12}" +echo -n "${DEVELOPER_ID_INSTALLER_P12}" | base64 --decode -o "${INST_P12}" + +# Create temporary keychain +security create-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}" +security set-keychain-settings -lut 21600 "${KEYCHAIN_PATH}" +security unlock-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}" + +# Import the Apple intermediate CA certificate into the keychain +wget https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer +security import DeveloperIDG2CA.cer -k "${KEYCHAIN_PATH}" + +# Import certificates to keychain +security import "${APP_P12}" -P "${DEVELOPER_ID_APPLICATION_P12_PASS}" -A -t cert -f pkcs12 -k "${KEYCHAIN_PATH}" +security import "${INST_P12}" -P "${DEVELOPER_ID_INSTALLER_P12_PASS}" -A -t cert -f pkcs12 -k "${KEYCHAIN_PATH}" +security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "${KEYCHAIN_PATH}" +security list-keychain -d user -s "${KEYCHAIN_PATH}" +security default-keychain -d user -s "${KEYCHAIN_PATH}" + +security find-identity -v -p codesigning + +# Clean up the P12 files +rm -f "${APP_P12}" "${INST_P12}" diff --git a/ci/macos-universal-deps.sh b/ci/macos-universal-deps.sh index cd24f4066e..03ea7e00a3 100755 --- a/ci/macos-universal-deps.sh +++ b/ci/macos-universal-deps.sh @@ -22,16 +22,16 @@ echo "Installing dependencies for ${arch} into ${cache_dir}" ogg_version="1.3.5" vorbis_version="1.3.7" flac_version="1.4.3" -opus_version="1.4" +opus_version="1.5.2" blake2_version="0.98.1" zstd_version="1.5.6" -libarchive_version="3.7.3" -mpg123_version="1.32.4" +libarchive_version="3.7.7" +mpg123_version="1.32.8" lame_version="3.100" libsndfile_version="1.2.2" libusb_version="1.0.27" -openssl_version="3.0.13" -wx_version="3.2.5" +openssl_version="3.0.15" +wx_version="3.2.6" macos_deployment_target="10.13" @@ -166,7 +166,7 @@ cd .. rm -rf opus-${opus_version} #blake2 -if [ ! -f libb2-${blake2_version}.tar.gz ]; then +if [ ! -f libb2-${blake2_version}.tar.gz ]; then wget https://github.com/BLAKE2/libb2/releases/download/v${blake2_version}/libb2-${blake2_version}.tar.gz fi tar xzf libb2-${blake2_version}.tar.gz @@ -228,7 +228,20 @@ cd libarchive-${libarchive_version} #lipo -create libarchive.13.dylib.x86-64 libarchive.13.dylib.arm64 -output .libs/libarchive.13.dylib mkdir bld cd bld -cmake -DCMAKE_OSX_ARCHITECTURES="${arch}" -DCMAKE_INSTALL_PREFIX=${cache_dir} -DENABLE_LZ4=false -DZSTD_INCLUDE_DIR=${cache_dir}/include -DZSTD_LIBRARY=${cache_dir}/lib/libzstd.dylib -DLIBB2_INCLUDE_DIR=${cache_dir}/include -DLIBB2_LIBRARY=${cache_dir}/lib/libb2.dylib -DCMAKE_POLICY_DEFAULT_CMP0068=NEW -DCMAKE_SKIP_BUILD_RPATH=FALSE -DCMAKE_BUILD_WITH_INSTALL_RPATH=FALSE -DCMAKE_INSTALL_RPATH=${cache_dir}/lib -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE .. +cmake -DCMAKE_OSX_ARCHITECTURES="${arch}" \ + -DCMAKE_INSTALL_PREFIX=${cache_dir} \ + -DENABLE_LZ4=false \ + -DZSTD_INCLUDE_DIR=${cache_dir}/include \ + -DZSTD_LIBRARY=${cache_dir}/lib/libzstd.dylib \ + -DLIBB2_INCLUDE_DIR=${cache_dir}/include \ + -DLIBB2_LIBRARY=${cache_dir}/lib/libb2.dylib \ + -DCMAKE_POLICY_DEFAULT_CMP0068=NEW \ + -DCMAKE_SKIP_BUILD_RPATH=FALSE \ + -DCMAKE_BUILD_WITH_INSTALL_RPATH=FALSE \ + -DCMAKE_INSTALL_RPATH=${cache_dir}/lib \ + -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE \ + -DOCPN_BUILD_SAMPLE=ON \ + .. #-DCMAKE_MACOSX_RPATH=FALSE .. make -j ${ncp} make install @@ -349,7 +362,7 @@ fi tar xjf openssl-${openssl_version}.tar.gz cd openssl-${openssl_version} if [[ "${arch}" = *"x86_64"* ]]; then - ./Configure --prefix="${cache_dir}" darwin64-x86_64-cc shared + ./Configure --prefix="${cache_dir}" darwin64-x86_64-cc shared make -j ${ncpu} if [[ "${arch}" = *"x86_64"* ]] && [[ "${arch}" = *"arm64"* ]]; then mv libcrypto.3.dylib libcrypto.3.dylib.x86-64 diff --git a/ci/universal-build-macos.sh b/ci/universal-build-macos.sh index e5c52928c9..50a129c26d 100755 --- a/ci/universal-build-macos.sh +++ b/ci/universal-build-macos.sh @@ -93,6 +93,7 @@ cmake -DOCPN_CI_BUILD=$CI_BUILD \ -DCMAKE_INSTALL_PREFIX=/tmp/opencpn -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ -DOCPN_RELEASE=${RELEASE} \ -DOCPN_BUILD_TEST=OFF \ + -DOCPN_BUILD_SAMPLE=ON \ -DOCPN_USE_DEPS_BUNDLE=ON \ -DCMAKE_OSX_ARCHITECTURES="${ARCHS}" \ -DOCPN_USE_SYSTEM_LIBARCHIVE=OFF \ @@ -123,7 +124,16 @@ make install # Dunno why the second is needed but it is, otherwise # plugin data is not included in the bundle # Make sure the code signatures are correct -codesign --force --deep --sign - /tmp/opencpn/bin/OpenCPN.app +if [ -z "${APPLE_DEVELOPER_ID}" ]; then + # We do not have a paid Apple developer account, let's just reset the signatures + codesign --force --deep --sign - /tmp/opencpn/bin/OpenCPN.app +else + # We do have an account and did set up the certificates in ci/mac-sign.sh, let's sign the application bundle + codesign --verbose --sign "${APPLE_DEVELOPER_ID}" --options=runtime --timestamp --options=runtime /tmp/opencpn/bin/OpenCPN.app/Contents/PlugIns/*.dylib + codesign --deep --force --verbose --sign "${APPLE_DEVELOPER_ID}" --entitlements ../buildosx/entitlements.plist --timestamp --options=runtime /tmp/opencpn/bin/OpenCPN.app +fi + +codesign -dv --verbose /tmp/opencpn/bin/OpenCPN.app dsymutil -o OpenCPN.dSYM /tmp/opencpn/bin/OpenCPN.app/Contents/MacOS/OpenCPN tar czf OpenCPN-$(git rev-parse --short HEAD).dSYM.tar.gz OpenCPN.dSYM @@ -133,5 +143,18 @@ if [[ ! -z "${CREATE_DMG+x}" ]]; then make create-dmg fi +# Sign the installer if we have an Apple developer account set up +if [ -n "${APPLE_DEVELOPER_ID}" ]; then + shopt -s nullglob + for pkg_file in OpenCPN*.pkg; do + productsign --sign "${APPLE_DEVELOPER_ID}" "${pkg_file}" pkg.signed + mv pkg.signed "${pkg_file}" + done + # The resulting package has to be submitted to Apple for notarization + # xcrun notarytool submit --apple-id --team-id --password + # Once succesfully notarized, run the stapler to include the notarization ticket into the installer + # xcrun stapler staple +fi + # The build is over, if there is error now it is not ours set +e diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 286aaea35e..9bd5632d59 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -86,4 +86,7 @@ if (APPLE) PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ WORLD_EXECUTE GROUP_EXECUTE OWNER_EXECUTE ) + set_target_properties(opencpn-cmd PROPERTIES + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES + ) endif () diff --git a/glutil/CMakeLists.txt b/glutil/CMakeLists.txt index d1253589a8..52e9afdc55 100644 --- a/glutil/CMakeLists.txt +++ b/glutil/CMakeLists.txt @@ -43,7 +43,7 @@ if (CMAKE_HOST_WIN32) target_link_libraries( ${APP_TARGET} PRIVATE setupapi.lib "GLU_static" psapi.lib iphlpapi.lib # glu32.lib - ) + ) # use gdi plus only on MSVC, it is not a free library if (MSVC) target_link_libraries(${APP_TARGET} PRIVATE gdiplus.lib) @@ -78,4 +78,7 @@ if (APPLE) PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ WORLD_EXECUTE GROUP_EXECUTE OWNER_EXECUTE ) + set_target_properties(${APP_TARGET} PROPERTIES + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES + ) endif () diff --git a/glutil/console.cpp b/glutil/console.cpp index 7473dce0c8..065f74605c 100644 --- a/glutil/console.cpp +++ b/glutil/console.cpp @@ -1,5 +1,3 @@ -/** \file console.cpp Simple CLI application to check OpenGL capabilities. */ - /************************************************************************** * Copyright (C) 2022 Alec Leamas * * Copyright (C) 2024 Pavel Kalian * @@ -18,7 +16,11 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** + ***************************************************************************/ + +/** + * \file + * Simple CLI application to check OpenGL capabilities. */ #include "wx/wxprec.h" diff --git a/gui/include/gui/comm_overflow_dlg.h b/gui/include/gui/comm_overflow_dlg.h index ca1ed79068..3d57ce73c9 100644 --- a/gui/include/gui/comm_overflow_dlg.h +++ b/gui/include/gui/comm_overflow_dlg.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file comm_oveflow_dlg.h Popup dialog on communication overflows. */ +/** + * \file + * Popup dialog on communication overflows + */ #ifndef COMM_OVERFLOW_DLG_H__ #define COMM_OVERFLOW_DLG_H__ diff --git a/gui/include/gui/gui_lib.h b/gui/include/gui/gui_lib.h index de707daaf5..9794089704 100644 --- a/gui/include/gui/gui_lib.h +++ b/gui/include/gui/gui_lib.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file gui_lib.h General purpose GUI support. */ +/** + * \file + * General purpose GUI support. + */ #ifndef GUI_LIB_H__ #define GUI_LIB_H__ diff --git a/gui/include/gui/ocpn_frame.h b/gui/include/gui/ocpn_frame.h index 1f0148662c..f8b61a65ce 100644 --- a/gui/include/gui/ocpn_frame.h +++ b/gui/include/gui/ocpn_frame.h @@ -333,6 +333,7 @@ class MyFrame : public wxFrame { void InitApiListeners(); void ReleaseApiListeners(); void UpdateStatusBar(void); + void ConfigureStatusBar(); private: void CheckToolbarPosition(); diff --git a/gui/include/gui/pluginmanager.h b/gui/include/gui/pluginmanager.h index 14d70e1890..27477b9fef 100644 --- a/gui/include/gui/pluginmanager.h +++ b/gui/include/gui/pluginmanager.h @@ -123,7 +123,8 @@ enum ActionVerb { REINSTALL_MANAGED_VERSION, DOWNGRADE_INSTALLED_MANAGED_VERSION, UNINSTALL_MANAGED_VERSION, - INSTALL_MANAGED_VERSION + INSTALL_MANAGED_VERSION, + UPDATE_IMPORTED_VERSION }; class PlugInMenuItemContainer { @@ -256,6 +257,7 @@ class PlugInManager : public wxEvtHandler { bool SendMouseEventToPlugins(wxMouseEvent& event); bool SendKeyEventToPlugins(wxKeyEvent& event); + void SendPreShutdownHookToPlugins(); void SendBaseConfigToAllPlugIns(); void SendS52ConfigToAllPlugIns(bool bReconfig = false); diff --git a/gui/include/gui/udev_rule_mgr.h b/gui/include/gui/udev_rule_mgr.h index 038c074567..9c1e8d3304 100644 --- a/gui/include/gui/udev_rule_mgr.h +++ b/gui/include/gui/udev_rule_mgr.h @@ -18,8 +18,9 @@ **************************************************************************/ /** - * \file udev_rule_mgr.h Access checks for comm devices and dongle. + * \file * + * Access checks for comm devices and dongle. * Only making anything useful on Linux. */ diff --git a/gui/src/chcanv.cpp b/gui/src/chcanv.cpp index 9050d1723d..3b362b7ebd 100644 --- a/gui/src/chcanv.cpp +++ b/gui/src/chcanv.cpp @@ -1218,6 +1218,7 @@ void ChartCanvas::SetShowGPS(bool bshow) { } void ChartCanvas::SetShowGPSCompassWindow(bool bshow) { + m_bShowCompassWin = bshow; if (m_Compass) { m_Compass->Show(m_bShowCompassWin); if (m_bShowCompassWin) m_Compass->UpdateStatus(); @@ -7119,7 +7120,10 @@ bool ChartCanvas::MouseEventMUIBar(wxMouseEvent &event) { cursor_region = CENTER; if (!g_btouch) SetCanvasCursor(event); - return true; + if (m_muiBar) + return true; + else + return false; } bool ChartCanvas::MouseEventSetup(wxMouseEvent &event, bool b_handle_dclick) { diff --git a/gui/src/comm_overflow_dlg.cpp b/gui/src/comm_overflow_dlg.cpp index ae9587b811..8b46d8922d 100644 --- a/gui/src/comm_overflow_dlg.cpp +++ b/gui/src/comm_overflow_dlg.cpp @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file comm_oveflow_dlg.cpp Implement comm_oveflow_dlg.cpp.h. */ +/** + * \file + * Implement comm_oveflow_dlg.cpp.h. + */ #include diff --git a/gui/src/glChartCanvas.cpp b/gui/src/glChartCanvas.cpp index b66984efce..464f7df22a 100644 --- a/gui/src/glChartCanvas.cpp +++ b/gui/src/glChartCanvas.cpp @@ -4995,14 +4995,9 @@ void glChartCanvas::onZoomTimerEvent(wxTimerEvent &event) { ZoomProject(m_runoffsetx, m_runoffsety, m_runswidth, m_runsheight); } else { - // qDebug() << "onZoomTimerEvent DONE" << m_nRun << m_nTotal; - zoomTimer.Stop(); if (m_zoomFinal) { - // qDebug() << "onZoomTimerEvent FINALZOOM" << m_zoomFinalZoom; - - m_pParentCanvas->ZoomCanvas(m_zoomFinalZoom, false); - + m_pParentCanvas->ZoomCanvasSimple(m_zoomFinalZoom); if (m_zoomFinaldx || m_zoomFinaldy) { m_pParentCanvas->PanCanvas(m_zoomFinaldx, m_zoomFinaldy); } @@ -5487,6 +5482,9 @@ void glChartCanvas::OnEvtZoomGesture(wxZoomGestureEvent &event) { // qDebug() << "finish totalzoom" << total_zoom_val << // projected_scale; + // Some ptaforms generate spurious gestureEnd events. Guard for this. + if (!m_binGesture) return; + float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2); float cc_y = m_fbo_offsety + (m_fbo_sheight / 2); float dy = 0; diff --git a/gui/src/gui_lib.cpp b/gui/src/gui_lib.cpp index d1a3d37fad..c0f467b7e7 100644 --- a/gui/src/gui_lib.cpp +++ b/gui/src/gui_lib.cpp @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file gui_lib.cpp Implements gui_lib.h */ +/** + * \file + * Implements gui_lib.h + */ #include #include diff --git a/gui/src/hotkeys_dlg.cpp b/gui/src/hotkeys_dlg.cpp index 040501b6f6..42612b2bb5 100644 --- a/gui/src/hotkeys_dlg.cpp +++ b/gui/src/hotkeys_dlg.cpp @@ -68,23 +68,28 @@ class ButtonsSizer : public wxStdDialogButtonSizer { /** Overall help message: key functions and bindings in a string matrix */ class GridSizer : public wxGridSizer { +private: + static constexpr int kGridSize{4}; + static constexpr int kNumMsgs{12}; + using MsgLine = std::array; + using Messages = std::array; + public: - GridSizer(wxWindow* parent) : wxGridSizer(kMacMessages[0].size()) { + GridSizer(wxWindow* parent) : wxGridSizer(kGridSize) { const auto osSystemId = wxPlatformInfo::Get().GetOperatingSystemId(); - const auto& kMessages = + const Messages& kMessages = (osSystemId & wxOS_MAC) ? kMacMessages : kWinLinuxMessages; - for (auto line = kMessages.begin(); line != kMessages.end(); line++) { - for (auto word = line->begin(); word != line->end(); word++) { - Add(new wxStaticText(parent, wxID_ANY, *word), + + for (const MsgLine& line : kMessages) + for (const wxString& word : line) + Add(new wxStaticText(parent, wxID_ANY, word), wxSizerFlags().DoubleBorder()); - } - } } private: // It's unclear whether _() actually works in this context or // if wxTRANSLATE is needed instead... - const std::array, 12> kWinLinuxMessages{ + const Messages kWinLinuxMessages{ // clang-format off {{_("Zoom in"), "+, PgUp", _("Zoom out"), "-, PgDown"}, @@ -110,7 +115,7 @@ class GridSizer : public wxGridSizer { {_("Drop mark"), _("Ctrl O, space bar"), "", ""}}}; - const std::array, 12> kMacMessages{ + const Messages kMacMessages{ {{_("Zoom in"), "+, PgUp", _("Zoom out"), "-, PgDown"}, {_("Fine zoom in"), "Alt +", diff --git a/gui/src/load_errors_dlg.cpp b/gui/src/load_errors_dlg.cpp index 2adc8747c5..6f86c8d6aa 100644 --- a/gui/src/load_errors_dlg.cpp +++ b/gui/src/load_errors_dlg.cpp @@ -18,8 +18,8 @@ **************************************************************************/ /** - * \file load_errors_dlg.h - * \brief Handle dialog reporting plugin load errors. + * \file + * Handle dialog reporting plugin load errors. * * PluginLoader emits an event containing a list of all plugins which cannot * be loaded for various reasons when loading is complete. If this list is diff --git a/gui/src/navutil.cpp b/gui/src/navutil.cpp index 4795b98579..814fae1629 100644 --- a/gui/src/navutil.cpp +++ b/gui/src/navutil.cpp @@ -645,7 +645,7 @@ int MyConfig::LoadMyConfig() { g_nAWDefault = 50; g_nAWMax = 1852; - g_ObjQFileExt = _T("txt,rtf,png,html,gif,tif"); + g_ObjQFileExt = _T("txt,rtf,png,html,gif,tif,jpg"); // Load the raw value, with no defaults, and no processing int ret_Val = LoadMyConfigRaw(); diff --git a/gui/src/ocpn_frame.cpp b/gui/src/ocpn_frame.cpp index c0a9782ca9..67d06f3505 100644 --- a/gui/src/ocpn_frame.cpp +++ b/gui/src/ocpn_frame.cpp @@ -631,7 +631,7 @@ static void onBellsFinishedCB(void *ptr) { static void OnDriverMsg(const ObservedEvt &ev) { auto msg = ev.GetString().ToStdString(); - OCPNMessageBox(GetTopWindow(), msg, _("Communication Error"), 0, 15); + OCPNMessageBox(GetTopWindow(), msg, _("Communication Error")); } // My frame constructor @@ -1562,6 +1562,9 @@ void MyFrame::OnCloseWindow(wxCloseEvent &event) { } } + // Give any requesting plugins a PreShutdownHook call + g_pi_manager->SendPreShutdownHookToPlugins(); + // We save perspective before closing to restore position next time // Pane is not closed so the child is not notified (OnPaneClose) if (g_pAISTargetList) { @@ -3379,7 +3382,7 @@ void MyFrame::SetToolbarItemSVG(int tool_id, wxString normalSVGfile, } } -void MyFrame::ApplyGlobalSettings(bool bnewtoolbar) { +void MyFrame::ConfigureStatusBar() { // ShowDebugWindow as a wxStatusBar m_StatusBarFieldCount = g_Platform->GetStatusBarFieldCount(); @@ -3401,6 +3404,10 @@ void MyFrame::ApplyGlobalSettings(bool bnewtoolbar) { SetStatusBar(NULL); } } +} + +void MyFrame::ApplyGlobalSettings(bool bnewtoolbar) { + ConfigureStatusBar(); wxSize lastOptSize = options_lastWindowSize; SendSizeEvent(); diff --git a/gui/src/ocpn_plugin_gui.cpp b/gui/src/ocpn_plugin_gui.cpp new file mode 100644 index 0000000000..8a040e71ff --- /dev/null +++ b/gui/src/ocpn_plugin_gui.cpp @@ -0,0 +1,2487 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: PlugIn GUI API Functions + * Author: David Register + * + *************************************************************************** + * Copyright (C) 2024 by David S. Register * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + +#include "dychart.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ocpn_plugin.h" +#include "pluginmanager.h" +#include "toolbar.h" +#include "options.h" +#include "s52plib.h" +#include "model/route.h" +#include "model/track.h" +#include "routemanagerdialog.h" +#include "model/multiplexer.h" +#include "chartdb.h" +#include "OCPNPlatform.h" +#include "OCPN_AUIManager.h" +#include "FontMgr.h" +#include "gui_lib.h" +#include "model/ais_decoder.h" +#include "ocpn_app.h" +#include "ocpn_frame.h" +#include "svg_utils.h" +#include "navutil.h" +#include "model/comm_navmsg_bus.h" +#include "chcanv.h" +#include "piano.h" +#include "waypointman_gui.h" +#include "routeman_gui.h" +#include "glChartCanvas.h" +#include "SoundFactory.h" +#include "SystemCmdSound.h" +#include "ais.h" + +extern PlugInManager* s_ppim; +extern MyConfig* pConfig; +extern OCPN_AUIManager* g_pauimgr; + +#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0) +extern wxLocale* plocale_def_lang; +#endif + +extern OCPNPlatform* g_Platform; +extern ChartDB* ChartData; +extern MyFrame* gFrame; +extern ocpnStyle::StyleManager* g_StyleManager; +extern options* g_pOptions; +extern Multiplexer* g_pMUX; +extern bool g_bShowChartBar; +extern Routeman* g_pRouteMan; +extern Select* pSelect; +extern RouteManagerDialog* pRouteManagerDialog; +extern RouteList* pRouteList; +extern std::vector g_TrackList; +extern PlugInManager* g_pi_manager; +extern s52plib* ps52plib; +extern wxString ChartListFileName; +extern bool g_boptionsactive; +extern options* g_options; +extern ColorScheme global_color_scheme; +extern wxArrayString g_locale_catalog_array; +extern int g_GUIScaleFactor; +extern int g_ChartScaleFactor; +extern wxString g_locale; +extern ocpnFloatingToolbarDialog* g_MainToolbar; + +extern int g_chart_zoom_modifier_raster; +extern int g_chart_zoom_modifier_vector; +extern double g_display_size_mm; +extern bool g_bopengl; +extern AisDecoder* g_pAIS; +extern ChartGroupArray* g_pGroupArray; + +// extern ChartGroupArray* g_pGroupArray; +extern unsigned int g_canvasConfig; + +extern wxString g_CmdSoundString; + +unsigned int gs_plib_flags; +wxString g_lastPluginMessage; +extern ChartCanvas* g_focusCanvas; +extern ChartCanvas* g_overlayCanvas; +extern bool g_bquiting; + +WX_DEFINE_ARRAY_PTR(ChartCanvas*, arrayofCanvasPtr); +extern arrayofCanvasPtr g_canvasArray; + +void NotifySetupOptionsPlugin(const PlugInData* pic); + +//--------------------------------------------------------------------------- +/* Implementation of OCPN core functions callable by plugins + * Sorted by API version number + * The definitions of this API are found in ocpn_plugin.h + * PlugIns may call these static functions as necessary for system services + */ +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// API 1.6 +//--------------------------------------------------------------------------- +/* Main Toolbar support */ +int InsertPlugInTool(wxString label, wxBitmap* bitmap, wxBitmap* bmpRollover, + wxItemKind kind, wxString shortHelp, wxString longHelp, + wxObject* clientData, int position, int tool_sel, + opencpn_plugin* pplugin) { + if (s_ppim) + return s_ppim->AddToolbarTool(label, bitmap, bmpRollover, kind, shortHelp, + longHelp, clientData, position, tool_sel, + pplugin); + else + return -1; +} + +void RemovePlugInTool(int tool_id) { + if (s_ppim) s_ppim->RemoveToolbarTool(tool_id); +} + +void SetToolbarToolViz(int item, bool viz) { + if (s_ppim) s_ppim->SetToolbarToolViz(item, viz); +} + +void SetToolbarItemState(int item, bool toggle) { + if (s_ppim) s_ppim->SetToolbarItemState(item, toggle); +} + +void SetToolbarToolBitmaps(int item, wxBitmap* bitmap, wxBitmap* bmpRollover) { + if (s_ppim) s_ppim->SetToolbarItemBitmaps(item, bitmap, bmpRollover); +} + +int InsertPlugInToolSVG(wxString label, wxString SVGfile, + wxString SVGfileRollover, wxString SVGfileToggled, + wxItemKind kind, wxString shortHelp, wxString longHelp, + wxObject* clientData, int position, int tool_sel, + opencpn_plugin* pplugin) { + if (s_ppim) + return s_ppim->AddToolbarTool(label, SVGfile, SVGfileRollover, + SVGfileToggled, kind, shortHelp, longHelp, + clientData, position, tool_sel, pplugin); + else + return -1; +} + +void SetToolbarToolBitmapsSVG(int item, wxString SVGfile, + wxString SVGfileRollover, + wxString SVGfileToggled) { + if (s_ppim) + s_ppim->SetToolbarItemBitmaps(item, SVGfile, SVGfileRollover, + SVGfileToggled); +} + +/* Canvas Context Menu support */ +int AddCanvasMenuItem(wxMenuItem* pitem, opencpn_plugin* pplugin, + const char* name) { + if (s_ppim) + return s_ppim->AddCanvasContextMenuItem(pitem, pplugin, name); + else + return -1; +} + +void SetCanvasMenuItemViz(int item, bool viz, const char* name) { + if (s_ppim) s_ppim->SetCanvasContextMenuItemViz(item, viz, name); +} + +void SetCanvasMenuItemGrey(int item, bool grey, const char* name) { + if (s_ppim) s_ppim->SetCanvasContextMenuItemGrey(item, grey, name); +} + +void RemoveCanvasMenuItem(int item, const char* name) { + if (s_ppim) s_ppim->RemoveCanvasContextMenuItem(item, name); +} + +int AddCanvasContextMenuItem(wxMenuItem* pitem, opencpn_plugin* pplugin) { + /* main context popup menu */ + return AddCanvasMenuItem(pitem, pplugin, ""); +} + +void SetCanvasContextMenuItemViz(int item, bool viz) { + SetCanvasMenuItemViz(item, viz); +} + +void SetCanvasContextMenuItemGrey(int item, bool grey) { + SetCanvasMenuItemGrey(item, grey); +} + +void RemoveCanvasContextMenuItem(int item) { RemoveCanvasMenuItem(item); } + +/* Utility functions */ +wxFileConfig* GetOCPNConfigObject(void) { + if (s_ppim) + return reinterpret_cast( + pConfig); // return the global application config object + else + return NULL; +} + +wxWindow* GetOCPNCanvasWindow() { + wxWindow* pret = NULL; + if (s_ppim) { + MyFrame* pFrame = s_ppim->GetParentFrame(); + pret = (wxWindow*)pFrame->GetPrimaryCanvas(); + } + return pret; +} + +void RequestRefresh(wxWindow* win) { + if (win) win->Refresh(false); +} + +void GetCanvasPixLL(PlugIn_ViewPort* vp, wxPoint* pp, double lat, double lon) { + // Make enough of an application viewport to run its method.... + ViewPort ocpn_vp; + ocpn_vp.clat = vp->clat; + ocpn_vp.clon = vp->clon; + ocpn_vp.m_projection_type = vp->m_projection_type; + ocpn_vp.view_scale_ppm = vp->view_scale_ppm; + ocpn_vp.skew = vp->skew; + ocpn_vp.rotation = vp->rotation; + ocpn_vp.pix_width = vp->pix_width; + ocpn_vp.pix_height = vp->pix_height; + + wxPoint ret = ocpn_vp.GetPixFromLL(lat, lon); + pp->x = ret.x; + pp->y = ret.y; +} + +void GetDoubleCanvasPixLL(PlugIn_ViewPort* vp, wxPoint2DDouble* pp, double lat, + double lon) { + // Make enough of an application viewport to run its method.... + ViewPort ocpn_vp; + ocpn_vp.clat = vp->clat; + ocpn_vp.clon = vp->clon; + ocpn_vp.m_projection_type = vp->m_projection_type; + ocpn_vp.view_scale_ppm = vp->view_scale_ppm; + ocpn_vp.skew = vp->skew; + ocpn_vp.rotation = vp->rotation; + ocpn_vp.pix_width = vp->pix_width; + ocpn_vp.pix_height = vp->pix_height; + + *pp = ocpn_vp.GetDoublePixFromLL(lat, lon); +} + +void GetCanvasLLPix(PlugIn_ViewPort* vp, wxPoint p, double* plat, + double* plon) { + // Make enough of an application viewport to run its method.... + ViewPort ocpn_vp; + ocpn_vp.clat = vp->clat; + ocpn_vp.clon = vp->clon; + ocpn_vp.m_projection_type = vp->m_projection_type; + ocpn_vp.view_scale_ppm = vp->view_scale_ppm; + ocpn_vp.skew = vp->skew; + ocpn_vp.rotation = vp->rotation; + ocpn_vp.pix_width = vp->pix_width; + ocpn_vp.pix_height = vp->pix_height; + + return ocpn_vp.GetLLFromPix(p, plat, plon); +} + +bool GetGlobalColor(wxString colorName, wxColour* pcolour) { + wxColour c = GetGlobalColor(colorName); + *pcolour = c; + + return true; +} + +wxFont* OCPNGetFont(wxString TextElement, int default_size) { + return FontMgr::Get().GetFont(TextElement, default_size); +} + +wxFont* GetOCPNScaledFont_PlugIn(wxString TextElement, int default_size) { + return GetOCPNScaledFont(TextElement, default_size); +} + +double GetOCPNGUIToolScaleFactor_PlugIn(int GUIScaleFactor) { + return g_Platform->GetToolbarScaleFactor(GUIScaleFactor); +} + +double GetOCPNGUIToolScaleFactor_PlugIn() { + return g_Platform->GetToolbarScaleFactor(g_GUIScaleFactor); +} + +float GetOCPNChartScaleFactor_Plugin() { + return g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor); +} + +wxFont GetOCPNGUIScaledFont_PlugIn(wxString item) { + return GetOCPNGUIScaledFont(item); +} + +bool AddPersistentFontKey(wxString TextElement) { + return FontMgr::Get().AddAuxKey(TextElement); +} + +wxString GetActiveStyleName() { + if (g_StyleManager) + return g_StyleManager->GetCurrentStyle()->name; + else + return _T(""); +} + +wxBitmap GetBitmapFromSVGFile(wxString filename, unsigned int width, + unsigned int height) { + wxBitmap bmp = LoadSVG(filename, width, height); + + if (bmp.IsOk()) + return bmp; + else { + // On error in requested width/height parameters, + // try to find and use dimensions embedded in the SVG file + unsigned int w, h; + SVGDocumentPixelSize(filename, w, h); + if (w == 0 || h == 0) { + // We did not succeed in deducing the size from SVG (svg element + // x misses width, height or both attributes), let's use some "safe" + // default + w = 32; + h = 32; + } + return LoadSVG(filename, w, h); + } +} + +bool IsTouchInterface_PlugIn(void) { return g_btouch; } + +wxColour GetFontColour_PlugIn(wxString TextElement) { + return FontMgr::Get().GetFontColor(TextElement); +} + +wxString* GetpSharedDataLocation(void) { + return g_Platform->GetSharedDataDirPtr(); +} + +ArrayOfPlugIn_AIS_Targets* GetAISTargetArray(void) { + if (!g_pAIS) return NULL; + + ArrayOfPlugIn_AIS_Targets* pret = new ArrayOfPlugIn_AIS_Targets; + + // Iterate over the AIS Target Hashmap + for (const auto& it : g_pAIS->GetTargetList()) { + auto td = it.second; + PlugIn_AIS_Target* ptarget = Create_PI_AIS_Target(td.get()); + pret->Add(ptarget); + } + +// Test one alarm target +#if 0 + AisTargetData td; + td.n_alarm_state = AIS_ALARM_SET; + PlugIn_AIS_Target *ptarget = Create_PI_AIS_Target(&td); + pret->Add(ptarget); +#endif + return pret; +} + +wxAuiManager* GetFrameAuiManager(void) { return g_pauimgr; } + +void SendPluginMessage(wxString message_id, wxString message_body) { + s_ppim->SendMessageToAllPlugins(message_id, message_body); + + // We will send an event to the main application frame (gFrame) + // for informational purposes. + // Of course, gFrame is encouraged to use any or all the + // data flying by if judged useful and dependable.... + + OCPN_MsgEvent Nevent(wxEVT_OCPN_MSG, 0); + Nevent.SetID(message_id); + Nevent.SetJSONText(message_body); + gFrame->GetEventHandler()->AddPendingEvent(Nevent); +} + +void DimeWindow(wxWindow* win) { DimeControl(win); } + +void JumpToPosition(double lat, double lon, double scale) { + gFrame->JumpToPosition(gFrame->GetFocusCanvas(), lat, lon, scale); +} + +/* Locale (i18N) support */ +bool AddLocaleCatalog(wxString catalog) { +#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0) + + if (plocale_def_lang) { + // Add this catalog to the persistent catalog array + g_locale_catalog_array.Add(catalog); + + return plocale_def_lang->AddCatalog(catalog); + } else +#endif + return false; +} + +wxString GetLocaleCanonicalName() { return g_locale; } + +/* NMEA interface support */ +void PushNMEABuffer(wxString buf) { + std::string full_sentence = buf.ToStdString(); + + if ((full_sentence[0] == '$') || (full_sentence[0] == '!')) { // Sanity check + std::string identifier; + // We notify based on full message, including the Talker ID + identifier = full_sentence.substr(1, 5); + + // notify message listener and also "ALL" N0183 messages, to support plugin + // API using original talker id + auto address = std::make_shared("virtual"); + auto msg = + std::make_shared(identifier, full_sentence, address); + auto msg_all = std::make_shared(*msg, "ALL"); + + auto& msgbus = NavMsgBus::GetInstance(); + + msgbus.Notify(std::move(msg)); + msgbus.Notify(std::move(msg_all)); + } +} + +/* Chart database access support */ +wxXmlDocument GetChartDatabaseEntryXML(int dbIndex, bool b_getGeom) { + wxXmlDocument doc = ChartData->GetXMLDescription(dbIndex, b_getGeom); + + return doc; +} + +bool UpdateChartDBInplace(wxArrayString dir_array, bool b_force_update, + bool b_ProgressDialog) { + // Make an array of CDI + ArrayOfCDI ChartDirArray; + for (unsigned int i = 0; i < dir_array.GetCount(); i++) { + wxString dirname = dir_array[i]; + ChartDirInfo cdi; + cdi.fullpath = dirname; + cdi.magic_number = _T(""); + ChartDirArray.Add(cdi); + } + bool b_ret = gFrame->UpdateChartDatabaseInplace(ChartDirArray, b_force_update, + b_ProgressDialog, + ChartData->GetDBFileName()); + gFrame->ChartsRefresh(); + return b_ret; +} + +wxArrayString GetChartDBDirArrayString() { + return ChartData->GetChartDirArrayString(); +} + +int AddChartToDBInPlace(wxString& full_path, bool b_RefreshCanvas) { + // extract the path from the chart name + wxFileName fn(full_path); + wxString fdir = fn.GetPath(); + + bool bret = false; + if (ChartData) { + bret = ChartData->AddSingleChart(full_path); + + if (bret) { + // Save to disk + pConfig->UpdateChartDirs(ChartData->GetChartDirArray()); + ChartData->SaveBinary(ChartListFileName); + + // Completely reload the chart database, for a fresh start + ArrayOfCDI XnewChartDirArray; + pConfig->LoadChartDirArray(XnewChartDirArray); + delete ChartData; + ChartData = new ChartDB(); + ChartData->LoadBinary(ChartListFileName, XnewChartDirArray); + + // Update group contents + if (g_pGroupArray) ChartData->ApplyGroupArray(g_pGroupArray); + + if (g_boptionsactive) { + g_options->UpdateDisplayedChartDirList(ChartData->GetChartDirArray()); + } + + if (b_RefreshCanvas || !gFrame->GetPrimaryCanvas()->GetQuiltMode()) { + gFrame->ChartsRefresh(); + } + } + } + return bret; +} + +int RemoveChartFromDBInPlace(wxString& full_path) { + bool bret = false; + if (ChartData) { + bret = ChartData->RemoveSingleChart(full_path); + + // Save to disk + pConfig->UpdateChartDirs(ChartData->GetChartDirArray()); + ChartData->SaveBinary(ChartListFileName); + + // Completely reload the chart database, for a fresh start + ArrayOfCDI XnewChartDirArray; + pConfig->LoadChartDirArray(XnewChartDirArray); + delete ChartData; + ChartData = new ChartDB(); + ChartData->LoadBinary(ChartListFileName, XnewChartDirArray); + + // Update group contents + if (g_pGroupArray) ChartData->ApplyGroupArray(g_pGroupArray); + + if (g_boptionsactive) { + g_options->UpdateDisplayedChartDirList(ChartData->GetChartDirArray()); + } + + gFrame->ChartsRefresh(); + } + + return bret; +} + +//--------------------------------------------------------------------------- +// API 1.9 +//--------------------------------------------------------------------------- +wxScrolledWindow* AddOptionsPage(OptionsParentPI parent, wxString title) { + if (!g_pOptions) return NULL; + + size_t parentid; + switch (parent) { + case PI_OPTIONS_PARENT_DISPLAY: + parentid = g_pOptions->m_pageDisplay; + break; + case PI_OPTIONS_PARENT_CONNECTIONS: + parentid = g_pOptions->m_pageConnections; + break; + case PI_OPTIONS_PARENT_CHARTS: + parentid = g_pOptions->m_pageCharts; + break; + case PI_OPTIONS_PARENT_SHIPS: + parentid = g_pOptions->m_pageShips; + break; + case PI_OPTIONS_PARENT_UI: + parentid = g_pOptions->m_pageUI; + break; + case PI_OPTIONS_PARENT_PLUGINS: + parentid = g_pOptions->m_pagePlugins; + break; + default: + wxLogMessage( + _T("Error in PluginManager::AddOptionsPage: Unknown parent")); + return NULL; + break; + } + + return g_pOptions->AddPage(parentid, title); +} + +bool DeleteOptionsPage(wxScrolledWindow* page) { + if (!g_pOptions) return false; + return g_pOptions->DeletePluginPage(page); +} + +bool DecodeSingleVDOMessage(const wxString& str, PlugIn_Position_Fix_Ex* pos, + wxString* accumulator) { + if (!pos) return false; + + GenericPosDatEx gpd; + AisError nerr = AIS_GENERIC_ERROR; + if (g_pAIS) nerr = g_pAIS->DecodeSingleVDO(str, &gpd, accumulator); + if (nerr == AIS_NoError) { + pos->Lat = gpd.kLat; + pos->Lon = gpd.kLon; + pos->Cog = gpd.kCog; + pos->Sog = gpd.kSog; + pos->Hdt = gpd.kHdt; + + // Fill in the dummy values + pos->FixTime = 0; + pos->Hdm = 1000; + pos->Var = 1000; + pos->nSats = 0; + + return true; + } + + return false; +} + +int GetChartbarHeight(void) { + int val = 0; + if (g_bShowChartBar) { + ChartCanvas* cc = gFrame->GetPrimaryCanvas(); + if (cc && cc->GetPiano()) { + val = cc->GetPiano()->GetHeight(); + } + } + return val; +} + +bool GetRoutepointGPX(RoutePoint* pRoutePoint, char* buffer, + unsigned int buffer_length) { + bool ret = false; + + NavObjectCollection1* pgpx = new NavObjectCollection1; + pgpx->AddGPXWaypoint(pRoutePoint); + wxString gpxfilename = wxFileName::CreateTempFileName(wxT("gpx")); + pgpx->SaveFile(gpxfilename); + delete pgpx; + + wxFFile gpxfile(gpxfilename); + wxString s; + if (gpxfile.ReadAll(&s)) { + if (s.Length() < buffer_length) { + strncpy(buffer, (const char*)s.mb_str(wxConvUTF8), buffer_length - 1); + ret = true; + } + } + + gpxfile.Close(); + ::wxRemoveFile(gpxfilename); + + return ret; +} + +bool GetActiveRoutepointGPX(char* buffer, unsigned int buffer_length) { + if (g_pRouteMan->IsAnyRouteActive()) + return GetRoutepointGPX(g_pRouteMan->GetpActivePoint(), buffer, + buffer_length); + else + return false; +} + +void PositionBearingDistanceMercator_Plugin(double lat, double lon, double brg, + double dist, double* dlat, + double* dlon) { + PositionBearingDistanceMercator(lat, lon, brg, dist, dlat, dlon); +} + +void DistanceBearingMercator_Plugin(double lat0, double lon0, double lat1, + double lon1, double* brg, double* dist) { + DistanceBearingMercator(lat0, lon0, lat1, lon1, brg, dist); +} + +double DistGreatCircle_Plugin(double slat, double slon, double dlat, + double dlon) { + return DistGreatCircle(slat, slon, dlat, dlon); +} + +void toTM_Plugin(float lat, float lon, float lat0, float lon0, double* x, + double* y) { + toTM(lat, lon, lat0, lon0, x, y); +} + +void fromTM_Plugin(double x, double y, double lat0, double lon0, double* lat, + double* lon) { + fromTM(x, y, lat0, lon0, lat, lon); +} + +void toSM_Plugin(double lat, double lon, double lat0, double lon0, double* x, + double* y) { + toSM(lat, lon, lat0, lon0, x, y); +} + +void fromSM_Plugin(double x, double y, double lat0, double lon0, double* lat, + double* lon) { + fromSM(x, y, lat0, lon0, lat, lon); +} + +void toSM_ECC_Plugin(double lat, double lon, double lat0, double lon0, + double* x, double* y) { + toSM_ECC(lat, lon, lat0, lon0, x, y); +} + +void fromSM_ECC_Plugin(double x, double y, double lat0, double lon0, + double* lat, double* lon) { + fromSM_ECC(x, y, lat0, lon0, lat, lon); +} + +double toUsrDistance_Plugin(double nm_distance, int unit) { + return toUsrDistance(nm_distance, unit); +} + +double fromUsrDistance_Plugin(double usr_distance, int unit) { + return fromUsrDistance(usr_distance, unit); +} + +double toUsrSpeed_Plugin(double kts_speed, int unit) { + return toUsrSpeed(kts_speed, unit); +} + +double toUsrWindSpeed_Plugin(double kts_speed, int unit) { + return toUsrWindSpeed(kts_speed, unit); +} + +double fromUsrSpeed_Plugin(double usr_speed, int unit) { + return fromUsrSpeed(usr_speed, unit); +} + +double fromUsrWindSpeed_Plugin(double usr_wspeed, int unit) { + return fromUsrWindSpeed(usr_wspeed, unit); +} + +double toUsrTemp_Plugin(double cel_temp, int unit) { + return toUsrTemp(cel_temp, unit); +} + +double fromUsrTemp_Plugin(double usr_temp, int unit) { + return fromUsrTemp(usr_temp, unit); +} + +wxString getUsrDistanceUnit_Plugin(int unit) { + return getUsrDistanceUnit(unit); +} + +wxString getUsrSpeedUnit_Plugin(int unit) { return getUsrSpeedUnit(unit); } + +wxString getUsrWindSpeedUnit_Plugin(int unit) { + return getUsrWindSpeedUnit(unit); +} + +wxString getUsrTempUnit_Plugin(int unit) { return getUsrTempUnit(unit); } + +bool PlugIn_GSHHS_CrossesLand(double lat1, double lon1, double lat2, + double lon2) { + static bool loaded = false; + if (!loaded) { + gshhsCrossesLandInit(); + loaded = true; + } + + return gshhsCrossesLand(lat1, lon1, lat2, lon2); +} + +void PlugInPlaySound(wxString& sound_file) { + PlugInPlaySoundEx(sound_file, -1); +} + +//--------------------------------------------------------------------------- +// API 1.10 +//--------------------------------------------------------------------------- + +// API Route and Waypoint Support +PlugIn_Waypoint::PlugIn_Waypoint() { m_HyperlinkList = NULL; } + +PlugIn_Waypoint::PlugIn_Waypoint(double lat, double lon, + const wxString& icon_ident, + const wxString& wp_name, + const wxString& GUID) { + wxDateTime now = wxDateTime::Now(); + m_CreateTime = now.ToUTC(); + m_HyperlinkList = NULL; + + m_lat = lat; + m_lon = lon; + m_IconName = icon_ident; + m_MarkName = wp_name; + m_GUID = GUID; +} + +PlugIn_Waypoint::~PlugIn_Waypoint() {} + +// PlugInRoute implementation +PlugIn_Route::PlugIn_Route(void) { pWaypointList = new Plugin_WaypointList; } + +PlugIn_Route::~PlugIn_Route(void) { + pWaypointList->DeleteContents(false); // do not delete Waypoints + pWaypointList->Clear(); + + delete pWaypointList; +} + +// PlugInTrack implementation +PlugIn_Track::PlugIn_Track(void) { pWaypointList = new Plugin_WaypointList; } + +PlugIn_Track::~PlugIn_Track(void) { + pWaypointList->DeleteContents(false); // do not delete Waypoints + pWaypointList->Clear(); + + delete pWaypointList; +} + +wxString GetNewGUID(void) { return GpxDocument::GetUUID(); } + +bool AddCustomWaypointIcon(wxBitmap* pimage, wxString key, + wxString description) { + wxImage image = pimage->ConvertToImage(); + WayPointmanGui(*pWayPointMan).ProcessIcon(image, key, description); + return true; +} + +static void cloneHyperlinkList(RoutePoint* dst, const PlugIn_Waypoint* src) { + // Transcribe (clone) the html HyperLink List, if present + if (src->m_HyperlinkList == nullptr) return; + + if (src->m_HyperlinkList->GetCount() > 0) { + wxPlugin_HyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst(); + while (linknode) { + Plugin_Hyperlink* link = linknode->GetData(); + + Hyperlink* h = new Hyperlink(); + h->DescrText = link->DescrText; + h->Link = link->Link; + h->LType = link->Type; + + dst->m_HyperlinkList->Append(h); + + linknode = linknode->GetNext(); + } + } +} + +bool AddSingleWaypoint(PlugIn_Waypoint* pwaypoint, bool b_permanent) { + // Validate the waypoint parameters a little bit + + // GUID + // Make sure that this GUID is indeed unique in the Routepoint list + bool b_unique = true; + wxRoutePointListNode* prpnode = pWayPointMan->GetWaypointList()->GetFirst(); + while (prpnode) { + RoutePoint* prp = prpnode->GetData(); + + if (prp->m_GUID == pwaypoint->m_GUID) { + b_unique = false; + break; + } + prpnode = prpnode->GetNext(); // RoutePoint + } + + if (!b_unique) return false; + + RoutePoint* pWP = + new RoutePoint(pwaypoint->m_lat, pwaypoint->m_lon, pwaypoint->m_IconName, + pwaypoint->m_MarkName, pwaypoint->m_GUID); + + pWP->m_bIsolatedMark = true; // This is an isolated mark + + cloneHyperlinkList(pWP, pwaypoint); + + pWP->m_MarkDescription = pwaypoint->m_MarkDescription; + + if (pwaypoint->m_CreateTime.IsValid()) + pWP->SetCreateTime(pwaypoint->m_CreateTime); + else { + wxDateTime dtnow(wxDateTime::Now()); + pWP->SetCreateTime(dtnow); + } + + pWP->m_btemp = (b_permanent == false); + + pSelect->AddSelectableRoutePoint(pwaypoint->m_lat, pwaypoint->m_lon, pWP); + if (b_permanent) pConfig->AddNewWayPoint(pWP, -1); + + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateWptListCtrl(); + + return true; +} + +bool DeleteSingleWaypoint(wxString& GUID) { + // Find the RoutePoint + bool b_found = false; + RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(GUID); + + if (prp) b_found = true; + + if (b_found) { + pWayPointMan->DestroyWaypoint(prp); + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateWptListCtrl(); + } + + return b_found; +} + +bool UpdateSingleWaypoint(PlugIn_Waypoint* pwaypoint) { + // Find the RoutePoint + bool b_found = false; + RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(pwaypoint->m_GUID); + + if (prp) b_found = true; + + if (b_found) { + double lat_save = prp->m_lat; + double lon_save = prp->m_lon; + + prp->m_lat = pwaypoint->m_lat; + prp->m_lon = pwaypoint->m_lon; + prp->SetIconName(pwaypoint->m_IconName); + prp->SetName(pwaypoint->m_MarkName); + prp->m_MarkDescription = pwaypoint->m_MarkDescription; + prp->SetVisible(pwaypoint->m_IsVisible); + if (pwaypoint->m_CreateTime.IsValid()) + prp->SetCreateTime(pwaypoint->m_CreateTime); + + // Transcribe (clone) the html HyperLink List, if present + + if (pwaypoint->m_HyperlinkList) { + prp->m_HyperlinkList->Clear(); + if (pwaypoint->m_HyperlinkList->GetCount() > 0) { + wxPlugin_HyperlinkListNode* linknode = + pwaypoint->m_HyperlinkList->GetFirst(); + while (linknode) { + Plugin_Hyperlink* link = linknode->GetData(); + + Hyperlink* h = new Hyperlink(); + h->DescrText = link->DescrText; + h->Link = link->Link; + h->LType = link->Type; + + prp->m_HyperlinkList->Append(h); + + linknode = linknode->GetNext(); + } + } + } + + if (prp) prp->ReLoadIcon(); + + auto canvas = gFrame->GetPrimaryCanvas(); + SelectCtx ctx(canvas->m_bShowNavobjects, canvas->GetCanvasTrueScale(), + canvas->GetScaleValue()); + SelectItem* pFind = + pSelect->FindSelection(ctx, lat_save, lon_save, SELTYPE_ROUTEPOINT); + if (pFind) { + pFind->m_slat = pwaypoint->m_lat; // update the SelectList entry + pFind->m_slon = pwaypoint->m_lon; + } + + if (!prp->m_btemp) pConfig->UpdateWayPoint(prp); + + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateWptListCtrl(); + } + + return b_found; +} + +// translate O route class to Plugin one +static void PlugInFromRoutePoint(PlugIn_Waypoint* dst, + /* const*/ RoutePoint* src) { + dst->m_lat = src->m_lat; + dst->m_lon = src->m_lon; + dst->m_IconName = src->GetIconName(); + dst->m_MarkName = src->GetName(); + dst->m_MarkDescription = src->m_MarkDescription; + dst->m_IsVisible = src->IsVisible(); + dst->m_CreateTime = src->GetCreateTime(); // not const + dst->m_GUID = src->m_GUID; + + // Transcribe (clone) the html HyperLink List, if present + if (src->m_HyperlinkList == nullptr) return; + + delete dst->m_HyperlinkList; + dst->m_HyperlinkList = nullptr; + + if (src->m_HyperlinkList->GetCount() > 0) { + dst->m_HyperlinkList = new Plugin_HyperlinkList; + + wxHyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst(); + while (linknode) { + Hyperlink* link = linknode->GetData(); + + Plugin_Hyperlink* h = new Plugin_Hyperlink(); + h->DescrText = link->DescrText; + h->Link = link->Link; + h->Type = link->LType; + + dst->m_HyperlinkList->Append(h); + + linknode = linknode->GetNext(); + } + } +} + +bool GetSingleWaypoint(wxString GUID, PlugIn_Waypoint* pwaypoint) { + // Find the RoutePoint + RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(GUID); + + if (!prp) return false; + + PlugInFromRoutePoint(pwaypoint, prp); + + return true; +} + +wxArrayString GetWaypointGUIDArray(void) { + wxArrayString result; + const RoutePointList* list = pWayPointMan->GetWaypointList(); + + wxRoutePointListNode* prpnode = list->GetFirst(); + while (prpnode) { + RoutePoint* prp = prpnode->GetData(); + result.Add(prp->m_GUID); + + prpnode = prpnode->GetNext(); // RoutePoint + } + + return result; +} + +wxArrayString GetRouteGUIDArray(void) { + wxArrayString result; + RouteList* list = pRouteList; + + wxRouteListNode* prpnode = list->GetFirst(); + while (prpnode) { + Route* proute = prpnode->GetData(); + result.Add(proute->m_GUID); + + prpnode = prpnode->GetNext(); // Route + } + + return result; +} + +wxArrayString GetTrackGUIDArray(void) { + wxArrayString result; + for (Track* ptrack : g_TrackList) { + result.Add(ptrack->m_GUID); + } + + return result; +} + +wxArrayString GetWaypointGUIDArray(OBJECT_LAYER_REQ req) { + wxArrayString result; + const RoutePointList* list = pWayPointMan->GetWaypointList(); + + wxRoutePointListNode* prpnode = list->GetFirst(); + while (prpnode) { + RoutePoint* prp = prpnode->GetData(); + switch (req) { + case OBJECTS_ALL: + result.Add(prp->m_GUID); + break; + case OBJECTS_NO_LAYERS: + if (!prp->m_bIsInLayer) result.Add(prp->m_GUID); + break; + case OBJECTS_ONLY_LAYERS: + if (prp->m_bIsInLayer) result.Add(prp->m_GUID); + break; + } + + prpnode = prpnode->GetNext(); // RoutePoint + } + + return result; +} + +wxArrayString GetRouteGUIDArray(OBJECT_LAYER_REQ req) { + wxArrayString result; + RouteList* list = pRouteList; + + wxRouteListNode* prpnode = list->GetFirst(); + while (prpnode) { + Route* proute = prpnode->GetData(); + switch (req) { + case OBJECTS_ALL: + result.Add(proute->m_GUID); + break; + case OBJECTS_NO_LAYERS: + if (!proute->m_bIsInLayer) result.Add(proute->m_GUID); + break; + case OBJECTS_ONLY_LAYERS: + if (proute->m_bIsInLayer) result.Add(proute->m_GUID); + break; + } + + prpnode = prpnode->GetNext(); // Route + } + + return result; +} + +wxArrayString GetTrackGUIDArray(OBJECT_LAYER_REQ req) { + wxArrayString result; + for (Track* ptrack : g_TrackList) { + switch (req) { + case OBJECTS_ALL: + result.Add(ptrack->m_GUID); + break; + case OBJECTS_NO_LAYERS: + if (!ptrack->m_bIsInLayer) result.Add(ptrack->m_GUID); + break; + case OBJECTS_ONLY_LAYERS: + if (ptrack->m_bIsInLayer) result.Add(ptrack->m_GUID); + break; + } + } + + return result; +} + +wxArrayString GetIconNameArray(void) { + wxArrayString result; + + for (int i = 0; i < pWayPointMan->GetNumIcons(); i++) { + wxString* ps = pWayPointMan->GetIconKey(i); + result.Add(*ps); + } + return result; +} + +bool AddPlugInRoute(PlugIn_Route* proute, bool b_permanent) { + Route* route = new Route(); + + PlugIn_Waypoint* pwp; + RoutePoint* pWP_src; + int ip = 0; + wxDateTime plannedDeparture; + + wxPlugin_WaypointListNode* pwpnode = proute->pWaypointList->GetFirst(); + while (pwpnode) { + pwp = pwpnode->GetData(); + + RoutePoint* pWP = new RoutePoint(pwp->m_lat, pwp->m_lon, pwp->m_IconName, + pwp->m_MarkName, pwp->m_GUID); + + // Transcribe (clone) the html HyperLink List, if present + cloneHyperlinkList(pWP, pwp); + pWP->m_MarkDescription = pwp->m_MarkDescription; + pWP->m_bShowName = false; + pWP->SetCreateTime(pwp->m_CreateTime); + + route->AddPoint(pWP); + + pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP); + + if (ip > 0) + pSelect->AddSelectableRouteSegment(pWP_src->m_lat, pWP_src->m_lon, + pWP->m_lat, pWP->m_lon, pWP_src, pWP, + route); + else + plannedDeparture = pwp->m_CreateTime; + ip++; + pWP_src = pWP; + + pwpnode = pwpnode->GetNext(); // PlugInWaypoint + } + + route->m_PlannedDeparture = plannedDeparture; + + route->m_RouteNameString = proute->m_NameString; + route->m_RouteStartString = proute->m_StartString; + route->m_RouteEndString = proute->m_EndString; + if (!proute->m_GUID.IsEmpty()) { + route->m_GUID = proute->m_GUID; + } + route->m_btemp = (b_permanent == false); + + pRouteList->Append(route); + + if (b_permanent) pConfig->AddNewRoute(route); + + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateRouteListCtrl(); + + return true; +} + +bool DeletePlugInRoute(wxString& GUID) { + bool b_found = false; + + // Find the Route + Route* pRoute = g_pRouteMan->FindRouteByGUID(GUID); + if (pRoute) { + g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance()); + b_found = true; + } + return b_found; +} + +bool UpdatePlugInRoute(PlugIn_Route* proute) { + bool b_found = false; + + // Find the Route + Route* pRoute = g_pRouteMan->FindRouteByGUID(proute->m_GUID); + if (pRoute) b_found = true; + + if (b_found) { + bool b_permanent = (pRoute->m_btemp == false); + g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance()); + + b_found = AddPlugInRoute(proute, b_permanent); + } + + return b_found; +} + +bool AddPlugInTrack(PlugIn_Track* ptrack, bool b_permanent) { + Track* track = new Track(); + + PlugIn_Waypoint* pwp = 0; + TrackPoint* pWP_src = 0; + int ip = 0; + + wxPlugin_WaypointListNode* pwpnode = ptrack->pWaypointList->GetFirst(); + while (pwpnode) { + pwp = pwpnode->GetData(); + + TrackPoint* pWP = new TrackPoint(pwp->m_lat, pwp->m_lon); + pWP->SetCreateTime(pwp->m_CreateTime); + + track->AddPoint(pWP); + + if (ip > 0) + pSelect->AddSelectableTrackSegment(pWP_src->m_lat, pWP_src->m_lon, + pWP->m_lat, pWP->m_lon, pWP_src, pWP, + track); + ip++; + pWP_src = pWP; + + pwpnode = pwpnode->GetNext(); // PlugInWaypoint + } + + track->SetName(ptrack->m_NameString); + track->m_TrackStartString = ptrack->m_StartString; + track->m_TrackEndString = ptrack->m_EndString; + track->m_GUID = ptrack->m_GUID; + track->m_btemp = (b_permanent == false); + + g_TrackList.push_back(track); + + if (b_permanent) pConfig->AddNewTrack(track); + + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateTrkListCtrl(); + + return true; +} + +bool DeletePlugInTrack(wxString& GUID) { + bool b_found = false; + + // Find the Route + Track* pTrack = g_pRouteMan->FindTrackByGUID(GUID); + if (pTrack) { + RoutemanGui(*g_pRouteMan).DeleteTrack(pTrack); + b_found = true; + } + + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateTrkListCtrl(); + + return b_found; +} + +bool UpdatePlugInTrack(PlugIn_Track* ptrack) { + bool b_found = false; + + // Find the Track + Track* pTrack = g_pRouteMan->FindTrackByGUID(ptrack->m_GUID); + if (pTrack) b_found = true; + + if (b_found) { + bool b_permanent = (pTrack->m_btemp == false); + RoutemanGui(*g_pRouteMan).DeleteTrack(pTrack); + + b_found = AddPlugInTrack(ptrack, b_permanent); + } + + return b_found; +} + +bool PlugInHasNormalizedViewPort(PlugIn_ViewPort* vp) { +#ifdef ocpnUSE_GL + ViewPort ocpn_vp; + ocpn_vp.m_projection_type = vp->m_projection_type; + + return glChartCanvas::HasNormalizedViewPort(ocpn_vp); +#else + return false; +#endif +} + +void PlugInMultMatrixViewport(PlugIn_ViewPort* vp, float lat, float lon) { +#ifdef ocpnUSE_GL + ViewPort ocpn_vp; + ocpn_vp.clat = vp->clat; + ocpn_vp.clon = vp->clon; + ocpn_vp.m_projection_type = vp->m_projection_type; + ocpn_vp.view_scale_ppm = vp->view_scale_ppm; + ocpn_vp.skew = vp->skew; + ocpn_vp.rotation = vp->rotation; + ocpn_vp.pix_width = vp->pix_width; + ocpn_vp.pix_height = vp->pix_height; + +// TODO fix for multicanvas glChartCanvas::MultMatrixViewPort(ocpn_vp, lat, +// lon); +#endif +} + +void PlugInNormalizeViewport(PlugIn_ViewPort* vp, float lat, float lon) { +#ifdef ocpnUSE_GL + ViewPort ocpn_vp; + glChartCanvas::NormalizedViewPort(ocpn_vp, lat, lon); + + vp->clat = ocpn_vp.clat; + vp->clon = ocpn_vp.clon; + vp->view_scale_ppm = ocpn_vp.view_scale_ppm; + vp->rotation = ocpn_vp.rotation; + vp->skew = ocpn_vp.skew; +#endif +} + +// Helper and interface classes + +//------------------------------------------------------------------------------- +// PlugIn_AIS_Target Implementation +//------------------------------------------------------------------------------- + +PlugIn_AIS_Target* Create_PI_AIS_Target(AisTargetData* ptarget) { + PlugIn_AIS_Target* pret = new PlugIn_AIS_Target; + + pret->MMSI = ptarget->MMSI; + pret->Class = ptarget->Class; + pret->NavStatus = ptarget->NavStatus; + pret->SOG = ptarget->SOG; + pret->COG = ptarget->COG; + pret->HDG = ptarget->HDG; + pret->Lon = ptarget->Lon; + pret->Lat = ptarget->Lat; + pret->ROTAIS = ptarget->ROTAIS; + pret->ShipType = ptarget->ShipType; + pret->IMO = ptarget->IMO; + + pret->Range_NM = ptarget->Range_NM; + pret->Brg = ptarget->Brg; + + // Per target collision parameters + pret->bCPA_Valid = ptarget->bCPA_Valid; + pret->TCPA = ptarget->TCPA; // Minutes + pret->CPA = ptarget->CPA; // Nautical Miles + + pret->alarm_state = (plugin_ais_alarm_type)ptarget->n_alert_state; + + memcpy(pret->CallSign, ptarget->CallSign, sizeof(ptarget->CallSign) - 1); + memcpy(pret->ShipName, ptarget->ShipName, sizeof(ptarget->ShipName) - 1); + + return pret; +} + +//--------------------------------------------------------------------------- +// API 1.11 +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// API 1.12 +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// API 1.13 +//--------------------------------------------------------------------------- +double fromDMM_Plugin(wxString sdms) { return fromDMM(sdms); } + +void SetCanvasRotation(double rotation) { + gFrame->GetPrimaryCanvas()->DoRotateCanvas(rotation); +} + +double GetCanvasTilt() { return gFrame->GetPrimaryCanvas()->GetVPTilt(); } + +void SetCanvasTilt(double tilt) { + gFrame->GetPrimaryCanvas()->DoTiltCanvas(tilt); +} + +void SetCanvasProjection(int projection) { + gFrame->GetPrimaryCanvas()->SetVPProjection(projection); +} + +OcpnSound* g_PluginSound = SoundFactory(); +static void onPlugInPlaySoundExFinished(void* ptr) {} + +// Start playing a sound to a given device and return status to plugin +bool PlugInPlaySoundEx(wxString& sound_file, int deviceIndex) { + bool ok = g_PluginSound->Load(sound_file, deviceIndex); + if (!ok) { + wxLogWarning("Cannot load sound file: %s", sound_file); + return false; + } + auto cmd_sound = dynamic_cast(g_PluginSound); + if (cmd_sound) cmd_sound->SetCmd(g_CmdSoundString.mb_str(wxConvUTF8)); + + g_PluginSound->SetFinishedCallback(onPlugInPlaySoundExFinished, NULL); + ok = g_PluginSound->Play(); + if (!ok) { + wxLogWarning("Cannot play sound file: %s", sound_file); + } + return ok; +} + +bool CheckEdgePan_PlugIn(int x, int y, bool dragging, int margin, int delta) { + return gFrame->GetPrimaryCanvas()->CheckEdgePan(x, y, dragging, margin, + delta); +} + +wxBitmap GetIcon_PlugIn(const wxString& name) { + ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle(); + return style->GetIcon(name); +} + +void SetCursor_PlugIn(wxCursor* pCursor) { + gFrame->GetPrimaryCanvas()->pPlugIn_Cursor = pCursor; +} + +void AddChartDirectory(wxString& path) { + if (g_options) { + g_options->AddChartDir(path); + } +} + +void ForceChartDBUpdate() { + if (g_options) { + g_options->pScanCheckBox->SetValue(true); + g_options->pUpdateCheckBox->SetValue(true); + } +} + +void ForceChartDBRebuild() { + if (g_options) { + g_options->pUpdateCheckBox->SetValue(true); + } +} + +wxDialog* GetActiveOptionsDialog() { return g_options; } + +int PlatformDirSelectorDialog(wxWindow* parent, wxString* file_spec, + wxString Title, wxString initDir) { + return g_Platform->DoDirSelectorDialog(parent, file_spec, Title, initDir); +} + +int PlatformFileSelectorDialog(wxWindow* parent, wxString* file_spec, + wxString Title, wxString initDir, + wxString suggestedName, wxString wildcard) { + return g_Platform->DoFileSelectorDialog(parent, file_spec, Title, initDir, + suggestedName, wildcard); +} + +//--------------------------------------------------------------------------- +// API 1.14 +//--------------------------------------------------------------------------- + +ViewPort CreateCompatibleViewportEx(const PlugIn_ViewPort& pivp) { + // Create a system ViewPort + ViewPort vp; + + vp.clat = pivp.clat; // center point + vp.clon = pivp.clon; + vp.view_scale_ppm = pivp.view_scale_ppm; + vp.skew = pivp.skew; + vp.rotation = pivp.rotation; + vp.chart_scale = pivp.chart_scale; + vp.pix_width = pivp.pix_width; + vp.pix_height = pivp.pix_height; + vp.rv_rect = pivp.rv_rect; + vp.b_quilt = pivp.b_quilt; + vp.m_projection_type = pivp.m_projection_type; + + if (gFrame->GetPrimaryCanvas()) + vp.ref_scale = gFrame->GetPrimaryCanvas()->GetVP().ref_scale; + else + vp.ref_scale = vp.chart_scale; + + vp.SetBoxes(); + vp.Validate(); // This VP is valid + + return vp; +} + +void PlugInAISDrawGL(wxGLCanvas* glcanvas, const PlugIn_ViewPort& vp) { + ViewPort ocpn_vp = CreateCompatibleViewportEx(vp); + + ocpnDC dc(*glcanvas); + dc.SetVP(ocpn_vp); + + AISDraw(dc, ocpn_vp, NULL); +} + +bool PlugInSetFontColor(const wxString TextElement, const wxColour color) { + return FontMgr::Get().SetFontColor(TextElement, color); +} + +//--------------------------------------------------------------------------- +// API 1.15 +//--------------------------------------------------------------------------- + +double PlugInGetDisplaySizeMM() { return g_Platform->GetDisplaySizeMM(); } + +wxFont* FindOrCreateFont_PlugIn(int point_size, wxFontFamily family, + wxFontStyle style, wxFontWeight weight, + bool underline, const wxString& facename, + wxFontEncoding encoding) { + return FontMgr::Get().FindOrCreateFont(point_size, family, style, weight, + underline, facename, encoding); +} + +int PluginGetMinAvailableGshhgQuality() { + return gFrame->GetPrimaryCanvas()->GetMinAvailableGshhgQuality(); +} +int PluginGetMaxAvailableGshhgQuality() { + return gFrame->GetPrimaryCanvas()->GetMaxAvailableGshhgQuality(); +} + +// disable builtin console canvas, and autopilot nmea sentences +void PlugInHandleAutopilotRoute(bool enable) { + g_bPluginHandleAutopilotRoute = enable; +} + +bool LaunchDefaultBrowser_Plugin(wxString url) { + if (g_Platform) g_Platform->platformLaunchDefaultBrowser(url); + + return true; +} + +//--------------------------------------------------------------------------- +// API 1.16 +//--------------------------------------------------------------------------- +wxString GetSelectedWaypointGUID_Plugin() { + ChartCanvas* cc = gFrame->GetFocusCanvas(); + if (cc && cc->GetSelectedRoutePoint()) { + return cc->GetSelectedRoutePoint()->m_GUID; + } + return wxEmptyString; +} + +wxString GetSelectedRouteGUID_Plugin() { + ChartCanvas* cc = gFrame->GetFocusCanvas(); + if (cc && cc->GetSelectedRoute()) { + return cc->GetSelectedRoute()->m_GUID; + } + return wxEmptyString; +} + +wxString GetSelectedTrackGUID_Plugin() { + ChartCanvas* cc = gFrame->GetFocusCanvas(); + if (cc && cc->GetSelectedTrack()) { + return cc->GetSelectedTrack()->m_GUID; + } + return wxEmptyString; +} + +std::unique_ptr GetWaypoint_Plugin(const wxString& GUID) { + std::unique_ptr w(new PlugIn_Waypoint); + GetSingleWaypoint(GUID, w.get()); + return w; +} + +std::unique_ptr GetRoute_Plugin(const wxString& GUID) { + std::unique_ptr r; + Route* route = g_pRouteMan->FindRouteByGUID(GUID); + if (route == nullptr) return r; + + r = std::unique_ptr(new PlugIn_Route); + PlugIn_Route* dst_route = r.get(); + + // PlugIn_Waypoint *pwp; + RoutePoint* src_wp; + wxRoutePointListNode* node = route->pRoutePointList->GetFirst(); + + while (node) { + src_wp = node->GetData(); + + PlugIn_Waypoint* dst_wp = new PlugIn_Waypoint(); + PlugInFromRoutePoint(dst_wp, src_wp); + + dst_route->pWaypointList->Append(dst_wp); + + node = node->GetNext(); + } + dst_route->m_NameString = route->m_RouteNameString; + dst_route->m_StartString = route->m_RouteStartString; + dst_route->m_EndString = route->m_RouteEndString; + dst_route->m_GUID = route->m_GUID; + + return r; +} + +std::unique_ptr GetTrack_Plugin(const wxString& GUID) { + std::unique_ptr t; + // Find the Track + Track* pTrack = g_pRouteMan->FindTrackByGUID(GUID); + if (!pTrack) return t; + + std::unique_ptr tk = + std::unique_ptr(new PlugIn_Track); + PlugIn_Track* dst_track = tk.get(); + dst_track->m_NameString = pTrack->GetName(); + dst_track->m_StartString = pTrack->m_TrackStartString; + dst_track->m_EndString = pTrack->m_TrackEndString; + dst_track->m_GUID = pTrack->m_GUID; + + for (int i = 0; i < pTrack->GetnPoints(); i++) { + TrackPoint* ptp = pTrack->GetPoint(i); + + PlugIn_Waypoint* dst_wp = new PlugIn_Waypoint(); + + dst_wp->m_lat = ptp->m_lat; + dst_wp->m_lon = ptp->m_lon; + dst_wp->m_CreateTime = ptp->GetCreateTime(); // not const + + dst_track->pWaypointList->Append(dst_wp); + } + + return tk; +} + +wxWindow* PluginGetFocusCanvas() { return g_focusCanvas; } + +wxWindow* PluginGetOverlayRenderCanvas() { + // if(g_overlayCanvas) + return g_overlayCanvas; + // else +} + +void CanvasJumpToPosition(wxWindow* canvas, double lat, double lon, + double scale) { + auto oCanvas = dynamic_cast(canvas); + if (oCanvas) gFrame->JumpToPosition(oCanvas, lat, lon, scale); +} + +bool ShuttingDown(void) { return g_bquiting; } + +wxWindow* GetCanvasUnderMouse(void) { return gFrame->GetCanvasUnderMouse(); } + +int GetCanvasIndexUnderMouse(void) { + ChartCanvas* l_canvas = gFrame->GetCanvasUnderMouse(); + if (l_canvas) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); ++i) { + if (l_canvas == g_canvasArray[i]) return i; + } + } + return 0; +} + +// std::vector GetCanvasArray() +// { +// std::vector rv; +// for(unsigned int i=0 ; i < g_canvasArray.GetCount() ; i++){ +// ChartCanvas *cc = g_canvasArray.Item(i); +// rv.push_back(cc); +// } +// +// return rv; +// } + +wxWindow* GetCanvasByIndex(int canvasIndex) { + if (g_canvasConfig == 0) + return gFrame->GetPrimaryCanvas(); + else { + if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) { + return g_canvasArray[canvasIndex]; + } + } + return NULL; +} + +bool CheckMUIEdgePan_PlugIn(int x, int y, bool dragging, int margin, int delta, + int canvasIndex) { + if (g_canvasConfig == 0) + return gFrame->GetPrimaryCanvas()->CheckEdgePan(x, y, dragging, margin, + delta); + else { + if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) { + return g_canvasArray[canvasIndex]->CheckEdgePan(x, y, dragging, margin, + delta); + } + } + + return false; +} + +void SetMUICursor_PlugIn(wxCursor* pCursor, int canvasIndex) { + if (g_canvasConfig == 0) + gFrame->GetPrimaryCanvas()->pPlugIn_Cursor = pCursor; + else { + if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) { + g_canvasArray[canvasIndex]->pPlugIn_Cursor = pCursor; + } + } +} + +int GetCanvasCount() { + if (g_canvasConfig == 1) return 2; + // else + return 1; +} + +int GetLatLonFormat() { return g_iSDMMFormat; } + +wxRect GetMasterToolbarRect() { + if (g_MainToolbar) + return g_MainToolbar->GetToolbarRect(); + else + return wxRect(0, 0, 1, 1); +} + +//--------------------------------------------------------------------------- +// API 1.17 +//--------------------------------------------------------------------------- + +void ZeroXTE() { + if (g_pRouteMan) { + g_pRouteMan->ZeroCurrentXTEToActivePoint(); + } +} + +static PlugIn_ViewPort CreatePlugInViewportEx(const ViewPort& vp) { + // Create a PlugIn Viewport + ViewPort tvp = vp; + PlugIn_ViewPort pivp; + + pivp.clat = tvp.clat; // center point + pivp.clon = tvp.clon; + pivp.view_scale_ppm = tvp.view_scale_ppm; + pivp.skew = tvp.skew; + pivp.rotation = tvp.rotation; + pivp.chart_scale = tvp.chart_scale; + pivp.pix_width = tvp.pix_width; + pivp.pix_height = tvp.pix_height; + pivp.rv_rect = tvp.rv_rect; + pivp.b_quilt = tvp.b_quilt; + pivp.m_projection_type = tvp.m_projection_type; + + pivp.lat_min = tvp.GetBBox().GetMinLat(); + pivp.lat_max = tvp.GetBBox().GetMaxLat(); + pivp.lon_min = tvp.GetBBox().GetMinLon(); + pivp.lon_max = tvp.GetBBox().GetMaxLon(); + + pivp.bValid = tvp.IsValid(); // This VP is valid + + return pivp; +} + +ListOfPI_S57Obj* PlugInManager::GetLightsObjRuleListVisibleAtLatLon( + ChartPlugInWrapper* target, float zlat, float zlon, const ViewPort& vp) { + ListOfPI_S57Obj* list = NULL; + if (target) { + PlugInChartBaseGLPlus2* picbgl = + dynamic_cast(target->GetPlugInChart()); + if (picbgl) { + PlugIn_ViewPort pi_vp = CreatePlugInViewportEx(vp); + list = picbgl->GetLightsObjRuleListVisibleAtLatLon(zlat, zlon, &pi_vp); + + return list; + } + PlugInChartBaseExtendedPlus2* picbx = + dynamic_cast(target->GetPlugInChart()); + if (picbx) { + PlugIn_ViewPort pi_vp = CreatePlugInViewportEx(vp); + list = picbx->GetLightsObjRuleListVisibleAtLatLon(zlat, zlon, &pi_vp); + + return list; + } else + return list; + } else + return list; +} + +// PlugInWaypointEx implementation + +#include +WX_DEFINE_LIST(Plugin_WaypointExList) + +// The class implementations +PlugIn_Waypoint_Ex::PlugIn_Waypoint_Ex() { InitDefaults(); } + +PlugIn_Waypoint_Ex::PlugIn_Waypoint_Ex( + double lat, double lon, const wxString& icon_ident, const wxString& wp_name, + const wxString& GUID, const double ScaMin, const bool bNameVisible, + const int nRangeRings, const double RangeDistance, + const wxColor RangeColor) { + InitDefaults(); + + wxDateTime now = wxDateTime::Now(); + m_CreateTime = now.ToUTC(); + m_HyperlinkList = NULL; + + m_lat = lat; + m_lon = lon; + IconName = icon_ident; + m_MarkName = wp_name; + m_GUID = GUID; + scamin = ScaMin; + IsNameVisible = bNameVisible; + nrange_rings = nRangeRings; + RangeRingSpace = RangeDistance; + RangeRingColor = RangeColor; +} + +void PlugIn_Waypoint_Ex::InitDefaults() { + m_HyperlinkList = NULL; + scamin = 1e9; + b_useScamin = false; + nrange_rings = 0; + RangeRingSpace = 1; + IsNameVisible = false; + IsVisible = true; + RangeRingColor = *wxBLACK; + m_CreateTime = wxDateTime::Now(); + IsActive = false; + m_lat = 0; + m_lon = 0; +} + +bool PlugIn_Waypoint_Ex::GetFSStatus() { + RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(m_GUID); + if (!prp) return false; + + if (prp->m_bIsInRoute && !prp->IsShared()) return false; + + return true; +} + +int PlugIn_Waypoint_Ex::GetRouteMembershipCount() { + // Search all routes to count the membership of this point + RoutePoint* pWP = pWayPointMan->FindRoutePointByGUID(m_GUID); + if (!pWP) return 0; + + int nCount = 0; + wxRouteListNode* node = pRouteList->GetFirst(); + while (node) { + Route* proute = node->GetData(); + wxRoutePointListNode* pnode = (proute->pRoutePointList)->GetFirst(); + while (pnode) { + RoutePoint* prp = pnode->GetData(); + if (prp == pWP) nCount++; + pnode = pnode->GetNext(); + } + + node = node->GetNext(); + } + + return nCount; +} + +PlugIn_Waypoint_Ex::~PlugIn_Waypoint_Ex() {} + +// PlugInRouteExtended implementation +PlugIn_Route_Ex::PlugIn_Route_Ex(void) { + pWaypointList = new Plugin_WaypointExList; +} + +PlugIn_Route_Ex::~PlugIn_Route_Ex(void) { + pWaypointList->DeleteContents(false); // do not delete Waypoints + pWaypointList->Clear(); + + delete pWaypointList; +} + +// The utility methods implementations + +// translate O route class to PlugIn_Waypoint_Ex +static void PlugInExFromRoutePoint(PlugIn_Waypoint_Ex* dst, + /* const*/ RoutePoint* src) { + dst->m_lat = src->m_lat; + dst->m_lon = src->m_lon; + dst->IconName = src->GetIconName(); + dst->m_MarkName = src->GetName(); + dst->m_MarkDescription = src->GetDescription(); + dst->IconDescription = pWayPointMan->GetIconDescription(src->GetIconName()); + dst->IsVisible = src->IsVisible(); + dst->m_CreateTime = src->GetCreateTime(); // not const + dst->m_GUID = src->m_GUID; + + // Transcribe (clone) the html HyperLink List, if present + if (src->m_HyperlinkList == nullptr) return; + + delete dst->m_HyperlinkList; + dst->m_HyperlinkList = nullptr; + + if (src->m_HyperlinkList->GetCount() > 0) { + dst->m_HyperlinkList = new Plugin_HyperlinkList; + + wxHyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst(); + while (linknode) { + Hyperlink* link = linknode->GetData(); + + Plugin_Hyperlink* h = new Plugin_Hyperlink(); + h->DescrText = link->DescrText; + h->Link = link->Link; + h->Type = link->LType; + + dst->m_HyperlinkList->Append(h); + + linknode = linknode->GetNext(); + } + } + + // Get the range ring info + dst->nrange_rings = src->m_iWaypointRangeRingsNumber; + dst->RangeRingSpace = src->m_fWaypointRangeRingsStep; + dst->RangeRingColor = src->m_wxcWaypointRangeRingsColour; + + // Get other extended info + dst->IsNameVisible = src->m_bShowName; + dst->scamin = src->GetScaMin(); + dst->b_useScamin = src->GetUseSca(); + dst->IsActive = src->m_bIsActive; +} + +static void cloneHyperlinkListEx(RoutePoint* dst, + const PlugIn_Waypoint_Ex* src) { + // Transcribe (clone) the html HyperLink List, if present + if (src->m_HyperlinkList == nullptr) return; + + if (src->m_HyperlinkList->GetCount() > 0) { + wxPlugin_HyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst(); + while (linknode) { + Plugin_Hyperlink* link = linknode->GetData(); + + Hyperlink* h = new Hyperlink(); + h->DescrText = link->DescrText; + h->Link = link->Link; + h->LType = link->Type; + + dst->m_HyperlinkList->Append(h); + + linknode = linknode->GetNext(); + } + } +} + +RoutePoint* CreateNewPoint(const PlugIn_Waypoint_Ex* src, bool b_permanent) { + RoutePoint* pWP = new RoutePoint(src->m_lat, src->m_lon, src->IconName, + src->m_MarkName, src->m_GUID); + + pWP->m_bIsolatedMark = true; // This is an isolated mark + + cloneHyperlinkListEx(pWP, src); + + pWP->m_MarkDescription = src->m_MarkDescription; + + if (src->m_CreateTime.IsValid()) + pWP->SetCreateTime(src->m_CreateTime); + else { + wxDateTime dtnow(wxDateTime::Now()); + pWP->SetCreateTime(dtnow); + } + + pWP->m_btemp = (b_permanent == false); + + // Extended fields + pWP->SetIconName(src->IconName); + pWP->SetWaypointRangeRingsNumber(src->nrange_rings); + pWP->SetWaypointRangeRingsStep(src->RangeRingSpace); + pWP->SetWaypointRangeRingsColour(src->RangeRingColor); + pWP->SetScaMin(src->scamin); + pWP->SetUseSca(src->b_useScamin); + pWP->SetNameShown(src->IsNameVisible); + pWP->SetVisible(src->IsVisible); + + return pWP; +} +bool GetSingleWaypointEx(wxString GUID, PlugIn_Waypoint_Ex* pwaypoint) { + // Find the RoutePoint + RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(GUID); + + if (!prp) return false; + + PlugInExFromRoutePoint(pwaypoint, prp); + + return true; +} + +bool AddSingleWaypointEx(PlugIn_Waypoint_Ex* pwaypointex, bool b_permanent) { + // Validate the waypoint parameters a little bit + + // GUID + // Make sure that this GUID is indeed unique in the Routepoint list + bool b_unique = true; + wxRoutePointListNode* prpnode = pWayPointMan->GetWaypointList()->GetFirst(); + while (prpnode) { + RoutePoint* prp = prpnode->GetData(); + + if (prp->m_GUID == pwaypointex->m_GUID) { + b_unique = false; + break; + } + prpnode = prpnode->GetNext(); // RoutePoint + } + + if (!b_unique) return false; + + RoutePoint* pWP = CreateNewPoint(pwaypointex, b_permanent); + + pWP->SetShowWaypointRangeRings(pwaypointex->nrange_rings > 0); + + pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP); + if (b_permanent) pConfig->AddNewWayPoint(pWP, -1); + + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateWptListCtrl(); + + return true; +} + +bool UpdateSingleWaypointEx(PlugIn_Waypoint_Ex* pwaypoint) { + // Find the RoutePoint + bool b_found = false; + RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(pwaypoint->m_GUID); + + if (prp) b_found = true; + + if (b_found) { + double lat_save = prp->m_lat; + double lon_save = prp->m_lon; + + prp->m_lat = pwaypoint->m_lat; + prp->m_lon = pwaypoint->m_lon; + prp->SetIconName(pwaypoint->IconName); + prp->SetName(pwaypoint->m_MarkName); + prp->m_MarkDescription = pwaypoint->m_MarkDescription; + prp->SetVisible(pwaypoint->IsVisible); + if (pwaypoint->m_CreateTime.IsValid()) + prp->SetCreateTime(pwaypoint->m_CreateTime); + + // Transcribe (clone) the html HyperLink List, if present + + if (pwaypoint->m_HyperlinkList) { + prp->m_HyperlinkList->Clear(); + if (pwaypoint->m_HyperlinkList->GetCount() > 0) { + wxPlugin_HyperlinkListNode* linknode = + pwaypoint->m_HyperlinkList->GetFirst(); + while (linknode) { + Plugin_Hyperlink* link = linknode->GetData(); + + Hyperlink* h = new Hyperlink(); + h->DescrText = link->DescrText; + h->Link = link->Link; + h->LType = link->Type; + + prp->m_HyperlinkList->Append(h); + + linknode = linknode->GetNext(); + } + } + } + + // Extended fields + prp->SetWaypointRangeRingsNumber(pwaypoint->nrange_rings); + prp->SetWaypointRangeRingsStep(pwaypoint->RangeRingSpace); + prp->SetWaypointRangeRingsColour(pwaypoint->RangeRingColor); + prp->SetScaMin(pwaypoint->scamin); + prp->SetUseSca(pwaypoint->b_useScamin); + prp->SetNameShown(pwaypoint->IsNameVisible); + + prp->SetShowWaypointRangeRings(pwaypoint->nrange_rings > 0); + + if (prp) prp->ReLoadIcon(); + + auto canvas = gFrame->GetPrimaryCanvas(); + SelectCtx ctx(canvas->m_bShowNavobjects, canvas->GetCanvasTrueScale(), + canvas->GetScaleValue()); + SelectItem* pFind = + pSelect->FindSelection(ctx, lat_save, lon_save, SELTYPE_ROUTEPOINT); + if (pFind) { + pFind->m_slat = pwaypoint->m_lat; // update the SelectList entry + pFind->m_slon = pwaypoint->m_lon; + } + + if (!prp->m_btemp) pConfig->UpdateWayPoint(prp); + + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateWptListCtrl(); + } + + return b_found; +} + +bool AddPlugInRouteEx(PlugIn_Route_Ex* proute, bool b_permanent) { + Route* route = new Route(); + + PlugIn_Waypoint_Ex* pwaypointex; + RoutePoint *pWP, *pWP_src; + int ip = 0; + wxDateTime plannedDeparture; + + wxPlugin_WaypointExListNode* pwpnode = proute->pWaypointList->GetFirst(); + while (pwpnode) { + pwaypointex = pwpnode->GetData(); + + pWP = pWayPointMan->FindRoutePointByGUID(pwaypointex->m_GUID); + if (!pWP) { + pWP = CreateNewPoint(pwaypointex, b_permanent); + pWP->m_bIsolatedMark = false; + } + + route->AddPoint(pWP); + + pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP); + + if (ip > 0) + pSelect->AddSelectableRouteSegment(pWP_src->m_lat, pWP_src->m_lon, + pWP->m_lat, pWP->m_lon, pWP_src, pWP, + route); + + plannedDeparture = pwaypointex->m_CreateTime; + ip++; + pWP_src = pWP; + + pwpnode = pwpnode->GetNext(); // PlugInWaypoint + } + + route->m_PlannedDeparture = plannedDeparture; + + route->m_RouteNameString = proute->m_NameString; + route->m_RouteStartString = proute->m_StartString; + route->m_RouteEndString = proute->m_EndString; + if (!proute->m_GUID.IsEmpty()) { + route->m_GUID = proute->m_GUID; + } + route->m_btemp = (b_permanent == false); + route->SetVisible(proute->m_isVisible); + route->m_RouteDescription = proute->m_Description; + + pRouteList->Append(route); + + if (b_permanent) pConfig->AddNewRoute(route); + + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateRouteListCtrl(); + + return true; +} + +bool UpdatePlugInRouteEx(PlugIn_Route_Ex* proute) { + bool b_found = false; + + // Find the Route + Route* pRoute = g_pRouteMan->FindRouteByGUID(proute->m_GUID); + if (pRoute) b_found = true; + + if (b_found) { + bool b_permanent = !pRoute->m_btemp; + g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance()); + + b_found = AddPlugInRouteEx(proute, b_permanent); + } + + return b_found; +} + +// std::unique_ptr GetWaypointEx_Plugin(const wxString &) +// { +// } + +// std::unique_ptr GetRouteEx_Plugin(const wxString &) +// { +// } + +std::unique_ptr GetWaypointEx_Plugin(const wxString& GUID) { + std::unique_ptr w(new PlugIn_Waypoint_Ex); + GetSingleWaypointEx(GUID, w.get()); + return w; +} + +std::unique_ptr GetRouteEx_Plugin(const wxString& GUID) { + std::unique_ptr r; + Route* route = g_pRouteMan->FindRouteByGUID(GUID); + if (route == nullptr) return r; + + r = std::unique_ptr(new PlugIn_Route_Ex); + PlugIn_Route_Ex* dst_route = r.get(); + + // PlugIn_Waypoint *pwp; + RoutePoint* src_wp; + wxRoutePointListNode* node = route->pRoutePointList->GetFirst(); + + while (node) { + src_wp = node->GetData(); + + PlugIn_Waypoint_Ex* dst_wp = new PlugIn_Waypoint_Ex(); + PlugInExFromRoutePoint(dst_wp, src_wp); + + dst_route->pWaypointList->Append(dst_wp); + + node = node->GetNext(); + } + dst_route->m_NameString = route->m_RouteNameString; + dst_route->m_StartString = route->m_RouteStartString; + dst_route->m_EndString = route->m_RouteEndString; + dst_route->m_GUID = route->m_GUID; + dst_route->m_isActive = g_pRouteMan->GetpActiveRoute() == route; + dst_route->m_isVisible = route->IsVisible(); + dst_route->m_Description = route->m_RouteDescription; + + return r; +} + +wxString GetActiveWaypointGUID( + void) { // if no active waypoint, returns wxEmptyString + RoutePoint* rp = g_pRouteMan->GetpActivePoint(); + if (!rp) + return wxEmptyString; + else + return rp->m_GUID; +} + +wxString GetActiveRouteGUID( + void) { // if no active route, returns wxEmptyString + Route* rt = g_pRouteMan->GetpActiveRoute(); + if (!rt) + return wxEmptyString; + else + return rt->m_GUID; +} + +/** Comm Global Watchdog Query */ +int GetGlobalWatchdogTimoutSeconds() { return gps_watchdog_timeout_ticks; } + +/** Comm Priority query support methods */ +std::vector GetPriorityMaps() { + MyApp& app = wxGetApp(); + return (app.m_comm_bridge.GetPriorityMaps()); +} + +std::vector GetActivePriorityIdentifiers() { + std::vector result; + + MyApp& app = wxGetApp(); + + std::string id = + app.m_comm_bridge.GetPriorityContainer("position").active_source; + result.push_back(id); + id = app.m_comm_bridge.GetPriorityContainer("velocity").active_source; + result.push_back(id); + id = app.m_comm_bridge.GetPriorityContainer("heading").active_source; + result.push_back(id); + id = app.m_comm_bridge.GetPriorityContainer("variation").active_source; + result.push_back(id); + id = app.m_comm_bridge.GetPriorityContainer("satellites").active_source; + result.push_back(id); + + return result; +} + +double OCPN_GetDisplayContentScaleFactor() { + double rv = 1.0; +#if defined(__WXOSX__) || defined(__WXGTK3__) + // Support scaled HDPI displays. + if (gFrame) rv = gFrame->GetContentScaleFactor(); +#endif + return rv; +} +double OCPN_GetWinDIPScaleFactor() { + double scaler = 1.0; +#ifdef __WXMSW__ + if (gFrame) scaler = (double)(gFrame->ToDIP(100)) / 100.; +#endif + return scaler; +} + +//--------------------------------------------------------------------------- +// API 1.18 +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// API 1.19 +//--------------------------------------------------------------------------- +void ExitOCPN() {} + +bool GetFullScreen() { return gFrame->IsFullScreen(); } + +void SetFullScreen(bool set_full_screen_on) { + bool state = gFrame->IsFullScreen(); + if (set_full_screen_on && !state) + gFrame->ToggleFullScreen(); + else if (!set_full_screen_on && state) + gFrame->ToggleFullScreen(); +} + +void EnableMUIBar(bool enable) { + extern bool g_useMUI; + bool current_mui_state = g_useMUI; + + g_useMUI = enable; + if (enable && !current_mui_state) { // OFF going ON + // ..For each canvas... + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->CreateMUIBar(); + } + } else if (!enable && current_mui_state) { // ON going OFF + // ..For each canvas... + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->DestroyMuiBar(); + } + } +} + +void EnableCompassGPSIcon(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowGPSCompassWindow(enable); + } +} + +extern bool g_bShowStatusBar; +void EnableStatusBar(bool enable) { + g_bShowStatusBar = enable; + gFrame->ConfigureStatusBar(); +} + +void EnableChartBar(bool enable) { + bool current_chartbar_state = g_bShowChartBar; + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (current_chartbar_state && !enable) { + gFrame->ToggleChartBar(cc); + g_bShowChartBar = current_chartbar_state; + } else if (!current_chartbar_state && enable) { + gFrame->ToggleChartBar(cc); + g_bShowChartBar = current_chartbar_state; + } + } + g_bShowChartBar = enable; +} + +extern bool g_bShowMenuBar; +void EnableMenu(bool enable) { + if (!enable) { + if (g_bShowMenuBar) { + g_bShowMenuBar = false; + if (gFrame->m_pMenuBar) { + gFrame->SetMenuBar(NULL); + gFrame->m_pMenuBar->Destroy(); + gFrame->m_pMenuBar = NULL; + } + } + } else { + g_bShowMenuBar = true; + gFrame->BuildMenuBar(); + } +} + +void SetGlobalColor(std::string table, std::string name, wxColor color) { + if (ps52plib) ps52plib->m_chartSymbols.UpdateTableColor(table, name, color); +} + +void EnableLatLonGrid(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowGrid(enable); + } +} + +void EnableChartOutlines(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowOutlines(enable); + } +} + +void EnableDepthUnitDisplay(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowDepthUnits(enable); + } +} + +void EnableAisTargetDisplay(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowAIS(enable); + } +} + +void EnableTideStationsDisplay(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->ShowTides(enable); + } +} + +void EnableCurrentStationsDisplay(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->ShowCurrents(enable); + } +} + +void EnableENCTextDisplay(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowENCText(enable); + } +} + +void EnableENCDepthSoundingsDisplay(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowENCDepth(enable); + } +} + +void EnableBuoyLightLabelsDisplay(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowENCBuoyLabels(enable); + } +} + +void EnableLightsDisplay(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowENCLights(enable); + } +} + +void EnableLightDescriptionsDisplay(bool enable) { + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetShowENCLightDesc(enable); + } +} + +void SetENCDisplayCategory(PI_DisCat cat) { + int valSet = STANDARD; + switch (cat) { + case PI_DISPLAYBASE: + valSet = DISPLAYBASE; + break; + case PI_STANDARD: + valSet = STANDARD; + break; + case PI_OTHER: + valSet = OTHER; + break; + case PI_MARINERS_STANDARD: + valSet = MARINERS_STANDARD; + break; + default: + valSet = STANDARD; + break; + } + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetENCDisplayCategory(valSet); + } +} + +void SetNavigationMode(PI_NavMode mode) { + int newMode = NORTH_UP_MODE; + if (mode == PI_COURSE_UP_MODE) + newMode = COURSE_UP_MODE; + else if (mode == PI_HEAD_UP_MODE) + newMode = HEAD_UP_MODE; + + for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) { + ChartCanvas* cc = g_canvasArray.Item(i); + if (cc) cc->SetUpMode(newMode); + } +} diff --git a/gui/src/ocpndc.cpp b/gui/src/ocpndc.cpp index 1338fbab40..dbf3f6973a 100644 --- a/gui/src/ocpndc.cpp +++ b/gui/src/ocpndc.cpp @@ -110,7 +110,7 @@ ocpnDC::ocpnDC(wxDC &pdc) dc(&pdc), m_pen(wxNullPen), m_brush(wxNullBrush) { -#if 0 // wxUSE_GRAPHICS_CONTEXT +#if wxUSE_GRAPHICS_CONTEXT pgc = NULL; auto pmdc = dynamic_cast(dc); if (pmdc) @@ -137,7 +137,7 @@ ocpnDC::ocpnDC() } ocpnDC::~ocpnDC() { -#if 0 // wxUSE_GRAPHICS_CONTEXT +#if wxUSE_GRAPHICS_CONTEXT if (pgc) delete pgc; #endif free(workBuf); @@ -849,8 +849,8 @@ void ocpnDC::DrawLines(int n, wxPoint points[], wxCoord xoffset, void ocpnDC::StrokeLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2) { #if wxUSE_GRAPHICS_CONTEXT - if (pgc) { - pgc->SetPen(dc->GetPen()); + if (pgc && dc) { + pgc->SetPen(GetPen()); pgc->StrokeLine(x1, y1, x2, y2); dc->CalcBoundingBox(x1, y1); @@ -872,7 +872,7 @@ void ocpnDC::StrokeLines(int n, wxPoint *points) { dPoints[i].m_x = points[i].x; dPoints[i].m_y = points[i].y; } - pgc->SetPen(dc->GetPen()); + pgc->SetPen(GetPen()); pgc->StrokeLines(n, dPoints); free(dPoints); } else @@ -1127,7 +1127,7 @@ void ocpnDC::DrawCircle(wxCoord x, wxCoord y, wxCoord radius) { void ocpnDC::StrokeCircle(wxCoord x, wxCoord y, wxCoord radius) { #if wxUSE_GRAPHICS_CONTEXT - if (pgc) { + if (pgc && dc) { wxGraphicsPath gpath = pgc->CreatePath(); gpath.AddCircle(x, y, radius); @@ -1537,7 +1537,7 @@ void ocpnDC::DrawPolygonTessellated(int n, wxPoint points[], wxCoord xoffset, void ocpnDC::StrokePolygon(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, float scale) { #if wxUSE_GRAPHICS_CONTEXT - if (pgc) { + if (pgc && dc) { wxGraphicsPath gpath = pgc->CreatePath(); gpath.MoveToPoint(points[0].x * scale + xoffset, points[0].y * scale + yoffset); diff --git a/gui/src/pluginmanager.cpp b/gui/src/pluginmanager.cpp index dacc192e7c..70d90c7b6c 100644 --- a/gui/src/pluginmanager.cpp +++ b/gui/src/pluginmanager.cpp @@ -223,8 +223,8 @@ extern unsigned int g_canvasConfig; extern wxString g_CmdSoundString; -unsigned int gs_plib_flags; -wxString g_lastPluginMessage; +extern unsigned int gs_plib_flags; +extern wxString g_lastPluginMessage; extern ChartCanvas* g_focusCanvas; extern ChartCanvas* g_overlayCanvas; extern bool g_bquiting; @@ -284,6 +284,24 @@ static int ComparePlugins(PlugInContainer** p1, PlugInContainer** p2) { return (*p1)->Key().compare((*p2)->Key()); } +static SemanticVersion ParseVersion(const PluginMetadata& metadata) { + auto version = metadata.version; + // Handle tag versions like v1.2.3: + if (version[0] == 'v') version = version.substr(1); + return SemanticVersion::parse(version); +} + +static bool IsUpdateAvailable(const PluginMetadata& metadata) { + auto imported_version = ParseVersion(metadata); + for (auto& md : PluginHandler::getInstance()->getAvailable()) { + if (md.name != metadata.name) continue; + if (md.is_imported) continue; + if (!PluginHandler::getInstance()->isCompatible(md)) continue; + if (ParseVersion(md) >= imported_version) return true; + } + return false; +} + /** * Handle messages for blacklisted plugins. Messages are deferred until * show_deferred_messages() is invoked, signaling that the UI is ready. @@ -367,6 +385,8 @@ wxString message_by_status(PluginStatus stat) { return _("Update to installed Plugin is available"); case PluginStatus::ManagedInstalledCurrentVersion: return _("Plugin is latest available"); + case PluginStatus::Imported: + return _("Plugin is imported"); case PluginStatus::ManagedInstalledDowngradeAvailable: return (""); case PluginStatus::PendingListRemoval: @@ -390,9 +410,8 @@ static const std::unordered_map {PluginStatus::ManagedInstalledCurrentVersion, "emblem-default.svg"}, {PluginStatus::ManagedInstalledDowngradeAvailable, "emblem-default.svg"}, - {PluginStatus::PendingListRemoval, "emblem-default.svg"} - - }); + {PluginStatus::PendingListRemoval, "emblem-default.svg"}, + {PluginStatus::Imported, "emblem-default.svg"}}); static const std::unordered_map literalstatus_by_status( @@ -409,7 +428,8 @@ static const std::unordered_map "ManagedInstalledCurrentVersion"}, {PluginStatus::ManagedInstalledDowngradeAvailable, "ManagedInstalledDowngradeAvailable"}, - {PluginStatus::PendingListRemoval, "PendingListRemoval"} + {PluginStatus::PendingListRemoval, "PendingListRemoval"}, + {PluginStatus::Imported, "Imported"} }); @@ -766,6 +786,7 @@ void pluginUtilHandler::OnPluginUtilAction(wxCommandEvent& event) { break; } + case ActionVerb::UPDATE_IMPORTED_VERSION: case ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION: case ActionVerb::REINSTALL_MANAGED_VERSION: case ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION: { @@ -1140,6 +1161,7 @@ bool PlugInManager::CallLateInit(void) { case 116: case 117: case 118: + case 119: ProcessLateInit(pic); break; } @@ -1206,7 +1228,8 @@ void PlugInManager::SendVectorChartObjectInfo(const wxString& chart, case 115: case 116: case 117: - case 118: { + case 118: + case 119: { opencpn_plugin_112* ppi = dynamic_cast(pic->m_pplugin); if (ppi) @@ -1419,7 +1442,8 @@ bool PlugInManager::RenderAllCanvasOverlayPlugIns(ocpnDC& dc, ppi116->RenderOverlayMultiCanvas(*pdc, &pivp, canvasIndex); break; } - case 118: { + case 118: + case 119: { if (priority <= 0) { opencpn_plugin_18* ppi = dynamic_cast(pic->m_pplugin); @@ -1497,7 +1521,8 @@ bool PlugInManager::RenderAllCanvasOverlayPlugIns(ocpnDC& dc, g_canvasConfig); break; } - case 118: { + case 118: + case 119: { if (priority <= 0) { opencpn_plugin_18* ppi = dynamic_cast(pic->m_pplugin); @@ -1584,7 +1609,8 @@ bool PlugInManager::RenderAllGLCanvasOverlayPlugIns(wxGLContext* pcontext, } break; } - case 118: { + case 118: + case 119: { if (priority <= 0) { opencpn_plugin_18* ppi = dynamic_cast(pic->m_pplugin); @@ -1624,7 +1650,8 @@ bool PlugInManager::SendMouseEventToPlugins(wxMouseEvent& event) { case 115: case 116: case 117: - case 118: { + case 118: + case 119: { opencpn_plugin_112* ppi = dynamic_cast(pic->m_pplugin); if (ppi) @@ -1655,7 +1682,8 @@ bool PlugInManager::SendKeyEventToPlugins(wxKeyEvent& event) { case 115: case 116: case 117: - case 118: { + case 118: + case 119: { opencpn_plugin_113* ppi = dynamic_cast(pic->m_pplugin); if (ppi && ppi->KeyboardEventHook(event)) bret = true; @@ -1685,6 +1713,27 @@ void PlugInManager::SendViewPortToRequestingPlugIns(ViewPort& vp) { } } +void PlugInManager::SendPreShutdownHookToPlugins() { + auto plugin_array = PluginLoader::getInstance()->GetPlugInArray(); + for (unsigned int i = 0; i < plugin_array->GetCount(); i++) { + PlugInContainer* pic = plugin_array->Item(i); + if (pic->m_enabled && pic->m_init_state) { + if (pic->m_cap_flag & WANTS_PRESHUTDOWN_HOOK) { + switch (pic->m_api_version) { + case 119: { + opencpn_plugin_119* ppi = + dynamic_cast(pic->m_pplugin); + if (ppi) ppi->PreShutdownHook(); + break; + } + default: + break; + } + } + } + } +} + void PlugInManager::SendCursorLatLonToAllPlugIns(double lat, double lon) { auto plugin_array = PluginLoader::getInstance()->GetPlugInArray(); for (unsigned int i = 0; i < plugin_array->GetCount(); i++) { @@ -1898,7 +1947,8 @@ void PlugInManager::SendMessageToAllPlugins(const wxString& message_id, case 115: case 116: case 117: - case 118: { + case 118: + case 119: { opencpn_plugin_18* ppi = dynamic_cast(pic->m_pplugin); if (ppi) @@ -1974,7 +2024,8 @@ void PlugInManager::SendPositionFixToAllPlugIns(GenericPosDatEx* ppos) { case 115: case 116: case 117: - case 118: { + case 118: + case 119: { opencpn_plugin_18* ppi = dynamic_cast(pic->m_pplugin); if (ppi) ppi->SetPositionFixEx(pfix_ex); @@ -2013,7 +2064,8 @@ void PlugInManager::SendActiveLegInfoToAllPlugIns( case 116: break; case 117: - case 118: { + case 118: + case 119: { opencpn_plugin_117* ppi = dynamic_cast(pic->m_pplugin); if (ppi) ppi->SetActiveLegInfo(leg); @@ -2057,7 +2109,8 @@ void PlugInManager::PrepareAllPluginContextMenus() { switch (pic->m_api_version) { case 116: case 117: - case 118: { + case 118: + case 119: { opencpn_plugin_116* ppi = dynamic_cast(pic->m_pplugin); if (ppi) ppi->PrepareContextMenu(canvasIndex); @@ -2471,5482 +2524,3434 @@ wxArrayString PlugInManager::GetPlugInChartClassNameArray(void) { return array; } -//---------------------------------------------------------------------------------------------------------- -// The PlugIn CallBack API Implementation -// The definitions of this API are found in ocpn_plugin.h -//---------------------------------------------------------------------------------------------------------- - -int InsertPlugInTool(wxString label, wxBitmap* bitmap, wxBitmap* bmpRollover, - wxItemKind kind, wxString shortHelp, wxString longHelp, - wxObject* clientData, int position, int tool_sel, - opencpn_plugin* pplugin) { - if (s_ppim) - return s_ppim->AddToolbarTool(label, bitmap, bmpRollover, kind, shortHelp, - longHelp, clientData, position, tool_sel, - pplugin); - else - return -1; -} +//------------------------------------------------------------------------------- +// PluginListPanel & PluginPanel Implementation +//------------------------------------------------------------------------------- -void RemovePlugInTool(int tool_id) { - if (s_ppim) s_ppim->RemoveToolbarTool(tool_id); -} +#define DISABLED_SETTINGS_MSG \ + _("These settings might destabilize OpenCPN and are by default disabled." \ + " To despite the dangers enable them manually add a CatalogExpert=1" \ + " line in the [PlugIns] section in the configuration file.") -void SetToolbarToolViz(int item, bool viz) { - if (s_ppim) s_ppim->SetToolbarToolViz(item, viz); -} +/* + * Panel with buttons to control plugin catalog management. + */ +CatalogMgrPanel::CatalogMgrPanel(wxWindow* parent) + : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize), + m_parent(parent) { + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + SetSizer(topSizer); -void SetToolbarItemState(int item, bool toggle) { - if (s_ppim) s_ppim->SetToolbarItemState(item, toggle); -} + topSizer->Add(new wxStaticLine(this), 0, wxGROW | wxLEFT | wxRIGHT, 4); -void SetToolbarToolBitmaps(int item, wxBitmap* bitmap, wxBitmap* bmpRollover) { - if (s_ppim) s_ppim->SetToolbarItemBitmaps(item, bitmap, bmpRollover); -} + wxStaticBox* itemStaticBoxSizer4Static = + new wxStaticBox(this, wxID_ANY, _("Plugin Catalog")); + wxStaticBoxSizer* itemStaticBoxSizer4 = + new wxStaticBoxSizer(itemStaticBoxSizer4Static, wxVERTICAL); + topSizer->Add(itemStaticBoxSizer4, 1, wxEXPAND | wxALL, 2); -int InsertPlugInToolSVG(wxString label, wxString SVGfile, - wxString SVGfileRollover, wxString SVGfileToggled, - wxItemKind kind, wxString shortHelp, wxString longHelp, - wxObject* clientData, int position, int tool_sel, - opencpn_plugin* pplugin) { - if (s_ppim) - return s_ppim->AddToolbarTool(label, SVGfile, SVGfileRollover, - SVGfileToggled, kind, shortHelp, longHelp, - clientData, position, tool_sel, pplugin); - else - return -1; -} +#ifndef __ANDROID__ + // First line + m_catalogText = new wxStaticText(this, wxID_STATIC, _T("")); + itemStaticBoxSizer4->Add(m_catalogText, + wxSizerFlags().Border().Proportion(1)); + m_catalogText->SetLabel(GetCatalogText(false)); -void SetToolbarToolBitmapsSVG(int item, wxString SVGfile, - wxString SVGfileRollover, - wxString SVGfileToggled) { - if (s_ppim) - s_ppim->SetToolbarItemBitmaps(item, SVGfile, SVGfileRollover, - SVGfileToggled); -} + // Next line + wxBoxSizer* rowSizer2 = new wxBoxSizer(wxHORIZONTAL); + itemStaticBoxSizer4->Add(rowSizer2, + wxSizerFlags().Expand().Border().Proportion(1)); -int AddCanvasMenuItem(wxMenuItem* pitem, opencpn_plugin* pplugin, - const char* name) { - if (s_ppim) - return s_ppim->AddCanvasContextMenuItem(pitem, pplugin, name); - else - return -1; -} + m_updateButton = new wxButton(this, wxID_ANY, _("Update Plugin Catalog"), + wxDefaultPosition, wxDefaultSize, 0); + rowSizer2->Add(m_updateButton, 0, wxALIGN_LEFT); + m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &CatalogMgrPanel::OnUpdateButton, this); + rowSizer2->AddSpacer(4 * GetCharWidth()); + m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."), + wxDefaultPosition, wxDefaultSize, 0); + rowSizer2->Add(m_tarballButton, 0, wxALIGN_LEFT | wxLEFT, 2 * GetCharWidth()); + m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &CatalogMgrPanel::OnTarballButton, this); -void SetCanvasMenuItemViz(int item, bool viz, const char* name) { - if (s_ppim) s_ppim->SetCanvasContextMenuItemViz(item, viz, name); -} + rowSizer2->AddSpacer(4 * GetCharWidth()); + m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."), + wxDefaultPosition, wxDefaultSize, 0); + ConfigVar expert("/PlugIns", "CatalogExpert", pConfig); + if (expert.Get(false)) { + m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &CatalogMgrPanel::OnPluginSettingsButton, this); + } else { + m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) { + wxMessageBox(DISABLED_SETTINGS_MSG, _("Disabled")); + }); + } + rowSizer2->AddSpacer(4 * GetCharWidth()); + rowSizer2->Add(m_adv_button, 0, wxALIGN_LEFT); -void SetCanvasMenuItemGrey(int item, bool grey, const char* name) { - if (s_ppim) s_ppim->SetCanvasContextMenuItemGrey(item, grey, name); -} + SetUpdateButtonLabel(); -void RemoveCanvasMenuItem(int item, const char* name) { - if (s_ppim) s_ppim->RemoveCanvasContextMenuItem(item, name); -} + // Next line + wxBoxSizer* rowSizer3 = new wxBoxSizer(wxHORIZONTAL); + itemStaticBoxSizer4->Add(rowSizer3, 0, wxEXPAND | wxALL, 4); -int AddCanvasContextMenuItem(wxMenuItem* pitem, opencpn_plugin* pplugin) { - /* main context popup menu */ - return AddCanvasMenuItem(pitem, pplugin, ""); -} + SetMinSize(wxSize(m_parent->GetClientSize().x - (4 * GetCharWidth()), -1)); + Fit(); -void SetCanvasContextMenuItemViz(int item, bool viz) { - SetCanvasMenuItemViz(item, viz); -} + GlobalVar catalog(&g_catalog_channel); + wxDEFINE_EVENT(EVT_CATALOG_CHANGE, wxCommandEvent); + catalog_listener.Listen(catalog, this, EVT_CATALOG_CHANGE); + Bind(EVT_CATALOG_CHANGE, [&](wxCommandEvent&) { SetUpdateButtonLabel(); }); -void SetCanvasContextMenuItemGrey(int item, bool grey) { - SetCanvasMenuItemGrey(item, grey); -} +#else // __ANDROID__ + SetBackgroundColour(wxColour(0x7c, 0xb0, 0xe9)); // light blue + ConfigVar expert("/PlugIns", "CatalogExpert", pConfig); + if (!expert.Get(false)) { + m_updateButton = + new wxButton(this, wxID_ANY, _("Update Plugin Catalog: master"), + wxDefaultPosition, wxDefaultSize, 0); + itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT); + m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &CatalogMgrPanel::OnUpdateButton, this); + SetUpdateButtonLabel(); + m_tarballButton = NULL; + m_adv_button = NULL; + } else { + // First line + m_catalogText = new wxStaticText(this, wxID_STATIC, GetCatalogText(false)); + itemStaticBoxSizer4->Add(m_catalogText, + wxSizerFlags().Border(wxALL, 5).Proportion(1)); + // m_catalogText->SetLabel(GetCatalogText(false)); -void RemoveCanvasContextMenuItem(int item) { RemoveCanvasMenuItem(item); } + m_updateButton = new wxButton( + this, wxID_ANY, "Update Plugin Catalog:master ", + wxDefaultPosition, wxDefaultSize, 0); + itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT | wxTOP, 5); + m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &CatalogMgrPanel::OnUpdateButton, this); + SetUpdateButtonLabel(); -wxFileConfig* GetOCPNConfigObject(void) { - if (s_ppim) - return pConfig; // return the global application config object - else - return NULL; -} + // Next line + m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."), + wxDefaultPosition, wxDefaultSize, 0); + itemStaticBoxSizer4->Add(m_adv_button, 0, wxALIGN_LEFT | wxTOP, + GetCharWidth()); + m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &CatalogMgrPanel::OnPluginSettingsButton, this); -wxWindow* GetOCPNCanvasWindow() { - wxWindow* pret = NULL; - if (s_ppim) { - MyFrame* pFrame = s_ppim->GetParentFrame(); - pret = (wxWindow*)pFrame->GetPrimaryCanvas(); + // Next line + m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."), + wxDefaultPosition, wxDefaultSize, 0); + itemStaticBoxSizer4->Add(m_tarballButton, 0, wxALIGN_LEFT | wxALL, + 2 * GetCharWidth()); + m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &CatalogMgrPanel::OnTarballButton, this); } - return pret; + +#endif } -void RequestRefresh(wxWindow* win) { - if (win) win->Refresh(); +CatalogMgrPanel::~CatalogMgrPanel() { + m_updateButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, + &CatalogMgrPanel::OnUpdateButton, this); + if (m_tarballButton) + m_tarballButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, + &CatalogMgrPanel::OnTarballButton, this); } -void GetCanvasPixLL(PlugIn_ViewPort* vp, wxPoint* pp, double lat, double lon) { - // Make enough of an application viewport to run its method.... - ViewPort ocpn_vp; - ocpn_vp.clat = vp->clat; - ocpn_vp.clon = vp->clon; - ocpn_vp.m_projection_type = vp->m_projection_type; - ocpn_vp.view_scale_ppm = vp->view_scale_ppm; - ocpn_vp.skew = vp->skew; - ocpn_vp.rotation = vp->rotation; - ocpn_vp.pix_width = vp->pix_width; - ocpn_vp.pix_height = vp->pix_height; +static const char* const DOWNLOAD_REPO_PROTO = + "https://raw.githubusercontent.com/OpenCPN/plugins/@branch@/" + "ocpn-plugins.xml"; - wxPoint ret = ocpn_vp.GetPixFromLL(lat, lon); - pp->x = ret.x; - pp->y = ret.y; -} +void CatalogMgrPanel::OnUpdateButton(wxCommandEvent& event) { + // Craft the url + std::string catalog(g_catalog_channel == "" ? "master" : g_catalog_channel); + std::string url(g_catalog_custom_url); + if (catalog != "custom") { + url = std::string(DOWNLOAD_REPO_PROTO); + ocpn::replace(url, "@branch@", catalog); + } + // Download to a temp file + std::string filePath = + wxFileName::CreateTempFileName("ocpn_dl").ToStdString(); -void GetDoubleCanvasPixLL(PlugIn_ViewPort* vp, wxPoint2DDouble* pp, double lat, - double lon) { - // Make enough of an application viewport to run its method.... - ViewPort ocpn_vp; - ocpn_vp.clat = vp->clat; - ocpn_vp.clon = vp->clon; - ocpn_vp.m_projection_type = vp->m_projection_type; - ocpn_vp.view_scale_ppm = vp->view_scale_ppm; - ocpn_vp.skew = vp->skew; - ocpn_vp.rotation = vp->rotation; - ocpn_vp.pix_width = vp->pix_width; - ocpn_vp.pix_height = vp->pix_height; + auto catalogHdlr = CatalogHandler::getInstance(); - *pp = ocpn_vp.GetDoublePixFromLL(lat, lon); -} + g_Platform->ShowBusySpinner(); + auto status = catalogHdlr->DownloadCatalog(filePath, url); + g_Platform->HideBusySpinner(); -void GetCanvasLLPix(PlugIn_ViewPort* vp, wxPoint p, double* plat, - double* plon) { - // Make enough of an application viewport to run its method.... - ViewPort ocpn_vp; - ocpn_vp.clat = vp->clat; - ocpn_vp.clon = vp->clon; - ocpn_vp.m_projection_type = vp->m_projection_type; - ocpn_vp.view_scale_ppm = vp->view_scale_ppm; - ocpn_vp.skew = vp->skew; - ocpn_vp.rotation = vp->rotation; - ocpn_vp.pix_width = vp->pix_width; - ocpn_vp.pix_height = vp->pix_height; + std::string message; + if (status != CatalogHandler::ServerStatus::OK) { + message = _("Cannot download data from url"); + OCPNMessageBox(this, message, _("OpenCPN Catalog update"), + wxICON_ERROR | wxOK); + return; + } - return ocpn_vp.GetLLFromPix(p, plat, plon); -} + // TODO Validate xml using xsd here.... +#ifdef __ANDROID__ + if (!AndroidSecureCopyFile(wxString(filePath.c_str()), + g_Platform->GetPrivateDataDir() + + wxFileName::GetPathSeparator() + + _T("ocpn-plugins.xml"))) { + OCPNMessageBox(this, _("Unable to copy catalog file"), + _("OpenCPN Catalog update"), wxICON_ERROR | wxOK); + return; + } +#else + // Copy the downloaded file to proper local location + if (!wxCopyFile(wxString(filePath.c_str()), + g_Platform->GetPrivateDataDir() + + wxFileName::GetPathSeparator() + + _T("ocpn-plugins.xml"))) { + OCPNMessageBox(this, _("Unable to copy catalog file"), + _("OpenCPN Catalog update"), wxICON_ERROR | wxOK); + return; + } +#endif -bool GetGlobalColor(wxString colorName, wxColour* pcolour) { - wxColour c = GetGlobalColor(colorName); - *pcolour = c; + // If this is the "master" catalog, also copy to plugin cache + if (catalog == "master") { + if (!ocpn::store_metadata(filePath.c_str())) { + OCPNMessageBox(this, _("Unable to copy catalog file to cache"), + _("OpenCPN Catalog update"), wxICON_ERROR | wxOK); + return; + } + } - return true; -} + // Record in the config file the name of the catalog downloaded + pConfig->SetPath(_T("/PlugIns/")); + pConfig->Write("LatestCatalogDownloaded", catalog.c_str()); + pConfig->Flush(); -wxFont* OCPNGetFont(wxString TextElement, int default_size) { - return FontMgr::Get().GetFont(TextElement, default_size); -} + // Reset the PluginHandler catalog file source. + // This will case the Handler to find, load, and parse the just-downloaded + // catalog as copied to g_Platform->GetPrivateDataDir()... + auto pluginHandler = PluginHandler::getInstance(); + pluginHandler->setMetadata(""); -wxFont* GetOCPNScaledFont_PlugIn(wxString TextElement, int default_size) { - return GetOCPNScaledFont(TextElement, default_size); -} + // Also clear the cached values in the CatalogHandler, forcing + // a reload and parse of the catalog. + auto cataloghdlr = CatalogHandler::getInstance(); + cataloghdlr->ClearCatalogData(); -double GetOCPNGUIToolScaleFactor_PlugIn(int GUIScaleFactor) { - return g_Platform->GetToolbarScaleFactor(GUIScaleFactor); -} + // Reload all plugins, which will also update the status fields + LoadAllPlugIns(false); -double GetOCPNGUIToolScaleFactor_PlugIn() { - return g_Platform->GetToolbarScaleFactor(g_GUIScaleFactor); + // Update this Panel, and the entire list. +#ifndef __ANDROID__ + m_catalogText->SetLabel(GetCatalogText(true)); +#endif + if (m_PluginListPanel) m_PluginListPanel->ReloadPluginPanels(); + OCPNMessageBox(this, _("Catalog update successful"), + _("OpenCPN Catalog update"), wxICON_INFORMATION | wxOK); } -float GetOCPNChartScaleFactor_Plugin() { - return g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor); -} +void CatalogMgrPanel::OnPluginSettingsButton(wxCommandEvent& event) { + auto dialog = new CatalogSettingsDialog(this); -wxFont GetOCPNGUIScaledFont_PlugIn(wxString item) { - return GetOCPNGUIScaledFont(item); -} +#ifdef __ANDROID__ + androidDisableRotation(); +#endif -bool AddPersistentFontKey(wxString TextElement) { - return FontMgr::Get().AddAuxKey(TextElement); -} + dialog->ShowModal(); -wxString GetActiveStyleName() { - if (g_StyleManager) - return g_StyleManager->GetCurrentStyle()->name; - else - return _T(""); -} - -wxBitmap GetBitmapFromSVGFile(wxString filename, unsigned int width, - unsigned int height) { - wxBitmap bmp = LoadSVG(filename, width, height); - - if (bmp.IsOk()) - return bmp; - else { - // On error in requested width/height parameters, - // try to find and use dimensions embedded in the SVG file - unsigned int w, h; - SVGDocumentPixelSize(filename, w, h); - if (w == 0 || h == 0) { - // We did not succeed in deducing the size from SVG (svg element - // x misses width, height or both attributes), let's use some "safe" - // default - w = 32; - h = 32; - } - return LoadSVG(filename, w, h); - } -} - -bool IsTouchInterface_PlugIn(void) { return g_btouch; } - -wxColour GetFontColour_PlugIn(wxString TextElement) { - return FontMgr::Get().GetFontColor(TextElement); -} - -wxString* GetpSharedDataLocation(void) { - return g_Platform->GetSharedDataDirPtr(); +#ifdef __ANDROID__ + androidEnableRotation(); +#endif } -ArrayOfPlugIn_AIS_Targets* GetAISTargetArray(void) { - if (!g_pAIS) return NULL; - - ArrayOfPlugIn_AIS_Targets* pret = new ArrayOfPlugIn_AIS_Targets; +void CatalogMgrPanel::OnTarballButton(wxCommandEvent& event) { + // Present a file selector dialog to get the file name.. + wxString path; + int response = g_Platform->DoFileSelectorDialog( + this, &path, _("Select tarball file"), GetImportInitDir(), "", + "tar files (*.tar.gz)|*.tar.gz|All Files (*.*)|*.*"); - // Iterate over the AIS Target Hashmap - for (const auto& it : g_pAIS->GetTargetList()) { - auto td = it.second; - PlugIn_AIS_Target* ptarget = Create_PI_AIS_Target(td.get()); - pret->Add(ptarget); + if (response != wxID_OK) { + return; } - -// Test one alarm target -#if 0 - AisTargetData td; - td.n_alarm_state = AIS_ALARM_SET; - PlugIn_AIS_Target *ptarget = Create_PI_AIS_Target(&td); - pret->Add(ptarget); -#endif - return pret; + auto handler = PluginHandler::getInstance(); + PluginMetadata metadata; + bool ok = handler->ExtractMetadata(path.ToStdString(), metadata); + if (!ok) { + OCPNMessageBox( + this, + _("Error extracting metadata from tarball (missing metadata.xml?)"), + _("OpenCPN Plugin Import Error")); + return; + } + if (!PluginHandler::isCompatible(metadata)) { + OCPNMessageBox(this, _("Incompatible import plugin detected."), + _("OpenCPN Plugin Import Error")); + handler->uninstall(metadata.name); + return; + } + UninstallPlugin(metadata.name); + ok = handler->installPlugin(metadata, path.ToStdString()); + if (!ok) { + OCPNMessageBox(this, _("Error extracting import plugin tarball."), + _("OpenCPN Plugin Import Error")); + return; + } + metadata.is_imported = true; + auto metadata_path = PluginHandler::ImportedMetadataPath(metadata.name); + std::ofstream file(metadata_path); + file << metadata.to_string(); + if (!file.good()) { + WARNING_LOG << "Error saving metadata file: " << metadata_path + << " for imported plugin: " << metadata.name; + } + LoadAllPlugIns(false, true); + PluginHandler::getInstance()->SetInstalledMetadata(metadata); + m_PluginListPanel->ReloadPluginPanels(); + wxString ws(_("Plugin")); + ws += metadata.name + _(" successfully imported"); + OCPNMessageBox(gFrame, ws, _("Installation complete"), + wxICON_INFORMATION | wxOK | wxCENTRE); } -wxAuiManager* GetFrameAuiManager(void) { return g_pauimgr; } +wxString CatalogMgrPanel::GetCatalogText(bool updated) { + wxString catalog; + catalog = updated ? _("Active Catalog") : _("Last Catalog"); + catalog += _T(": "); -bool AddLocaleCatalog(wxString catalog) { -#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0) + // Check the config file to learn what was the last catalog downloaded. + pConfig->SetPath(_T("/PlugIns/")); + wxString latestCatalog = + pConfig->Read(_T("LatestCatalogDownloaded"), _T("default")); + catalog += latestCatalog; - if (plocale_def_lang) { - // Add this catalog to the persistent catalog array - g_locale_catalog_array.Add(catalog); +#ifndef __ANDROID__ + // Get the version from the currently active catalog, by which we mean + // the latest catalog parsed. + auto pluginHandler = PluginHandler::getInstance(); + std::string date = pluginHandler->GetCatalogData()->date; - return plocale_def_lang->AddCatalog(catalog); - } else + catalog += wxString(" ") + _("Last change: ") + " " + date; + if (!updated) catalog += _T(" : ") + _("Please Update Plugin Catalog."); #endif - return false; -} - -void PushNMEABuffer(wxString buf) { - std::string full_sentence = buf.ToStdString(); - - if ((full_sentence[0] == '$') || (full_sentence[0] == '!')) { // Sanity check - std::string identifier; - // We notify based on full message, including the Talker ID - identifier = full_sentence.substr(1, 5); - // notify message listener and also "ALL" N0183 messages, to support plugin - // API using original talker id - auto address = std::make_shared("virtual"); - auto msg = - std::make_shared(identifier, full_sentence, address); - auto msg_all = std::make_shared(*msg, "ALL"); - - auto& msgbus = NavMsgBus::GetInstance(); - - msgbus.Notify(std::move(msg)); - msgbus.Notify(std::move(msg_all)); - } + return catalog; } -wxXmlDocument GetChartDatabaseEntryXML(int dbIndex, bool b_getGeom) { - wxXmlDocument doc = ChartData->GetXMLDescription(dbIndex, b_getGeom); - - return doc; +void CatalogMgrPanel::SetUpdateButtonLabel() { + wxString label = _("Update Plugin Catalog"); + label += _T(": "); + label += g_catalog_channel == "" ? "master" : g_catalog_channel; + m_updateButton->SetLabel(label); + Layout(); } -bool UpdateChartDBInplace(wxArrayString dir_array, bool b_force_update, - bool b_ProgressDialog) { - // Make an array of CDI - ArrayOfCDI ChartDirArray; - for (unsigned int i = 0; i < dir_array.GetCount(); i++) { - wxString dirname = dir_array[i]; - ChartDirInfo cdi; - cdi.fullpath = dirname; - cdi.magic_number = _T(""); - ChartDirArray.Add(cdi); +wxString CatalogMgrPanel::GetImportInitDir() { + // Check the config file for the last Import path. + pConfig->SetPath(_T("/PlugIns/")); + wxString lastImportDir; + lastImportDir = pConfig->Read(_T("LatestImportDir"), + g_Platform->GetWritableDocumentsDir()); + if (wxDirExists(lastImportDir)) { + return lastImportDir; } - bool b_ret = gFrame->UpdateChartDatabaseInplace(ChartDirArray, b_force_update, - b_ProgressDialog, - ChartData->GetDBFileName()); - gFrame->ChartsRefresh(); - return b_ret; -} - -wxArrayString GetChartDBDirArrayString() { - return ChartData->GetChartDirArrayString(); + return (g_Platform->GetWritableDocumentsDir()); } -int AddChartToDBInPlace(wxString& full_path, bool b_RefreshCanvas) { - // extract the path from the chart name - wxFileName fn(full_path); - wxString fdir = fn.GetPath(); +BEGIN_EVENT_TABLE(PluginListPanel, wxScrolledWindow) +// EVT_BUTTON( ID_CMD_BUTTON_PERFORM_ACTION, +// PluginListPanel::OnPluginPanelAction ) +END_EVENT_TABLE() - bool bret = false; - if (ChartData) { - bret = ChartData->AddSingleChart(full_path); - - if (bret) { - // Save to disk - pConfig->UpdateChartDirs(ChartData->GetChartDirArray()); - ChartData->SaveBinary(ChartListFileName); - - // Completely reload the chart database, for a fresh start - ArrayOfCDI XnewChartDirArray; - pConfig->LoadChartDirArray(XnewChartDirArray); - delete ChartData; - ChartData = new ChartDB(); - ChartData->LoadBinary(ChartListFileName, XnewChartDirArray); - - // Update group contents - if (g_pGroupArray) ChartData->ApplyGroupArray(g_pGroupArray); - - if (g_boptionsactive) { - g_options->UpdateDisplayedChartDirList(ChartData->GetChartDirArray()); - } +PluginListPanel::PluginListPanel(wxWindow* parent, wxWindowID id, + const wxPoint& pos, const wxSize& size) + : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL), + m_PluginSelected(0) { + m_is_loading.clear(); + SetSizer(new wxBoxSizer(wxVERTICAL)); + ReloadPluginPanels(); +} - if (b_RefreshCanvas || !gFrame->GetPrimaryCanvas()->GetQuiltMode()) { - gFrame->ChartsRefresh(); +void PluginListPanel::SelectByName(wxString& name) { + for (auto it = GetChildren().GetFirst(); it; it = it->GetNext()) { + auto pluginPanel = dynamic_cast(it->GetData()); + if (pluginPanel) { + if (pluginPanel->GetPluginPtr()->m_common_name.IsSameAs(name)) { + pluginPanel->SetSelected(true); + pluginPanel->Layout(); + SelectPlugin(pluginPanel); + break; } } } - return bret; } -int RemoveChartFromDBInPlace(wxString& full_path) { - bool bret = false; - if (ChartData) { - bret = ChartData->RemoveSingleChart(full_path); - - // Save to disk - pConfig->UpdateChartDirs(ChartData->GetChartDirArray()); - ChartData->SaveBinary(ChartListFileName); - - // Completely reload the chart database, for a fresh start - ArrayOfCDI XnewChartDirArray; - pConfig->LoadChartDirArray(XnewChartDirArray); - delete ChartData; - ChartData = new ChartDB(); - ChartData->LoadBinary(ChartListFileName, XnewChartDirArray); - - // Update group contents - if (g_pGroupArray) ChartData->ApplyGroupArray(g_pGroupArray); - - if (g_boptionsactive) { - g_options->UpdateDisplayedChartDirList(ChartData->GetChartDirArray()); +/** Return sorted list of all installed plugins. */ +std::vector GetInstalled() { + std::vector result; + auto loader = PluginLoader::getInstance(); + for (size_t i = 0; i < loader->GetPlugInArray()->GetCount(); i++) { + auto const item = loader->GetPlugInArray()->Item(i); + if (item->m_managed_metadata.name.empty()) { + const auto name = item->m_common_name.ToStdString(); + item->m_managed_metadata = PluginLoader::MetadataByName(name); } - - gFrame->ChartsRefresh(); + PluginLoader::UpdatePlugin(item, item->m_managed_metadata); + result.push_back(item); } - - return bret; -} - -wxString GetLocaleCanonicalName() { return g_locale; } - -void SendPluginMessage(wxString message_id, wxString message_body) { - s_ppim->SendMessageToAllPlugins(message_id, message_body); - - // We will send an event to the main application frame (gFrame) - // for informational purposes. - // Of course, gFrame is encouraged to use any or all the - // data flying by if judged useful and dependable.... - - OCPN_MsgEvent Nevent(wxEVT_OCPN_MSG, 0); - Nevent.SetID(message_id); - Nevent.SetJSONText(message_body); - gFrame->GetEventHandler()->AddPendingEvent(Nevent); + auto compare = [](const PlugInData* lhs, const PlugInData* rhs) { + std::string slhs, srhs; + for (auto& cl : lhs->Key()) slhs += toupper(cl); + for (auto& cr : rhs->Key()) srhs += toupper(cr); + return slhs.compare(srhs) < 0; + }; + std::sort(result.begin(), result.end(), compare); + return result; } -void DimeWindow(wxWindow* win) { DimeControl(win); } - -void JumpToPosition(double lat, double lon, double scale) { - gFrame->JumpToPosition(gFrame->GetFocusCanvas(), lat, lon, scale); +/* Is plugin with given name present in loaded or safe list if installed? */ +static bool IsPluginLoaded(const std::string& name) { + if (safe_mode::get_mode()) { + auto installed = PluginHandler::getInstance()->GetInstalldataPlugins(); + auto found = + std::find(installed.begin(), installed.end(), ocpn::tolower(name)); + return found != installed.end(); + } else { + auto loaded = PluginLoader::getInstance()->GetPlugInArray(); + for (size_t i = 0; i < loaded->GetCount(); i++) { + if (loaded->Item(i)->m_common_name.ToStdString() == name) return true; + } + return false; + } } -/* API 1.9 */ -wxScrolledWindow* AddOptionsPage(OptionsParentPI parent, wxString title) { - if (!g_pOptions) return NULL; - - size_t parentid; - switch (parent) { - case PI_OPTIONS_PARENT_DISPLAY: - parentid = g_pOptions->m_pageDisplay; - break; - case PI_OPTIONS_PARENT_CONNECTIONS: - parentid = g_pOptions->m_pageConnections; - break; - case PI_OPTIONS_PARENT_CHARTS: - parentid = g_pOptions->m_pageCharts; - break; - case PI_OPTIONS_PARENT_SHIPS: - parentid = g_pOptions->m_pageShips; - break; - case PI_OPTIONS_PARENT_UI: - parentid = g_pOptions->m_pageUI; - break; - case PI_OPTIONS_PARENT_PLUGINS: - parentid = g_pOptions->m_pagePlugins; - break; - default: - wxLogMessage( - _T("Error in PluginManager::AddOptionsPage: Unknown parent")); - return NULL; - break; +void PluginListPanel::ReloadPluginPanels() { + if (m_is_loading.test_and_set()) { + // recursive call... + DEBUG_LOG << "LoadAllPlugins: recursive invocation"; + return; } - return g_pOptions->AddPage(parentid, title); -} + auto plugins = PluginLoader::getInstance()->GetPlugInArray(); + m_PluginItems.Clear(); -bool DeleteOptionsPage(wxScrolledWindow* page) { - if (!g_pOptions) return false; - return g_pOptions->DeletePluginPage(page); -} + wxWindowList kids = GetChildren(); + for (unsigned int i = 0; i < kids.GetCount(); i++) { + wxWindowListNode* node = kids.Item(i); + wxWindow* win = node->GetData(); + PluginPanel* pp = dynamic_cast(win); + if (pp) win->Destroy(); + } + GetSizer()->Clear(); -bool DecodeSingleVDOMessage(const wxString& str, PlugIn_Position_Fix_Ex* pos, - wxString* accumulator) { - if (!pos) return false; + Hide(); + m_PluginSelected = 0; - GenericPosDatEx gpd; - AisError nerr = AIS_GENERIC_ERROR; - if (g_pAIS) nerr = g_pAIS->DecodeSingleVDO(str, &gpd, accumulator); - if (nerr == AIS_NoError) { - pos->Lat = gpd.kLat; - pos->Lon = gpd.kLon; - pos->Cog = gpd.kCog; - pos->Sog = gpd.kSog; - pos->Hdt = gpd.kHdt; + if (safe_mode::get_mode()) { + /** Add panels for installed, unloaded plugins. */ + auto installed = PluginHandler::getInstance()->GetInstalldataPlugins(); + for (const auto& name : installed) AddPlugin(name); + } else { + /* The catalog entries. */ + auto available = getCompatiblePlugins(); - // Fill in the dummy values - pos->FixTime = 0; - pos->Hdm = 1000; - pos->Var = 1000; - pos->nSats = 0; + /* Remove those which are loaded or in safe list. */ + auto predicate = [](const PluginMetadata& md) { + return IsPluginLoaded(md.name); + }; + auto end = std::remove_if(available.begin(), available.end(), predicate); + available.erase(end, available.end()); - return true; - } + // Sort on case-insensitive name + struct CompSort { + bool operator()(const PluginMetadata& lhs, + const PluginMetadata rhs) const { + std::string slhs, srhs; + for (auto& cl : lhs.name) slhs += toupper(cl); + for (auto& cr : rhs.name) srhs += toupper(cr); + return slhs.compare(srhs) < 0; + } + } comp_sort; - return false; -} + std::set unique_sorted_entries(comp_sort); + for (const auto& p : available) unique_sorted_entries.insert(p); -int GetChartbarHeight(void) { - int val = 0; - if (g_bShowChartBar) { - ChartCanvas* cc = gFrame->GetPrimaryCanvas(); - if (cc && cc->GetPiano()) { - val = cc->GetPiano()->GetHeight(); - } - } - return val; -} + // Build the list of panels. -bool GetRoutepointGPX(RoutePoint* pRoutePoint, char* buffer, - unsigned int buffer_length) { - bool ret = false; + // Add Installed and active plugins + for (const auto& p : GetInstalled()) + if (p->m_enabled) AddPlugin(*p); - NavObjectCollection1* pgpx = new NavObjectCollection1; - pgpx->AddGPXWaypoint(pRoutePoint); - wxString gpxfilename = wxFileName::CreateTempFileName(wxT("gpx")); - pgpx->SaveFile(gpxfilename); - delete pgpx; + // Add Installed and inactive plugins + for (const auto& p : GetInstalled()) + if (!p->m_enabled) AddPlugin(*p); - wxFFile gpxfile(gpxfilename); - wxString s; - if (gpxfile.ReadAll(&s)) { - if (s.Length() < buffer_length) { - strncpy(buffer, (const char*)s.mb_str(wxConvUTF8), buffer_length - 1); - ret = true; - } + // Add available plugins, sorted + for (const auto& p : unique_sorted_entries) AddPlugin(PlugInData(p)); } - gpxfile.Close(); - ::wxRemoveFile(gpxfilename); - - return ret; -} + Show(); + Layout(); + Refresh(true); + Scroll(0, 0); -bool GetActiveRoutepointGPX(char* buffer, unsigned int buffer_length) { - if (g_pRouteMan->IsAnyRouteActive()) - return GetRoutepointGPX(g_pRouteMan->GetpActivePoint(), buffer, - buffer_length); - else - return false; + m_is_loading.clear(); } -void PositionBearingDistanceMercator_Plugin(double lat, double lon, double brg, - double dist, double* dlat, - double* dlon) { - PositionBearingDistanceMercator(lat, lon, brg, dist, dlat, dlon); -} - -void DistanceBearingMercator_Plugin(double lat0, double lon0, double lat1, - double lon1, double* brg, double* dist) { - DistanceBearingMercator(lat0, lon0, lat1, lon1, brg, dist); +void PluginListPanel::AddPlugin(const std::string& name) { + auto panel = new PluginPanel(this, name); + DimeControl(panel); + panel->SetSelected(false); + GetSizer()->Add(panel, 0, wxEXPAND); + m_PluginItems.Add(panel); + m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0; + GetSizer()->AddSpacer(m_pluginSpacer); } -double DistGreatCircle_Plugin(double slat, double slon, double dlat, - double dlon) { - return DistGreatCircle(slat, slon, dlat, dlon); -} +void PluginListPanel::AddPlugin(const PlugInData& pic) { + auto pPluginPanel = + new PluginPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, pic); + pPluginPanel->SetSelected(false); + GetSizer()->Add(pPluginPanel, 0, wxEXPAND); + m_PluginItems.Add(pPluginPanel); -void toTM_Plugin(float lat, float lon, float lat0, float lon0, double* x, - double* y) { - toTM(lat, lon, lat0, lon0, x, y); -} + m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0; + GetSizer()->AddSpacer(m_pluginSpacer); -void fromTM_Plugin(double x, double y, double lat0, double lon0, double* lat, - double* lon) { - fromTM(x, y, lat0, lon0, lat, lon); + // wxStaticLine* itemStaticLine = new wxStaticLine( m_panel, wxID_ANY, + // wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + // m_pitemBoxSizer01->Add( itemStaticLine, wxSizerFlags().Expand()); } -void toSM_Plugin(double lat, double lon, double lat0, double lon0, double* x, - double* y) { - toSM(lat, lon, lat0, lon0, x, y); -} +// When a child Panel is selected, its size grows to include "Preferences" +// and Enable" buttons. As a consequence, the vertical size of the +// ListPanel grows as well.Calculate and add a spacer to bottom of +// ListPanel so that initial ListPanel minimum size calculations account +// for selected Panel size growth. Sadly, this does not work right on wxQt. +// So, just punt for now... +int PluginListPanel::ComputePluginSpace(ArrayOfPluginPanel plugins, + wxBoxSizer* sizer) { + int max_dy = 0; + for (size_t i = 0; i < plugins.GetCount(); i++) { + auto panel = plugins.Item(i); + bool was_selected = panel->GetSelected(); + panel->SetSelected(false); + sizer->Layout(); + wxSize unselected = panel->GetSize(); -void fromSM_Plugin(double x, double y, double lat0, double lon0, double* lat, - double* lon) { - fromSM(x, y, lat0, lon0, lat, lon); -} + panel->SetSelected(true); // switch to selected, a bit bigger + sizer->Layout(); + wxSize selected = panel->GetSize(); -void toSM_ECC_Plugin(double lat, double lon, double lat0, double lon0, - double* x, double* y) { - toSM_ECC(lat, lon, lat0, lon0, x, y); + int dy = selected.GetHeight() - unselected.GetHeight(); + max_dy = wxMax(max_dy, dy); + panel->SetSelected(was_selected); + } + return max_dy; } -void fromSM_ECC_Plugin(double x, double y, double lat0, double lon0, - double* lat, double* lon) { - fromSM_ECC(x, y, lat0, lon0, lat, lon); -} +PluginListPanel::~PluginListPanel() {} -double toUsrDistance_Plugin(double nm_distance, int unit) { - return toUsrDistance(nm_distance, unit); +void PluginListPanel::UpdateSelections() { + for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) { + PluginPanel* pPluginPanel = m_PluginItems[i]; + if (pPluginPanel) { + pPluginPanel->SetSelected(pPluginPanel->GetSelected()); + } + } } -double fromUsrDistance_Plugin(double usr_distance, int unit) { - return fromUsrDistance(usr_distance, unit); -} +void PluginListPanel::SelectPlugin(PluginPanel* pi) { + int xs, ys; + GetViewStart(&xs, &ys); + Scroll(0, 0); -double toUsrSpeed_Plugin(double kts_speed, int unit) { - return toUsrSpeed(kts_speed, unit); -} + if (m_PluginSelected) { + m_PluginSelected->SetSelected(false); + m_PluginSelected->Layout(); + } -double toUsrWindSpeed_Plugin(double kts_speed, int unit) { - return toUsrWindSpeed(kts_speed, unit); -} + if (pi == NULL) m_PluginSelected->SetSelected(false); -double fromUsrSpeed_Plugin(double usr_speed, int unit) { - return fromUsrSpeed(usr_speed, unit); -} + m_PluginSelected = pi; -double fromUsrWindSpeed_Plugin(double usr_wspeed, int unit) { - return fromUsrWindSpeed(usr_wspeed, unit); -} + GetSizer()->Layout(); + Refresh(false); + wxSize size = GetBestVirtualSize(); + SetVirtualSize(size); -double toUsrTemp_Plugin(double cel_temp, int unit) { - return toUsrTemp(cel_temp, unit); -} + // Measure, and ensure that the selected item is fully visible in the + // vertical scroll box. + int htop = 0; + for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) { + PluginPanel* pPluginPanel = m_PluginItems[i]; + int yd = pPluginPanel->GetSize().y; + htop += yd; + htop += m_pluginSpacer; + if (pPluginPanel == pi) { + int piBottom = htop - (ys * g_options->GetScrollRate()); + if (piBottom > GetClientSize().y) { + ys += (piBottom - GetClientSize().y) / g_options->GetScrollRate(); + } + break; + } + } -double fromUsrTemp_Plugin(double usr_temp, int unit) { - return fromUsrTemp(usr_temp, unit); + Scroll(xs, ys); } -wxString getUsrDistanceUnit_Plugin(int unit) { - return getUsrDistanceUnit(unit); -} +void PluginListPanel::MoveUp(PluginPanel* pi) { + int pos = m_PluginItems.Index(pi); + if (pos == 0) // The first one can't be moved further up + return; + m_PluginItems.RemoveAt(pos); + // m_pitemBoxSizer01->Remove( pos * 2 + 1 ); + // m_pitemBoxSizer01->Remove( pos * 2 ); + m_PluginItems.Insert(pi, pos - 1); + wxStaticLine* itemStaticLine = new wxStaticLine( + this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL); + // m_pitemBoxSizer01->Insert( (pos - 1) * 2, itemStaticLine, 0, + // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos - 1) * 2, pi, 0, + // wxEXPAND|wxALL, 0 ); -wxString getUsrSpeedUnit_Plugin(int unit) { return getUsrSpeedUnit(unit); } + m_PluginSelected = pi; -wxString getUsrWindSpeedUnit_Plugin(int unit) { - return getUsrWindSpeedUnit(unit); + GetSizer()->Layout(); + m_parent->Layout(); + Refresh(true); } -wxString getUsrTempUnit_Plugin(int unit) { return getUsrTempUnit(unit); } - -bool PlugIn_GSHHS_CrossesLand(double lat1, double lon1, double lat2, - double lon2) { - static bool loaded = false; - if (!loaded) { - gshhsCrossesLandInit(); - loaded = true; - } +void PluginListPanel::MoveDown(PluginPanel* pi) { + int pos = m_PluginItems.Index(pi); + if (pos == (int)m_PluginItems.Count() - + 1) // The last one can't be moved further down + return; + m_PluginItems.RemoveAt(pos); + // m_pitemBoxSizer01->Remove( pos * 2 + 1 ); + // m_pitemBoxSizer01->Remove( pos * 2 ); + m_PluginItems.Insert(pi, pos + 1); + wxStaticLine* itemStaticLine = new wxStaticLine( + this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL); + // m_pitemBoxSizer01->Insert( (pos + 1) * 2 - 1, itemStaticLine, 0, + // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos + 1) * 2, pi, 0, + // wxEXPAND|wxALL, 0 ); - return gshhsCrossesLand(lat1, lon1, lat2, lon2); -} + m_PluginSelected = pi; -void PlugInPlaySound(wxString& sound_file) { - PlugInPlaySoundEx(sound_file, -1); + GetSizer()->Layout(); + m_parent->Layout(); + Refresh(false); } -// API 1.10 Route and Waypoint Support -// wxBitmap *FindSystemWaypointIcon( wxString& icon_name ); - -// PlugInWaypoint implementation -PlugIn_Waypoint::PlugIn_Waypoint() { m_HyperlinkList = NULL; } - -PlugIn_Waypoint::PlugIn_Waypoint(double lat, double lon, - const wxString& icon_ident, - const wxString& wp_name, - const wxString& GUID) { - wxDateTime now = wxDateTime::Now(); - m_CreateTime = now.ToUTC(); - m_HyperlinkList = NULL; +static bool canUninstall(std::string name) { + PluginHandler* pluginHandler = PluginHandler::getInstance(); + // std::transform(name.begin(), name.end(), name.begin(), ::tolower); - m_lat = lat; - m_lon = lon; - m_IconName = icon_ident; - m_MarkName = wp_name; - m_GUID = GUID; + for (auto plugin : pluginHandler->getInstalled()) { + if (plugin.name == name) { + if (safe_mode::get_mode()) + return true; + else + return !plugin.readonly; + } + } + return false; } -PlugIn_Waypoint::~PlugIn_Waypoint() {} - -// PlugInRoute implementation -PlugIn_Route::PlugIn_Route(void) { pWaypointList = new Plugin_WaypointList; } - -PlugIn_Route::~PlugIn_Route(void) { - pWaypointList->DeleteContents(false); // do not delete Waypoints - pWaypointList->Clear(); - - delete pWaypointList; -} +PluginPanel::PluginPanel(wxPanel* parent, const std::string& name) + : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxBORDER_NONE), + m_is_safe_panel(true) { + m_PluginListPanel = dynamic_cast(parent); + wxASSERT(m_PluginListPanel != 0); + wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL); + SetSizer(top_sizer); + wxBoxSizer* top_horizontal = new wxBoxSizer(wxHORIZONTAL); + top_sizer->Add(top_horizontal, 0, wxEXPAND); -// PlugInTrack implementation -PlugIn_Track::PlugIn_Track(void) { pWaypointList = new Plugin_WaypointList; } + double iconSize = GetCharWidth() * 4; + double dpi_mult = g_Platform->GetDisplayDIPMult(this); + int icon_scale = iconSize * dpi_mult; + ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle(); + wxBitmap bitmap(style->GetIcon("default_pi", icon_scale, icon_scale)); + m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap); + top_horizontal->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10); -PlugIn_Track::~PlugIn_Track(void) { - pWaypointList->DeleteContents(false); // do not delete Waypoints - pWaypointList->Clear(); + m_pName = new wxStaticText(this, wxID_ANY, name); + top_horizontal->Add(m_pName, wxID_ANY, wxALIGN_CENTER_VERTICAL); + m_pVersion = new wxStaticText(this, wxID_ANY, ""); + top_horizontal->Add(m_pVersion); + m_pVersion->Hide(); - delete pWaypointList; + m_pButtons = new wxBoxSizer(wxHORIZONTAL); + top_horizontal->Add(m_pButtons); + m_info_btn = new WebsiteButton(this, "https:\\opencpn.org"); + top_horizontal->Add(m_info_btn); + m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"), + wxDefaultPosition, wxDefaultSize, 0); + top_horizontal->Add(m_pButtonUninstall, 0, wxALIGN_CENTER_VERTICAL | wxALL, + 2); + auto uninstall = [&](wxCommandEvent ev) { + auto n = m_pName->GetLabel().ToStdString(); + int result = + OCPNMessageBox(gFrame, std::string(_("Uninstall plugin ")) + n + "?", + _("Un-Installation"), wxICON_QUESTION | wxOK | wxCANCEL); + if (result != wxID_OK) return; + PluginHandler::getInstance()->ClearInstallData(n); + m_PluginListPanel->ReloadPluginPanels(); + }; + m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED, uninstall); } -wxString GetNewGUID(void) { return GpxDocument::GetUUID(); } +BEGIN_EVENT_TABLE(PluginPanel, wxPanel) +EVT_PAINT(PluginPanel::OnPaint) +END_EVENT_TABLE() -bool AddCustomWaypointIcon(wxBitmap* pimage, wxString key, - wxString description) { - wxImage image = pimage->ConvertToImage(); - WayPointmanGui(*pWayPointMan).ProcessIcon(image, key, description); - return true; -} +PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos, + const wxSize& size, const PlugInData plugin) + : wxPanel(parent, id, pos, size, wxBORDER_NONE), + m_plugin(plugin), + m_is_safe_panel(false) { + m_PluginListPanel = (PluginListPanel*)parent; //->GetParent(); + m_PluginListPanel = dynamic_cast(parent /*->GetParent()*/); + wxASSERT(m_PluginListPanel != 0); -static void cloneHyperlinkList(RoutePoint* dst, const PlugIn_Waypoint* src) { - // Transcribe (clone) the html HyperLink List, if present - if (src->m_HyperlinkList == nullptr) return; + m_bSelected = false; + m_penWidthUnselected = g_Platform->GetDisplayDPmm() * .25; + m_penWidthSelected = g_Platform->GetDisplayDPmm() * .5; - if (src->m_HyperlinkList->GetCount() > 0) { - wxPlugin_HyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst(); - while (linknode) { - Plugin_Hyperlink* link = linknode->GetData(); + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + SetSizer(topSizer); - Hyperlink* h = new Hyperlink(); - h->DescrText = link->DescrText; - h->Link = link->Link; - h->LType = link->Type; + wxBoxSizer* itemBoxSizer01 = new wxBoxSizer(wxHORIZONTAL); + topSizer->Add(itemBoxSizer01, 0, wxEXPAND); + Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - dst->m_HyperlinkList->Append(h); + double iconSize = GetCharWidth() * 4; + double dpi_mult = g_Platform->GetDisplayDIPMult(this); + int icon_scale = iconSize * dpi_mult; - linknode = linknode->GetNext(); - } + wxImage plugin_icon; + ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle(); + if (m_plugin.m_bitmap.IsOk()) { + plugin_icon = m_plugin.m_bitmap.ConvertToImage(); } -} - -bool AddSingleWaypoint(PlugIn_Waypoint* pwaypoint, bool b_permanent) { - // Validate the waypoint parameters a little bit - - // GUID - // Make sure that this GUID is indeed unique in the Routepoint list - bool b_unique = true; - wxRoutePointListNode* prpnode = pWayPointMan->GetWaypointList()->GetFirst(); - while (prpnode) { - RoutePoint* prp = prpnode->GetData(); - - if (prp->m_GUID == pwaypoint->m_GUID) { - b_unique = false; - break; - } - prpnode = prpnode->GetNext(); // RoutePoint + wxBitmap bitmap; + if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) { + wxFileName path(g_Platform->GetSharedDataDir(), "packageBox.svg"); + path.AppendDir("uidata"); + path.AppendDir("traditional"); // FIXME(leamas) cache it. + bitmap = LoadSVG(path.GetFullPath(), icon_scale, icon_scale); + } else if (plugin_icon.IsOk()) { + int nowSize = plugin_icon.GetWidth(); + plugin_icon.Rescale(icon_scale, icon_scale, wxIMAGE_QUALITY_HIGH); + bitmap = wxBitmap(plugin_icon); + } else { + bitmap = wxBitmap(style->GetIcon("default_pi", icon_scale, icon_scale)); } + m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap); - if (!b_unique) return false; - - RoutePoint* pWP = - new RoutePoint(pwaypoint->m_lat, pwaypoint->m_lon, pwaypoint->m_IconName, - pwaypoint->m_MarkName, pwaypoint->m_GUID); - - pWP->m_bIsolatedMark = true; // This is an isolated mark - - cloneHyperlinkList(pWP, pwaypoint); - - pWP->m_MarkDescription = pwaypoint->m_MarkDescription; + itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10); + m_itemStaticBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, + this); + m_itemStaticBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, + this); - if (pwaypoint->m_CreateTime.IsValid()) - pWP->SetCreateTime(pwaypoint->m_CreateTime); - else { - wxDateTime dtnow(wxDateTime::Now()); - pWP->SetCreateTime(dtnow); - } + wxBoxSizer* itemBoxSizer02 = new wxBoxSizer(wxVERTICAL); + itemBoxSizer01->Add(itemBoxSizer02, 1, wxEXPAND | wxALL, 0); - pWP->m_btemp = (b_permanent == false); + // Calculate character width available + int nChars = g_options->GetSize().x / GetCharWidth(); + bool bCompact = false; + if (nChars < 60) // Arbitrary, detecting mobile devices in portrait mode. + bCompact = true; - pSelect->AddSelectableRoutePoint(pwaypoint->m_lat, pwaypoint->m_lon, pWP); - if (b_permanent) pConfig->AddNewWayPoint(pWP, -1); - - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateWptListCtrl(); - - return true; -} - -bool DeleteSingleWaypoint(wxString& GUID) { - // Find the RoutePoint - bool b_found = false; - RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(GUID); - - if (prp) b_found = true; - - if (b_found) { - pWayPointMan->DestroyWaypoint(prp); - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateWptListCtrl(); - } + if (bCompact) { + // Might need to shorten the Plugin name string + wxString nameString = m_plugin.m_common_name; + int maxWidth = g_Platform->getDisplaySize().x * 3 / 10; + wxScreenDC dc; + int nameWidth; + dc.GetTextExtent(m_plugin.m_common_name, &nameWidth, NULL); + if (nameWidth > maxWidth) { + nameString = wxControl::Ellipsize(m_plugin.m_common_name, dc, + wxELLIPSIZE_END, maxWidth); + } + m_pName = new wxStaticText(this, wxID_ANY, nameString); + m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); + itemBoxSizer02->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 5); - return b_found; -} + wxFlexGridSizer* sl1 = new wxFlexGridSizer(2, 0, 0); + sl1->AddGrowableCol(1); + itemBoxSizer02->Add(sl1, 0, wxEXPAND); -bool UpdateSingleWaypoint(PlugIn_Waypoint* pwaypoint) { - // Find the RoutePoint - bool b_found = false; - RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(pwaypoint->m_GUID); + m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA")); + sl1->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 5); + if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) { + m_pVersion->Hide(); + } + m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - if (prp) b_found = true; + m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled")); + sl1->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 5); + m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this); - if (b_found) { - double lat_save = prp->m_lat; - double lon_save = prp->m_lon; + // Might need to shorten the Plugin description string + wxString descriptionString = m_plugin.m_short_description; + int maxDescriptionWidth = g_Platform->getDisplaySize().x - (iconSize * 4); + int descriptionWidth; + dc.GetTextExtent(m_plugin.m_short_description, &descriptionWidth, NULL); + if (descriptionWidth > maxDescriptionWidth) + descriptionString = + wxControl::Ellipsize(m_plugin.m_short_description, dc, + wxELLIPSIZE_END, maxDescriptionWidth); - prp->m_lat = pwaypoint->m_lat; - prp->m_lon = pwaypoint->m_lon; - prp->SetIconName(pwaypoint->m_IconName); - prp->SetName(pwaypoint->m_MarkName); - prp->m_MarkDescription = pwaypoint->m_MarkDescription; - prp->SetVisible(pwaypoint->m_IsVisible); - if (pwaypoint->m_CreateTime.IsValid()) - prp->SetCreateTime(pwaypoint->m_CreateTime); + // This invocation has the effect of setting the minimum width of the + // descriptor field. + m_pDescription = + new wxStaticText(this, wxID_ANY, descriptionString, wxDefaultPosition, + wxSize(maxDescriptionWidth, -1), wxST_NO_AUTORESIZE); + itemBoxSizer02->Add(m_pDescription, 0, wxEXPAND | wxALL, 5); + m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - // Transcribe (clone) the html HyperLink List, if present + } else { + wxFlexGridSizer* itemBoxSizer03 = new wxFlexGridSizer(4, 0, 0); + itemBoxSizer03->AddGrowableCol(2); + itemBoxSizer02->Add(itemBoxSizer03, 0, wxEXPAND); - if (pwaypoint->m_HyperlinkList) { - prp->m_HyperlinkList->Clear(); - if (pwaypoint->m_HyperlinkList->GetCount() > 0) { - wxPlugin_HyperlinkListNode* linknode = - pwaypoint->m_HyperlinkList->GetFirst(); - while (linknode) { - Plugin_Hyperlink* link = linknode->GetData(); + wxString nameString = m_plugin.m_common_name; + m_pName = new wxStaticText(this, wxID_ANY, nameString); + m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - Hyperlink* h = new Hyperlink(); - h->DescrText = link->DescrText; - h->Link = link->Link; - h->LType = link->Type; + // Avoid known bug in wxGTK3 +#ifndef __WXGTK3__ + wxFont font = GetFont(); + font.SetWeight(wxFONTWEIGHT_BOLD); + m_pName->SetFont(font); +#endif - prp->m_HyperlinkList->Append(h); + itemBoxSizer03->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 10); - linknode = linknode->GetNext(); - } - } + m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA")); + itemBoxSizer03->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 10); + if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable || + m_plugin.m_status == PluginStatus::System || + (m_plugin.m_status == PluginStatus::Unmanaged && + !m_plugin.m_managed_metadata.is_orphan)) { + m_pVersion->Hide(); } + m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - if (prp) prp->ReLoadIcon(); - - auto canvas = gFrame->GetPrimaryCanvas(); - SelectCtx ctx(canvas->m_bShowNavobjects, canvas->GetCanvasTrueScale(), - canvas->GetScaleValue()); - SelectItem* pFind = - pSelect->FindSelection(ctx, lat_save, lon_save, SELTYPE_ROUTEPOINT); - if (pFind) { - pFind->m_slat = pwaypoint->m_lat; // update the SelectList entry - pFind->m_slon = pwaypoint->m_lon; - } + m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled")); + itemBoxSizer03->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 10); + m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this); - if (!prp->m_btemp) pConfig->UpdateWayPoint(prp); + itemBoxSizer03->Add(5 * GetCharWidth(), 1, 0, wxALIGN_RIGHT | wxTOP, 10); - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateWptListCtrl(); + m_pDescription = new wxStaticText( + this, wxID_ANY, m_plugin.m_short_description, wxDefaultPosition, + wxSize(-1, -1) /*, wxST_NO_AUTORESIZE*/); + itemBoxSizer02->Add(m_pDescription, 1, wxEXPAND | wxALL, 5); + m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); } - return b_found; -} - -// translate O route class to Plugin one -static void PlugInFromRoutePoint(PlugIn_Waypoint* dst, - /* const*/ RoutePoint* src) { - dst->m_lat = src->m_lat; - dst->m_lon = src->m_lon; - dst->m_IconName = src->GetIconName(); - dst->m_MarkName = src->GetName(); - dst->m_MarkDescription = src->m_MarkDescription; - dst->m_IsVisible = src->IsVisible(); - dst->m_CreateTime = src->GetCreateTime(); // not const - dst->m_GUID = src->m_GUID; - - // Transcribe (clone) the html HyperLink List, if present - if (src->m_HyperlinkList == nullptr) return; - - delete dst->m_HyperlinkList; - dst->m_HyperlinkList = nullptr; - - if (src->m_HyperlinkList->GetCount() > 0) { - dst->m_HyperlinkList = new Plugin_HyperlinkList; - - wxHyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst(); - while (linknode) { - Hyperlink* link = linknode->GetData(); + if (!bCompact) { + m_info_btn = new WebsiteButton(this, "https:\\opencpn.org"); + m_info_btn->Hide(); + itemBoxSizer02->Add(m_info_btn, 0); - Plugin_Hyperlink* h = new Plugin_Hyperlink(); - h->DescrText = link->DescrText; - h->Link = link->Link; - h->Type = link->LType; + m_pButtons = new wxBoxSizer(wxHORIZONTAL); + itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0); + m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"), + wxDefaultPosition, wxDefaultSize, 0); + m_pButtons->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 2); - dst->m_HyperlinkList->Append(h); + m_pButtons->AddSpacer(3 * GetCharWidth()); - linknode = linknode->GetNext(); - } - } -} + m_pButtonAction = + new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX", + wxDefaultPosition, wxDefaultSize, 0); + m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2); -bool GetSingleWaypoint(wxString GUID, PlugIn_Waypoint* pwaypoint) { - // Find the RoutePoint - RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(GUID); + m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"), + wxDefaultPosition, wxDefaultSize, 0); + m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2); + } else { + m_pButtons = new wxBoxSizer(wxVERTICAL); + itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0); - if (!prp) return false; + wxBoxSizer* tline = new wxBoxSizer(wxHORIZONTAL); + m_pButtons->Add(tline, 0, wxALL, 2); - PlugInFromRoutePoint(pwaypoint, prp); + m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"), + wxDefaultPosition, wxDefaultSize, 0); + tline->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 0); - return true; -} + tline->AddSpacer(3 * GetCharWidth()); -wxArrayString GetWaypointGUIDArray(void) { - wxArrayString result; - RoutePointList* list = pWayPointMan->GetWaypointList(); + m_info_btn = new WebsiteButton(this, "https:\\opencpn.org"); + m_info_btn->Hide(); + tline->Add(m_info_btn, 0); - wxRoutePointListNode* prpnode = list->GetFirst(); - while (prpnode) { - RoutePoint* prp = prpnode->GetData(); - result.Add(prp->m_GUID); + m_pButtonAction = + new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX", + wxDefaultPosition, wxDefaultSize); + m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2); - prpnode = prpnode->GetNext(); // RoutePoint + m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"), + wxDefaultPosition, wxDefaultSize, 0); + m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2); } - return result; -} + wxBitmap statusBitmap; + const auto stat = m_plugin.m_status; + auto icon_name = icon_by_status.at(stat); + if (stat == PluginStatus::Imported + && IsUpdateAvailable(m_plugin.m_managed_metadata)) { + icon_name = + icon_by_status.at(PluginStatus::ManagedInstalledUpdateAvailable); + } -wxArrayString GetRouteGUIDArray(void) { - wxArrayString result; - RouteList* list = pRouteList; + wxFileName path(g_Platform->GetSharedDataDir(), icon_name); + path.AppendDir("uidata"); + path.AppendDir("traditional"); + bool ok = false; + int bmsize = GetCharWidth() * 3 * dpi_mult; + if (path.IsFileReadable()) { + statusBitmap = LoadSVG(path.GetFullPath(), bmsize, bmsize); + ok = statusBitmap.IsOk(); + } + if (!ok) { + auto style = g_StyleManager->GetCurrentStyle(); + statusBitmap = wxBitmap(style->GetIcon(_T("default_pi"), bmsize, bmsize)); + wxLogMessage("Icon: %s not found.", path.GetFullPath()); + } - wxRouteListNode* prpnode = list->GetFirst(); - while (prpnode) { - Route* proute = prpnode->GetData(); - result.Add(proute->m_GUID); + m_itemStatusIconBitmap = new wxStaticBitmap(this, wxID_ANY, statusBitmap); + m_itemStatusIconBitmap->SetToolTip(message_by_status(stat)); + m_itemStatusIconBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, + this); + m_itemStatusIconBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, + this); - prpnode = prpnode->GetNext(); // Route - } + itemBoxSizer01->Add(m_itemStatusIconBitmap, 0, wxEXPAND | wxALL, 20); - return result; -} + itemBoxSizer02->AddSpacer(GetCharWidth()); -wxArrayString GetTrackGUIDArray(void) { - wxArrayString result; - for (Track* ptrack : g_TrackList) { - result.Add(ptrack->m_GUID); - } + m_pButtonPreferences->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &PluginPanel::OnPluginPreferences, this); + m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &PluginPanel::OnPluginUninstall, this); + m_pButtonAction->Bind(wxEVT_COMMAND_BUTTON_CLICKED, + &PluginPanel::OnPluginAction, this); - return result; + SetSelected(m_bSelected); + SetAutoLayout(true); + Fit(); } -wxArrayString GetWaypointGUIDArray(OBJECT_LAYER_REQ req) { - wxArrayString result; - RoutePointList* list = pWayPointMan->GetWaypointList(); - - wxRoutePointListNode* prpnode = list->GetFirst(); - while (prpnode) { - RoutePoint* prp = prpnode->GetData(); - switch (req) { - case OBJECTS_ALL: - result.Add(prp->m_GUID); - break; - case OBJECTS_NO_LAYERS: - if (!prp->m_bIsInLayer) result.Add(prp->m_GUID); - break; - case OBJECTS_ONLY_LAYERS: - if (prp->m_bIsInLayer) result.Add(prp->m_GUID); - break; - } +PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos, + const wxSize& size, PluginMetadata md) + : PluginPanel(parent, id, pos, size, PlugInData(md)) {}; - prpnode = prpnode->GetNext(); // RoutePoint +PluginPanel::~PluginPanel() { + Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + if (m_is_safe_panel) return; + m_itemStaticBitmap->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, + this); + m_pName->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + m_pVersion->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + m_pDescription->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); + if (m_pButtonAction) { + m_pButtonAction->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, + &PluginPanel::OnPluginAction, this); } - - return result; + m_pButtonPreferences->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, + &PluginPanel::OnPluginPreferences, this); + m_cbEnable->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, + &PluginPanel::OnPluginEnableToggle, this); } -wxArrayString GetRouteGUIDArray(OBJECT_LAYER_REQ req) { - wxArrayString result; - RouteList* list = pRouteList; - - wxRouteListNode* prpnode = list->GetFirst(); - while (prpnode) { - Route* proute = prpnode->GetData(); - switch (req) { - case OBJECTS_ALL: - result.Add(proute->m_GUID); - break; - case OBJECTS_NO_LAYERS: - if (!proute->m_bIsInLayer) result.Add(proute->m_GUID); - break; - case OBJECTS_ONLY_LAYERS: - if (proute->m_bIsInLayer) result.Add(proute->m_GUID); - break; - } +void PluginPanel::SetActionLabel(wxString& label) { + m_pButtonAction->SetLabel(label); + Refresh(); +} - prpnode = prpnode->GetNext(); // Route - } +static wxStopWatch swclick; +static int downx, downy; - return result; +void PluginPanel::OnPluginSelected(wxMouseEvent& event) { +#ifdef __ANDROID__ + swclick.Start(); + event.GetPosition(&downx, &downy); +#else + DoPluginSelect(); +#endif } -wxArrayString GetTrackGUIDArray(OBJECT_LAYER_REQ req) { - wxArrayString result; - for (Track* ptrack : g_TrackList) { - switch (req) { - case OBJECTS_ALL: - result.Add(ptrack->m_GUID); - break; - case OBJECTS_NO_LAYERS: - if (!ptrack->m_bIsInLayer) result.Add(ptrack->m_GUID); - break; - case OBJECTS_ONLY_LAYERS: - if (ptrack->m_bIsInLayer) result.Add(ptrack->m_GUID); - break; +void PluginPanel::OnPluginSelectedUp(wxMouseEvent& event) { +#ifdef __ANDROID__ + qDebug() << swclick.Time(); + if (swclick.Time() < 200) { + int upx, upy; + event.GetPosition(&upx, &upy); + if ((fabs(upx - downx) < GetCharWidth()) && + (fabs(upy - downy) < GetCharWidth())) { + DoPluginSelect(); } } - - return result; + swclick.Start(); +#endif } -wxArrayString GetIconNameArray(void) { - wxArrayString result; +void PluginPanel::DoPluginSelect() { + if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) { + // auto dialog = dynamic_cast(GetParent()); + // auto dialog = dynamic_cast(m_parent); + // wxASSERT(dialog != 0); - for (int i = 0; i < pWayPointMan->GetNumIcons(); i++) { - wxString* ps = pWayPointMan->GetIconKey(i); - result.Add(*ps); + // Install the new plugin, auto-enabling as a convenience measure. + run_update_dialog(m_PluginListPanel, &m_plugin, false, 0, true); + } else if (m_bSelected) { + SetSelected(false); + m_PluginListPanel->SelectPlugin(NULL); + } else { + SetSelected(true); + m_PluginListPanel->SelectPlugin(this); } - return result; } -bool AddPlugInRoute(PlugIn_Route* proute, bool b_permanent) { - Route* route = new Route(); - - PlugIn_Waypoint* pwp; - RoutePoint* pWP_src; - int ip = 0; - wxDateTime plannedDeparture; +/** + * Return metadata for installed plugin with given name or default, + * empty PluginMetadata() if not found. + */ +static PluginMetadata GetMetadataByName(const std::string& name) { + auto plugins = PluginHandler::getInstance()->getInstalled(); + auto predicate = [name](const PluginMetadata& pm) { return pm.name == name; }; + auto found = std::find_if(plugins.begin(), plugins.end(), predicate); + if (found == plugins.end()) { + wxLogDebug("Cannot find metadata for %s", name.c_str()); + } + return found != plugins.end() ? *found : PluginMetadata(); +} - wxPlugin_WaypointListNode* pwpnode = proute->pWaypointList->GetFirst(); - while (pwpnode) { - pwp = pwpnode->GetData(); +void PluginPanel::SetSelected(bool selected) { + m_bSelected = selected; - RoutePoint* pWP = new RoutePoint(pwp->m_lat, pwp->m_lon, pwp->m_IconName, - pwp->m_MarkName, pwp->m_GUID); + m_pVersion->SetLabel( + PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName)); + if (selected) { + SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND)); + m_pButtons->Show(true); + bool unInstallPossible = canUninstall(m_plugin.m_common_name.ToStdString()); - // Transcribe (clone) the html HyperLink List, if present - cloneHyperlinkList(pWP, pwp); - pWP->m_MarkDescription = pwp->m_MarkDescription; - pWP->m_bShowName = false; - pWP->SetCreateTime(pwp->m_CreateTime); + // Directly mark Legacy and system plugins as "not uninstallable" + if (m_plugin.m_status == PluginStatus::LegacyUpdateAvailable || + m_plugin.m_status == PluginStatus::Unmanaged || + m_plugin.m_status == PluginStatus::System) + unInstallPossible = false; - route->AddPoint(pWP); + // Orphan plugins can usually be uninstalled, at best effort. + if (m_plugin.m_managed_metadata.is_orphan) unInstallPossible = true; - pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP); + m_pButtonUninstall->Show(unInstallPossible); - if (ip > 0) - pSelect->AddSelectableRouteSegment(pWP_src->m_lat, pWP_src->m_lon, - pWP->m_lat, pWP->m_lon, pWP_src, pWP, - route); - else - plannedDeparture = pwp->m_CreateTime; - ip++; - pWP_src = pWP; + if (m_plugin.m_managed_metadata.info_url.size()) { + m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str()); + m_info_btn->Show(); + } - pwpnode = pwpnode->GetNext(); // PlugInWaypoint - } + m_cbEnable->Show(true); - route->m_PlannedDeparture = plannedDeparture; + // Configure the "Action" button + wxString label; + SemanticVersion newVersion; + switch (m_plugin.m_status) { + case PluginStatus::LegacyUpdateAvailable: + label = _("Upgrade to Version "); + label += wxString(m_plugin.m_managed_metadata.version.c_str()); + m_action = ActionVerb::UPGRADE_TO_MANAGED_VERSION; + m_pButtonAction->Enable(); + break; - route->m_RouteNameString = proute->m_NameString; - route->m_RouteStartString = proute->m_StartString; - route->m_RouteEndString = proute->m_EndString; - if (!proute->m_GUID.IsEmpty()) { - route->m_GUID = proute->m_GUID; - } - route->m_btemp = (b_permanent == false); + case PluginStatus::ManagedInstallAvailable: + label = _("Install..."); + m_action = ActionVerb::INSTALL_MANAGED_VERSION; + m_pButtonAction->Enable(); + break; - pRouteList->Append(route); + case PluginStatus::ManagedInstalledUpdateAvailable: + label = _("Update to "); + label += wxString(m_plugin.m_managed_metadata.version.c_str()); + m_action = ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION; + m_pButtonAction->Enable(); + break; - if (b_permanent) pConfig->AddNewRoute(route); + case PluginStatus::ManagedInstalledCurrentVersion: + label = _("Reinstall"); + m_action = ActionVerb::REINSTALL_MANAGED_VERSION; + m_pButtonAction->Enable(); + break; - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateRouteListCtrl(); + case PluginStatus::ManagedInstalledDowngradeAvailable: + label = _("Downgrade"); + m_action = ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION; + m_pButtonAction->Enable(); + break; - return true; -} + case PluginStatus::Imported: + if (IsUpdateAvailable(m_plugin.m_managed_metadata)) { + label = _("Update"); + m_action = ActionVerb::UPDATE_IMPORTED_VERSION; + } else { + m_pButtonAction->Hide(); + m_action = ActionVerb::NOP; + } + break; -bool DeletePlugInRoute(wxString& GUID) { - bool b_found = false; + case PluginStatus::Unmanaged: + m_action = ActionVerb::NOP; + m_pButtonAction->Hide(); + break; - // Find the Route - Route* pRoute = g_pRouteMan->FindRouteByGUID(GUID); - if (pRoute) { - g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance()); - b_found = true; - } - return b_found; -} + case PluginStatus::System: + m_action = ActionVerb::NOP; + m_pButtonAction->Hide(); + break; -bool UpdatePlugInRoute(PlugIn_Route* proute) { - bool b_found = false; + default: + label = "TBD"; + m_action = ActionVerb::NOP; + break; + } + SetActionLabel(label); + Layout(); + } else { + SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND)); + // m_pDescription->SetLabel( m_pPlugin->m_short_description ); +#ifndef __WXQT__ + // m_pButtons->Show(false); +#else + // m_pButtons->Show(true); +#endif + //(); - // Find the Route - Route* pRoute = g_pRouteMan->FindRouteByGUID(proute->m_GUID); - if (pRoute) b_found = true; + m_pButtons->Show(false); + m_info_btn->Hide(); - if (b_found) { - bool b_permanent = (pRoute->m_btemp == false); - g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance()); + if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) + m_cbEnable->Show(false); - b_found = AddPlugInRoute(proute, b_permanent); + Layout(); } - return b_found; -} - -bool AddPlugInTrack(PlugIn_Track* ptrack, bool b_permanent) { - Track* track = new Track(); - - PlugIn_Waypoint* pwp = 0; - TrackPoint* pWP_src = 0; - int ip = 0; + // m_pButtons->Show(selected); // For most platforms, show buttons if + // selected m_pButtonsUpDown->Show(selected); +#ifdef __ANDROID__ + // Some Android devices (e.g. Kyocera) have trouble with wxBitmapButton... + // m_pButtonsUpDown->Show(false); + // m_pButtons->Show(true); // Always enable buttons for Android +#endif - wxPlugin_WaypointListNode* pwpnode = ptrack->pWaypointList->GetFirst(); - while (pwpnode) { - pwp = pwpnode->GetData(); + Layout(); - TrackPoint* pWP = new TrackPoint(pwp->m_lat, pwp->m_lon); - pWP->SetCreateTime(pwp->m_CreateTime); + if (selected) { + SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND)); + } else { + SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND)); + } - track->AddPoint(pWP); + SetEnabled(m_plugin.m_enabled); - if (ip > 0) - pSelect->AddSelectableTrackSegment(pWP_src->m_lat, pWP_src->m_lon, - pWP->m_lat, pWP->m_lon, pWP_src, pWP, - track); - ip++; - pWP_src = pWP; +#ifdef __ANDROID__ + // Android (wxQT) sizers have troubles... + // So we set some layout factors to avoid re-sizing on select/deselect. + // m_rgSizer->Show(true); + // m_pButtons->Show(true); + // m_pButtonAction->Hide(); + // m_pButtonUninstall->Hide(); - pwpnode = pwpnode->GetNext(); // PlugInWaypoint - } + Fit(); + // m_PluginListPanel->m_pitemBoxSizer01->Layout(); +#endif +} - track->SetName(ptrack->m_NameString); - track->m_TrackStartString = ptrack->m_StartString; - track->m_TrackEndString = ptrack->m_EndString; - track->m_GUID = ptrack->m_GUID; - track->m_btemp = (b_permanent == false); +void PluginPanel::OnPaint(wxPaintEvent& event) { + wxPaintDC dc(this); - g_TrackList.push_back(track); + int penWidth = m_penWidthUnselected; + wxColour color = GetDialogColor(DLG_UNSELECTED_BACKGROUND); + wxColour border = GetDialogColor(DLG_UNSELECTED_ACCENT); - if (b_permanent) pConfig->AddNewTrack(track); + if (m_bSelected) { + penWidth = m_penWidthSelected; + color = GetDialogColor(DLG_SELECTED_BACKGROUND); + border = GetDialogColor(DLG_SELECTED_ACCENT); + } - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateTrkListCtrl(); + wxBrush b(color, wxBRUSHSTYLE_SOLID); + dc.SetBrush(b); + dc.SetPen(wxPen(border, penWidth)); - return true; + dc.DrawRoundedRectangle(5, 5, GetSize().x - 10, GetSize().y - 10, 5); } -bool DeletePlugInTrack(wxString& GUID) { - bool b_found = false; +void PluginPanel::OnPluginPreferences(wxCommandEvent& event) { + if (m_plugin.m_enabled && m_plugin.m_init_state && + (m_plugin.m_cap_flag & WANTS_PREFERENCES)) { +#ifdef __ANDROID__ + androidDisableRotation(); + PluginLoader::getInstance()->ShowPreferencesDialog(m_plugin, + GetGrandParent()); + // GrandParent will be the entire list panel, not the plugin panel. + // Ensures better centering on small screens +#else + PluginLoader::getInstance()->ShowPreferencesDialog(m_plugin, this); +#endif + } +} - // Find the Route - Track* pTrack = g_pRouteMan->FindTrackByGUID(GUID); - if (pTrack) { - RoutemanGui(*g_pRouteMan).DeleteTrack(pTrack); - b_found = true; +void PluginPanel::OnPluginEnableToggle(wxCommandEvent& event) { + SetEnabled(event.IsChecked()); + m_pVersion->SetLabel( + PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName)); + if (m_plugin.m_status == PluginStatus::System) { + // Force pluginmanager to reload all panels. Not kosher -- + // the EventVar should really only be notified from within PluginLoader. + PluginLoader::getInstance()->evt_pluglist_change.Notify(); } +} - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateTrkListCtrl(); +void PluginPanel::OnPluginUninstall(wxCommandEvent& event) { + m_action = ActionVerb::UNINSTALL_MANAGED_VERSION; - return b_found; + // Chain up to the utility event handler + wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED); + actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION); + actionEvent.SetClientData(this); + g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent); } -bool UpdatePlugInTrack(PlugIn_Track* ptrack) { - bool b_found = false; +void PluginPanel::OnPluginAction(wxCommandEvent& event) { + // Chain up to the utility event handler + wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED); + actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION); + actionEvent.SetClientData(this); + g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent); - // Find the Track - Track* pTrack = g_pRouteMan->FindTrackByGUID(ptrack->m_GUID); - if (pTrack) b_found = true; + return; +} - if (b_found) { - bool b_permanent = (pTrack->m_btemp == false); - RoutemanGui(*g_pRouteMan).DeleteTrack(pTrack); +static void SetWindowFontStyle(wxWindow* window, wxFontStyle style) { + auto font = window->GetFont(); + font.SetStyle(style); + window->SetFont(font); +} - b_found = AddPlugInTrack(ptrack, b_permanent); +void PluginPanel::SetEnabled(bool enabled) { + if (m_is_safe_panel) return; + PluginLoader::getInstance()->SetEnabled(m_plugin.m_common_name, enabled); + PluginLoader::getInstance()->UpdatePlugIns(); + NotifySetupOptionsPlugin(&m_plugin); + if (!enabled && !m_bSelected) { + SetWindowFontStyle(m_pName, wxFONTSTYLE_ITALIC); + SetWindowFontStyle(m_pVersion, wxFONTSTYLE_ITALIC); + SetWindowFontStyle(m_pDescription, wxFONTSTYLE_ITALIC); +#ifdef x__ANDROID__ + m_pName->Disable(); + m_pVersion->Disable(); + m_pDescription->Disable(); +#endif + } else { + SetWindowFontStyle(m_pName, wxFONTSTYLE_NORMAL); + SetWindowFontStyle(m_pVersion, wxFONTSTYLE_NORMAL); + SetWindowFontStyle(m_pDescription, wxFONTSTYLE_NORMAL); +#ifdef x__ANDROID__ + m_pName->Enable(); + m_pVersion->Enable(); + m_pDescription->Enable(); +#endif } - return b_found; -} +#ifdef __ANDROID__ + m_pName->Enable(enabled || m_bSelected); + m_pVersion->Enable(enabled || m_bSelected); + m_pDescription->Enable(enabled || m_bSelected); +#endif -bool PlugInHasNormalizedViewPort(PlugIn_ViewPort* vp) { -#ifdef ocpnUSE_GL - ViewPort ocpn_vp; - ocpn_vp.m_projection_type = vp->m_projection_type; + if (m_bSelected) { + wxString description = m_plugin.m_long_description; + if (description.IsEmpty()) + description = wxString(m_plugin.m_managed_metadata.description.c_str()); - return glChartCanvas::HasNormalizedViewPort(ocpn_vp); -#else - return false; -#endif + PanelHardBreakWrapper wrapper(this, description, + g_options->GetSize().x * 7 / 10); + m_pDescription->SetLabel(wrapper.GetWrapped()); + if (m_plugin.m_managed_metadata.info_url.size()) { + m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str()); + m_info_btn->Show(); + } + } else { + wxString description = m_plugin.m_short_description; + if (description.IsEmpty()) + description = wxString(m_plugin.m_managed_metadata.summary.c_str()); + PanelHardBreakWrapper wrapper(this, description, + g_options->GetSize().x * 7 / 10); + m_pDescription->SetLabel(wrapper.GetWrapped()); + } + + m_pButtonPreferences->Enable(enabled && + (m_plugin.m_cap_flag & WANTS_PREFERENCES)); + m_cbEnable->SetValue(enabled); } -void PlugInMultMatrixViewport(PlugIn_ViewPort* vp, float lat, float lon) { -#ifdef ocpnUSE_GL - ViewPort ocpn_vp; - ocpn_vp.clat = vp->clat; - ocpn_vp.clon = vp->clon; - ocpn_vp.m_projection_type = vp->m_projection_type; - ocpn_vp.view_scale_ppm = vp->view_scale_ppm; - ocpn_vp.skew = vp->skew; - ocpn_vp.rotation = vp->rotation; - ocpn_vp.pix_width = vp->pix_width; - ocpn_vp.pix_height = vp->pix_height; - -// TODO fix for multicanvas glChartCanvas::MultMatrixViewPort(ocpn_vp, lat, -// lon); -#endif +void PluginPanel::OnPluginUp(wxCommandEvent& event) { + m_PluginListPanel->MoveUp(this); } -void PlugInNormalizeViewport(PlugIn_ViewPort* vp, float lat, float lon) { -#ifdef ocpnUSE_GL - ViewPort ocpn_vp; - glChartCanvas::NormalizedViewPort(ocpn_vp, lat, lon); - - vp->clat = ocpn_vp.clat; - vp->clon = ocpn_vp.clon; - vp->view_scale_ppm = ocpn_vp.view_scale_ppm; - vp->rotation = ocpn_vp.rotation; - vp->skew = ocpn_vp.skew; -#endif +void PluginPanel::OnPluginDown(wxCommandEvent& event) { + m_PluginListPanel->MoveDown(this); } -// Helper and interface classes +/** Invokes client browser on plugin info_url when clicked. */ +WebsiteButton::WebsiteButton(wxWindow* parent, const char* url) + : wxPanel(parent), m_url(url) { + auto vbox = new wxBoxSizer(wxVERTICAL); + auto button = new wxButton(this, wxID_ANY, _("Website")); + button->Enable(strlen(url) > 0); + vbox->Add(button); + SetSizer(vbox); + Bind(wxEVT_COMMAND_BUTTON_CLICKED, + [=](wxCommandEvent&) { wxLaunchDefaultBrowser(m_url); }); +} -//------------------------------------------------------------------------------- -// PlugIn_AIS_Target Implementation -//------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// PlugInChartBase Implmentation +// This class is the base class for Plug-able chart types +// ---------------------------------------------------------------------------- -PlugIn_AIS_Target* Create_PI_AIS_Target(AisTargetData* ptarget) { - PlugIn_AIS_Target* pret = new PlugIn_AIS_Target; +PlugInChartBase::PlugInChartBase() { m_Chart_Error_Factor = 0.; } - pret->MMSI = ptarget->MMSI; - pret->Class = ptarget->Class; - pret->NavStatus = ptarget->NavStatus; - pret->SOG = ptarget->SOG; - pret->COG = ptarget->COG; - pret->HDG = ptarget->HDG; - pret->Lon = ptarget->Lon; - pret->Lat = ptarget->Lat; - pret->ROTAIS = ptarget->ROTAIS; - pret->ShipType = ptarget->ShipType; - pret->IMO = ptarget->IMO; +PlugInChartBase::~PlugInChartBase() {} - pret->Range_NM = ptarget->Range_NM; - pret->Brg = ptarget->Brg; +wxString PlugInChartBase::GetFileSearchMask(void) { return _T(""); } - // Per target collision parameters - pret->bCPA_Valid = ptarget->bCPA_Valid; - pret->TCPA = ptarget->TCPA; // Minutes - pret->CPA = ptarget->CPA; // Nautical Miles +int PlugInChartBase::Init(const wxString& name, int init_flags) { return 0; } - pret->alarm_state = (plugin_ais_alarm_type)ptarget->n_alert_state; +// Accessors - memcpy(pret->CallSign, ptarget->CallSign, sizeof(ptarget->CallSign) - 1); - memcpy(pret->ShipName, ptarget->ShipName, sizeof(ptarget->ShipName) - 1); +double PlugInChartBase::GetNormalScaleMin(double canvas_scale_factor, + bool b_allow_overzoom) { + return 1.0; +} - return pret; +double PlugInChartBase::GetNormalScaleMax(double canvas_scale_factor, + int canvas_width) { + return 2.0e7; } -//------------------------------------------------------------------------------- -// PluginListPanel & PluginPanel Implementation -//------------------------------------------------------------------------------- +bool PlugInChartBase::GetChartExtent(ExtentPI* pext) { return false; } -#define DISABLED_SETTINGS_MSG \ - _("These settings might destabilize OpenCPN and are by default disabled." \ - " To despite the dangers enable them manually add a CatalogExpert=1" \ - " line in the [PlugIns] section in the configuration file.") +wxBitmap& PlugInChartBase::RenderRegionView(const PlugIn_ViewPort& VPoint, + const wxRegion& Region) { + return wxNullBitmap; +} -/* - * Panel with buttons to control plugin catalog management. - */ -CatalogMgrPanel::CatalogMgrPanel(wxWindow* parent) - : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize), - m_parent(parent) { - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - SetSizer(topSizer); +bool PlugInChartBase::AdjustVP(PlugIn_ViewPort& vp_last, + PlugIn_ViewPort& vp_proposed) { + return false; +} - topSizer->Add(new wxStaticLine(this), 0, wxGROW | wxLEFT | wxRIGHT, 4); +void PlugInChartBase::GetValidCanvasRegion(const PlugIn_ViewPort& VPoint, + wxRegion* pValidRegion) {} - wxStaticBox* itemStaticBoxSizer4Static = - new wxStaticBox(this, wxID_ANY, _("Plugin Catalog")); - wxStaticBoxSizer* itemStaticBoxSizer4 = - new wxStaticBoxSizer(itemStaticBoxSizer4Static, wxVERTICAL); - topSizer->Add(itemStaticBoxSizer4, 1, wxEXPAND | wxALL, 2); +void PlugInChartBase::SetColorScheme(int cs, bool bApplyImmediate) {} -#ifndef __ANDROID__ - // First line - m_catalogText = new wxStaticText(this, wxID_STATIC, _T("")); - itemStaticBoxSizer4->Add(m_catalogText, - wxSizerFlags().Border().Proportion(1)); - m_catalogText->SetLabel(GetCatalogText(false)); +double PlugInChartBase::GetNearestPreferredScalePPM(double target_scale_ppm) { + return 1.0; +} - // Next line - wxBoxSizer* rowSizer2 = new wxBoxSizer(wxHORIZONTAL); - itemStaticBoxSizer4->Add(rowSizer2, - wxSizerFlags().Expand().Border().Proportion(1)); +wxBitmap* PlugInChartBase::GetThumbnail(int tnx, int tny, int cs) { + return NULL; +} - m_updateButton = new wxButton(this, wxID_ANY, _("Update Plugin Catalog"), - wxDefaultPosition, wxDefaultSize, 0); - rowSizer2->Add(m_updateButton, 0, wxALIGN_LEFT); - m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &CatalogMgrPanel::OnUpdateButton, this); - rowSizer2->AddSpacer(4 * GetCharWidth()); - m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."), - wxDefaultPosition, wxDefaultSize, 0); - rowSizer2->Add(m_tarballButton, 0, wxALIGN_LEFT | wxLEFT, 2 * GetCharWidth()); - m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &CatalogMgrPanel::OnTarballButton, this); +void PlugInChartBase::ComputeSourceRectangle(const PlugIn_ViewPort& vp, + wxRect* pSourceRect) {} - rowSizer2->AddSpacer(4 * GetCharWidth()); - m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."), - wxDefaultPosition, wxDefaultSize, 0); - ConfigVar expert("/PlugIns", "CatalogExpert", pConfig); - if (expert.Get(false)) { - m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &CatalogMgrPanel::OnPluginSettingsButton, this); - } else { - m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) { - wxMessageBox(DISABLED_SETTINGS_MSG, _("Disabled")); - }); - } - rowSizer2->AddSpacer(4 * GetCharWidth()); - rowSizer2->Add(m_adv_button, 0, wxALIGN_LEFT); +double PlugInChartBase::GetRasterScaleFactor() { return 1.0; } - SetUpdateButtonLabel(); +bool PlugInChartBase::GetChartBits(wxRect& source, unsigned char* pPix, + int sub_samp) { + return false; +} - // Next line - wxBoxSizer* rowSizer3 = new wxBoxSizer(wxHORIZONTAL); - itemStaticBoxSizer4->Add(rowSizer3, 0, wxEXPAND | wxALL, 4); +int PlugInChartBase::GetSize_X() { return 1; } - SetMinSize(wxSize(m_parent->GetClientSize().x - (4 * GetCharWidth()), -1)); - Fit(); +int PlugInChartBase::GetSize_Y() { return 1; } - GlobalVar catalog(&g_catalog_channel); - wxDEFINE_EVENT(EVT_CATALOG_CHANGE, wxCommandEvent); - catalog_listener.Listen(catalog, this, EVT_CATALOG_CHANGE); - Bind(EVT_CATALOG_CHANGE, [&](wxCommandEvent&) { SetUpdateButtonLabel(); }); +void PlugInChartBase::latlong_to_chartpix(double lat, double lon, double& pixx, + double& pixy) {} -#else // __ANDROID__ - SetBackgroundColour(wxColour(0x7c, 0xb0, 0xe9)); // light blue - ConfigVar expert("/PlugIns", "CatalogExpert", pConfig); - if (!expert.Get(false)) { - m_updateButton = - new wxButton(this, wxID_ANY, _("Update Plugin Catalog: master"), - wxDefaultPosition, wxDefaultSize, 0); - itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT); - m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &CatalogMgrPanel::OnUpdateButton, this); - SetUpdateButtonLabel(); - m_tarballButton = NULL; - m_adv_button = NULL; - } else { - // First line - m_catalogText = new wxStaticText(this, wxID_STATIC, GetCatalogText(false)); - itemStaticBoxSizer4->Add(m_catalogText, - wxSizerFlags().Border(wxALL, 5).Proportion(1)); - // m_catalogText->SetLabel(GetCatalogText(false)); +void PlugInChartBase::chartpix_to_latlong(double pixx, double pixy, + double* plat, double* plon) {} - m_updateButton = new wxButton( - this, wxID_ANY, "Update Plugin Catalog:master ", - wxDefaultPosition, wxDefaultSize, 0); - itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT | wxTOP, 5); - m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &CatalogMgrPanel::OnUpdateButton, this); - SetUpdateButtonLabel(); +// ---------------------------------------------------------------------------- +// PlugInChartBaseGL Implementation +// +// ---------------------------------------------------------------------------- - // Next line - m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."), - wxDefaultPosition, wxDefaultSize, 0); - itemStaticBoxSizer4->Add(m_adv_button, 0, wxALIGN_LEFT | wxTOP, - GetCharWidth()); - m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &CatalogMgrPanel::OnPluginSettingsButton, this); +PlugInChartBaseGL::PlugInChartBaseGL() {} - // Next line - m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."), - wxDefaultPosition, wxDefaultSize, 0); - itemStaticBoxSizer4->Add(m_tarballButton, 0, wxALIGN_LEFT | wxALL, - 2 * GetCharWidth()); - m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &CatalogMgrPanel::OnTarballButton, this); - } +PlugInChartBaseGL::~PlugInChartBaseGL() {} -#endif +int PlugInChartBaseGL::RenderRegionViewOnGL(const wxGLContext& glc, + const PlugIn_ViewPort& VPoint, + const wxRegion& Region, + bool b_use_stencil) { + return 0; } -CatalogMgrPanel::~CatalogMgrPanel() { - m_updateButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, - &CatalogMgrPanel::OnUpdateButton, this); - if (m_tarballButton) - m_tarballButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, - &CatalogMgrPanel::OnTarballButton, this); +ListOfPI_S57Obj* PlugInChartBaseGL::GetObjRuleListAtLatLon( + float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) { + return NULL; } -static const char* const DOWNLOAD_REPO_PROTO = - "https://raw.githubusercontent.com/OpenCPN/plugins/@branch@/" - "ocpn-plugins.xml"; +wxString PlugInChartBaseGL::CreateObjDescriptions(ListOfPI_S57Obj* obj_list) { + return _T(""); +} -void CatalogMgrPanel::OnUpdateButton(wxCommandEvent& event) { - // Craft the url - std::string catalog(g_catalog_channel == "" ? "master" : g_catalog_channel); - std::string url(g_catalog_custom_url); - if (catalog != "custom") { - url = std::string(DOWNLOAD_REPO_PROTO); - ocpn::replace(url, "@branch@", catalog); - } - // Download to a temp file - std::string filePath = - wxFileName::CreateTempFileName("ocpn_dl").ToStdString(); +int PlugInChartBaseGL::GetNoCOVREntries() { return 0; } - auto catalogHdlr = CatalogHandler::getInstance(); +int PlugInChartBaseGL::GetNoCOVRTablePoints(int iTable) { return 0; } - g_Platform->ShowBusySpinner(); - auto status = catalogHdlr->DownloadCatalog(filePath, url); - g_Platform->HideBusySpinner(); +int PlugInChartBaseGL::GetNoCOVRTablenPoints(int iTable) { return 0; } - std::string message; - if (status != CatalogHandler::ServerStatus::OK) { - message = _("Cannot download data from url"); - OCPNMessageBox(this, message, _("OpenCPN Catalog update"), - wxICON_ERROR | wxOK); - return; - } +float* PlugInChartBaseGL::GetNoCOVRTableHead(int iTable) { return 0; } - // TODO Validate xml using xsd here.... -#ifdef __ANDROID__ - if (!AndroidSecureCopyFile(wxString(filePath.c_str()), - g_Platform->GetPrivateDataDir() + - wxFileName::GetPathSeparator() + - _T("ocpn-plugins.xml"))) { - OCPNMessageBox(this, _("Unable to copy catalog file"), - _("OpenCPN Catalog update"), wxICON_ERROR | wxOK); - return; - } -#else - // Copy the downloaded file to proper local location - if (!wxCopyFile(wxString(filePath.c_str()), - g_Platform->GetPrivateDataDir() + - wxFileName::GetPathSeparator() + - _T("ocpn-plugins.xml"))) { - OCPNMessageBox(this, _("Unable to copy catalog file"), - _("OpenCPN Catalog update"), wxICON_ERROR | wxOK); - return; - } -#endif +// ---------------------------------------------------------------------------- +// PlugInChartBaseExtended Implementation +// +// ---------------------------------------------------------------------------- - // If this is the "master" catalog, also copy to plugin cache - if (catalog == "master") { - if (!ocpn::store_metadata(filePath.c_str())) { - OCPNMessageBox(this, _("Unable to copy catalog file to cache"), - _("OpenCPN Catalog update"), wxICON_ERROR | wxOK); - return; - } - } +PlugInChartBaseExtended::PlugInChartBaseExtended() {} - // Record in the config file the name of the catalog downloaded - pConfig->SetPath(_T("/PlugIns/")); - pConfig->Write("LatestCatalogDownloaded", catalog.c_str()); - pConfig->Flush(); - - // Reset the PluginHandler catalog file source. - // This will case the Handler to find, load, and parse the just-downloaded - // catalog as copied to g_Platform->GetPrivateDataDir()... - auto pluginHandler = PluginHandler::getInstance(); - pluginHandler->setMetadata(""); - - // Also clear the cached values in the CatalogHandler, forcing - // a reload and parse of the catalog. - auto cataloghdlr = CatalogHandler::getInstance(); - cataloghdlr->ClearCatalogData(); - - // Reload all plugins, which will also update the status fields - LoadAllPlugIns(false); - - // Update this Panel, and the entire list. -#ifndef __ANDROID__ - m_catalogText->SetLabel(GetCatalogText(true)); -#endif - if (m_PluginListPanel) m_PluginListPanel->ReloadPluginPanels(); - OCPNMessageBox(this, _("Catalog update successful"), - _("OpenCPN Catalog update"), wxICON_INFORMATION | wxOK); -} - -void CatalogMgrPanel::OnPluginSettingsButton(wxCommandEvent& event) { - auto dialog = new CatalogSettingsDialog(this); - -#ifdef __ANDROID__ - androidDisableRotation(); -#endif - - dialog->ShowModal(); - -#ifdef __ANDROID__ - androidEnableRotation(); -#endif -} - -void CatalogMgrPanel::OnTarballButton(wxCommandEvent& event) { - // Present a file selector dialog to get the file name.. - wxString path; - int response = g_Platform->DoFileSelectorDialog( - this, &path, _("Select tarball file"), GetImportInitDir(), "", - "tar files (*.tar.gz)|*.tar.gz|All Files (*.*)|*.*"); - - if (response != wxID_OK) { - return; - } - auto handler = PluginHandler::getInstance(); - PluginMetadata metadata; - bool ok = handler->ExtractMetadata(path.ToStdString(), metadata); - if (!ok) { - OCPNMessageBox( - this, - _("Error extracting metadata from tarball (missing metadata.xml?)"), - _("OpenCPN Plugin Import Error")); - return; - } - if (!PluginHandler::isCompatible(metadata)) { - OCPNMessageBox(this, _("Incompatible import plugin detected."), - _("OpenCPN Plugin Import Error")); - handler->uninstall(metadata.name); - return; - } - UninstallPlugin(metadata.name); - ok = handler->installPlugin(metadata, path.ToStdString()); - if (!ok) { - OCPNMessageBox(this, _("Error extracting import plugin tarball."), - _("OpenCPN Plugin Import Error")); - return; - } - metadata.is_imported = true; - auto metadata_path = PluginHandler::ImportedMetadataPath(metadata.name); - std::ofstream file(metadata_path); - file << metadata.to_string(); - if (!file.good()) { - WARNING_LOG << "Error saving metadata file: " << metadata_path - << " for imported plugin: " << metadata.name; - } - LoadAllPlugIns(false, true); - PluginHandler::getInstance()->SetInstalledMetadata(metadata); - m_PluginListPanel->ReloadPluginPanels(); - wxString ws(_("Plugin")); - ws += metadata.name + _(" successfully imported"); - OCPNMessageBox(gFrame, ws, _("Installation complete"), - wxICON_INFORMATION | wxOK | wxCENTRE); -} - -wxString CatalogMgrPanel::GetCatalogText(bool updated) { - wxString catalog; - catalog = updated ? _("Active Catalog") : _("Last Catalog"); - catalog += _T(": "); - - // Check the config file to learn what was the last catalog downloaded. - pConfig->SetPath(_T("/PlugIns/")); - wxString latestCatalog = - pConfig->Read(_T("LatestCatalogDownloaded"), _T("default")); - catalog += latestCatalog; - -#ifndef __ANDROID__ - // Get the version from the currently active catalog, by which we mean - // the latest catalog parsed. - auto pluginHandler = PluginHandler::getInstance(); - std::string date = pluginHandler->GetCatalogData()->date; - - catalog += wxString(" ") + _("Last change: ") + " " + date; - if (!updated) catalog += _T(" : ") + _("Please Update Plugin Catalog."); -#endif - - return catalog; -} - -void CatalogMgrPanel::SetUpdateButtonLabel() { - wxString label = _("Update Plugin Catalog"); - label += _T(": "); - label += g_catalog_channel == "" ? "master" : g_catalog_channel; - m_updateButton->SetLabel(label); - Layout(); -} - -wxString CatalogMgrPanel::GetImportInitDir() { - // Check the config file for the last Import path. - pConfig->SetPath(_T("/PlugIns/")); - wxString lastImportDir; - lastImportDir = pConfig->Read(_T("LatestImportDir"), - g_Platform->GetWritableDocumentsDir()); - if (wxDirExists(lastImportDir)) { - return lastImportDir; - } - return (g_Platform->GetWritableDocumentsDir()); -} - -BEGIN_EVENT_TABLE(PluginListPanel, wxScrolledWindow) -// EVT_BUTTON( ID_CMD_BUTTON_PERFORM_ACTION, -// PluginListPanel::OnPluginPanelAction ) -END_EVENT_TABLE() - -PluginListPanel::PluginListPanel(wxWindow* parent, wxWindowID id, - const wxPoint& pos, const wxSize& size) - : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL), - m_PluginSelected(0) { - m_is_loading.clear(); - SetSizer(new wxBoxSizer(wxVERTICAL)); - ReloadPluginPanels(); -} - -void PluginListPanel::SelectByName(wxString& name) { - for (auto it = GetChildren().GetFirst(); it; it = it->GetNext()) { - auto pluginPanel = dynamic_cast(it->GetData()); - if (pluginPanel) { - if (pluginPanel->GetPluginPtr()->m_common_name.IsSameAs(name)) { - pluginPanel->SetSelected(true); - pluginPanel->Layout(); - SelectPlugin(pluginPanel); - break; - } - } - } -} - -/** Return sorted list of all installed plugins. */ -std::vector GetInstalled() { - std::vector result; - auto loader = PluginLoader::getInstance(); - for (size_t i = 0; i < loader->GetPlugInArray()->GetCount(); i++) { - auto const item = loader->GetPlugInArray()->Item(i); - if (item->m_managed_metadata.name.empty()) { - const auto name = item->m_common_name.ToStdString(); - item->m_managed_metadata = PluginLoader::MetadataByName(name); - } - PluginLoader::UpdatePlugin(item, item->m_managed_metadata); - result.push_back(item); - } - auto compare = [](const PlugInData* lhs, const PlugInData* rhs) { - std::string slhs, srhs; - for (auto& cl : lhs->Key()) slhs += toupper(cl); - for (auto& cr : rhs->Key()) srhs += toupper(cr); - return slhs.compare(srhs) < 0; - }; - std::sort(result.begin(), result.end(), compare); - return result; -} - -/* Is plugin with given name present in loaded or safe list if installed? */ -static bool IsPluginLoaded(const std::string& name) { - if (safe_mode::get_mode()) { - auto installed = PluginHandler::getInstance()->GetInstalldataPlugins(); - auto found = - std::find(installed.begin(), installed.end(), ocpn::tolower(name)); - return found != installed.end(); - } else { - auto loaded = PluginLoader::getInstance()->GetPlugInArray(); - for (size_t i = 0; i < loaded->GetCount(); i++) { - if (loaded->Item(i)->m_common_name.ToStdString() == name) return true; - } - return false; - } -} - -void PluginListPanel::ReloadPluginPanels() { - if (m_is_loading.test_and_set()) { - // recursive call... - DEBUG_LOG << "LoadAllPlugins: recursive invocation"; - return; - } - - auto plugins = PluginLoader::getInstance()->GetPlugInArray(); - m_PluginItems.Clear(); - - wxWindowList kids = GetChildren(); - for (unsigned int i = 0; i < kids.GetCount(); i++) { - wxWindowListNode* node = kids.Item(i); - wxWindow* win = node->GetData(); - PluginPanel* pp = dynamic_cast(win); - if (pp) win->Destroy(); - } - GetSizer()->Clear(); - - Hide(); - m_PluginSelected = 0; - - if (safe_mode::get_mode()) { - /** Add panels for installed, unloaded plugins. */ - auto installed = PluginHandler::getInstance()->GetInstalldataPlugins(); - for (const auto& name : installed) AddPlugin(name); - } else { - /* The catalog entries. */ - auto available = getCompatiblePlugins(); - - /* Remove those which are loaded or in safe list. */ - auto predicate = [](const PluginMetadata& md) { - return IsPluginLoaded(md.name); - }; - auto end = std::remove_if(available.begin(), available.end(), predicate); - available.erase(end, available.end()); - - // Sort on case-insensitive name - struct CompSort { - bool operator()(const PluginMetadata& lhs, - const PluginMetadata rhs) const { - std::string slhs, srhs; - for (auto& cl : lhs.name) slhs += toupper(cl); - for (auto& cr : rhs.name) srhs += toupper(cr); - return slhs.compare(srhs) < 0; - } - } comp_sort; - - std::set unique_sorted_entries(comp_sort); - for (const auto& p : available) unique_sorted_entries.insert(p); - - // Build the list of panels. - - // Add Installed and active plugins - for (const auto& p : GetInstalled()) - if (p->m_enabled) AddPlugin(*p); - - // Add Installed and inactive plugins - for (const auto& p : GetInstalled()) - if (!p->m_enabled) AddPlugin(*p); - - // Add available plugins, sorted - for (const auto& p : unique_sorted_entries) AddPlugin(PlugInData(p)); - } - - Show(); - Layout(); - Refresh(true); - Scroll(0, 0); - - m_is_loading.clear(); -} - -void PluginListPanel::AddPlugin(const std::string& name) { - auto panel = new PluginPanel(this, name); - DimeControl(panel); - panel->SetSelected(false); - GetSizer()->Add(panel, 0, wxEXPAND); - m_PluginItems.Add(panel); - m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0; - GetSizer()->AddSpacer(m_pluginSpacer); -} - -void PluginListPanel::AddPlugin(const PlugInData& pic) { - auto pPluginPanel = - new PluginPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, pic); - pPluginPanel->SetSelected(false); - GetSizer()->Add(pPluginPanel, 0, wxEXPAND); - m_PluginItems.Add(pPluginPanel); - - m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0; - GetSizer()->AddSpacer(m_pluginSpacer); - - // wxStaticLine* itemStaticLine = new wxStaticLine( m_panel, wxID_ANY, - // wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - // m_pitemBoxSizer01->Add( itemStaticLine, wxSizerFlags().Expand()); -} - -// When a child Panel is selected, its size grows to include "Preferences" -// and Enable" buttons. As a consequence, the vertical size of the -// ListPanel grows as well.Calculate and add a spacer to bottom of -// ListPanel so that initial ListPanel minimum size calculations account -// for selected Panel size growth. Sadly, this does not work right on wxQt. -// So, just punt for now... -int PluginListPanel::ComputePluginSpace(ArrayOfPluginPanel plugins, - wxBoxSizer* sizer) { - int max_dy = 0; - for (size_t i = 0; i < plugins.GetCount(); i++) { - auto panel = plugins.Item(i); - bool was_selected = panel->GetSelected(); - panel->SetSelected(false); - sizer->Layout(); - wxSize unselected = panel->GetSize(); - - panel->SetSelected(true); // switch to selected, a bit bigger - sizer->Layout(); - wxSize selected = panel->GetSize(); - - int dy = selected.GetHeight() - unselected.GetHeight(); - max_dy = wxMax(max_dy, dy); - panel->SetSelected(was_selected); - } - return max_dy; -} - -PluginListPanel::~PluginListPanel() {} - -void PluginListPanel::UpdateSelections() { - for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) { - PluginPanel* pPluginPanel = m_PluginItems[i]; - if (pPluginPanel) { - pPluginPanel->SetSelected(pPluginPanel->GetSelected()); - } - } -} - -void PluginListPanel::SelectPlugin(PluginPanel* pi) { - int xs, ys; - GetViewStart(&xs, &ys); - Scroll(0, 0); - - if (m_PluginSelected) { - m_PluginSelected->SetSelected(false); - m_PluginSelected->Layout(); - } - - if (pi == NULL) m_PluginSelected->SetSelected(false); - - m_PluginSelected = pi; - - GetSizer()->Layout(); - Refresh(false); - wxSize size = GetBestVirtualSize(); - SetVirtualSize(size); - - // Measure, and ensure that the selected item is fully visible in the - // vertical scroll box. - int htop = 0; - for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) { - PluginPanel* pPluginPanel = m_PluginItems[i]; - int yd = pPluginPanel->GetSize().y; - htop += yd; - htop += m_pluginSpacer; - if (pPluginPanel == pi) { - int piBottom = htop - (ys * g_options->GetScrollRate()); - if (piBottom > GetClientSize().y) { - ys += (piBottom - GetClientSize().y) / g_options->GetScrollRate(); - } - break; - } - } - - Scroll(xs, ys); -} - -void PluginListPanel::MoveUp(PluginPanel* pi) { - int pos = m_PluginItems.Index(pi); - if (pos == 0) // The first one can't be moved further up - return; - m_PluginItems.RemoveAt(pos); - // m_pitemBoxSizer01->Remove( pos * 2 + 1 ); - // m_pitemBoxSizer01->Remove( pos * 2 ); - m_PluginItems.Insert(pi, pos - 1); - wxStaticLine* itemStaticLine = new wxStaticLine( - this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL); - // m_pitemBoxSizer01->Insert( (pos - 1) * 2, itemStaticLine, 0, - // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos - 1) * 2, pi, 0, - // wxEXPAND|wxALL, 0 ); - - m_PluginSelected = pi; - - GetSizer()->Layout(); - m_parent->Layout(); - Refresh(true); -} - -void PluginListPanel::MoveDown(PluginPanel* pi) { - int pos = m_PluginItems.Index(pi); - if (pos == (int)m_PluginItems.Count() - - 1) // The last one can't be moved further down - return; - m_PluginItems.RemoveAt(pos); - // m_pitemBoxSizer01->Remove( pos * 2 + 1 ); - // m_pitemBoxSizer01->Remove( pos * 2 ); - m_PluginItems.Insert(pi, pos + 1); - wxStaticLine* itemStaticLine = new wxStaticLine( - this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL); - // m_pitemBoxSizer01->Insert( (pos + 1) * 2 - 1, itemStaticLine, 0, - // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos + 1) * 2, pi, 0, - // wxEXPAND|wxALL, 0 ); - - m_PluginSelected = pi; - - GetSizer()->Layout(); - m_parent->Layout(); - Refresh(false); -} - -static bool canUninstall(std::string name) { - PluginHandler* pluginHandler = PluginHandler::getInstance(); - // std::transform(name.begin(), name.end(), name.begin(), ::tolower); - - for (auto plugin : pluginHandler->getInstalled()) { - if (plugin.name == name) { - if (safe_mode::get_mode()) - return true; - else - return !plugin.readonly; - } - } - return false; -} - -PluginPanel::PluginPanel(wxPanel* parent, const std::string& name) - : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, - wxBORDER_NONE), - m_is_safe_panel(true) { - m_PluginListPanel = dynamic_cast(parent); - wxASSERT(m_PluginListPanel != 0); - wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL); - SetSizer(top_sizer); - wxBoxSizer* top_horizontal = new wxBoxSizer(wxHORIZONTAL); - top_sizer->Add(top_horizontal, 0, wxEXPAND); - - double iconSize = GetCharWidth() * 4; - double dpi_mult = g_Platform->GetDisplayDIPMult(this); - int icon_scale = iconSize * dpi_mult; - ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle(); - wxBitmap bitmap(style->GetIcon("default_pi", icon_scale, icon_scale)); - m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap); - top_horizontal->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10); - - m_pName = new wxStaticText(this, wxID_ANY, name); - top_horizontal->Add(m_pName, wxID_ANY, wxALIGN_CENTER_VERTICAL); - m_pVersion = new wxStaticText(this, wxID_ANY, ""); - top_horizontal->Add(m_pVersion); - m_pVersion->Hide(); - - m_pButtons = new wxBoxSizer(wxHORIZONTAL); - top_horizontal->Add(m_pButtons); - m_info_btn = new WebsiteButton(this, "https:\\opencpn.org"); - top_horizontal->Add(m_info_btn); - m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"), - wxDefaultPosition, wxDefaultSize, 0); - top_horizontal->Add(m_pButtonUninstall, 0, wxALIGN_CENTER_VERTICAL | wxALL, - 2); - auto uninstall = [&](wxCommandEvent ev) { - auto n = m_pName->GetLabel().ToStdString(); - int result = - OCPNMessageBox(gFrame, std::string(_("Uninstall plugin ")) + n + "?", - _("Un-Installation"), wxICON_QUESTION | wxOK | wxCANCEL); - if (result != wxID_OK) return; - PluginHandler::getInstance()->ClearInstallData(n); - m_PluginListPanel->ReloadPluginPanels(); - }; - m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED, uninstall); -} - -BEGIN_EVENT_TABLE(PluginPanel, wxPanel) -EVT_PAINT(PluginPanel::OnPaint) -END_EVENT_TABLE() - -PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos, - const wxSize& size, const PlugInData plugin) - : wxPanel(parent, id, pos, size, wxBORDER_NONE), - m_plugin(plugin), - m_is_safe_panel(false) { - m_PluginListPanel = (PluginListPanel*)parent; //->GetParent(); - m_PluginListPanel = dynamic_cast(parent /*->GetParent()*/); - wxASSERT(m_PluginListPanel != 0); - - m_bSelected = false; - m_penWidthUnselected = g_Platform->GetDisplayDPmm() * .25; - m_penWidthSelected = g_Platform->GetDisplayDPmm() * .5; - - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - SetSizer(topSizer); - - wxBoxSizer* itemBoxSizer01 = new wxBoxSizer(wxHORIZONTAL); - topSizer->Add(itemBoxSizer01, 0, wxEXPAND); - Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - - double iconSize = GetCharWidth() * 4; - double dpi_mult = g_Platform->GetDisplayDIPMult(this); - int icon_scale = iconSize * dpi_mult; - - wxImage plugin_icon; - ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle(); - if (m_plugin.m_bitmap.IsOk()) { - plugin_icon = m_plugin.m_bitmap.ConvertToImage(); - } - wxBitmap bitmap; - if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) { - wxFileName path(g_Platform->GetSharedDataDir(), "packageBox.svg"); - path.AppendDir("uidata"); - path.AppendDir("traditional"); // FIXME(leamas) cache it. - bitmap = LoadSVG(path.GetFullPath(), icon_scale, icon_scale); - } else if (plugin_icon.IsOk()) { - int nowSize = plugin_icon.GetWidth(); - plugin_icon.Rescale(icon_scale, icon_scale, wxIMAGE_QUALITY_HIGH); - bitmap = wxBitmap(plugin_icon); - } else { - bitmap = wxBitmap(style->GetIcon("default_pi", icon_scale, icon_scale)); - } - m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap); - - itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10); - m_itemStaticBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, - this); - m_itemStaticBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, - this); - - wxBoxSizer* itemBoxSizer02 = new wxBoxSizer(wxVERTICAL); - itemBoxSizer01->Add(itemBoxSizer02, 1, wxEXPAND | wxALL, 0); - - // Calculate character width available - int nChars = g_options->GetSize().x / GetCharWidth(); - bool bCompact = false; - if (nChars < 60) // Arbitrary, detecting mobile devices in portrait mode. - bCompact = true; - - if (bCompact) { - // Might need to shorten the Plugin name string - wxString nameString = m_plugin.m_common_name; - int maxWidth = g_Platform->getDisplaySize().x * 3 / 10; - wxScreenDC dc; - int nameWidth; - dc.GetTextExtent(m_plugin.m_common_name, &nameWidth, NULL); - if (nameWidth > maxWidth) { - nameString = wxControl::Ellipsize(m_plugin.m_common_name, dc, - wxELLIPSIZE_END, maxWidth); - } - m_pName = new wxStaticText(this, wxID_ANY, nameString); - m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - itemBoxSizer02->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 5); - - wxFlexGridSizer* sl1 = new wxFlexGridSizer(2, 0, 0); - sl1->AddGrowableCol(1); - itemBoxSizer02->Add(sl1, 0, wxEXPAND); - - m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA")); - sl1->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 5); - if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) { - m_pVersion->Hide(); - } - m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - - m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled")); - sl1->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 5); - m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this); - - // Might need to shorten the Plugin description string - wxString descriptionString = m_plugin.m_short_description; - int maxDescriptionWidth = g_Platform->getDisplaySize().x - (iconSize * 4); - int descriptionWidth; - dc.GetTextExtent(m_plugin.m_short_description, &descriptionWidth, NULL); - if (descriptionWidth > maxDescriptionWidth) - descriptionString = - wxControl::Ellipsize(m_plugin.m_short_description, dc, - wxELLIPSIZE_END, maxDescriptionWidth); - - // This invocation has the effect of setting the minimum width of the - // descriptor field. - m_pDescription = - new wxStaticText(this, wxID_ANY, descriptionString, wxDefaultPosition, - wxSize(maxDescriptionWidth, -1), wxST_NO_AUTORESIZE); - itemBoxSizer02->Add(m_pDescription, 0, wxEXPAND | wxALL, 5); - m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - - } else { - wxFlexGridSizer* itemBoxSizer03 = new wxFlexGridSizer(4, 0, 0); - itemBoxSizer03->AddGrowableCol(2); - itemBoxSizer02->Add(itemBoxSizer03, 0, wxEXPAND); - - wxString nameString = m_plugin.m_common_name; - m_pName = new wxStaticText(this, wxID_ANY, nameString); - m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - - // Avoid known bug in wxGTK3 -#ifndef __WXGTK3__ - wxFont font = GetFont(); - font.SetWeight(wxFONTWEIGHT_BOLD); - m_pName->SetFont(font); -#endif - - itemBoxSizer03->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 10); - - m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA")); - itemBoxSizer03->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 10); - if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable || - m_plugin.m_status == PluginStatus::System || - (m_plugin.m_status == PluginStatus::Unmanaged && - !m_plugin.m_managed_metadata.is_orphan)) { - m_pVersion->Hide(); - } - m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - - m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled")); - itemBoxSizer03->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 10); - m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this); - - itemBoxSizer03->Add(5 * GetCharWidth(), 1, 0, wxALIGN_RIGHT | wxTOP, 10); - - m_pDescription = new wxStaticText( - this, wxID_ANY, m_plugin.m_short_description, wxDefaultPosition, - wxSize(-1, -1) /*, wxST_NO_AUTORESIZE*/); - itemBoxSizer02->Add(m_pDescription, 1, wxEXPAND | wxALL, 5); - m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this); - } - - if (!bCompact) { - m_info_btn = new WebsiteButton(this, "https:\\opencpn.org"); - m_info_btn->Hide(); - itemBoxSizer02->Add(m_info_btn, 0); - - m_pButtons = new wxBoxSizer(wxHORIZONTAL); - itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0); - m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"), - wxDefaultPosition, wxDefaultSize, 0); - m_pButtons->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 2); - - m_pButtons->AddSpacer(3 * GetCharWidth()); - - m_pButtonAction = - new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX", - wxDefaultPosition, wxDefaultSize, 0); - m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2); - - m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"), - wxDefaultPosition, wxDefaultSize, 0); - m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2); - } else { - m_pButtons = new wxBoxSizer(wxVERTICAL); - itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0); - - wxBoxSizer* tline = new wxBoxSizer(wxHORIZONTAL); - m_pButtons->Add(tline, 0, wxALL, 2); - - m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"), - wxDefaultPosition, wxDefaultSize, 0); - tline->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 0); - - tline->AddSpacer(3 * GetCharWidth()); - - m_info_btn = new WebsiteButton(this, "https:\\opencpn.org"); - m_info_btn->Hide(); - tline->Add(m_info_btn, 0); - - m_pButtonAction = - new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX", - wxDefaultPosition, wxDefaultSize); - m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2); - - m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"), - wxDefaultPosition, wxDefaultSize, 0); - m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2); - } - - wxBitmap statusBitmap; - const auto stat = m_plugin.m_status; - auto icon_name = icon_by_status.at(stat); - - wxFileName path(g_Platform->GetSharedDataDir(), icon_name); - path.AppendDir("uidata"); - path.AppendDir("traditional"); - bool ok = false; - int bmsize = GetCharWidth() * 3 * dpi_mult; - if (path.IsFileReadable()) { - statusBitmap = LoadSVG(path.GetFullPath(), bmsize, bmsize); - ok = statusBitmap.IsOk(); - } - if (!ok) { - auto style = g_StyleManager->GetCurrentStyle(); - statusBitmap = wxBitmap(style->GetIcon(_T("default_pi"), bmsize, bmsize)); - wxLogMessage("Icon: %s not found.", path.GetFullPath()); - } - - m_itemStatusIconBitmap = new wxStaticBitmap(this, wxID_ANY, statusBitmap); - m_itemStatusIconBitmap->SetToolTip(message_by_status(stat)); - m_itemStatusIconBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, - this); - m_itemStatusIconBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, - this); - - itemBoxSizer01->Add(m_itemStatusIconBitmap, 0, wxEXPAND | wxALL, 20); - - itemBoxSizer02->AddSpacer(GetCharWidth()); - - m_pButtonPreferences->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &PluginPanel::OnPluginPreferences, this); - m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &PluginPanel::OnPluginUninstall, this); - m_pButtonAction->Bind(wxEVT_COMMAND_BUTTON_CLICKED, - &PluginPanel::OnPluginAction, this); - - SetSelected(m_bSelected); - SetAutoLayout(true); - Fit(); -} - -PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos, - const wxSize& size, PluginMetadata md) - : PluginPanel(parent, id, pos, size, PlugInData(md)) {}; - -PluginPanel::~PluginPanel() { - Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - if (m_is_safe_panel) return; - m_itemStaticBitmap->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, - this); - m_pName->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - m_pVersion->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - m_pDescription->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this); - if (m_pButtonAction) { - m_pButtonAction->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, - &PluginPanel::OnPluginAction, this); - } - m_pButtonPreferences->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, - &PluginPanel::OnPluginPreferences, this); - m_cbEnable->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, - &PluginPanel::OnPluginEnableToggle, this); -} - -void PluginPanel::SetActionLabel(wxString& label) { - m_pButtonAction->SetLabel(label); - Refresh(); -} - -static wxStopWatch swclick; -static int downx, downy; - -void PluginPanel::OnPluginSelected(wxMouseEvent& event) { -#ifdef __ANDROID__ - swclick.Start(); - event.GetPosition(&downx, &downy); -#else - DoPluginSelect(); -#endif -} - -void PluginPanel::OnPluginSelectedUp(wxMouseEvent& event) { -#ifdef __ANDROID__ - qDebug() << swclick.Time(); - if (swclick.Time() < 200) { - int upx, upy; - event.GetPosition(&upx, &upy); - if ((fabs(upx - downx) < GetCharWidth()) && - (fabs(upy - downy) < GetCharWidth())) { - DoPluginSelect(); - } - } - swclick.Start(); -#endif -} - -void PluginPanel::DoPluginSelect() { - if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) { - // auto dialog = dynamic_cast(GetParent()); - // auto dialog = dynamic_cast(m_parent); - // wxASSERT(dialog != 0); - - // Install the new plugin, auto-enabling as a convenience measure. - run_update_dialog(m_PluginListPanel, &m_plugin, false, 0, true); - } else if (m_bSelected) { - SetSelected(false); - m_PluginListPanel->SelectPlugin(NULL); - } else { - SetSelected(true); - m_PluginListPanel->SelectPlugin(this); - } -} - -/** - * Return metadata for installed plugin with given name or default, - * empty PluginMetadata() if not found. - */ -static PluginMetadata GetMetadataByName(const std::string& name) { - auto plugins = PluginHandler::getInstance()->getInstalled(); - auto predicate = [name](const PluginMetadata& pm) { return pm.name == name; }; - auto found = std::find_if(plugins.begin(), plugins.end(), predicate); - if (found == plugins.end()) { - wxLogDebug("Cannot find metadata for %s", name.c_str()); - } - return found != plugins.end() ? *found : PluginMetadata(); -} - -void PluginPanel::SetSelected(bool selected) { - m_bSelected = selected; - - m_pVersion->SetLabel( - PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName)); - if (selected) { - SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND)); - m_pButtons->Show(true); - bool unInstallPossible = canUninstall(m_plugin.m_common_name.ToStdString()); - - // Directly mark Legacy and system plugins as "not uninstallable" - if (m_plugin.m_status == PluginStatus::LegacyUpdateAvailable || - m_plugin.m_status == PluginStatus::Unmanaged || - m_plugin.m_status == PluginStatus::System) - unInstallPossible = false; - - // Orphan plugins can usually be uninstalled, at best effort. - if (m_plugin.m_managed_metadata.is_orphan) unInstallPossible = true; - - m_pButtonUninstall->Show(unInstallPossible); - - if (m_plugin.m_managed_metadata.info_url.size()) { - m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str()); - m_info_btn->Show(); - } - - m_cbEnable->Show(true); - - // Configure the "Action" button - wxString label; - SemanticVersion newVersion; - switch (m_plugin.m_status) { - case PluginStatus::LegacyUpdateAvailable: - label = _("Upgrade to Version "); - label += wxString(m_plugin.m_managed_metadata.version.c_str()); - m_action = ActionVerb::UPGRADE_TO_MANAGED_VERSION; - m_pButtonAction->Enable(); - break; - - case PluginStatus::ManagedInstallAvailable: - label = _("Install..."); - m_action = ActionVerb::INSTALL_MANAGED_VERSION; - m_pButtonAction->Enable(); - break; - - case PluginStatus::ManagedInstalledUpdateAvailable: - label = _("Update to "); - label += wxString(m_plugin.m_managed_metadata.version.c_str()); - m_action = ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION; - m_pButtonAction->Enable(); - break; - - case PluginStatus::ManagedInstalledCurrentVersion: - label = _("Reinstall"); - m_action = ActionVerb::REINSTALL_MANAGED_VERSION; - m_pButtonAction->Enable(); - break; - - case PluginStatus::ManagedInstalledDowngradeAvailable: - label = _("Downgrade"); - m_action = ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION; - m_pButtonAction->Enable(); - break; - - case PluginStatus::Unmanaged: - m_action = ActionVerb::NOP; - m_pButtonAction->Hide(); - break; - - case PluginStatus::System: - m_action = ActionVerb::NOP; - m_pButtonAction->Hide(); - break; - - default: - label = "TBD"; - m_action = ActionVerb::NOP; - break; - } - SetActionLabel(label); - const auto plugin_name = m_plugin.m_common_name.ToStdString(); - if (ocpn::exists(PluginHandler::ImportedMetadataPath(plugin_name))) { - m_pButtonAction->Hide(); - } - - Layout(); - } else { - SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND)); - // m_pDescription->SetLabel( m_pPlugin->m_short_description ); -#ifndef __WXQT__ - // m_pButtons->Show(false); -#else - // m_pButtons->Show(true); -#endif - //(); - - m_pButtons->Show(false); - m_info_btn->Hide(); - - if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) - m_cbEnable->Show(false); - - Layout(); - } - - // m_pButtons->Show(selected); // For most platforms, show buttons if - // selected m_pButtonsUpDown->Show(selected); -#ifdef __ANDROID__ - // Some Android devices (e.g. Kyocera) have trouble with wxBitmapButton... - // m_pButtonsUpDown->Show(false); - // m_pButtons->Show(true); // Always enable buttons for Android -#endif - - Layout(); - - if (selected) { - SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND)); - } else { - SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND)); - } - - SetEnabled(m_plugin.m_enabled); - -#ifdef __ANDROID__ - // Android (wxQT) sizers have troubles... - // So we set some layout factors to avoid re-sizing on select/deselect. - // m_rgSizer->Show(true); - // m_pButtons->Show(true); - // m_pButtonAction->Hide(); - // m_pButtonUninstall->Hide(); - - Fit(); - // m_PluginListPanel->m_pitemBoxSizer01->Layout(); -#endif -} - -void PluginPanel::OnPaint(wxPaintEvent& event) { - wxPaintDC dc(this); - - int penWidth = m_penWidthUnselected; - wxColour color = GetDialogColor(DLG_UNSELECTED_BACKGROUND); - wxColour border = GetDialogColor(DLG_UNSELECTED_ACCENT); - - if (m_bSelected) { - penWidth = m_penWidthSelected; - color = GetDialogColor(DLG_SELECTED_BACKGROUND); - border = GetDialogColor(DLG_SELECTED_ACCENT); - } - - wxBrush b(color, wxBRUSHSTYLE_SOLID); - dc.SetBrush(b); - dc.SetPen(wxPen(border, penWidth)); - - dc.DrawRoundedRectangle(5, 5, GetSize().x - 10, GetSize().y - 10, 5); -} - -void PluginPanel::OnPluginPreferences(wxCommandEvent& event) { - if (m_plugin.m_enabled && m_plugin.m_init_state && - (m_plugin.m_cap_flag & WANTS_PREFERENCES)) { -#ifdef __ANDROID__ - androidDisableRotation(); - PluginLoader::getInstance()->ShowPreferencesDialog(m_plugin, - GetGrandParent()); - // GrandParent will be the entire list panel, not the plugin panel. - // Ensures better centering on small screens -#else - PluginLoader::getInstance()->ShowPreferencesDialog(m_plugin, this); -#endif - } -} - -void PluginPanel::OnPluginEnableToggle(wxCommandEvent& event) { - SetEnabled(event.IsChecked()); - m_pVersion->SetLabel( - PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName)); - if (m_plugin.m_status == PluginStatus::System) { - // Force pluginmanager to reload all panels. Not kosher -- - // the EventVar should really only be notified from within PluginLoader. - PluginLoader::getInstance()->evt_pluglist_change.Notify(); - } -} - -void PluginPanel::OnPluginUninstall(wxCommandEvent& event) { - m_action = ActionVerb::UNINSTALL_MANAGED_VERSION; - - // Chain up to the utility event handler - wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED); - actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION); - actionEvent.SetClientData(this); - g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent); -} - -void PluginPanel::OnPluginAction(wxCommandEvent& event) { - // Chain up to the utility event handler - wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED); - actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION); - actionEvent.SetClientData(this); - g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent); - - return; -} - -static void SetWindowFontStyle(wxWindow* window, wxFontStyle style) { - auto font = window->GetFont(); - font.SetStyle(style); - window->SetFont(font); -} - -void PluginPanel::SetEnabled(bool enabled) { - if (m_is_safe_panel) return; - PluginLoader::getInstance()->SetEnabled(m_plugin.m_common_name, enabled); - PluginLoader::getInstance()->UpdatePlugIns(); - NotifySetupOptionsPlugin(&m_plugin); - if (!enabled && !m_bSelected) { - SetWindowFontStyle(m_pName, wxFONTSTYLE_ITALIC); - SetWindowFontStyle(m_pVersion, wxFONTSTYLE_ITALIC); - SetWindowFontStyle(m_pDescription, wxFONTSTYLE_ITALIC); -#ifdef x__ANDROID__ - m_pName->Disable(); - m_pVersion->Disable(); - m_pDescription->Disable(); -#endif - } else { - SetWindowFontStyle(m_pName, wxFONTSTYLE_NORMAL); - SetWindowFontStyle(m_pVersion, wxFONTSTYLE_NORMAL); - SetWindowFontStyle(m_pDescription, wxFONTSTYLE_NORMAL); -#ifdef x__ANDROID__ - m_pName->Enable(); - m_pVersion->Enable(); - m_pDescription->Enable(); -#endif - } - -#ifdef __ANDROID__ - m_pName->Enable(enabled || m_bSelected); - m_pVersion->Enable(enabled || m_bSelected); - m_pDescription->Enable(enabled || m_bSelected); -#endif - - if (m_bSelected) { - wxString description = m_plugin.m_long_description; - if (description.IsEmpty()) - description = wxString(m_plugin.m_managed_metadata.description.c_str()); - - PanelHardBreakWrapper wrapper(this, description, - g_options->GetSize().x * 7 / 10); - m_pDescription->SetLabel(wrapper.GetWrapped()); - if (m_plugin.m_managed_metadata.info_url.size()) { - m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str()); - m_info_btn->Show(); - } - } else { - wxString description = m_plugin.m_short_description; - if (description.IsEmpty()) - description = wxString(m_plugin.m_managed_metadata.summary.c_str()); - PanelHardBreakWrapper wrapper(this, description, - g_options->GetSize().x * 7 / 10); - m_pDescription->SetLabel(wrapper.GetWrapped()); - } - - m_pButtonPreferences->Enable(enabled && - (m_plugin.m_cap_flag & WANTS_PREFERENCES)); - m_cbEnable->SetValue(enabled); -} - -void PluginPanel::OnPluginUp(wxCommandEvent& event) { - m_PluginListPanel->MoveUp(this); -} - -void PluginPanel::OnPluginDown(wxCommandEvent& event) { - m_PluginListPanel->MoveDown(this); -} - -/** Invokes client browser on plugin info_url when clicked. */ -WebsiteButton::WebsiteButton(wxWindow* parent, const char* url) - : wxPanel(parent), m_url(url) { - auto vbox = new wxBoxSizer(wxVERTICAL); - auto button = new wxButton(this, wxID_ANY, _("Website")); - button->Enable(strlen(url) > 0); - vbox->Add(button); - SetSizer(vbox); - Bind(wxEVT_COMMAND_BUTTON_CLICKED, - [=](wxCommandEvent&) { wxLaunchDefaultBrowser(m_url); }); -} - -// ---------------------------------------------------------------------------- -// PlugInChartBase Implmentation -// This class is the base class for Plug-able chart types -// ---------------------------------------------------------------------------- - -PlugInChartBase::PlugInChartBase() { m_Chart_Error_Factor = 0.; } - -PlugInChartBase::~PlugInChartBase() {} - -wxString PlugInChartBase::GetFileSearchMask(void) { return _T(""); } - -int PlugInChartBase::Init(const wxString& name, int init_flags) { return 0; } - -// Accessors - -double PlugInChartBase::GetNormalScaleMin(double canvas_scale_factor, - bool b_allow_overzoom) { - return 1.0; -} - -double PlugInChartBase::GetNormalScaleMax(double canvas_scale_factor, - int canvas_width) { - return 2.0e7; -} - -bool PlugInChartBase::GetChartExtent(ExtentPI* pext) { return false; } - -wxBitmap& PlugInChartBase::RenderRegionView(const PlugIn_ViewPort& VPoint, - const wxRegion& Region) { - return wxNullBitmap; -} - -bool PlugInChartBase::AdjustVP(PlugIn_ViewPort& vp_last, - PlugIn_ViewPort& vp_proposed) { - return false; -} - -void PlugInChartBase::GetValidCanvasRegion(const PlugIn_ViewPort& VPoint, - wxRegion* pValidRegion) {} - -void PlugInChartBase::SetColorScheme(int cs, bool bApplyImmediate) {} - -double PlugInChartBase::GetNearestPreferredScalePPM(double target_scale_ppm) { - return 1.0; -} - -wxBitmap* PlugInChartBase::GetThumbnail(int tnx, int tny, int cs) { - return NULL; -} - -void PlugInChartBase::ComputeSourceRectangle(const PlugIn_ViewPort& vp, - wxRect* pSourceRect) {} - -double PlugInChartBase::GetRasterScaleFactor() { return 1.0; } - -bool PlugInChartBase::GetChartBits(wxRect& source, unsigned char* pPix, - int sub_samp) { - return false; -} - -int PlugInChartBase::GetSize_X() { return 1; } - -int PlugInChartBase::GetSize_Y() { return 1; } - -void PlugInChartBase::latlong_to_chartpix(double lat, double lon, double& pixx, - double& pixy) {} - -void PlugInChartBase::chartpix_to_latlong(double pixx, double pixy, - double* plat, double* plon) {} - -// ---------------------------------------------------------------------------- -// PlugInChartBaseGL Implementation -// -// ---------------------------------------------------------------------------- - -PlugInChartBaseGL::PlugInChartBaseGL() {} - -PlugInChartBaseGL::~PlugInChartBaseGL() {} - -int PlugInChartBaseGL::RenderRegionViewOnGL(const wxGLContext& glc, - const PlugIn_ViewPort& VPoint, - const wxRegion& Region, - bool b_use_stencil) { - return 0; -} - -ListOfPI_S57Obj* PlugInChartBaseGL::GetObjRuleListAtLatLon( - float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) { - return NULL; -} - -wxString PlugInChartBaseGL::CreateObjDescriptions(ListOfPI_S57Obj* obj_list) { - return _T(""); -} - -int PlugInChartBaseGL::GetNoCOVREntries() { return 0; } - -int PlugInChartBaseGL::GetNoCOVRTablePoints(int iTable) { return 0; } - -int PlugInChartBaseGL::GetNoCOVRTablenPoints(int iTable) { return 0; } - -float* PlugInChartBaseGL::GetNoCOVRTableHead(int iTable) { return 0; } - -// ---------------------------------------------------------------------------- -// PlugInChartBaseExtended Implementation -// -// ---------------------------------------------------------------------------- - -PlugInChartBaseExtended::PlugInChartBaseExtended() {} - -PlugInChartBaseExtended::~PlugInChartBaseExtended() {} - -int PlugInChartBaseExtended::RenderRegionViewOnGL(const wxGLContext& glc, - const PlugIn_ViewPort& VPoint, - const wxRegion& Region, - bool b_use_stencil) { - return 0; -} - -int PlugInChartBaseExtended::RenderRegionViewOnGLNoText( - const wxGLContext& glc, const PlugIn_ViewPort& VPoint, - const wxRegion& Region, bool b_use_stencil) { - return 0; -} - -int PlugInChartBaseExtended::RenderRegionViewOnGLTextOnly( - const wxGLContext& glc, const PlugIn_ViewPort& VPoint, - const wxRegion& Region, bool b_use_stencil) { - return 0; -} - -wxBitmap& PlugInChartBaseExtended::RenderRegionViewOnDCNoText( - const PlugIn_ViewPort& VPoint, const wxRegion& Region) { - return wxNullBitmap; -} - -bool PlugInChartBaseExtended::RenderRegionViewOnDCTextOnly( - wxMemoryDC& dc, const PlugIn_ViewPort& VPoint, const wxRegion& Region) { - return false; -} - -ListOfPI_S57Obj* PlugInChartBaseExtended::GetObjRuleListAtLatLon( - float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) { - return NULL; -} - -wxString PlugInChartBaseExtended::CreateObjDescriptions( - ListOfPI_S57Obj* obj_list) { - return _T(""); -} - -int PlugInChartBaseExtended::GetNoCOVREntries() { return 0; } - -int PlugInChartBaseExtended::GetNoCOVRTablePoints(int iTable) { return 0; } - -int PlugInChartBaseExtended::GetNoCOVRTablenPoints(int iTable) { return 0; } - -float* PlugInChartBaseExtended::GetNoCOVRTableHead(int iTable) { return 0; } - -void PlugInChartBaseExtended::ClearPLIBTextList() {} - -// ---------------------------------------------------------------------------- -// PlugInChartBaseExtendedPlus2 Implementation -// -// ---------------------------------------------------------------------------- - -PlugInChartBaseExtendedPlus2::PlugInChartBaseExtendedPlus2() {} - -PlugInChartBaseExtendedPlus2::~PlugInChartBaseExtendedPlus2() {} - -ListOfPI_S57Obj* -PlugInChartBaseExtendedPlus2::GetLightsObjRuleListVisibleAtLatLon( - float lat, float lon, PlugIn_ViewPort* VPoint) { - return NULL; -} - -// ---------------------------------------------------------------------------- -// PlugInChartBaseGLPlus2 Implementation -// -// ---------------------------------------------------------------------------- - -PlugInChartBaseGLPlus2::PlugInChartBaseGLPlus2() {} - -PlugInChartBaseGLPlus2::~PlugInChartBaseGLPlus2() {} - -ListOfPI_S57Obj* PlugInChartBaseGLPlus2::GetLightsObjRuleListVisibleAtLatLon( - float lat, float lon, PlugIn_ViewPort* VPoint) { - return NULL; -} - -// ---------------------------------------------------------------------------- -// ChartPlugInWrapper Implementation -// This class is a wrapper/interface to PlugIn charts(PlugInChartBase) -// ---------------------------------------------------------------------------- - -ChartPlugInWrapper::ChartPlugInWrapper() {} - -ChartPlugInWrapper::ChartPlugInWrapper(const wxString& chart_class) { - m_ppo = ::wxCreateDynamicObject(chart_class); - m_ppicb = dynamic_cast(m_ppo); -} - -ChartPlugInWrapper::~ChartPlugInWrapper() { - if (m_ppicb) delete m_ppicb; -} - -wxString ChartPlugInWrapper::GetFileSearchMask(void) { - if (m_ppicb) - return m_ppicb->GetFileSearchMask(); - else - return _T(""); -} - -InitReturn ChartPlugInWrapper::Init(const wxString& name, - ChartInitFlag init_flags) { - if (m_ppicb) { - wxWindow* pa = wxWindow::FindFocus(); - - InitReturn ret_val = (InitReturn)m_ppicb->Init(name, (int)init_flags); - - // Here we transcribe all the required wrapped member elements up into - // the chartbase object which is the parent of this class - if (ret_val == INIT_OK) { - m_FullPath = m_ppicb->GetFullPath(); - m_ChartType = (ChartTypeEnum)m_ppicb->GetChartType(); - m_ChartFamily = (ChartFamilyEnum)m_ppicb->GetChartFamily(); - m_projection = (OcpnProjType)m_ppicb->GetChartProjection(); - m_EdDate = m_ppicb->GetEditionDate(); - m_Name = m_ppicb->GetName(); - m_ID = m_ppicb->GetID(); - m_DepthUnits = m_ppicb->GetDepthUnits(); - m_SoundingsDatum = m_ppicb->GetSoundingsDatum(); - m_datum_str = m_ppicb->GetDatumString(); - m_SE = m_ppicb->GetSE(); - m_EdDate = m_ppicb->GetEditionDate(); - m_ExtraInfo = m_ppicb->GetExtraInfo(); - Chart_Error_Factor = m_ppicb->GetChartErrorFactor(); - m_depth_unit_id = (ChartDepthUnitType)m_ppicb->GetDepthUnitId(); - m_Chart_Skew = m_ppicb->GetChartSkew(); - m_Chart_Scale = m_ppicb->GetNativeScale(); - - // We estimate ppm_avg as needed by raster texture cache logic... - // This number works for average BSB charts, scanned with average - // resolution - m_ppm_avg = 10000. / m_ppicb->GetNativeScale(); // fallback value - - // Calcuculate a "better" ppm from the chart geo extent and raster size. - if ((fabs(m_Chart_Skew) < .01) && - (CHART_FAMILY_RASTER == m_ChartFamily)) { - Extent extent; - if (GetChartExtent(&extent)) { - double lon_range = extent.ELON - extent.WLON; - if ((lon_range > 0) && - (lon_range < 90.0)) // Be safe about IDL crossing and huge charts - m_ppm_avg = GetSize_X() / (lon_range * 1852 * 60); - } - } - - m_overlayENC = false; - if (m_ChartFamily == (ChartFamilyEnum)PI_CHART_FAMILY_VECTOR) { - wxCharBuffer buf = m_FullPath.ToUTF8(); - m_overlayENC = s57chart::IsCellOverlayType(buf.data()); - } - - bReadyToRender = m_ppicb->IsReadyToRender(); - } else { - // Mark the chart as unable to render - m_ChartType = CHART_TYPE_UNKNOWN; - m_ChartFamily = CHART_FAMILY_UNKNOWN; - } - - // PlugIn may invoke wxExecute(), which steals the keyboard focus - // So take it back - auto pc = dynamic_cast(pa); - if (pc) pc->SetFocus(); - - return ret_val; - } else - return INIT_FAIL_REMOVE; -} - -// Accessors -int ChartPlugInWrapper::GetCOVREntries() { - if (m_ppicb) - return m_ppicb->GetCOVREntries(); - else - return 0; -} - -int ChartPlugInWrapper::GetCOVRTablePoints(int iTable) { - if (m_ppicb) - return m_ppicb->GetCOVRTablePoints(iTable); - else - return 0; -} - -int ChartPlugInWrapper::GetCOVRTablenPoints(int iTable) { - if (m_ppicb) - return m_ppicb->GetCOVRTablenPoints(iTable); - else - return 0; -} - -float* ChartPlugInWrapper::GetCOVRTableHead(int iTable) { - if (m_ppicb) - return m_ppicb->GetCOVRTableHead(iTable); - else - return 0; -} - -// TODO -// PlugIn chart types do not properly support NoCovr Regions -// Proper fix is to update PlugIn Chart Type API -// Derive an extended PlugIn chart class from existing class, -// and use some kind of RTTI to figure out which class to call. -int ChartPlugInWrapper::GetNoCOVREntries() { - if (m_ppicb) { - PlugInChartBaseGL* ppicbgl = dynamic_cast(m_ppicb); - if (ppicbgl) { - return ppicbgl->GetNoCOVREntries(); - } - } - return 0; -} - -int ChartPlugInWrapper::GetNoCOVRTablePoints(int iTable) { - if (m_ppicb) { - PlugInChartBaseGL* ppicbgl = dynamic_cast(m_ppicb); - if (ppicbgl) { - return ppicbgl->GetNoCOVRTablePoints(iTable); - } - } - return 0; -} - -int ChartPlugInWrapper::GetNoCOVRTablenPoints(int iTable) { - if (m_ppicb) { - PlugInChartBaseGL* ppicbgl = dynamic_cast(m_ppicb); - if (ppicbgl) { - return ppicbgl->GetNoCOVRTablenPoints(iTable); - } - } - return 0; -} - -float* ChartPlugInWrapper::GetNoCOVRTableHead(int iTable) { - if (m_ppicb) { - PlugInChartBaseGL* ppicbgl = dynamic_cast(m_ppicb); - if (ppicbgl) { - return ppicbgl->GetNoCOVRTableHead(iTable); - } - } - return 0; -} - -bool ChartPlugInWrapper::GetChartExtent(Extent* pext) { - if (m_ppicb) { - ExtentPI xpi; - if (m_ppicb->GetChartExtent(&xpi)) { - pext->NLAT = xpi.NLAT; - pext->SLAT = xpi.SLAT; - pext->ELON = xpi.ELON; - pext->WLON = xpi.WLON; - - return true; - } else - return false; - } else - return false; -} - -ThumbData* ChartPlugInWrapper::GetThumbData(int tnx, int tny, float lat, - float lon) { - if (m_ppicb) { - // Create the bitmap if needed, doing a deep copy from the Bitmap owned - // by the PlugIn Chart - if (!pThumbData->pDIBThumb) { - wxBitmap* pBMPOwnedByChart = - m_ppicb->GetThumbnail(tnx, tny, m_global_color_scheme); - if (pBMPOwnedByChart) { - wxImage img = pBMPOwnedByChart->ConvertToImage(); - pThumbData->pDIBThumb = new wxBitmap(img); - } else - pThumbData->pDIBThumb = NULL; - } - - pThumbData->Thumb_Size_X = tnx; - pThumbData->Thumb_Size_Y = tny; - - /* - // Plot the supplied Lat/Lon on the thumbnail - int divx = m_ppicb->Size_X / tnx; - int divy = m_ppicb->Size_Y / tny; - - int div_factor = __min(divx, divy); - - int pixx, pixy; - - - // Using a temporary synthetic ViewPort and source rectangle, - // calculate the ships position on the thumbnail - ViewPort tvp; - tvp.pix_width = tnx; - tvp.pix_height = tny; - tvp.view_scale_ppm = GetPPM() / div_factor; - wxRect trex = Rsrc; - Rsrc.x = 0; - Rsrc.y = 0; - latlong_to_pix_vp(lat, lon, pixx, pixy, tvp); - Rsrc = trex; - - pThumbData->ShipX = pixx;// / div_factor; - pThumbData->ShipY = pixy;// / div_factor; - */ - pThumbData->ShipX = 0; - pThumbData->ShipY = 0; - - return pThumbData; - } else - return NULL; -} - -ThumbData* ChartPlugInWrapper::GetThumbData() { return pThumbData; } - -bool ChartPlugInWrapper::UpdateThumbData(double lat, double lon) { - return true; -} - -double ChartPlugInWrapper::GetNormalScaleMin(double canvas_scale_factor, - bool b_allow_overzoom) { - if (m_ppicb) - return m_ppicb->GetNormalScaleMin(canvas_scale_factor, b_allow_overzoom); - else - return 1.0; -} - -double ChartPlugInWrapper::GetNormalScaleMax(double canvas_scale_factor, - int canvas_width) { - if (m_ppicb) - return m_ppicb->GetNormalScaleMax(canvas_scale_factor, canvas_width); - else - return 2.0e7; -} - -/* RectRegion: - * This is the Screen region desired to be updated. Will - * be either 1 rectangle(full screen) or two rectangles (panning with FBO - * accelerated pan logic) - * - * Region: - * This is the LLRegion describing the quilt active region - * for this chart. - * - * So, Actual rendering area onscreen should be clipped to the - * intersection of the two regions. - */ - -// Render helpers -void RenderRotateToViewPort(const ViewPort& VPoint) { -#ifndef USE_ANDROID_GLES2 - float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0; - glTranslatef(xt, yt, 0); - glRotatef(VPoint.rotation * 180. / PI, 0, 0, 1); - glTranslatef(-xt, -yt, 0); -#endif -} - -void UndoRenderRotateToViewPort(const ViewPort& VPoint) { -#ifndef USE_ANDROID_GLES2 - float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0; - glTranslatef(xt, yt, 0); - glRotatef(-VPoint.rotation * 180. / PI, 0, 0, 1); - glTranslatef(-xt, -yt, 0); -#endif -} - -bool ChartPlugInWrapper::RenderRegionViewOnGL(const wxGLContext& glc, - const ViewPort& VPoint, - const OCPNRegion& RectRegion, - const LLRegion& Region) { -#ifdef ocpnUSE_GL - if (m_ppicb) { - ViewPort vp = VPoint; // non-const copy - - gs_plib_flags = 0; // reset the CAPs flag - PlugInChartBaseGL* ppicb_gl = dynamic_cast(m_ppicb); - PlugInChartBaseExtended* ppicb_x = - dynamic_cast(m_ppicb); - if (!Region.Empty() && (ppicb_gl || ppicb_x)) { - wxRegion* r = RectRegion.GetNew_wxRegion(); - for (OCPNRegionIterator upd(RectRegion); upd.HaveRects(); - upd.NextRect()) { - LLRegion chart_region = vp.GetLLRegion(upd.GetRect()); - chart_region.Intersect(Region); - - if (!chart_region.Empty()) { - ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region); - - glChartCanvas::SetClipRect(cvp, upd.GetRect(), false); - - // ps52plib->m_last_clip_rect = upd.GetRect(); - -#ifndef USE_ANDROID_GLES2 -// glPushMatrix(); // Adjust for rotation -#endif - RenderRotateToViewPort(VPoint); - - PlugIn_ViewPort pivp = CreatePlugInViewport(cvp); - if (ppicb_x) - ppicb_x->RenderRegionViewOnGL(glc, pivp, *r, - glChartCanvas::s_b_useStencil); - else if (ppicb_gl) - ppicb_gl->RenderRegionViewOnGL(glc, pivp, *r, - glChartCanvas::s_b_useStencil); - UndoRenderRotateToViewPort(VPoint); - -#ifndef USE_ANDROID_GLES2 -// glPopMatrix(); -#endif - glChartCanvas::DisableClipRegion(); - - } //! empty - } // for - delete r; - } - } else - return false; -#endif - return true; -} - -// int indexrr; - -bool ChartPlugInWrapper::RenderRegionViewOnGLNoText( - const wxGLContext& glc, const ViewPort& VPoint, - const OCPNRegion& RectRegion, const LLRegion& Region) { -#ifdef ocpnUSE_GL - if (m_ppicb) { - // printf("\nCPIW::RRVOGLNT %d %d \n", indexrr++, m_Chart_Scale); - - gs_plib_flags = 0; // reset the CAPs flag - PlugInChartBaseExtended* ppicb_x = - dynamic_cast(m_ppicb); - PlugInChartBaseGL* ppicb = dynamic_cast(m_ppicb); - if (!Region.Empty() && ppicb_x) { - // Start with a clean slate - glChartCanvas::SetClipRect(VPoint, VPoint.rv_rect, false); - glChartCanvas::DisableClipRegion(); - - // Apply rotation to this chart - RenderRotateToViewPort(VPoint); - - PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); - wxRegion* r = RectRegion.GetNew_wxRegion(); - - ppicb_x->RenderRegionViewOnGLNoText(glc, pivp, *r, - glChartCanvas::s_b_useStencil); - - // Undo rotation - UndoRenderRotateToViewPort(VPoint); - - delete r; - } - - else if (!Region.Empty() && - ppicb) // Legacy Vector GL Plugin chart (e.g.S63) - { - ViewPort vp = VPoint; // non-const copy - wxRegion* r = RectRegion.GetNew_wxRegion(); - for (OCPNRegionIterator upd(RectRegion); upd.HaveRects(); - upd.NextRect()) { - LLRegion chart_region = vp.GetLLRegion(upd.GetRect()); - chart_region.Intersect(Region); - - if (!chart_region.Empty()) { - ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region); - - glChartCanvas::SetClipRect(cvp, upd.GetRect(), false); - - RenderRotateToViewPort(VPoint); - - PlugIn_ViewPort pivp = CreatePlugInViewport(cvp); - ppicb->RenderRegionViewOnGL(glc, pivp, *r, - glChartCanvas::s_b_useStencil); - - // Undo rotation - UndoRenderRotateToViewPort(VPoint); - - glChartCanvas::DisableClipRegion(); - - } //! empty - } // for - delete r; - } - - } else - return false; -#endif - return true; -} - -bool ChartPlugInWrapper::RenderRegionViewOnGLTextOnly( - const wxGLContext& glc, const ViewPort& VPoint, const OCPNRegion& Region) { -#ifdef ocpnUSE_GL - if (m_ppicb) { - gs_plib_flags = 0; // reset the CAPs flag - PlugInChartBaseExtended* ppicb_x = - dynamic_cast(m_ppicb); - if (!Region.Empty() && ppicb_x) { - wxRegion* r = Region.GetNew_wxRegion(); - for (OCPNRegionIterator upd(Region); upd.HaveRects(); upd.NextRect()) { -#ifndef USE_ANDROID_GLES2 -// glPushMatrix(); // Adjust for rotation -#endif - RenderRotateToViewPort(VPoint); - - PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); - ppicb_x->RenderRegionViewOnGLTextOnly(glc, pivp, *r, - glChartCanvas::s_b_useStencil); - UndoRenderRotateToViewPort(VPoint); - -#ifndef USE_ANDROID_GLES2 -// glPopMatrix(); -#endif - - } // for - delete r; - } - } else - return false; -#endif - return true; -} - -bool ChartPlugInWrapper::RenderRegionViewOnDC(wxMemoryDC& dc, - const ViewPort& VPoint, - const OCPNRegion& Region) { - if (m_ppicb) { - gs_plib_flags = 0; // reset the CAPs flag - PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); - if (Region.IsOk()) { - wxRegion* r = Region.GetNew_wxRegion(); - if (!m_overlayENC) - dc.SelectObject(m_ppicb->RenderRegionView(pivp, *r)); - else { - wxBitmap& obmp = m_ppicb->RenderRegionView(pivp, *r); - - // Create a mask to remove the NODTA areas from overlay cells. - wxColour nodat = GetGlobalColor(_T ( "NODTA" )); - wxColour nodat_sub = nodat; - -#ifdef ocpnUSE_ocpnBitmap - nodat_sub = wxColour(nodat.Blue(), nodat.Green(), nodat.Red()); -#endif - m_pMask = new wxMask(obmp, nodat_sub); - obmp.SetMask(m_pMask); - - dc.SelectObject(obmp); - } - - delete r; - return true; - } else - return false; - } else - return false; -} - -bool ChartPlugInWrapper::RenderRegionViewOnDCNoText(wxMemoryDC& dc, - const ViewPort& VPoint, - const OCPNRegion& Region) { - if (m_ppicb) { - gs_plib_flags = 0; // reset the CAPs flag - PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); - - PlugInChartBaseExtended* pCBx = - dynamic_cast(m_ppicb); - PlugInChartBase* ppicb = dynamic_cast(m_ppicb); - - if (Region.IsOk() && (pCBx || ppicb)) { - wxRegion* r = Region.GetNew_wxRegion(); - - if (pCBx) - dc.SelectObject(pCBx->RenderRegionViewOnDCNoText(pivp, *r)); - else if (ppicb) - dc.SelectObject(ppicb->RenderRegionView(pivp, *r)); - - delete r; - return true; - } else - return false; - } else - return false; -} - -bool ChartPlugInWrapper::RenderRegionViewOnDCTextOnly( - wxMemoryDC& dc, const ViewPort& VPoint, const OCPNRegion& Region) { - if (m_ppicb) { - bool ret_val = false; - gs_plib_flags = 0; // reset the CAPs flag - PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); - if (Region.IsOk()) { - wxRegion* r = Region.GetNew_wxRegion(); - - PlugInChartBaseExtended* pCBx = - dynamic_cast(m_ppicb); - if (pCBx) ret_val = pCBx->RenderRegionViewOnDCTextOnly(dc, pivp, *r); - - delete r; - return ret_val; - } else - return false; - } else - return false; -} - -void ChartPlugInWrapper::ClearPLIBTextList() { - if (m_ppicb) { - PlugInChartBaseExtended* pCBx = - dynamic_cast(m_ppicb); - if (pCBx) pCBx->ClearPLIBTextList(); - } -} - -bool ChartPlugInWrapper::AdjustVP(ViewPort& vp_last, ViewPort& vp_proposed) { - if (m_ppicb) { - PlugIn_ViewPort pivp_last = CreatePlugInViewport(vp_last); - PlugIn_ViewPort pivp_proposed = CreatePlugInViewport(vp_proposed); - return m_ppicb->AdjustVP(pivp_last, pivp_proposed); - } else - return false; -} - -void ChartPlugInWrapper::GetValidCanvasRegion(const ViewPort& VPoint, - OCPNRegion* pValidRegion) { - if (m_ppicb) { - PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); - // currently convert using wxRegion, - // this should be changed as wxRegion is proven unstable/buggy on various - // platforms - wxRegion region; - m_ppicb->GetValidCanvasRegion(pivp, ®ion); - *pValidRegion = OCPNRegion(region); - } - - return; -} - -void ChartPlugInWrapper::SetColorScheme(ColorScheme cs, bool bApplyImmediate) { - if (m_ppicb) { - m_ppicb->SetColorScheme(cs, bApplyImmediate); - } - m_global_color_scheme = cs; - // Force a new thumbnail - if (pThumbData) pThumbData->pDIBThumb = NULL; -} - -double ChartPlugInWrapper::GetNearestPreferredScalePPM( - double target_scale_ppm) { - if (m_ppicb) - return m_ppicb->GetNearestPreferredScalePPM(target_scale_ppm); - else - return 1.0; -} - -void ChartPlugInWrapper::ComputeSourceRectangle(const ViewPort& VPoint, - wxRect* pSourceRect) { - if (m_ppicb) { - PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); - m_ppicb->ComputeSourceRectangle(pivp, pSourceRect); - } -} - -double ChartPlugInWrapper::GetRasterScaleFactor(const ViewPort& vp) { - if (m_ppicb) { - return (wxRound(100000 * GetPPM() / vp.view_scale_ppm)) / 100000.; - } else - return 1.0; -} - -bool ChartPlugInWrapper::GetChartBits(wxRect& source, unsigned char* pPix, - int sub_samp) { - wxCriticalSectionLocker locker(m_critSect); - - if (m_ppicb) - - return m_ppicb->GetChartBits(source, pPix, sub_samp); - else - return false; -} - -int ChartPlugInWrapper::GetSize_X() { - if (m_ppicb) - return m_ppicb->GetSize_X(); - else - return 1; -} - -int ChartPlugInWrapper::GetSize_Y() { - if (m_ppicb) - return m_ppicb->GetSize_Y(); - else - return 1; -} - -void ChartPlugInWrapper::latlong_to_chartpix(double lat, double lon, - double& pixx, double& pixy) { - if (m_ppicb) m_ppicb->latlong_to_chartpix(lat, lon, pixx, pixy); -} - -void ChartPlugInWrapper::chartpix_to_latlong(double pixx, double pixy, - double* plat, double* plon) { - if (m_ppicb) m_ppicb->chartpix_to_latlong(pixx, pixy, plat, plon); -} - -/* API 1.11 */ - -/* API 1.11 adds some more common functions to avoid unnecessary code - * duplication */ - -wxString toSDMM_PlugIn(int NEflag, double a, bool hi_precision) { - return toSDMM(NEflag, a, hi_precision); -} - -wxColour GetBaseGlobalColor(wxString colorName) { - return GetGlobalColor(colorName); -} - -int OCPNMessageBox_PlugIn(wxWindow* parent, const wxString& message, - const wxString& caption, int style, int x, int y) { - return OCPNMessageBox(parent, message, caption, style, 100, x, y); -} - -wxString GetOCPN_ExePath(void) { return g_Platform->GetExePath(); } - -wxString* GetpPlugInLocation() { return g_Platform->GetPluginDirPtr(); } - -wxString GetWritableDocumentsDir(void) { - return g_Platform->GetWritableDocumentsDir(); -} - -wxString GetPlugInPath(opencpn_plugin* pplugin) { - wxString ret_val; - auto loader = PluginLoader::getInstance(); - for (unsigned int i = 0; i < loader->GetPlugInArray()->GetCount(); i++) { - PlugInContainer* pic = loader->GetPlugInArray()->Item(i); - if (pic->m_pplugin == pplugin) { - ret_val = pic->m_plugin_file; - break; - } - } - return ret_val; -} - -// API 1.11 Access to Vector PlugIn charts - -ListOfPI_S57Obj* PlugInManager::GetPlugInObjRuleListAtLatLon( - ChartPlugInWrapper* target, float zlat, float zlon, float SelectRadius, - const ViewPort& vp) { - ListOfPI_S57Obj* list = NULL; - if (target) { - PlugInChartBaseGL* picbgl = - dynamic_cast(target->GetPlugInChart()); - if (picbgl) { - PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp); - list = picbgl->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp); - - return list; - } - PlugInChartBaseExtended* picbx = - dynamic_cast(target->GetPlugInChart()); - if (picbx) { - PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp); - list = picbx->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp); - - return list; - } else - return list; - } else - return list; -} - -wxString PlugInManager::CreateObjDescriptions(ChartPlugInWrapper* target, - ListOfPI_S57Obj* rule_list) { - wxString ret_str; - if (target) { - PlugInChartBaseGL* picbgl = - dynamic_cast(target->GetPlugInChart()); - if (picbgl) { - ret_str = picbgl->CreateObjDescriptions(rule_list); - } else { - PlugInChartBaseExtended* picbx = - dynamic_cast(target->GetPlugInChart()); - if (picbx) { - ret_str = picbx->CreateObjDescriptions(rule_list); - } - } - } - return ret_str; -} - -// API 1.11 Access to S52 PLIB -wxString PI_GetPLIBColorScheme() { - return _T(""); // ps52plib->GetPLIBColorScheme() -} - -int PI_GetPLIBDepthUnitInt() { - if (ps52plib) - return ps52plib->m_nDepthUnitDisplay; - else - return 0; -} - -int PI_GetPLIBSymbolStyle() { - if (ps52plib) - return ps52plib->m_nSymbolStyle; - else - return 0; -} - -int PI_GetPLIBBoundaryStyle() { - if (ps52plib) - return ps52plib->m_nBoundaryStyle; - else - return 0; -} - -bool PI_PLIBObjectRenderCheck(PI_S57Obj* pObj, PlugIn_ViewPort* vp) { - if (ps52plib) { - // Create and populate a compatible s57 Object - S57Obj cobj; - chart_context ctx; - CreateCompatibleS57Object(pObj, &cobj, &ctx); - - ViewPort cvp = CreateCompatibleViewport(*vp); - - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - - // Create and populate a minimally compatible object container - ObjRazRules rzRules; - rzRules.obj = &cobj; - rzRules.LUP = pContext->LUP; - rzRules.sm_transform_parms = 0; - rzRules.child = NULL; - rzRules.next = NULL; - - if (pContext->LUP) { - ps52plib->SetVPointCompat( - cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, cvp.rotation, - cvp.clat, cvp.clon, cvp.chart_scale, cvp.rv_rect, cvp.GetBBox(), - cvp.ref_scale, GetOCPNCanvasWindow()->GetContentScaleFactor()); - ps52plib->PrepareForRender(); - - return ps52plib->ObjectRenderCheck(&rzRules); - } else - return false; - } else - return false; -} - -int PI_GetPLIBStateHash() { - if (ps52plib) - return ps52plib->GetStateHash(); - else - return 0; -} - -void CreateCompatibleS57Object(PI_S57Obj* pObj, S57Obj* cobj, - chart_context* pctx) { - strncpy(cobj->FeatureName, pObj->FeatureName, 8); - cobj->Primitive_type = (GeoPrim_t)pObj->Primitive_type; - cobj->att_array = pObj->att_array; - cobj->attVal = pObj->attVal; - cobj->n_attr = pObj->n_attr; - - cobj->x = pObj->x; - cobj->y = pObj->y; - cobj->z = pObj->z; - cobj->npt = pObj->npt; - - cobj->iOBJL = pObj->iOBJL; - cobj->Index = pObj->Index; - - cobj->geoPt = (pt*)pObj->geoPt; - cobj->geoPtz = pObj->geoPtz; - cobj->geoPtMulti = pObj->geoPtMulti; - - cobj->m_lat = pObj->m_lat; - cobj->m_lon = pObj->m_lon; - - cobj->m_DisplayCat = (DisCat)pObj->m_DisplayCat; - cobj->x_rate = pObj->x_rate; - cobj->y_rate = pObj->y_rate; - cobj->x_origin = pObj->x_origin; - cobj->y_origin = pObj->y_origin; - - cobj->Scamin = pObj->Scamin; - cobj->nRef = pObj->nRef; - cobj->bIsAton = pObj->bIsAton; - cobj->bIsAssociable = pObj->bIsAssociable; - - cobj->m_n_lsindex = pObj->m_n_lsindex; - cobj->m_lsindex_array = pObj->m_lsindex_array; - cobj->m_n_edge_max_points = pObj->m_n_edge_max_points; - - if (gs_plib_flags & PLIB_CAPS_OBJSEGLIST) { - cobj->m_ls_list_legacy = - (PI_line_segment_element*) - pObj->m_ls_list; // note the cast, assumes in-sync layout - } else - cobj->m_ls_list_legacy = 0; - cobj->m_ls_list = 0; - - if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) - cobj->m_bcategory_mutable = pObj->m_bcategory_mutable; - else - cobj->m_bcategory_mutable = true; // assume all objects are mutable - - cobj->m_DPRI = -1; // default is unassigned, fixed at render time - if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) { - if (pObj->m_DPRI == -1) { - S52PLIB_Context* pCtx = (S52PLIB_Context*)pObj->S52_Context; - if (pCtx->LUP) cobj->m_DPRI = pCtx->LUP->DPRI - '0'; - } else - cobj->m_DPRI = pObj->m_DPRI; - } - - cobj->pPolyTessGeo = (PolyTessGeo*)pObj->pPolyTessGeo; - cobj->m_chart_context = (chart_context*)pObj->m_chart_context; - - if (pObj->auxParm3 != 1234) { - pObj->auxParm3 = 1234; - pObj->auxParm0 = -99; - } - - cobj->auxParm0 = pObj->auxParm0; - cobj->auxParm1 = 0; - cobj->auxParm2 = 0; - cobj->auxParm3 = 0; +PlugInChartBaseExtended::~PlugInChartBaseExtended() {} - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; +int PlugInChartBaseExtended::RenderRegionViewOnGL(const wxGLContext& glc, + const PlugIn_ViewPort& VPoint, + const wxRegion& Region, + bool b_use_stencil) { + return 0; +} - if (pContext->bBBObj_valid) - // this is ugly because plugins still use BoundingBox - cobj->BBObj.Set(pContext->BBObj.GetMinY(), pContext->BBObj.GetMinX(), - pContext->BBObj.GetMaxY(), pContext->BBObj.GetMaxX()); +int PlugInChartBaseExtended::RenderRegionViewOnGLNoText( + const wxGLContext& glc, const PlugIn_ViewPort& VPoint, + const wxRegion& Region, bool b_use_stencil) { + return 0; +} - cobj->CSrules = pContext->CSrules; - cobj->bCS_Added = pContext->bCS_Added; +int PlugInChartBaseExtended::RenderRegionViewOnGLTextOnly( + const wxGLContext& glc, const PlugIn_ViewPort& VPoint, + const wxRegion& Region, bool b_use_stencil) { + return 0; +} - cobj->FText = pContext->FText; - cobj->bFText_Added = pContext->bFText_Added; - cobj->rText = pContext->rText; +wxBitmap& PlugInChartBaseExtended::RenderRegionViewOnDCNoText( + const PlugIn_ViewPort& VPoint, const wxRegion& Region) { + return wxNullBitmap; +} - cobj->bIsClone = true; // Protect cloned object pointers in S57Obj dtor +bool PlugInChartBaseExtended::RenderRegionViewOnDCTextOnly( + wxMemoryDC& dc, const PlugIn_ViewPort& VPoint, const wxRegion& Region) { + return false; +} - if (pctx) { - cobj->m_chart_context = pctx; - chart_context* ppctx = (chart_context*)pObj->m_chart_context; +ListOfPI_S57Obj* PlugInChartBaseExtended::GetObjRuleListAtLatLon( + float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) { + return NULL; +} - if (ppctx) { - cobj->m_chart_context->m_pvc_hash = ppctx->m_pvc_hash; - cobj->m_chart_context->m_pve_hash = ppctx->m_pve_hash; - cobj->m_chart_context->ref_lat = ppctx->ref_lat; - cobj->m_chart_context->ref_lon = ppctx->ref_lon; - cobj->m_chart_context->pFloatingATONArray = ppctx->pFloatingATONArray; - cobj->m_chart_context->pRigidATONArray = ppctx->pRigidATONArray; - cobj->m_chart_context->safety_contour = ppctx->safety_contour; - cobj->m_chart_context->vertex_buffer = ppctx->vertex_buffer; - } - cobj->m_chart_context->chart = - 0; // note bene, this is always NULL for a PlugIn chart - cobj->m_chart_context->chart_type = S52_CHART_TYPE_PLUGIN; - } +wxString PlugInChartBaseExtended::CreateObjDescriptions( + ListOfPI_S57Obj* obj_list) { + return _T(""); } -bool PI_PLIBSetContext(PI_S57Obj* pObj) { - S52PLIB_Context* ctx; - if (!pObj->S52_Context) { - ctx = new S52PLIB_Context; - pObj->S52_Context = ctx; - } +int PlugInChartBaseExtended::GetNoCOVREntries() { return 0; } - ctx = (S52PLIB_Context*)pObj->S52_Context; +int PlugInChartBaseExtended::GetNoCOVRTablePoints(int iTable) { return 0; } - S57Obj cobj; - CreateCompatibleS57Object(pObj, &cobj, NULL); +int PlugInChartBaseExtended::GetNoCOVRTablenPoints(int iTable) { return 0; } - LUPname LUP_Name = PAPER_CHART; +float* PlugInChartBaseExtended::GetNoCOVRTableHead(int iTable) { return 0; } - // Force a re-evaluation of CS rules - ctx->CSrules = NULL; - ctx->bCS_Added = false; +void PlugInChartBaseExtended::ClearPLIBTextList() {} - // Clear the rendered text cache - if (ctx->bFText_Added) { - ctx->bFText_Added = false; - delete ctx->FText; - ctx->FText = NULL; - } +// ---------------------------------------------------------------------------- +// PlugInChartBaseExtendedPlus2 Implementation +// +// ---------------------------------------------------------------------------- - // Reset object selection box - ctx->bBBObj_valid = true; - ctx->BBObj.SetMin(pObj->lon_min, pObj->lat_min); - ctx->BBObj.SetMax(pObj->lon_max, pObj->lat_max); +PlugInChartBaseExtendedPlus2::PlugInChartBaseExtendedPlus2() {} - // This is where Simplified or Paper-Type point features are selected - switch (cobj.Primitive_type) { - case GEO_POINT: - case GEO_META: - case GEO_PRIM: +PlugInChartBaseExtendedPlus2::~PlugInChartBaseExtendedPlus2() {} - if (PAPER_CHART == ps52plib->m_nSymbolStyle) - LUP_Name = PAPER_CHART; - else - LUP_Name = SIMPLIFIED; +ListOfPI_S57Obj* +PlugInChartBaseExtendedPlus2::GetLightsObjRuleListVisibleAtLatLon( + float lat, float lon, PlugIn_ViewPort* VPoint) { + return NULL; +} - break; +// ---------------------------------------------------------------------------- +// PlugInChartBaseGLPlus2 Implementation +// +// ---------------------------------------------------------------------------- - case GEO_LINE: - LUP_Name = LINES; - break; +PlugInChartBaseGLPlus2::PlugInChartBaseGLPlus2() {} - case GEO_AREA: - if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle) - LUP_Name = PLAIN_BOUNDARIES; - else - LUP_Name = SYMBOLIZED_BOUNDARIES; +PlugInChartBaseGLPlus2::~PlugInChartBaseGLPlus2() {} - break; - } +ListOfPI_S57Obj* PlugInChartBaseGLPlus2::GetLightsObjRuleListVisibleAtLatLon( + float lat, float lon, PlugIn_ViewPort* VPoint) { + return NULL; +} - LUPrec* lup = ps52plib->S52_LUPLookup(LUP_Name, cobj.FeatureName, &cobj); - ctx->LUP = lup; +// ---------------------------------------------------------------------------- +// ChartPlugInWrapper Implementation +// This class is a wrapper/interface to PlugIn charts(PlugInChartBase) +// ---------------------------------------------------------------------------- - // Convert LUP to rules set - ps52plib->_LUP2rules(lup, &cobj); +ChartPlugInWrapper::ChartPlugInWrapper() {} - ctx->MPSRulesList = NULL; +ChartPlugInWrapper::ChartPlugInWrapper(const wxString& chart_class) { + m_ppo = ::wxCreateDynamicObject(chart_class); + m_ppicb = dynamic_cast(m_ppo); +} - return true; +ChartPlugInWrapper::~ChartPlugInWrapper() { + if (m_ppicb) delete m_ppicb; } -void PI_UpdateContext(PI_S57Obj* pObj) { - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - if (pContext) { - pContext->bBBObj_valid = true; - pContext->BBObj.SetMin(pObj->lon_min, pObj->lat_min); - pContext->BBObj.SetMax(pObj->lon_max, pObj->lat_max); - } +wxString ChartPlugInWrapper::GetFileSearchMask(void) { + if (m_ppicb) + return m_ppicb->GetFileSearchMask(); + else + return _T(""); } -void UpdatePIObjectPlibContext(PI_S57Obj* pObj, S57Obj* cobj, - ObjRazRules* rzRules) { - // Update the PLIB context after the render operation - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; +InitReturn ChartPlugInWrapper::Init(const wxString& name, + ChartInitFlag init_flags) { + if (m_ppicb) { + wxWindow* pa = wxWindow::FindFocus(); - pContext->CSrules = cobj->CSrules; - pContext->bCS_Added = cobj->bCS_Added; + InitReturn ret_val = (InitReturn)m_ppicb->Init(name, (int)init_flags); - pContext->FText = cobj->FText; - pContext->bFText_Added = cobj->bFText_Added; - pContext->rText = cobj->rText; + // Here we transcribe all the required wrapped member elements up into + // the chartbase object which is the parent of this class + if (ret_val == INIT_OK) { + m_FullPath = m_ppicb->GetFullPath(); + m_ChartType = (ChartTypeEnum)m_ppicb->GetChartType(); + m_ChartFamily = (ChartFamilyEnum)m_ppicb->GetChartFamily(); + m_projection = (OcpnProjType)m_ppicb->GetChartProjection(); + m_EdDate = m_ppicb->GetEditionDate(); + m_Name = m_ppicb->GetName(); + m_ID = m_ppicb->GetID(); + m_DepthUnits = m_ppicb->GetDepthUnits(); + m_SoundingsDatum = m_ppicb->GetSoundingsDatum(); + m_datum_str = m_ppicb->GetDatumString(); + m_SE = m_ppicb->GetSE(); + m_EdDate = m_ppicb->GetEditionDate(); + m_ExtraInfo = m_ppicb->GetExtraInfo(); + Chart_Error_Factor = m_ppicb->GetChartErrorFactor(); + m_depth_unit_id = (ChartDepthUnitType)m_ppicb->GetDepthUnitId(); + m_Chart_Skew = m_ppicb->GetChartSkew(); + m_Chart_Scale = m_ppicb->GetNativeScale(); - if (cobj->BBObj.GetValid()) { - // ugly as plugins still use BoundingBox - pContext->BBObj = - BoundingBox(cobj->BBObj.GetMinLon(), cobj->BBObj.GetMinLat(), - cobj->BBObj.GetMaxLon(), cobj->BBObj.GetMaxLat()); - pContext->bBBObj_valid = true; - } + // We estimate ppm_avg as needed by raster texture cache logic... + // This number works for average BSB charts, scanned with average + // resolution + m_ppm_avg = 10000. / m_ppicb->GetNativeScale(); // fallback value - // Render operation may have promoted the object's display category - // (e.g.WRECKS) - pObj->m_DisplayCat = (PI_DisCat)cobj->m_DisplayCat; + // Calcuculate a "better" ppm from the chart geo extent and raster size. + if ((fabs(m_Chart_Skew) < .01) && + (CHART_FAMILY_RASTER == m_ChartFamily)) { + Extent extent; + if (GetChartExtent(&extent)) { + double lon_range = extent.ELON - extent.WLON; + if ((lon_range > 0) && + (lon_range < 90.0)) // Be safe about IDL crossing and huge charts + m_ppm_avg = GetSize_X() / (lon_range * 1852 * 60); + } + } - if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) pObj->m_DPRI = cobj->m_DPRI; + m_overlayENC = false; + if (m_ChartFamily == (ChartFamilyEnum)PI_CHART_FAMILY_VECTOR) { + wxCharBuffer buf = m_FullPath.ToUTF8(); + m_overlayENC = s57chart::IsCellOverlayType(buf.data()); + } + + bReadyToRender = m_ppicb->IsReadyToRender(); + } else { + // Mark the chart as unable to render + m_ChartType = CHART_TYPE_UNKNOWN; + m_ChartFamily = CHART_FAMILY_UNKNOWN; + } - pContext->ChildRazRules = rzRules->child; - pContext->MPSRulesList = rzRules->mps; + // PlugIn may invoke wxExecute(), which steals the keyboard focus + // So take it back + auto pc = dynamic_cast(pa); + if (pc) pc->SetFocus(); - pObj->auxParm0 = cobj->auxParm0; + return ret_val; + } else + return INIT_FAIL_REMOVE; } -bool PI_GetObjectRenderBox(PI_S57Obj* pObj, double* lat_min, double* lat_max, - double* lon_min, double* lon_max) { - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - if (pContext) { - if (lat_min) *lat_min = pContext->BBObj.GetMinY(); - if (lat_max) *lat_max = pContext->BBObj.GetMaxY(); - if (lon_min) *lon_min = pContext->BBObj.GetMinX(); - if (lon_max) *lon_max = pContext->BBObj.GetMaxX(); - return pContext->bBBObj_valid; - } else - return false; +// Accessors +int ChartPlugInWrapper::GetCOVREntries() { + if (m_ppicb) + return m_ppicb->GetCOVREntries(); + else + return 0; } -PI_LUPname PI_GetObjectLUPName(PI_S57Obj* pObj) { - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - if (pContext) { - LUPrec* lup = pContext->LUP; - if (lup) return (PI_LUPname)(lup->TNAM); - } - return (PI_LUPname)(-1); +int ChartPlugInWrapper::GetCOVRTablePoints(int iTable) { + if (m_ppicb) + return m_ppicb->GetCOVRTablePoints(iTable); + else + return 0; } -PI_DisPrio PI_GetObjectDisplayPriority(PI_S57Obj* pObj) { - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - if (pContext) { - LUPrec* lup = pContext->LUP; - if (lup) return (PI_DisPrio)(lup->DPRI); - } +int ChartPlugInWrapper::GetCOVRTablenPoints(int iTable) { + if (m_ppicb) + return m_ppicb->GetCOVRTablenPoints(iTable); + else + return 0; +} - return (PI_DisPrio)(-1); +float* ChartPlugInWrapper::GetCOVRTableHead(int iTable) { + if (m_ppicb) + return m_ppicb->GetCOVRTableHead(iTable); + else + return 0; } -PI_DisCat PI_GetObjectDisplayCategory(PI_S57Obj* pObj) { - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - if (pContext) { - LUPrec* lup = pContext->LUP; - if (lup) return (PI_DisCat)(lup->DISC); +// TODO +// PlugIn chart types do not properly support NoCovr Regions +// Proper fix is to update PlugIn Chart Type API +// Derive an extended PlugIn chart class from existing class, +// and use some kind of RTTI to figure out which class to call. +int ChartPlugInWrapper::GetNoCOVREntries() { + if (m_ppicb) { + PlugInChartBaseGL* ppicbgl = dynamic_cast(m_ppicb); + if (ppicbgl) { + return ppicbgl->GetNoCOVREntries(); + } } - return (PI_DisCat)(-1); -} -double PI_GetPLIBMarinerSafetyContour() { - return S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR); + return 0; } -void PI_PLIBSetLineFeaturePriority(PI_S57Obj* pObj, int prio) { - // Create and populate a compatible s57 Object - S57Obj cobj; - chart_context ctx; - CreateCompatibleS57Object(pObj, &cobj, &ctx); - - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - - // Create and populate a minimally compatible object container - ObjRazRules rzRules; - rzRules.obj = &cobj; - rzRules.LUP = pContext->LUP; - rzRules.sm_transform_parms = 0; - rzRules.child = NULL; - rzRules.next = NULL; - rzRules.mps = pContext->MPSRulesList; - - if (pContext->LUP) { - ps52plib->SetLineFeaturePriority(&rzRules, prio); - - // Update the PLIB context after the render operation - UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); +int ChartPlugInWrapper::GetNoCOVRTablePoints(int iTable) { + if (m_ppicb) { + PlugInChartBaseGL* ppicbgl = dynamic_cast(m_ppicb); + if (ppicbgl) { + return ppicbgl->GetNoCOVRTablePoints(iTable); + } } + return 0; } -void PI_PLIBPrepareForNewRender(void) { - if (ps52plib) { - ps52plib->PrepareForRender(); - ps52plib->ClearTextList(); - - if (gs_plib_flags & PLIB_CAPS_LINE_BUFFER) - ps52plib->EnableGLLS(true); // Newer PlugIns can use GLLS - else - ps52plib->EnableGLLS(false); // Older cannot +int ChartPlugInWrapper::GetNoCOVRTablenPoints(int iTable) { + if (m_ppicb) { + PlugInChartBaseGL* ppicbgl = dynamic_cast(m_ppicb); + if (ppicbgl) { + return ppicbgl->GetNoCOVRTablenPoints(iTable); + } } + return 0; } -void PI_PLIBSetRenderCaps(unsigned int flags) { gs_plib_flags = flags; } - -void PI_PLIBFreeContext(void* pContext) { - S52PLIB_Context* pctx = (S52PLIB_Context*)pContext; +float* ChartPlugInWrapper::GetNoCOVRTableHead(int iTable) { + if (m_ppicb) { + PlugInChartBaseGL* ppicbgl = dynamic_cast(m_ppicb); + if (ppicbgl) { + return ppicbgl->GetNoCOVRTableHead(iTable); + } + } + return 0; +} - if (pctx->ChildRazRules) { - ObjRazRules* ctop = pctx->ChildRazRules; - while (ctop) { - delete ctop->obj; +bool ChartPlugInWrapper::GetChartExtent(Extent* pext) { + if (m_ppicb) { + ExtentPI xpi; + if (m_ppicb->GetChartExtent(&xpi)) { + pext->NLAT = xpi.NLAT; + pext->SLAT = xpi.SLAT; + pext->ELON = xpi.ELON; + pext->WLON = xpi.WLON; - if (ps52plib) ps52plib->DestroyLUP(ctop->LUP); + return true; + } else + return false; + } else + return false; +} - ObjRazRules* cnxx = ctop->next; - delete ctop; - ctop = cnxx; +ThumbData* ChartPlugInWrapper::GetThumbData(int tnx, int tny, float lat, + float lon) { + if (m_ppicb) { + // Create the bitmap if needed, doing a deep copy from the Bitmap owned + // by the PlugIn Chart + if (!pThumbData->pDIBThumb) { + wxBitmap* pBMPOwnedByChart = + m_ppicb->GetThumbnail(tnx, tny, m_global_color_scheme); + if (pBMPOwnedByChart) { + wxImage img = pBMPOwnedByChart->ConvertToImage(); + pThumbData->pDIBThumb = new wxBitmap(img); + } else + pThumbData->pDIBThumb = NULL; } - } - if (pctx->MPSRulesList) { - if (ps52plib && pctx->MPSRulesList->cs_rules) { - for (unsigned int i = 0; i < pctx->MPSRulesList->cs_rules->GetCount(); - i++) { - Rules* top = pctx->MPSRulesList->cs_rules->Item(i); - ps52plib->DestroyRulesChain(top); - } - delete pctx->MPSRulesList->cs_rules; - } - free(pctx->MPSRulesList); - } + pThumbData->Thumb_Size_X = tnx; + pThumbData->Thumb_Size_Y = tny; - delete pctx->FText; + /* + // Plot the supplied Lat/Lon on the thumbnail + int divx = m_ppicb->Size_X / tnx; + int divy = m_ppicb->Size_Y / tny; - delete pctx; -} + int div_factor = __min(divx, divy); -int PI_PLIBRenderObjectToDC(wxDC* pdc, PI_S57Obj* pObj, PlugIn_ViewPort* vp) { - // Create and populate a compatible s57 Object - S57Obj cobj; - chart_context ctx; - CreateCompatibleS57Object(pObj, &cobj, &ctx); + int pixx, pixy; - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - // Set up object SM rendering constants - sm_parms transform; - toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon, - &transform.easting_vp_center, &transform.northing_vp_center); + // Using a temporary synthetic ViewPort and source rectangle, + // calculate the ships position on the thumbnail + ViewPort tvp; + tvp.pix_width = tnx; + tvp.pix_height = tny; + tvp.view_scale_ppm = GetPPM() / div_factor; + wxRect trex = Rsrc; + Rsrc.x = 0; + Rsrc.y = 0; + latlong_to_pix_vp(lat, lon, pixx, pixy, tvp); + Rsrc = trex; - // Create and populate a minimally compatible object container - ObjRazRules rzRules; - rzRules.obj = &cobj; - rzRules.LUP = pContext->LUP; - rzRules.sm_transform_parms = &transform; - rzRules.child = pContext->ChildRazRules; - rzRules.next = NULL; - rzRules.mps = pContext->MPSRulesList; + pThumbData->ShipX = pixx;// / div_factor; + pThumbData->ShipY = pixy;// / div_factor; + */ + pThumbData->ShipX = 0; + pThumbData->ShipY = 0; - if (pContext->LUP) { - ViewPort cvp = CreateCompatibleViewport(*vp); + return pThumbData; + } else + return NULL; +} - // Do the render - // FIXME (plib) - ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, - cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale, - cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale, - GetOCPNCanvasWindow()->GetContentScaleFactor()); - ps52plib->PrepareForRender(); +ThumbData* ChartPlugInWrapper::GetThumbData() { return pThumbData; } - ps52plib->RenderObjectToDC(pdc, &rzRules); +bool ChartPlugInWrapper::UpdateThumbData(double lat, double lon) { + return true; +} - // Update the PLIB context after the render operation - UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); - } +double ChartPlugInWrapper::GetNormalScaleMin(double canvas_scale_factor, + bool b_allow_overzoom) { + if (m_ppicb) + return m_ppicb->GetNormalScaleMin(canvas_scale_factor, b_allow_overzoom); + else + return 1.0; +} - return 1; +double ChartPlugInWrapper::GetNormalScaleMax(double canvas_scale_factor, + int canvas_width) { + if (m_ppicb) + return m_ppicb->GetNormalScaleMax(canvas_scale_factor, canvas_width); + else + return 2.0e7; } -int PI_PLIBRenderAreaToDC(wxDC* pdc, PI_S57Obj* pObj, PlugIn_ViewPort* vp, - wxRect rect, unsigned char* pixbuf) { - // Create a compatible render canvas - render_canvas_parms pb_spec; +/* RectRegion: + * This is the Screen region desired to be updated. Will + * be either 1 rectangle(full screen) or two rectangles (panning with FBO + * accelerated pan logic) + * + * Region: + * This is the LLRegion describing the quilt active region + * for this chart. + * + * So, Actual rendering area onscreen should be clipped to the + * intersection of the two regions. + */ - pb_spec.depth = BPP; - pb_spec.pb_pitch = ((rect.width * pb_spec.depth / 8)); - pb_spec.lclip = rect.x; - pb_spec.rclip = rect.x + rect.width - 1; - pb_spec.pix_buff = pixbuf; // the passed buffer - pb_spec.width = rect.width; - pb_spec.height = rect.height; - pb_spec.x = rect.x; - pb_spec.y = rect.y; -#ifdef ocpnUSE_ocpnBitmap - pb_spec.b_revrgb = true; -#else - pb_spec.b_revrgb = false; +// Render helpers +void RenderRotateToViewPort(const ViewPort& VPoint) { +#ifndef USE_ANDROID_GLES2 + float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0; + glTranslatef(xt, yt, 0); + glRotatef(VPoint.rotation * 180. / PI, 0, 0, 1); + glTranslatef(-xt, -yt, 0); #endif +} - pb_spec.b_revrgb = false; +void UndoRenderRotateToViewPort(const ViewPort& VPoint) { +#ifndef USE_ANDROID_GLES2 + float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0; + glTranslatef(xt, yt, 0); + glRotatef(-VPoint.rotation * 180. / PI, 0, 0, 1); + glTranslatef(-xt, -yt, 0); +#endif +} - // Create and populate a compatible s57 Object - S57Obj cobj; - chart_context ctx; - CreateCompatibleS57Object(pObj, &cobj, &ctx); +bool ChartPlugInWrapper::RenderRegionViewOnGL(const wxGLContext& glc, + const ViewPort& VPoint, + const OCPNRegion& RectRegion, + const LLRegion& Region) { +#ifdef ocpnUSE_GL + if (m_ppicb) { + ViewPort vp = VPoint; // non-const copy - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + gs_plib_flags = 0; // reset the CAPs flag + PlugInChartBaseGL* ppicb_gl = dynamic_cast(m_ppicb); + PlugInChartBaseExtended* ppicb_x = + dynamic_cast(m_ppicb); + if (!Region.Empty() && (ppicb_gl || ppicb_x)) { + wxRegion* r = RectRegion.GetNew_wxRegion(); + for (OCPNRegionIterator upd(RectRegion); upd.HaveRects(); + upd.NextRect()) { + LLRegion chart_region = vp.GetLLRegion(upd.GetRect()); + chart_region.Intersect(Region); - // Set up object SM rendering constants - sm_parms transform; - toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon, - &transform.easting_vp_center, &transform.northing_vp_center); + if (!chart_region.Empty()) { + ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region); - // Create and populate a minimally compatible object container - ObjRazRules rzRules; - rzRules.obj = &cobj; - rzRules.LUP = pContext->LUP; - rzRules.sm_transform_parms = &transform; - rzRules.child = pContext->ChildRazRules; - rzRules.next = NULL; - rzRules.mps = pContext->MPSRulesList; + glChartCanvas::SetClipRect(cvp, upd.GetRect(), false); - ViewPort cvp = CreateCompatibleViewport(*vp); + // ps52plib->m_last_clip_rect = upd.GetRect(); - // If the PlugIn does not support it nativiely, build a fully described - // Geomoetry - if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) { - if (!pObj->geoPtMulti) { // do this only once - PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo; +#ifndef USE_ANDROID_GLES2 +// glPushMatrix(); // Adjust for rotation +#endif + RenderRotateToViewPort(VPoint); - if (!tess) return 1; // bail on empty data + PlugIn_ViewPort pivp = CreatePlugInViewport(cvp); + if (ppicb_x) + ppicb_x->RenderRegionViewOnGL(glc, pivp, *r, + glChartCanvas::s_b_useStencil); + else if (ppicb_gl) + ppicb_gl->RenderRegionViewOnGL(glc, pivp, *r, + glChartCanvas::s_b_useStencil); + UndoRenderRotateToViewPort(VPoint); - PolyTriGroup* ptg = new PolyTriGroup; - ptg->tri_prim_head = - tess->Get_PolyTriGroup_head()->tri_prim_head; // tph; - ptg->bsingle_alloc = false; - ptg->data_type = DATA_TYPE_DOUBLE; - tess->Set_PolyTriGroup_head(ptg); +#ifndef USE_ANDROID_GLES2 +// glPopMatrix(); +#endif + glChartCanvas::DisableClipRegion(); - double* pd = (double*)malloc(sizeof(double)); - pObj->geoPtMulti = pd; // Hack hack + } //! empty + } // for + delete r; } - } - - if (pContext->LUP) { - // Do the render - // FIXME (plib) - ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, - cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale, - cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale, - GetOCPNCanvasWindow()->GetContentScaleFactor()); - ps52plib->PrepareForRender(); - - ps52plib->RenderAreaToDC(pdc, &rzRules, &pb_spec); - - // Update the PLIB context after the render operation - UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); - } - - return 1; + } else + return false; +#endif + return true; } -int PI_PLIBRenderAreaToGL(const wxGLContext& glcc, PI_S57Obj* pObj, - PlugIn_ViewPort* vp, wxRect& render_rect) { +// int indexrr; + +bool ChartPlugInWrapper::RenderRegionViewOnGLNoText( + const wxGLContext& glc, const ViewPort& VPoint, + const OCPNRegion& RectRegion, const LLRegion& Region) { #ifdef ocpnUSE_GL - // Create and populate a compatible s57 Object - S57Obj cobj; - chart_context ctx; - CreateCompatibleS57Object(pObj, &cobj, &ctx); + if (m_ppicb) { + // printf("\nCPIW::RRVOGLNT %d %d \n", indexrr++, m_Chart_Scale); - // chart_context *pct = (chart_context *)pObj->m_chart_context; + gs_plib_flags = 0; // reset the CAPs flag + PlugInChartBaseExtended* ppicb_x = + dynamic_cast(m_ppicb); + PlugInChartBaseGL* ppicb = dynamic_cast(m_ppicb); + if (!Region.Empty() && ppicb_x) { + // Start with a clean slate + glChartCanvas::SetClipRect(VPoint, VPoint.rv_rect, false); + glChartCanvas::DisableClipRegion(); - // If the PlugIn does not support it nativiely, build a fully described - // Geomoetry + // Apply rotation to this chart + RenderRotateToViewPort(VPoint); - if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) { - if (!pObj->geoPtMulti) { // only do this once - PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo; + PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); + wxRegion* r = RectRegion.GetNew_wxRegion(); - if (!tess) return 1; // bail on empty data + ppicb_x->RenderRegionViewOnGLNoText(glc, pivp, *r, + glChartCanvas::s_b_useStencil); - PolyTriGroup* ptg = - new PolyTriGroup; // this will leak a little, but is POD - ptg->tri_prim_head = tess->Get_PolyTriGroup_head()->tri_prim_head; - ptg->bsingle_alloc = false; - ptg->data_type = DATA_TYPE_DOUBLE; - tess->Set_PolyTriGroup_head(ptg); + // Undo rotation + UndoRenderRotateToViewPort(VPoint); - // Mark this object using geoPtMulti - // The malloc will get free'ed when the object is deleted. - double* pd = (double*)malloc(sizeof(double)); - pObj->geoPtMulti = pd; // Hack hack + delete r; } - cobj.auxParm0 = -6; // signal that this object render cannot use VBO - cobj.auxParm1 = -1; // signal that this object render cannot have single - // buffer conversion done - } else { // it is a newer PLugIn, so can do single buffer conversion and VBOs - if (pObj->auxParm0 < 1) - cobj.auxParm0 = -7; // signal that this object render can use a - // persistent VBO for area triangle vertices - } - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + else if (!Region.Empty() && + ppicb) // Legacy Vector GL Plugin chart (e.g.S63) + { + ViewPort vp = VPoint; // non-const copy + wxRegion* r = RectRegion.GetNew_wxRegion(); + for (OCPNRegionIterator upd(RectRegion); upd.HaveRects(); + upd.NextRect()) { + LLRegion chart_region = vp.GetLLRegion(upd.GetRect()); + chart_region.Intersect(Region); - // Set up object SM rendering constants - sm_parms transform; - toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon, - &transform.easting_vp_center, &transform.northing_vp_center); + if (!chart_region.Empty()) { + ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region); - // Create and populate a minimally compatible object container - ObjRazRules rzRules; - rzRules.obj = &cobj; - rzRules.LUP = pContext->LUP; - rzRules.sm_transform_parms = &transform; - rzRules.child = pContext->ChildRazRules; - rzRules.next = NULL; - rzRules.mps = pContext->MPSRulesList; + glChartCanvas::SetClipRect(cvp, upd.GetRect(), false); - if (pContext->LUP) { - ViewPort cvp = CreateCompatibleViewport(*vp); + RenderRotateToViewPort(VPoint); - // Do the render - // FIXME (plib) - ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, - cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale, - cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale, - GetOCPNCanvasWindow()->GetContentScaleFactor()); - ps52plib->PrepareForRender(); + PlugIn_ViewPort pivp = CreatePlugInViewport(cvp); + ppicb->RenderRegionViewOnGL(glc, pivp, *r, + glChartCanvas::s_b_useStencil); - ps52plib->RenderAreaToGL(glcc, &rzRules); + // Undo rotation + UndoRenderRotateToViewPort(VPoint); - // Update the PLIB context after the render operation - UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); - } + glChartCanvas::DisableClipRegion(); + + } //! empty + } // for + delete r; + } + } else + return false; #endif - return 1; + return true; } -int PI_PLIBRenderObjectToGL(const wxGLContext& glcc, PI_S57Obj* pObj, - PlugIn_ViewPort* vp, wxRect& render_rect) { - // Create and populate a compatible s57 Object - S57Obj cobj; - chart_context ctx; - CreateCompatibleS57Object(pObj, &cobj, &ctx); +bool ChartPlugInWrapper::RenderRegionViewOnGLTextOnly( + const wxGLContext& glc, const ViewPort& VPoint, const OCPNRegion& Region) { +#ifdef ocpnUSE_GL + if (m_ppicb) { + gs_plib_flags = 0; // reset the CAPs flag + PlugInChartBaseExtended* ppicb_x = + dynamic_cast(m_ppicb); + if (!Region.Empty() && ppicb_x) { + wxRegion* r = Region.GetNew_wxRegion(); + for (OCPNRegionIterator upd(Region); upd.HaveRects(); upd.NextRect()) { +#ifndef USE_ANDROID_GLES2 +// glPushMatrix(); // Adjust for rotation +#endif + RenderRotateToViewPort(VPoint); - S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); + ppicb_x->RenderRegionViewOnGLTextOnly(glc, pivp, *r, + glChartCanvas::s_b_useStencil); + UndoRenderRotateToViewPort(VPoint); - // Set up object SM rendering constants - sm_parms transform; - toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon, - &transform.easting_vp_center, &transform.northing_vp_center); +#ifndef USE_ANDROID_GLES2 +// glPopMatrix(); +#endif - // Create and populate a minimally compatible object container - ObjRazRules rzRules; - rzRules.obj = &cobj; - rzRules.LUP = pContext->LUP; - rzRules.sm_transform_parms = &transform; - rzRules.child = pContext->ChildRazRules; - rzRules.next = NULL; - rzRules.mps = pContext->MPSRulesList; + } // for + delete r; + } + } else + return false; +#endif + return true; +} - if (pContext->LUP) { - ViewPort cvp = CreateCompatibleViewport(*vp); +bool ChartPlugInWrapper::RenderRegionViewOnDC(wxMemoryDC& dc, + const ViewPort& VPoint, + const OCPNRegion& Region) { + if (m_ppicb) { + gs_plib_flags = 0; // reset the CAPs flag + PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); + if (Region.IsOk()) { + wxRegion* r = Region.GetNew_wxRegion(); + if (!m_overlayENC) + dc.SelectObject(m_ppicb->RenderRegionView(pivp, *r)); + else { + wxBitmap& obmp = m_ppicb->RenderRegionView(pivp, *r); - // Do the render - // FIXME (plib) - ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, - cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale, - cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale, - GetOCPNCanvasWindow()->GetContentScaleFactor()); - ps52plib->PrepareForRender(); + // Create a mask to remove the NODTA areas from overlay cells. + wxColour nodat = GetGlobalColor(_T ( "NODTA" )); + wxColour nodat_sub = nodat; - ps52plib->RenderObjectToGL(glcc, &rzRules); +#ifdef ocpnUSE_ocpnBitmap + nodat_sub = wxColour(nodat.Blue(), nodat.Green(), nodat.Red()); +#endif + m_pMask = new wxMask(obmp, nodat_sub); + obmp.SetMask(m_pMask); - // Update the PLIB context after the render operation - UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); - } + dc.SelectObject(obmp); + } - return 1; + delete r; + return true; + } else + return false; + } else + return false; } -/* API 1.13 */ - -/* API 1.13 adds some more common functions to avoid unnecessary code - * duplication */ +bool ChartPlugInWrapper::RenderRegionViewOnDCNoText(wxMemoryDC& dc, + const ViewPort& VPoint, + const OCPNRegion& Region) { + if (m_ppicb) { + gs_plib_flags = 0; // reset the CAPs flag + PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); -double fromDMM_Plugin(wxString sdms) { return fromDMM(sdms); } + PlugInChartBaseExtended* pCBx = + dynamic_cast(m_ppicb); + PlugInChartBase* ppicb = dynamic_cast(m_ppicb); -void SetCanvasRotation(double rotation) { - gFrame->GetPrimaryCanvas()->DoRotateCanvas(rotation); -} + if (Region.IsOk() && (pCBx || ppicb)) { + wxRegion* r = Region.GetNew_wxRegion(); -double GetCanvasTilt() { return gFrame->GetPrimaryCanvas()->GetVPTilt(); } + if (pCBx) + dc.SelectObject(pCBx->RenderRegionViewOnDCNoText(pivp, *r)); + else if (ppicb) + dc.SelectObject(ppicb->RenderRegionView(pivp, *r)); -void SetCanvasTilt(double tilt) { - gFrame->GetPrimaryCanvas()->DoTiltCanvas(tilt); + delete r; + return true; + } else + return false; + } else + return false; } -void SetCanvasProjection(int projection) { - gFrame->GetPrimaryCanvas()->SetVPProjection(projection); -} +bool ChartPlugInWrapper::RenderRegionViewOnDCTextOnly( + wxMemoryDC& dc, const ViewPort& VPoint, const OCPNRegion& Region) { + if (m_ppicb) { + bool ret_val = false; + gs_plib_flags = 0; // reset the CAPs flag + PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); + if (Region.IsOk()) { + wxRegion* r = Region.GetNew_wxRegion(); -OcpnSound* g_PluginSound = SoundFactory(); -static void onPlugInPlaySoundExFinished(void* ptr) {} + PlugInChartBaseExtended* pCBx = + dynamic_cast(m_ppicb); + if (pCBx) ret_val = pCBx->RenderRegionViewOnDCTextOnly(dc, pivp, *r); -// Start playing a sound to a given device and return status to plugin -bool PlugInPlaySoundEx(wxString& sound_file, int deviceIndex) { - bool ok = g_PluginSound->Load(sound_file, deviceIndex); - if (!ok) { - wxLogWarning("Cannot load sound file: %s", sound_file); + delete r; + return ret_val; + } else + return false; + } else return false; - } - auto cmd_sound = dynamic_cast(g_PluginSound); - if (cmd_sound) cmd_sound->SetCmd(g_CmdSoundString.mb_str(wxConvUTF8)); +} - g_PluginSound->SetFinishedCallback(onPlugInPlaySoundExFinished, NULL); - ok = g_PluginSound->Play(); - if (!ok) { - wxLogWarning("Cannot play sound file: %s", sound_file); +void ChartPlugInWrapper::ClearPLIBTextList() { + if (m_ppicb) { + PlugInChartBaseExtended* pCBx = + dynamic_cast(m_ppicb); + if (pCBx) pCBx->ClearPLIBTextList(); } - return ok; } -bool CheckEdgePan_PlugIn(int x, int y, bool dragging, int margin, int delta) { - return gFrame->GetPrimaryCanvas()->CheckEdgePan(x, y, dragging, margin, - delta); +bool ChartPlugInWrapper::AdjustVP(ViewPort& vp_last, ViewPort& vp_proposed) { + if (m_ppicb) { + PlugIn_ViewPort pivp_last = CreatePlugInViewport(vp_last); + PlugIn_ViewPort pivp_proposed = CreatePlugInViewport(vp_proposed); + return m_ppicb->AdjustVP(pivp_last, pivp_proposed); + } else + return false; } -wxBitmap GetIcon_PlugIn(const wxString& name) { - ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle(); - return style->GetIcon(name); -} +void ChartPlugInWrapper::GetValidCanvasRegion(const ViewPort& VPoint, + OCPNRegion* pValidRegion) { + if (m_ppicb) { + PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); + // currently convert using wxRegion, + // this should be changed as wxRegion is proven unstable/buggy on various + // platforms + wxRegion region; + m_ppicb->GetValidCanvasRegion(pivp, ®ion); + *pValidRegion = OCPNRegion(region); + } -void SetCursor_PlugIn(wxCursor* pCursor) { - gFrame->GetPrimaryCanvas()->pPlugIn_Cursor = pCursor; + return; } -void AddChartDirectory(wxString& path) { - if (g_options) { - g_options->AddChartDir(path); +void ChartPlugInWrapper::SetColorScheme(ColorScheme cs, bool bApplyImmediate) { + if (m_ppicb) { + m_ppicb->SetColorScheme(cs, bApplyImmediate); } + m_global_color_scheme = cs; + // Force a new thumbnail + if (pThumbData) pThumbData->pDIBThumb = NULL; } -void ForceChartDBUpdate() { - if (g_options) { - g_options->pScanCheckBox->SetValue(true); - g_options->pUpdateCheckBox->SetValue(true); - } +double ChartPlugInWrapper::GetNearestPreferredScalePPM( + double target_scale_ppm) { + if (m_ppicb) + return m_ppicb->GetNearestPreferredScalePPM(target_scale_ppm); + else + return 1.0; } -void ForceChartDBRebuild() { - if (g_options) { - g_options->pUpdateCheckBox->SetValue(true); +void ChartPlugInWrapper::ComputeSourceRectangle(const ViewPort& VPoint, + wxRect* pSourceRect) { + if (m_ppicb) { + PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint); + m_ppicb->ComputeSourceRectangle(pivp, pSourceRect); } } -wxDialog* GetActiveOptionsDialog() { return g_options; } - -int PlatformDirSelectorDialog(wxWindow* parent, wxString* file_spec, - wxString Title, wxString initDir) { - return g_Platform->DoDirSelectorDialog(parent, file_spec, Title, initDir); -} - -int PlatformFileSelectorDialog(wxWindow* parent, wxString* file_spec, - wxString Title, wxString initDir, - wxString suggestedName, wxString wildcard) { - return g_Platform->DoFileSelectorDialog(parent, file_spec, Title, initDir, - suggestedName, wildcard); +double ChartPlugInWrapper::GetRasterScaleFactor(const ViewPort& vp) { + if (m_ppicb) { + return (wxRound(100000 * GetPPM() / vp.view_scale_ppm)) / 100000.; + } else + return 1.0; } -// http File Download Support +bool ChartPlugInWrapper::GetChartBits(wxRect& source, unsigned char* pPix, + int sub_samp) { + wxCriticalSectionLocker locker(m_critSect); -// OCPN_downloadEvent Implementation + if (m_ppicb) -OCPN_downloadEvent::OCPN_downloadEvent(wxEventType commandType, int id) - : wxEvent(id, commandType) { - m_stat = OCPN_DL_UNKNOWN; - m_condition = OCPN_DL_EVENT_TYPE_UNKNOWN; - m_b_complete = false; - m_sofarBytes = 0; + return m_ppicb->GetChartBits(source, pPix, sub_samp); + else + return false; } -OCPN_downloadEvent::~OCPN_downloadEvent() {} - -wxEvent* OCPN_downloadEvent::Clone() const { - OCPN_downloadEvent* newevent = new OCPN_downloadEvent(*this); - newevent->m_stat = this->m_stat; - newevent->m_condition = this->m_condition; +int ChartPlugInWrapper::GetSize_X() { + if (m_ppicb) + return m_ppicb->GetSize_X(); + else + return 1; +} - newevent->m_totalBytes = this->m_totalBytes; - newevent->m_sofarBytes = this->m_sofarBytes; - newevent->m_b_complete = this->m_b_complete; +int ChartPlugInWrapper::GetSize_Y() { + if (m_ppicb) + return m_ppicb->GetSize_Y(); + else + return 1; +} - return newevent; +void ChartPlugInWrapper::latlong_to_chartpix(double lat, double lon, + double& pixx, double& pixy) { + if (m_ppicb) m_ppicb->latlong_to_chartpix(lat, lon, pixx, pixy); } -// const wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType(); -DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType(); +void ChartPlugInWrapper::chartpix_to_latlong(double pixx, double pixy, + double* plat, double* plon) { + if (m_ppicb) m_ppicb->chartpix_to_latlong(pixx, pixy, plat, plon); +} -_OCPN_DLStatus g_download_status; -_OCPN_DLCondition g_download_condition; +//---------------------------------------------------------------------------------------------------------- +// The PlugIn CallBack API Implementation +// The definitions of this API are found in ocpn_plugin.h +//---------------------------------------------------------------------------------------------------------- -#define DL_EVENT_TIMER 4388 +/* API 1.11 */ -class PI_DLEvtHandler : public wxEvtHandler { -public: - PI_DLEvtHandler(); - ~PI_DLEvtHandler(); +/* API 1.11 adds some more common functions to avoid unnecessary code + * duplication */ - void onDLEvent(OCPN_downloadEvent& event); - void setBackgroundMode(long ID, wxEvtHandler* handler); - void clearBackgroundMode(); - void onTimerEvent(wxTimerEvent& event); +wxString toSDMM_PlugIn(int NEflag, double a, bool hi_precision) { + return toSDMM(NEflag, a, hi_precision); +} - long m_id; - wxTimer m_eventTimer; - wxEvtHandler* m_download_evHandler; +wxColour GetBaseGlobalColor(wxString colorName) { + return GetGlobalColor(colorName); +} - long m_sofarBytes; - long m_totalBytes; -}; +int OCPNMessageBox_PlugIn(wxWindow* parent, const wxString& message, + const wxString& caption, int style, int x, int y) { + return OCPNMessageBox(parent, message, caption, style, 100, x, y); +} -PI_DLEvtHandler::PI_DLEvtHandler() { - g_download_status = OCPN_DL_UNKNOWN; - g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN; +wxString GetOCPN_ExePath(void) { return g_Platform->GetExePath(); } - m_download_evHandler = NULL; - m_id = -1; - m_sofarBytes = 0; - m_totalBytes = 0; +wxString* GetpPlugInLocation() { return g_Platform->GetPluginDirPtr(); } + +wxString GetWritableDocumentsDir(void) { + return g_Platform->GetWritableDocumentsDir(); } -PI_DLEvtHandler::~PI_DLEvtHandler() { - m_eventTimer.Stop(); - Disconnect( - wxEVT_TIMER, - (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent); +wxString GetPlugInPath(opencpn_plugin* pplugin) { + wxString ret_val; + auto loader = PluginLoader::getInstance(); + for (unsigned int i = 0; i < loader->GetPlugInArray()->GetCount(); i++) { + PlugInContainer* pic = loader->GetPlugInArray()->Item(i); + if (pic->m_pplugin == pplugin) { + ret_val = pic->m_plugin_file; + break; + } + } + return ret_val; } -void PI_DLEvtHandler::onDLEvent(OCPN_downloadEvent& event) { - // qDebug() << "Got Event " << (int)event.getDLEventStatus() << - // (int)event.getDLEventCondition(); +// API 1.11 Access to Vector PlugIn charts - g_download_status = event.getDLEventStatus(); - g_download_condition = event.getDLEventCondition(); +ListOfPI_S57Obj* PlugInManager::GetPlugInObjRuleListAtLatLon( + ChartPlugInWrapper* target, float zlat, float zlon, float SelectRadius, + const ViewPort& vp) { + ListOfPI_S57Obj* list = NULL; + if (target) { + PlugInChartBaseGL* picbgl = + dynamic_cast(target->GetPlugInChart()); + if (picbgl) { + PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp); + list = picbgl->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp); - // This is an END event, happening at the end of BACKGROUND file download - if (m_download_evHandler && - (OCPN_DL_EVENT_TYPE_END == event.getDLEventCondition())) { - OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0); - ev.setComplete(true); - ev.setTransferred(m_sofarBytes); - ev.setTotal(m_totalBytes); + return list; + } + PlugInChartBaseExtended* picbx = + dynamic_cast(target->GetPlugInChart()); + if (picbx) { + PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp); + list = picbx->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp); - ev.setDLEventStatus(event.getDLEventStatus()); - ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); + return list; + } else + return list; + } else + return list; +} - m_download_evHandler->AddPendingEvent(ev); - m_eventTimer.Stop(); -#ifdef __ANDROID__ - finishAndroidFileDownload(); -#endif +wxString PlugInManager::CreateObjDescriptions(ChartPlugInWrapper* target, + ListOfPI_S57Obj* rule_list) { + wxString ret_str; + if (target) { + PlugInChartBaseGL* picbgl = + dynamic_cast(target->GetPlugInChart()); + if (picbgl) { + ret_str = picbgl->CreateObjDescriptions(rule_list); + } else { + PlugInChartBaseExtended* picbx = + dynamic_cast(target->GetPlugInChart()); + if (picbx) { + ret_str = picbx->CreateObjDescriptions(rule_list); + } + } } + return ret_str; +} - event.Skip(); +// API 1.11 Access to S52 PLIB +wxString PI_GetPLIBColorScheme() { + return _T(""); // ps52plib->GetPLIBColorScheme() } -void PI_DLEvtHandler::setBackgroundMode(long ID, wxEvtHandler* handler) { - m_id = ID; - m_download_evHandler = handler; +int PI_GetPLIBDepthUnitInt() { + if (ps52plib) + return ps52plib->m_nDepthUnitDisplay; + else + return 0; +} - m_eventTimer.SetOwner(this, DL_EVENT_TIMER); +int PI_GetPLIBSymbolStyle() { + if (ps52plib) + return ps52plib->m_nSymbolStyle; + else + return 0; +} - Connect( - wxEVT_TIMER, - (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent); - m_eventTimer.Start(1000, wxTIMER_CONTINUOUS); +int PI_GetPLIBBoundaryStyle() { + if (ps52plib) + return ps52plib->m_nBoundaryStyle; + else + return 0; } -void PI_DLEvtHandler::clearBackgroundMode() { - m_download_evHandler = NULL; - m_eventTimer.Stop(); +bool PI_PLIBObjectRenderCheck(PI_S57Obj* pObj, PlugIn_ViewPort* vp) { + if (ps52plib) { + // Create and populate a compatible s57 Object + S57Obj cobj; + chart_context ctx; + CreateCompatibleS57Object(pObj, &cobj, &ctx); + + ViewPort cvp = CreateCompatibleViewport(*vp); + + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + + // Create and populate a minimally compatible object container + ObjRazRules rzRules; + rzRules.obj = &cobj; + rzRules.LUP = pContext->LUP; + rzRules.sm_transform_parms = 0; + rzRules.child = NULL; + rzRules.next = NULL; + + if (pContext->LUP) { + ps52plib->SetVPointCompat( + cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, cvp.rotation, + cvp.clat, cvp.clon, cvp.chart_scale, cvp.rv_rect, cvp.GetBBox(), + cvp.ref_scale, GetOCPNCanvasWindow()->GetContentScaleFactor()); + ps52plib->PrepareForRender(); + + return ps52plib->ObjectRenderCheck(&rzRules); + } else + return false; + } else + return false; } -void PI_DLEvtHandler::onTimerEvent(wxTimerEvent& event) { -#ifdef __ANDROID__ - // Query the download status, and post to the original requestor - // This method only happens on Background file downloads +int PI_GetPLIBStateHash() { + if (ps52plib) + return ps52plib->GetStateHash(); + else + return 0; +} - wxString sstat; - int stat = queryAndroidFileDownload(m_id, &sstat); +void CreateCompatibleS57Object(PI_S57Obj* pObj, S57Obj* cobj, + chart_context* pctx) { + strncpy(cobj->FeatureName, pObj->FeatureName, 8); + cobj->Primitive_type = (GeoPrim_t)pObj->Primitive_type; + cobj->att_array = pObj->att_array; + cobj->attVal = pObj->attVal; + cobj->n_attr = pObj->n_attr; - OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0); - long sofarBytes = 0; - long totalBytes = -1; - long state = -1; + cobj->x = pObj->x; + cobj->y = pObj->y; + cobj->z = pObj->z; + cobj->npt = pObj->npt; - if (stat) { // some error - qDebug() << "Error on queryAndroidFileDownload, ending download"; - ev.setComplete(true); - ev.setTransferred(sofarBytes); - ev.setTotal(totalBytes); + cobj->iOBJL = pObj->iOBJL; + cobj->Index = pObj->Index; - ev.setDLEventStatus(OCPN_DL_FAILED); - ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); - } else { - wxStringTokenizer tk(sstat, _T(";")); - if (tk.HasMoreTokens()) { - wxString token = tk.GetNextToken(); - token.ToLong(&state); - token = tk.GetNextToken(); - token.ToLong(&sofarBytes); - token = tk.GetNextToken(); - token.ToLong(&totalBytes); - } + cobj->geoPt = (pt*)pObj->geoPt; + cobj->geoPtz = pObj->geoPtz; + cobj->geoPtMulti = pObj->geoPtMulti; - qDebug() << state << sofarBytes << totalBytes; + cobj->m_lat = pObj->m_lat; + cobj->m_lon = pObj->m_lon; - m_sofarBytes = sofarBytes; - m_totalBytes = totalBytes; + cobj->m_DisplayCat = (DisCat)pObj->m_DisplayCat; + cobj->x_rate = pObj->x_rate; + cobj->y_rate = pObj->y_rate; + cobj->x_origin = pObj->x_origin; + cobj->y_origin = pObj->y_origin; - ev.setTransferred(sofarBytes); - ev.setTotal(totalBytes); + cobj->Scamin = pObj->Scamin; + cobj->nRef = pObj->nRef; + cobj->bIsAton = pObj->bIsAton; + cobj->bIsAssociable = pObj->bIsAssociable; - if (state == 16) { // error - qDebug() << "Event OCPN_DL_FAILED/OCPN_DL_EVENT_TYPE_END"; - ev.setComplete(true); - ev.setDLEventStatus(OCPN_DL_FAILED); - ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); - } else if (state == 8) { // Completed OK - qDebug() << "Event OCPN_DL_NO_ERROR/OCPN_DL_EVENT_TYPE_END"; - ev.setComplete(true); - ev.setDLEventStatus(OCPN_DL_NO_ERROR); - ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); - } else { - ev.setComplete(false); - ev.setDLEventStatus(OCPN_DL_UNKNOWN); - ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS); - } + cobj->m_n_lsindex = pObj->m_n_lsindex; + cobj->m_lsindex_array = pObj->m_lsindex_array; + cobj->m_n_edge_max_points = pObj->m_n_edge_max_points; - // 2;0;148686 - } + if (gs_plib_flags & PLIB_CAPS_OBJSEGLIST) { + cobj->m_ls_list_legacy = + (PI_line_segment_element*) + pObj->m_ls_list; // note the cast, assumes in-sync layout + } else + cobj->m_ls_list_legacy = 0; + cobj->m_ls_list = 0; - if (m_download_evHandler) { - // qDebug() << "Sending event on timer..."; - m_download_evHandler->AddPendingEvent(ev); - } + if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) + cobj->m_bcategory_mutable = pObj->m_bcategory_mutable; + else + cobj->m_bcategory_mutable = true; // assume all objects are mutable - // Background download is all done. - if (OCPN_DL_EVENT_TYPE_END == ev.getDLEventCondition()) { - m_eventTimer.Stop(); - finishAndroidFileDownload(); + cobj->m_DPRI = -1; // default is unassigned, fixed at render time + if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) { + if (pObj->m_DPRI == -1) { + S52PLIB_Context* pCtx = (S52PLIB_Context*)pObj->S52_Context; + if (pCtx->LUP) cobj->m_DPRI = pCtx->LUP->DPRI - '0'; + } else + cobj->m_DPRI = pObj->m_DPRI; } -#endif -} + cobj->pPolyTessGeo = (PolyTessGeo*)pObj->pPolyTessGeo; + cobj->m_chart_context = (chart_context*)pObj->m_chart_context; -PI_DLEvtHandler* g_piEventHandler; + if (pObj->auxParm3 != 1234) { + pObj->auxParm3 = 1234; + pObj->auxParm0 = -99; + } -// Blocking download of single file -_OCPN_DLStatus OCPN_downloadFile(const wxString& url, - const wxString& outputFile, - const wxString& title, const wxString& message, - const wxBitmap& bitmap, wxWindow* parent, - long style, int timeout_secs) { -#ifdef __ANDROID__ + cobj->auxParm0 = pObj->auxParm0; + cobj->auxParm1 = 0; + cobj->auxParm2 = 0; + cobj->auxParm3 = 0; - wxString msg = _T("Downloading file synchronously: "); - msg += url; - msg += _T(" to: "); - msg += outputFile; - wxLogMessage(msg); + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - // Validate the write location - int vres = validateAndroidWriteLocation(outputFile); - if (vres == 0) // Pending permission dialog - return OCPN_DL_ABORTED; + if (pContext->bBBObj_valid) + // this is ugly because plugins still use BoundingBox + cobj->BBObj.Set(pContext->BBObj.GetMinY(), pContext->BBObj.GetMinX(), + pContext->BBObj.GetMaxY(), pContext->BBObj.GetMaxX()); - // Create a single event handler to receive status events - if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler; + cobj->CSrules = pContext->CSrules; + cobj->bCS_Added = pContext->bCS_Added; - // Reset global status indicators - g_download_status = OCPN_DL_UNKNOWN; - g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN; + cobj->FText = pContext->FText; + cobj->bFText_Added = pContext->bFText_Added; + cobj->rText = pContext->rText; - // Create a connection for the expected events from Android Activity - g_piEventHandler->Connect( - wxEVT_DOWNLOAD_EVENT, - (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent); + cobj->bIsClone = true; // Protect cloned object pointers in S57Obj dtor - long dl_ID = -1; + if (pctx) { + cobj->m_chart_context = pctx; + chart_context* ppctx = (chart_context*)pObj->m_chart_context; - // Make sure the outputfile is a file URI - wxString fURI = outputFile; - if (!fURI.StartsWith(_T("file://"))) { - fURI.Prepend(_T("file://")); + if (ppctx) { + cobj->m_chart_context->m_pvc_hash = ppctx->m_pvc_hash; + cobj->m_chart_context->m_pve_hash = ppctx->m_pve_hash; + cobj->m_chart_context->ref_lat = ppctx->ref_lat; + cobj->m_chart_context->ref_lon = ppctx->ref_lon; + cobj->m_chart_context->pFloatingATONArray = ppctx->pFloatingATONArray; + cobj->m_chart_context->pRigidATONArray = ppctx->pRigidATONArray; + cobj->m_chart_context->safety_contour = ppctx->safety_contour; + cobj->m_chart_context->vertex_buffer = ppctx->vertex_buffer; + } + cobj->m_chart_context->chart = + 0; // note bene, this is always NULL for a PlugIn chart + cobj->m_chart_context->chart_type = S52_CHART_TYPE_PLUGIN; } +} - int res = startAndroidFileDownload(url, fURI, g_piEventHandler, &dl_ID); - // Started OK? - if (res) { - finishAndroidFileDownload(); - g_piEventHandler->Disconnect( - wxEVT_DOWNLOAD_EVENT, - (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent); - // delete g_piEventHandler; - return OCPN_DL_FAILED; +bool PI_PLIBSetContext(PI_S57Obj* pObj) { + S52PLIB_Context* ctx; + if (!pObj->S52_Context) { + ctx = new S52PLIB_Context; + pObj->S52_Context = ctx; } - wxDateTime dl_start_time = wxDateTime::Now(); - - // Spin, waiting for timeout or event from downstream, and checking status - while (1) { - wxTimeSpan dt = wxDateTime::Now() - dl_start_time; - qDebug() << "Spin.." << dt.GetSeconds().GetLo(); - - if (dt.GetSeconds() > timeout_secs) { - qDebug() << "USER_TIMOUT"; - finishAndroidFileDownload(); - g_piEventHandler->Disconnect( - wxEVT_DOWNLOAD_EVENT, - (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent); - // delete g_piEventHandler; - return (OCPN_DL_USER_TIMEOUT); - } + ctx = (S52PLIB_Context*)pObj->S52_Context; - if (g_download_condition != OCPN_DL_EVENT_TYPE_UNKNOWN) { - if (OCPN_DL_EVENT_TYPE_END == g_download_condition) { - _OCPN_DLStatus ss = g_download_status; - finishAndroidFileDownload(); - g_piEventHandler->Disconnect( - wxEVT_DOWNLOAD_EVENT, - (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler:: - onDLEvent); - // delete g_piEventHandler; - qDebug() << "RETURN DL_END" << (int)ss; - return ss; // The actual return code - } - } + S57Obj cobj; + CreateCompatibleS57Object(pObj, &cobj, NULL); - wxString sstat; - int stat = queryAndroidFileDownload(dl_ID, &sstat); - if (stat) { // some error - qDebug() << "Error on queryAndroidFileDownload"; - finishAndroidFileDownload(); - g_piEventHandler->Disconnect( - wxEVT_DOWNLOAD_EVENT, - (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent); - // delete g_piEventHandler; + LUPname LUP_Name = PAPER_CHART; - return OCPN_DL_FAILED; // so abort - } + // Force a re-evaluation of CS rules + ctx->CSrules = NULL; + ctx->bCS_Added = false; - wxSleep(1); - wxSafeYield(); + // Clear the rendered text cache + if (ctx->bFText_Added) { + ctx->bFText_Added = false; + delete ctx->FText; + ctx->FText = NULL; } -#elif defined(OCPN_USE_CURL) - wxFileName tfn = wxFileName::CreateTempFileName(outputFile); - wxFileOutputStream output(tfn.GetFullPath()); - - wxCurlDownloadDialog ddlg(url, &output, title, message + url, bitmap, parent, - style); - wxCurlDialogReturnFlag ret = ddlg.RunModal(); - output.Close(); + // Reset object selection box + ctx->bBBObj_valid = true; + ctx->BBObj.SetMin(pObj->lon_min, pObj->lat_min); + ctx->BBObj.SetMax(pObj->lon_max, pObj->lat_max); - _OCPN_DLStatus result = OCPN_DL_UNKNOWN; + // This is where Simplified or Paper-Type point features are selected + switch (cobj.Primitive_type) { + case GEO_POINT: + case GEO_META: + case GEO_PRIM: - switch (ret) { - case wxCDRF_SUCCESS: { - if (wxCopyFile(tfn.GetFullPath(), outputFile)) - result = OCPN_DL_NO_ERROR; + if (PAPER_CHART == ps52plib->m_nSymbolStyle) + LUP_Name = PAPER_CHART; else - result = OCPN_DL_FAILED; + LUP_Name = SIMPLIFIED; + break; - } - case wxCDRF_FAILED: { - result = OCPN_DL_FAILED; + + case GEO_LINE: + LUP_Name = LINES; break; - } - case wxCDRF_USER_ABORTED: { - result = OCPN_DL_ABORTED; + + case GEO_AREA: + if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle) + LUP_Name = PLAIN_BOUNDARIES; + else + LUP_Name = SYMBOLIZED_BOUNDARIES; + break; - } - default: - wxASSERT(false); // This should never happen because we handle all - // possible cases of ret } - if (wxFileExists(tfn.GetFullPath())) wxRemoveFile(tfn.GetFullPath()); - return result; -#else - return OCPN_DL_FAILED; -#endif -} + LUPrec* lup = ps52plib->S52_LUPLookup(LUP_Name, cobj.FeatureName, &cobj); + ctx->LUP = lup; -// Non-Blocking download of single file -_OCPN_DLStatus OCPN_downloadFileBackground(const wxString& url, - const wxString& outputFile, - wxEvtHandler* handler, - long* handle) { -#ifdef __ANDROID__ - wxString msg = _T("Downloading file asynchronously: "); - msg += url; - msg += _T(" to: "); - msg += outputFile; - wxLogMessage(msg); + // Convert LUP to rules set + ps52plib->_LUP2rules(lup, &cobj); - // Create a single event handler to receive status events + ctx->MPSRulesList = NULL; - if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler; + return true; +} - long dl_ID = -1; +void PI_UpdateContext(PI_S57Obj* pObj) { + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + if (pContext) { + pContext->bBBObj_valid = true; + pContext->BBObj.SetMin(pObj->lon_min, pObj->lat_min); + pContext->BBObj.SetMax(pObj->lon_max, pObj->lat_max); + } +} - int res = startAndroidFileDownload(url, outputFile, NULL /*g_piEventHandler*/, - &dl_ID); - // Started OK? - if (res) { - finishAndroidFileDownload(); - return OCPN_DL_FAILED; +void UpdatePIObjectPlibContext(PI_S57Obj* pObj, S57Obj* cobj, + ObjRazRules* rzRules) { + // Update the PLIB context after the render operation + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + + pContext->CSrules = cobj->CSrules; + pContext->bCS_Added = cobj->bCS_Added; + + pContext->FText = cobj->FText; + pContext->bFText_Added = cobj->bFText_Added; + pContext->rText = cobj->rText; + + if (cobj->BBObj.GetValid()) { + // ugly as plugins still use BoundingBox + pContext->BBObj = + BoundingBox(cobj->BBObj.GetMinLon(), cobj->BBObj.GetMinLat(), + cobj->BBObj.GetMaxLon(), cobj->BBObj.GetMaxLat()); + pContext->bBBObj_valid = true; } - // configure the local event handler for background transfer - g_piEventHandler->setBackgroundMode(dl_ID, handler); + // Render operation may have promoted the object's display category + // (e.g.WRECKS) + pObj->m_DisplayCat = (PI_DisCat)cobj->m_DisplayCat; - if (handle) *handle = dl_ID; + if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) pObj->m_DPRI = cobj->m_DPRI; - return OCPN_DL_STARTED; + pContext->ChildRazRules = rzRules->child; + pContext->MPSRulesList = rzRules->mps; -#elif defined(OCPN_USE_CURL) - if (g_pi_manager->m_pCurlThread) // We allow just one download at a time. Do - // we want more? Or at least return some - // other status in this case? - return OCPN_DL_FAILED; - g_pi_manager->m_pCurlThread = - new wxCurlDownloadThread(g_pi_manager, CurlThreadId); - bool http = (url.StartsWith(wxS("http:")) || url.StartsWith(wxS("https:"))); - bool keep = false; - if (http && g_pi_manager->m_pCurl && - dynamic_cast(g_pi_manager->m_pCurl.get())) { - keep = true; - } - if (!keep) { - g_pi_manager->m_pCurl = 0; - } + pObj->auxParm0 = cobj->auxParm0; +} - bool failed = false; - if (!g_pi_manager->HandleCurlThreadError( - g_pi_manager->m_pCurlThread->SetURL(url, g_pi_manager->m_pCurl), - g_pi_manager->m_pCurlThread, url)) - failed = true; - if (!failed) { - g_pi_manager->m_pCurl = g_pi_manager->m_pCurlThread->GetCurlSharedPtr(); - if (!g_pi_manager->HandleCurlThreadError( - g_pi_manager->m_pCurlThread->SetOutputStream( - new wxFileOutputStream(outputFile)), - g_pi_manager->m_pCurlThread)) - failed = true; - } - if (!failed) { - g_pi_manager->m_download_evHandler = handler; - g_pi_manager->m_downloadHandle = handle; +bool PI_GetObjectRenderBox(PI_S57Obj* pObj, double* lat_min, double* lat_max, + double* lon_min, double* lon_max) { + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + if (pContext) { + if (lat_min) *lat_min = pContext->BBObj.GetMinY(); + if (lat_max) *lat_max = pContext->BBObj.GetMaxY(); + if (lon_min) *lon_min = pContext->BBObj.GetMinX(); + if (lon_max) *lon_max = pContext->BBObj.GetMaxX(); + return pContext->bBBObj_valid; + } else + return false; +} - wxCurlThreadError err = g_pi_manager->m_pCurlThread->Download(); - if (err != wxCTE_NO_ERROR) { - g_pi_manager->HandleCurlThreadError( - err, g_pi_manager->m_pCurlThread); // shows a message to the user - g_pi_manager->m_pCurlThread->Abort(); - failed = true; - } +PI_LUPname PI_GetObjectLUPName(PI_S57Obj* pObj) { + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + if (pContext) { + LUPrec* lup = pContext->LUP; + if (lup) return (PI_LUPname)(lup->TNAM); } + return (PI_LUPname)(-1); +} - if (!failed) return OCPN_DL_STARTED; - - if (g_pi_manager->m_pCurlThread) { - if (g_pi_manager->m_pCurlThread->IsAlive()) - g_pi_manager->m_pCurlThread->Abort(); - if (g_pi_manager->m_pCurlThread->GetOutputStream()) - delete (g_pi_manager->m_pCurlThread->GetOutputStream()); - wxDELETE(g_pi_manager->m_pCurlThread); - g_pi_manager->m_download_evHandler = NULL; - g_pi_manager->m_downloadHandle = NULL; - return OCPN_DL_STARTED; +PI_DisPrio PI_GetObjectDisplayPriority(PI_S57Obj* pObj) { + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + if (pContext) { + LUPrec* lup = pContext->LUP; + if (lup) return (PI_DisPrio)(lup->DPRI); } - g_pi_manager->m_pCurl = 0; - return OCPN_DL_FAILED; -#else - return OCPN_DL_FAILED; -#endif + return (PI_DisPrio)(-1); } -void OCPN_cancelDownloadFileBackground(long handle) { -#ifdef OCPN_USE_CURL - -#ifdef __ANDROID__ - cancelAndroidFileDownload(handle); - finishAndroidFileDownload(); - if (g_piEventHandler) g_piEventHandler->clearBackgroundMode(); -#else - if (g_pi_manager->m_pCurlThread) { - g_pi_manager->m_pCurlThread->Abort(); - delete (g_pi_manager->m_pCurlThread->GetOutputStream()); - wxDELETE(g_pi_manager->m_pCurlThread); - g_pi_manager->m_download_evHandler = NULL; - g_pi_manager->m_downloadHandle = NULL; +PI_DisCat PI_GetObjectDisplayCategory(PI_S57Obj* pObj) { + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; + if (pContext) { + LUPrec* lup = pContext->LUP; + if (lup) return (PI_DisCat)(lup->DISC); } - g_pi_manager->m_pCurl = 0; -#endif -#endif + return (PI_DisCat)(-1); +} +double PI_GetPLIBMarinerSafetyContour() { + return S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR); } -_OCPN_DLStatus OCPN_postDataHttp(const wxString& url, - const wxString& parameters, wxString& result, - int timeout_secs) { -#ifdef __ANDROID__ - wxString lparms = parameters; - wxString postResult = doAndroidPOST(url, lparms, timeout_secs * 1000); - if (postResult.IsSameAs(_T("NOK"))) return OCPN_DL_FAILED; - - result = postResult; - return OCPN_DL_NO_ERROR; +void PI_PLIBSetLineFeaturePriority(PI_S57Obj* pObj, int prio) { + // Create and populate a compatible s57 Object + S57Obj cobj; + chart_context ctx; + CreateCompatibleS57Object(pObj, &cobj, &ctx); -#elif defined(OCPN_USE_CURL) - wxCurlHTTP post; - post.SetOpt(CURLOPT_TIMEOUT, timeout_secs); - size_t res = post.Post(parameters.ToAscii(), parameters.Len(), url); + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - if (res) { - result = wxString(post.GetResponseBody().c_str(), wxConvUTF8); - return OCPN_DL_NO_ERROR; - } else - result = wxEmptyString; + // Create and populate a minimally compatible object container + ObjRazRules rzRules; + rzRules.obj = &cobj; + rzRules.LUP = pContext->LUP; + rzRules.sm_transform_parms = 0; + rzRules.child = NULL; + rzRules.next = NULL; + rzRules.mps = pContext->MPSRulesList; - return OCPN_DL_FAILED; + if (pContext->LUP) { + ps52plib->SetLineFeaturePriority(&rzRules, prio); -#else - return OCPN_DL_FAILED; -#endif + // Update the PLIB context after the render operation + UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); + } } -bool OCPN_isOnline() { -#ifdef __ANDROID__ - return androidCheckOnline(); -#endif - -#if !defined(__ANDROID__) && defined(OCPN_USE_CURL) - if (wxDateTime::GetTimeNow() > - g_pi_manager->m_last_online_chk + ONLINE_CHECK_RETRY) { - wxCurlHTTP get; - get.Head(_T("http://yahoo.com/")); - g_pi_manager->m_last_online = get.GetResponseCode() > 0; +void PI_PLIBPrepareForNewRender(void) { + if (ps52plib) { + ps52plib->PrepareForRender(); + ps52plib->ClearTextList(); - g_pi_manager->m_last_online_chk = wxDateTime::GetTimeNow(); + if (gs_plib_flags & PLIB_CAPS_LINE_BUFFER) + ps52plib->EnableGLLS(true); // Newer PlugIns can use GLLS + else + ps52plib->EnableGLLS(false); // Older cannot } - return g_pi_manager->m_last_online; -#else - return false; -#endif } -#if !defined(__ANDROID__) && defined(OCPN_USE_CURL) -void PlugInManager::OnEndPerformCurlDownload(wxCurlEndPerformEvent& ev) { - OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0); - if (ev.IsSuccessful()) { - event.setDLEventStatus(OCPN_DL_NO_ERROR); - } else { - g_pi_manager->m_pCurl = 0; - event.setDLEventStatus(OCPN_DL_FAILED); - } - event.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); - event.setComplete(true); +void PI_PLIBSetRenderCaps(unsigned int flags) { gs_plib_flags = flags; } - if (m_download_evHandler) { - m_download_evHandler->AddPendingEvent(event); - m_download_evHandler = NULL; - m_downloadHandle = NULL; +void PI_PLIBFreeContext(void* pContext) { + S52PLIB_Context* pctx = (S52PLIB_Context*)pContext; + + if (pctx->ChildRazRules) { + ObjRazRules* ctop = pctx->ChildRazRules; + while (ctop) { + delete ctop->obj; + + if (ps52plib) ps52plib->DestroyLUP(ctop->LUP); + + ObjRazRules* cnxx = ctop->next; + delete ctop; + ctop = cnxx; + } } - if (m_pCurlThread) { - m_pCurlThread->Wait(); - if (!m_pCurlThread->IsAborting()) { - delete (m_pCurlThread->GetOutputStream()); - wxDELETE(m_pCurlThread); + if (pctx->MPSRulesList) { + if (ps52plib && pctx->MPSRulesList->cs_rules) { + for (unsigned int i = 0; i < pctx->MPSRulesList->cs_rules->GetCount(); + i++) { + Rules* top = pctx->MPSRulesList->cs_rules->Item(i); + ps52plib->DestroyRulesChain(top); + } + delete pctx->MPSRulesList->cs_rules; } + free(pctx->MPSRulesList); } -} -void PlugInManager::OnCurlDownload(wxCurlDownloadEvent& ev) { - OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0); - event.setDLEventStatus(OCPN_DL_UNKNOWN); - event.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS); - event.setTotal(ev.GetTotalBytes()); - event.setTransferred(ev.GetDownloadedBytes()); - event.setComplete(false); + delete pctx->FText; - if (m_download_evHandler) { - m_download_evHandler->AddPendingEvent(event); - } + delete pctx; } -bool PlugInManager::HandleCurlThreadError(wxCurlThreadError err, - wxCurlBaseThread* p, - const wxString& url) { - switch (err) { - case wxCTE_NO_ERROR: - return true; // ignore this +int PI_PLIBRenderObjectToDC(wxDC* pdc, PI_S57Obj* pObj, PlugIn_ViewPort* vp) { + // Create and populate a compatible s57 Object + S57Obj cobj; + chart_context ctx; + CreateCompatibleS57Object(pObj, &cobj, &ctx); + + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; - case wxCTE_NO_RESOURCE: - wxLogError( - wxS("Insufficient resources for correct execution of the program.")); - break; + // Set up object SM rendering constants + sm_parms transform; + toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon, + &transform.easting_vp_center, &transform.northing_vp_center); - case wxCTE_ALREADY_RUNNING: - wxFAIL; // should never happen! - break; + // Create and populate a minimally compatible object container + ObjRazRules rzRules; + rzRules.obj = &cobj; + rzRules.LUP = pContext->LUP; + rzRules.sm_transform_parms = &transform; + rzRules.child = pContext->ChildRazRules; + rzRules.next = NULL; + rzRules.mps = pContext->MPSRulesList; - case wxCTE_INVALID_PROTOCOL: - wxLogError(wxS("The URL '%s' uses an unsupported protocol."), - url.c_str()); - break; + if (pContext->LUP) { + ViewPort cvp = CreateCompatibleViewport(*vp); - case wxCTE_NO_VALID_STREAM: - wxFAIL; // should never happen - the user streams should always be valid! - break; + // Do the render + // FIXME (plib) + ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, + cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale, + cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale, + GetOCPNCanvasWindow()->GetContentScaleFactor()); + ps52plib->PrepareForRender(); - case wxCTE_ABORTED: - return true; // ignore this + ps52plib->RenderObjectToDC(pdc, &rzRules); - case wxCTE_CURL_ERROR: { - wxString ws = wxS("unknown"); - if (p->GetCurlSession()) - ws = - wxString(p->GetCurlSession()->GetErrorString().c_str(), wxConvUTF8); - wxLogError(wxS("Network error: %s"), ws.c_str()); - } break; + // Update the PLIB context after the render operation + UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); } - // stop the thread - if (p->IsAlive()) p->Abort(); - - // this is an unrecoverable error: - return false; + return 1; } -#endif - -bool LaunchDefaultBrowser_Plugin(wxString url) { - if (g_Platform) g_Platform->platformLaunchDefaultBrowser(url); - return true; -} +int PI_PLIBRenderAreaToDC(wxDC* pdc, PI_S57Obj* pObj, PlugIn_ViewPort* vp, + wxRect rect, unsigned char* pixbuf) { + // Create a compatible render canvas + render_canvas_parms pb_spec; -/* API 1.14 */ + pb_spec.depth = BPP; + pb_spec.pb_pitch = ((rect.width * pb_spec.depth / 8)); + pb_spec.lclip = rect.x; + pb_spec.rclip = rect.x + rect.width - 1; + pb_spec.pix_buff = pixbuf; // the passed buffer + pb_spec.width = rect.width; + pb_spec.height = rect.height; + pb_spec.x = rect.x; + pb_spec.y = rect.y; +#ifdef ocpnUSE_ocpnBitmap + pb_spec.b_revrgb = true; +#else + pb_spec.b_revrgb = false; +#endif -void PlugInAISDrawGL(wxGLCanvas* glcanvas, const PlugIn_ViewPort& vp) { - ViewPort ocpn_vp = CreateCompatibleViewport(vp); + pb_spec.b_revrgb = false; - ocpnDC dc(*glcanvas); - dc.SetVP(ocpn_vp); + // Create and populate a compatible s57 Object + S57Obj cobj; + chart_context ctx; + CreateCompatibleS57Object(pObj, &cobj, &ctx); - AISDraw(dc, ocpn_vp, NULL); -} + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; -bool PlugInSetFontColor(const wxString TextElement, const wxColour color) { - return FontMgr::Get().SetFontColor(TextElement, color); -} + // Set up object SM rendering constants + sm_parms transform; + toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon, + &transform.easting_vp_center, &transform.northing_vp_center); -/* API 1.15 */ + // Create and populate a minimally compatible object container + ObjRazRules rzRules; + rzRules.obj = &cobj; + rzRules.LUP = pContext->LUP; + rzRules.sm_transform_parms = &transform; + rzRules.child = pContext->ChildRazRules; + rzRules.next = NULL; + rzRules.mps = pContext->MPSRulesList; -double PlugInGetDisplaySizeMM() { return g_Platform->GetDisplaySizeMM(); } + ViewPort cvp = CreateCompatibleViewport(*vp); -wxFont* FindOrCreateFont_PlugIn(int point_size, wxFontFamily family, - wxFontStyle style, wxFontWeight weight, - bool underline, const wxString& facename, - wxFontEncoding encoding) { - return FontMgr::Get().FindOrCreateFont(point_size, family, style, weight, - underline, facename, encoding); -} + // If the PlugIn does not support it nativiely, build a fully described + // Geomoetry + if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) { + if (!pObj->geoPtMulti) { // do this only once + PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo; -int PluginGetMinAvailableGshhgQuality() { - return gFrame->GetPrimaryCanvas()->GetMinAvailableGshhgQuality(); -} -int PluginGetMaxAvailableGshhgQuality() { - return gFrame->GetPrimaryCanvas()->GetMaxAvailableGshhgQuality(); -} + if (!tess) return 1; // bail on empty data -// disable builtin console canvas, and autopilot nmea sentences -void PlugInHandleAutopilotRoute(bool enable) { - g_bPluginHandleAutopilotRoute = enable; -} + PolyTriGroup* ptg = new PolyTriGroup; + ptg->tri_prim_head = + tess->Get_PolyTriGroup_head()->tri_prim_head; // tph; + ptg->bsingle_alloc = false; + ptg->data_type = DATA_TYPE_DOUBLE; + tess->Set_PolyTriGroup_head(ptg); -/* API 1.16 */ -wxString GetSelectedWaypointGUID_Plugin() { - ChartCanvas* cc = gFrame->GetFocusCanvas(); - if (cc && cc->GetSelectedRoutePoint()) { - return cc->GetSelectedRoutePoint()->m_GUID; + double* pd = (double*)malloc(sizeof(double)); + pObj->geoPtMulti = pd; // Hack hack + } } - return wxEmptyString; -} -wxString GetSelectedRouteGUID_Plugin() { - ChartCanvas* cc = gFrame->GetFocusCanvas(); - if (cc && cc->GetSelectedRoute()) { - return cc->GetSelectedRoute()->m_GUID; - } - return wxEmptyString; -} + if (pContext->LUP) { + // Do the render + // FIXME (plib) + ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, + cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale, + cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale, + GetOCPNCanvasWindow()->GetContentScaleFactor()); + ps52plib->PrepareForRender(); + + ps52plib->RenderAreaToDC(pdc, &rzRules, &pb_spec); -wxString GetSelectedTrackGUID_Plugin() { - ChartCanvas* cc = gFrame->GetFocusCanvas(); - if (cc && cc->GetSelectedTrack()) { - return cc->GetSelectedTrack()->m_GUID; + // Update the PLIB context after the render operation + UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); } - return wxEmptyString; -} -std::unique_ptr GetWaypoint_Plugin(const wxString& GUID) { - std::unique_ptr w(new PlugIn_Waypoint); - GetSingleWaypoint(GUID, w.get()); - return w; + return 1; } -std::unique_ptr GetRoute_Plugin(const wxString& GUID) { - std::unique_ptr r; - Route* route = g_pRouteMan->FindRouteByGUID(GUID); - if (route == nullptr) return r; +int PI_PLIBRenderAreaToGL(const wxGLContext& glcc, PI_S57Obj* pObj, + PlugIn_ViewPort* vp, wxRect& render_rect) { +#ifdef ocpnUSE_GL + // Create and populate a compatible s57 Object + S57Obj cobj; + chart_context ctx; + CreateCompatibleS57Object(pObj, &cobj, &ctx); - r = std::unique_ptr(new PlugIn_Route); - PlugIn_Route* dst_route = r.get(); + // chart_context *pct = (chart_context *)pObj->m_chart_context; - // PlugIn_Waypoint *pwp; - RoutePoint* src_wp; - wxRoutePointListNode* node = route->pRoutePointList->GetFirst(); + // If the PlugIn does not support it nativiely, build a fully described + // Geomoetry - while (node) { - src_wp = node->GetData(); + if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) { + if (!pObj->geoPtMulti) { // only do this once + PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo; - PlugIn_Waypoint* dst_wp = new PlugIn_Waypoint(); - PlugInFromRoutePoint(dst_wp, src_wp); + if (!tess) return 1; // bail on empty data - dst_route->pWaypointList->Append(dst_wp); + PolyTriGroup* ptg = + new PolyTriGroup; // this will leak a little, but is POD + ptg->tri_prim_head = tess->Get_PolyTriGroup_head()->tri_prim_head; + ptg->bsingle_alloc = false; + ptg->data_type = DATA_TYPE_DOUBLE; + tess->Set_PolyTriGroup_head(ptg); - node = node->GetNext(); + // Mark this object using geoPtMulti + // The malloc will get free'ed when the object is deleted. + double* pd = (double*)malloc(sizeof(double)); + pObj->geoPtMulti = pd; // Hack hack + } + cobj.auxParm0 = -6; // signal that this object render cannot use VBO + cobj.auxParm1 = -1; // signal that this object render cannot have single + // buffer conversion done + } else { // it is a newer PLugIn, so can do single buffer conversion and VBOs + if (pObj->auxParm0 < 1) + cobj.auxParm0 = -7; // signal that this object render can use a + // persistent VBO for area triangle vertices } - dst_route->m_NameString = route->m_RouteNameString; - dst_route->m_StartString = route->m_RouteStartString; - dst_route->m_EndString = route->m_RouteEndString; - dst_route->m_GUID = route->m_GUID; - return r; -} + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; -std::unique_ptr GetTrack_Plugin(const wxString& GUID) { - std::unique_ptr t; - // Find the Track - Track* pTrack = g_pRouteMan->FindTrackByGUID(GUID); - if (!pTrack) return t; + // Set up object SM rendering constants + sm_parms transform; + toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon, + &transform.easting_vp_center, &transform.northing_vp_center); - std::unique_ptr tk = - std::unique_ptr(new PlugIn_Track); - PlugIn_Track* dst_track = tk.get(); - dst_track->m_NameString = pTrack->GetName(); - dst_track->m_StartString = pTrack->m_TrackStartString; - dst_track->m_EndString = pTrack->m_TrackEndString; - dst_track->m_GUID = pTrack->m_GUID; + // Create and populate a minimally compatible object container + ObjRazRules rzRules; + rzRules.obj = &cobj; + rzRules.LUP = pContext->LUP; + rzRules.sm_transform_parms = &transform; + rzRules.child = pContext->ChildRazRules; + rzRules.next = NULL; + rzRules.mps = pContext->MPSRulesList; - for (int i = 0; i < pTrack->GetnPoints(); i++) { - TrackPoint* ptp = pTrack->GetPoint(i); + if (pContext->LUP) { + ViewPort cvp = CreateCompatibleViewport(*vp); - PlugIn_Waypoint* dst_wp = new PlugIn_Waypoint(); + // Do the render + // FIXME (plib) + ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, + cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale, + cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale, + GetOCPNCanvasWindow()->GetContentScaleFactor()); + ps52plib->PrepareForRender(); - dst_wp->m_lat = ptp->m_lat; - dst_wp->m_lon = ptp->m_lon; - dst_wp->m_CreateTime = ptp->GetCreateTime(); // not const + ps52plib->RenderAreaToGL(glcc, &rzRules); - dst_track->pWaypointList->Append(dst_wp); + // Update the PLIB context after the render operation + UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); } - return tk; -} - -wxWindow* PluginGetFocusCanvas() { return g_focusCanvas; } - -wxWindow* PluginGetOverlayRenderCanvas() { - // if(g_overlayCanvas) - return g_overlayCanvas; - // else +#endif + return 1; } -void CanvasJumpToPosition(wxWindow* canvas, double lat, double lon, - double scale) { - auto oCanvas = dynamic_cast(canvas); - if (oCanvas) gFrame->JumpToPosition(oCanvas, lat, lon, scale); -} +int PI_PLIBRenderObjectToGL(const wxGLContext& glcc, PI_S57Obj* pObj, + PlugIn_ViewPort* vp, wxRect& render_rect) { + // Create and populate a compatible s57 Object + S57Obj cobj; + chart_context ctx; + CreateCompatibleS57Object(pObj, &cobj, &ctx); -bool ShuttingDown(void) { return g_bquiting; } + S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context; -wxWindow* GetCanvasUnderMouse(void) { return gFrame->GetCanvasUnderMouse(); } + // Set up object SM rendering constants + sm_parms transform; + toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon, + &transform.easting_vp_center, &transform.northing_vp_center); -int GetCanvasIndexUnderMouse(void) { - ChartCanvas* l_canvas = gFrame->GetCanvasUnderMouse(); - if (l_canvas) { - for (unsigned int i = 0; i < g_canvasArray.GetCount(); ++i) { - if (l_canvas == g_canvasArray[i]) return i; - } - } - return 0; -} + // Create and populate a minimally compatible object container + ObjRazRules rzRules; + rzRules.obj = &cobj; + rzRules.LUP = pContext->LUP; + rzRules.sm_transform_parms = &transform; + rzRules.child = pContext->ChildRazRules; + rzRules.next = NULL; + rzRules.mps = pContext->MPSRulesList; -// std::vector GetCanvasArray() -// { -// std::vector rv; -// for(unsigned int i=0 ; i < g_canvasArray.GetCount() ; i++){ -// ChartCanvas *cc = g_canvasArray.Item(i); -// rv.push_back(cc); -// } -// -// return rv; -// } - -wxWindow* GetCanvasByIndex(int canvasIndex) { - if (g_canvasConfig == 0) - return gFrame->GetPrimaryCanvas(); - else { - if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) { - return g_canvasArray[canvasIndex]; - } - } - return NULL; -} + if (pContext->LUP) { + ViewPort cvp = CreateCompatibleViewport(*vp); -bool CheckMUIEdgePan_PlugIn(int x, int y, bool dragging, int margin, int delta, - int canvasIndex) { - if (g_canvasConfig == 0) - return gFrame->GetPrimaryCanvas()->CheckEdgePan(x, y, dragging, margin, - delta); - else { - if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) { - return g_canvasArray[canvasIndex]->CheckEdgePan(x, y, dragging, margin, - delta); - } - } + // Do the render + // FIXME (plib) + ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, + cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale, + cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale, + GetOCPNCanvasWindow()->GetContentScaleFactor()); + ps52plib->PrepareForRender(); - return false; -} + ps52plib->RenderObjectToGL(glcc, &rzRules); -void SetMUICursor_PlugIn(wxCursor* pCursor, int canvasIndex) { - if (g_canvasConfig == 0) - gFrame->GetPrimaryCanvas()->pPlugIn_Cursor = pCursor; - else { - if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) { - g_canvasArray[canvasIndex]->pPlugIn_Cursor = pCursor; - } + // Update the PLIB context after the render operation + UpdatePIObjectPlibContext(pObj, &cobj, &rzRules); } -} -int GetCanvasCount() { - if (g_canvasConfig == 1) return 2; - // else return 1; } -int GetLatLonFormat() { return g_iSDMMFormat; } - -wxRect GetMasterToolbarRect() { - if (g_MainToolbar) - return g_MainToolbar->GetToolbarRect(); - else - return wxRect(0, 0, 1, 1); -} - -/* API 1.17 */ +// http File Download Support -void ZeroXTE() { - if (g_pRouteMan) { - g_pRouteMan->ZeroCurrentXTEToActivePoint(); - } +// OCPN_downloadEvent Implementation + +OCPN_downloadEvent::OCPN_downloadEvent(wxEventType commandType, int id) + : wxEvent(id, commandType) { + m_stat = OCPN_DL_UNKNOWN; + m_condition = OCPN_DL_EVENT_TYPE_UNKNOWN; + m_b_complete = false; + m_sofarBytes = 0; } -ListOfPI_S57Obj* PlugInManager::GetLightsObjRuleListVisibleAtLatLon( - ChartPlugInWrapper* target, float zlat, float zlon, const ViewPort& vp) { - ListOfPI_S57Obj* list = NULL; - if (target) { - PlugInChartBaseGLPlus2* picbgl = - dynamic_cast(target->GetPlugInChart()); - if (picbgl) { - PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp); - list = picbgl->GetLightsObjRuleListVisibleAtLatLon(zlat, zlon, &pi_vp); +OCPN_downloadEvent::~OCPN_downloadEvent() {} - return list; - } - PlugInChartBaseExtendedPlus2* picbx = - dynamic_cast(target->GetPlugInChart()); - if (picbx) { - PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp); - list = picbx->GetLightsObjRuleListVisibleAtLatLon(zlat, zlon, &pi_vp); +wxEvent* OCPN_downloadEvent::Clone() const { + OCPN_downloadEvent* newevent = new OCPN_downloadEvent(*this); + newevent->m_stat = this->m_stat; + newevent->m_condition = this->m_condition; - return list; - } else - return list; - } else - return list; + newevent->m_totalBytes = this->m_totalBytes; + newevent->m_sofarBytes = this->m_sofarBytes; + newevent->m_b_complete = this->m_b_complete; + + return newevent; } -// PlugInWaypointEx implementation -WX_DEFINE_LIST(Plugin_WaypointExList) - -// The class implementations -PlugIn_Waypoint_Ex::PlugIn_Waypoint_Ex() { InitDefaults(); } - -PlugIn_Waypoint_Ex::PlugIn_Waypoint_Ex( - double lat, double lon, const wxString& icon_ident, const wxString& wp_name, - const wxString& GUID, const double ScaMin, const bool bNameVisible, - const int nRangeRings, const double RangeDistance, - const wxColor RangeColor) { - InitDefaults(); - - wxDateTime now = wxDateTime::Now(); - m_CreateTime = now.ToUTC(); - m_HyperlinkList = NULL; - - m_lat = lat; - m_lon = lon; - IconName = icon_ident; - m_MarkName = wp_name; - m_GUID = GUID; - scamin = ScaMin; - IsNameVisible = bNameVisible; - nrange_rings = nRangeRings; - RangeRingSpace = RangeDistance; - RangeRingColor = RangeColor; -} - -void PlugIn_Waypoint_Ex::InitDefaults() { - m_HyperlinkList = NULL; - scamin = 1e9; - b_useScamin = false; - nrange_rings = 0; - RangeRingSpace = 1; - IsNameVisible = false; - IsVisible = true; - RangeRingColor = *wxBLACK; - m_CreateTime = wxDateTime::Now(); - IsActive = false; - m_lat = 0; - m_lon = 0; -} - -bool PlugIn_Waypoint_Ex::GetFSStatus() { - RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(m_GUID); - if (!prp) return false; - - if (prp->m_bIsInRoute && !prp->IsShared()) return false; +// const wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType(); +DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType(); - return true; -} +_OCPN_DLStatus g_download_status; +_OCPN_DLCondition g_download_condition; -int PlugIn_Waypoint_Ex::GetRouteMembershipCount() { - // Search all routes to count the membership of this point - RoutePoint* pWP = pWayPointMan->FindRoutePointByGUID(m_GUID); - if (!pWP) return 0; - - int nCount = 0; - wxRouteListNode* node = pRouteList->GetFirst(); - while (node) { - Route* proute = node->GetData(); - wxRoutePointListNode* pnode = (proute->pRoutePointList)->GetFirst(); - while (pnode) { - RoutePoint* prp = pnode->GetData(); - if (prp == pWP) nCount++; - pnode = pnode->GetNext(); - } +#define DL_EVENT_TIMER 4388 - node = node->GetNext(); - } +class PI_DLEvtHandler : public wxEvtHandler { +public: + PI_DLEvtHandler(); + ~PI_DLEvtHandler(); - return nCount; -} + void onDLEvent(OCPN_downloadEvent& event); + void setBackgroundMode(long ID, wxEvtHandler* handler); + void clearBackgroundMode(); + void onTimerEvent(wxTimerEvent& event); -PlugIn_Waypoint_Ex::~PlugIn_Waypoint_Ex() {} + long m_id; + wxTimer m_eventTimer; + wxEvtHandler* m_download_evHandler; -// PlugInRouteExtended implementation -PlugIn_Route_Ex::PlugIn_Route_Ex(void) { - pWaypointList = new Plugin_WaypointExList; -} + long m_sofarBytes; + long m_totalBytes; +}; -PlugIn_Route_Ex::~PlugIn_Route_Ex(void) { - pWaypointList->DeleteContents(false); // do not delete Waypoints - pWaypointList->Clear(); +PI_DLEvtHandler::PI_DLEvtHandler() { + g_download_status = OCPN_DL_UNKNOWN; + g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN; - delete pWaypointList; + m_download_evHandler = NULL; + m_id = -1; + m_sofarBytes = 0; + m_totalBytes = 0; } -// The utility methods implementations +PI_DLEvtHandler::~PI_DLEvtHandler() { + m_eventTimer.Stop(); + Disconnect( + wxEVT_TIMER, + (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent); +} -// translate O route class to PlugIn_Waypoint_Ex -static void PlugInExFromRoutePoint(PlugIn_Waypoint_Ex* dst, - /* const*/ RoutePoint* src) { - dst->m_lat = src->m_lat; - dst->m_lon = src->m_lon; - dst->IconName = src->GetIconName(); - dst->m_MarkName = src->GetName(); - dst->m_MarkDescription = src->GetDescription(); - dst->IconDescription = pWayPointMan->GetIconDescription(src->GetIconName()); - dst->IsVisible = src->IsVisible(); - dst->m_CreateTime = src->GetCreateTime(); // not const - dst->m_GUID = src->m_GUID; +void PI_DLEvtHandler::onDLEvent(OCPN_downloadEvent& event) { + // qDebug() << "Got Event " << (int)event.getDLEventStatus() << + // (int)event.getDLEventCondition(); - // Transcribe (clone) the html HyperLink List, if present - if (src->m_HyperlinkList == nullptr) return; + g_download_status = event.getDLEventStatus(); + g_download_condition = event.getDLEventCondition(); - delete dst->m_HyperlinkList; - dst->m_HyperlinkList = nullptr; + // This is an END event, happening at the end of BACKGROUND file download + if (m_download_evHandler && + (OCPN_DL_EVENT_TYPE_END == event.getDLEventCondition())) { + OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0); + ev.setComplete(true); + ev.setTransferred(m_sofarBytes); + ev.setTotal(m_totalBytes); - if (src->m_HyperlinkList->GetCount() > 0) { - dst->m_HyperlinkList = new Plugin_HyperlinkList; + ev.setDLEventStatus(event.getDLEventStatus()); + ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); - wxHyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst(); - while (linknode) { - Hyperlink* link = linknode->GetData(); + m_download_evHandler->AddPendingEvent(ev); + m_eventTimer.Stop(); +#ifdef __ANDROID__ + finishAndroidFileDownload(); +#endif + } - Plugin_Hyperlink* h = new Plugin_Hyperlink(); - h->DescrText = link->DescrText; - h->Link = link->Link; - h->Type = link->LType; + event.Skip(); +} - dst->m_HyperlinkList->Append(h); +void PI_DLEvtHandler::setBackgroundMode(long ID, wxEvtHandler* handler) { + m_id = ID; + m_download_evHandler = handler; - linknode = linknode->GetNext(); - } - } + m_eventTimer.SetOwner(this, DL_EVENT_TIMER); - // Get the range ring info - dst->nrange_rings = src->m_iWaypointRangeRingsNumber; - dst->RangeRingSpace = src->m_fWaypointRangeRingsStep; - dst->RangeRingColor = src->m_wxcWaypointRangeRingsColour; + Connect( + wxEVT_TIMER, + (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent); + m_eventTimer.Start(1000, wxTIMER_CONTINUOUS); +} - // Get other extended info - dst->IsNameVisible = src->m_bShowName; - dst->scamin = src->GetScaMin(); - dst->b_useScamin = src->GetUseSca(); - dst->IsActive = src->m_bIsActive; +void PI_DLEvtHandler::clearBackgroundMode() { + m_download_evHandler = NULL; + m_eventTimer.Stop(); } -static void cloneHyperlinkListEx(RoutePoint* dst, - const PlugIn_Waypoint_Ex* src) { - // Transcribe (clone) the html HyperLink List, if present - if (src->m_HyperlinkList == nullptr) return; +void PI_DLEvtHandler::onTimerEvent(wxTimerEvent& event) { +#ifdef __ANDROID__ + // Query the download status, and post to the original requestor + // This method only happens on Background file downloads - if (src->m_HyperlinkList->GetCount() > 0) { - wxPlugin_HyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst(); - while (linknode) { - Plugin_Hyperlink* link = linknode->GetData(); + wxString sstat; + int stat = queryAndroidFileDownload(m_id, &sstat); - Hyperlink* h = new Hyperlink(); - h->DescrText = link->DescrText; - h->Link = link->Link; - h->LType = link->Type; + OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0); + long sofarBytes = 0; + long totalBytes = -1; + long state = -1; - dst->m_HyperlinkList->Append(h); + if (stat) { // some error + qDebug() << "Error on queryAndroidFileDownload, ending download"; + ev.setComplete(true); + ev.setTransferred(sofarBytes); + ev.setTotal(totalBytes); - linknode = linknode->GetNext(); + ev.setDLEventStatus(OCPN_DL_FAILED); + ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); + } else { + wxStringTokenizer tk(sstat, _T(";")); + if (tk.HasMoreTokens()) { + wxString token = tk.GetNextToken(); + token.ToLong(&state); + token = tk.GetNextToken(); + token.ToLong(&sofarBytes); + token = tk.GetNextToken(); + token.ToLong(&totalBytes); } - } -} -RoutePoint* CreateNewPoint(const PlugIn_Waypoint_Ex* src, bool b_permanent) { - RoutePoint* pWP = new RoutePoint(src->m_lat, src->m_lon, src->IconName, - src->m_MarkName, src->m_GUID); + qDebug() << state << sofarBytes << totalBytes; - pWP->m_bIsolatedMark = true; // This is an isolated mark + m_sofarBytes = sofarBytes; + m_totalBytes = totalBytes; - cloneHyperlinkListEx(pWP, src); + ev.setTransferred(sofarBytes); + ev.setTotal(totalBytes); - pWP->m_MarkDescription = src->m_MarkDescription; + if (state == 16) { // error + qDebug() << "Event OCPN_DL_FAILED/OCPN_DL_EVENT_TYPE_END"; + ev.setComplete(true); + ev.setDLEventStatus(OCPN_DL_FAILED); + ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); + } else if (state == 8) { // Completed OK + qDebug() << "Event OCPN_DL_NO_ERROR/OCPN_DL_EVENT_TYPE_END"; + ev.setComplete(true); + ev.setDLEventStatus(OCPN_DL_NO_ERROR); + ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); + } else { + ev.setComplete(false); + ev.setDLEventStatus(OCPN_DL_UNKNOWN); + ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS); + } - if (src->m_CreateTime.IsValid()) - pWP->SetCreateTime(src->m_CreateTime); - else { - wxDateTime dtnow(wxDateTime::Now()); - pWP->SetCreateTime(dtnow); + // 2;0;148686 } - pWP->m_btemp = (b_permanent == false); + if (m_download_evHandler) { + // qDebug() << "Sending event on timer..."; + m_download_evHandler->AddPendingEvent(ev); + } - // Extended fields - pWP->SetIconName(src->IconName); - pWP->SetWaypointRangeRingsNumber(src->nrange_rings); - pWP->SetWaypointRangeRingsStep(src->RangeRingSpace); - pWP->SetWaypointRangeRingsColour(src->RangeRingColor); - pWP->SetScaMin(src->scamin); - pWP->SetUseSca(src->b_useScamin); - pWP->SetNameShown(src->IsNameVisible); - pWP->SetVisible(src->IsVisible); + // Background download is all done. + if (OCPN_DL_EVENT_TYPE_END == ev.getDLEventCondition()) { + m_eventTimer.Stop(); + finishAndroidFileDownload(); + } - return pWP; +#endif } -bool GetSingleWaypointEx(wxString GUID, PlugIn_Waypoint_Ex* pwaypoint) { - // Find the RoutePoint - RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(GUID); - if (!prp) return false; +PI_DLEvtHandler* g_piEventHandler; - PlugInExFromRoutePoint(pwaypoint, prp); +// Blocking download of single file +_OCPN_DLStatus OCPN_downloadFile(const wxString& url, + const wxString& outputFile, + const wxString& title, const wxString& message, + const wxBitmap& bitmap, wxWindow* parent, + long style, int timeout_secs) { +#ifdef __ANDROID__ - return true; -} + wxString msg = _T("Downloading file synchronously: "); + msg += url; + msg += _T(" to: "); + msg += outputFile; + wxLogMessage(msg); -bool AddSingleWaypointEx(PlugIn_Waypoint_Ex* pwaypointex, bool b_permanent) { - // Validate the waypoint parameters a little bit + // Validate the write location + int vres = validateAndroidWriteLocation(outputFile); + if (vres == 0) // Pending permission dialog + return OCPN_DL_ABORTED; - // GUID - // Make sure that this GUID is indeed unique in the Routepoint list - bool b_unique = true; - wxRoutePointListNode* prpnode = pWayPointMan->GetWaypointList()->GetFirst(); - while (prpnode) { - RoutePoint* prp = prpnode->GetData(); + // Create a single event handler to receive status events + if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler; - if (prp->m_GUID == pwaypointex->m_GUID) { - b_unique = false; - break; - } - prpnode = prpnode->GetNext(); // RoutePoint - } + // Reset global status indicators + g_download_status = OCPN_DL_UNKNOWN; + g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN; - if (!b_unique) return false; + // Create a connection for the expected events from Android Activity + g_piEventHandler->Connect( + wxEVT_DOWNLOAD_EVENT, + (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent); - RoutePoint* pWP = CreateNewPoint(pwaypointex, b_permanent); + long dl_ID = -1; - pWP->SetShowWaypointRangeRings(pwaypointex->nrange_rings > 0); + // Make sure the outputfile is a file URI + wxString fURI = outputFile; + if (!fURI.StartsWith(_T("file://"))) { + fURI.Prepend(_T("file://")); + } - pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP); - if (b_permanent) pConfig->AddNewWayPoint(pWP, -1); + int res = startAndroidFileDownload(url, fURI, g_piEventHandler, &dl_ID); + // Started OK? + if (res) { + finishAndroidFileDownload(); + g_piEventHandler->Disconnect( + wxEVT_DOWNLOAD_EVENT, + (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent); + // delete g_piEventHandler; + return OCPN_DL_FAILED; + } - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateWptListCtrl(); + wxDateTime dl_start_time = wxDateTime::Now(); - return true; -} + // Spin, waiting for timeout or event from downstream, and checking status + while (1) { + wxTimeSpan dt = wxDateTime::Now() - dl_start_time; + qDebug() << "Spin.." << dt.GetSeconds().GetLo(); -bool UpdateSingleWaypointEx(PlugIn_Waypoint_Ex* pwaypoint) { - // Find the RoutePoint - bool b_found = false; - RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(pwaypoint->m_GUID); + if (dt.GetSeconds() > timeout_secs) { + qDebug() << "USER_TIMOUT"; + finishAndroidFileDownload(); + g_piEventHandler->Disconnect( + wxEVT_DOWNLOAD_EVENT, + (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent); + // delete g_piEventHandler; + return (OCPN_DL_USER_TIMEOUT); + } - if (prp) b_found = true; + if (g_download_condition != OCPN_DL_EVENT_TYPE_UNKNOWN) { + if (OCPN_DL_EVENT_TYPE_END == g_download_condition) { + _OCPN_DLStatus ss = g_download_status; + finishAndroidFileDownload(); + g_piEventHandler->Disconnect( + wxEVT_DOWNLOAD_EVENT, + (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler:: + onDLEvent); + // delete g_piEventHandler; + qDebug() << "RETURN DL_END" << (int)ss; + return ss; // The actual return code + } + } - if (b_found) { - double lat_save = prp->m_lat; - double lon_save = prp->m_lon; + wxString sstat; + int stat = queryAndroidFileDownload(dl_ID, &sstat); + if (stat) { // some error + qDebug() << "Error on queryAndroidFileDownload"; + finishAndroidFileDownload(); + g_piEventHandler->Disconnect( + wxEVT_DOWNLOAD_EVENT, + (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent); + // delete g_piEventHandler; - prp->m_lat = pwaypoint->m_lat; - prp->m_lon = pwaypoint->m_lon; - prp->SetIconName(pwaypoint->IconName); - prp->SetName(pwaypoint->m_MarkName); - prp->m_MarkDescription = pwaypoint->m_MarkDescription; - prp->SetVisible(pwaypoint->IsVisible); - if (pwaypoint->m_CreateTime.IsValid()) - prp->SetCreateTime(pwaypoint->m_CreateTime); + return OCPN_DL_FAILED; // so abort + } - // Transcribe (clone) the html HyperLink List, if present + wxSleep(1); + wxSafeYield(); + } - if (pwaypoint->m_HyperlinkList) { - prp->m_HyperlinkList->Clear(); - if (pwaypoint->m_HyperlinkList->GetCount() > 0) { - wxPlugin_HyperlinkListNode* linknode = - pwaypoint->m_HyperlinkList->GetFirst(); - while (linknode) { - Plugin_Hyperlink* link = linknode->GetData(); +#elif defined(OCPN_USE_CURL) + wxFileName tfn = wxFileName::CreateTempFileName(outputFile); + wxFileOutputStream output(tfn.GetFullPath()); - Hyperlink* h = new Hyperlink(); - h->DescrText = link->DescrText; - h->Link = link->Link; - h->LType = link->Type; + wxCurlDownloadDialog ddlg(url, &output, title, message + url, bitmap, parent, + style); + wxCurlDialogReturnFlag ret = ddlg.RunModal(); + output.Close(); - prp->m_HyperlinkList->Append(h); + _OCPN_DLStatus result = OCPN_DL_UNKNOWN; - linknode = linknode->GetNext(); - } - } + switch (ret) { + case wxCDRF_SUCCESS: { + if (wxCopyFile(tfn.GetFullPath(), outputFile)) + result = OCPN_DL_NO_ERROR; + else + result = OCPN_DL_FAILED; + break; } - - // Extended fields - prp->SetWaypointRangeRingsNumber(pwaypoint->nrange_rings); - prp->SetWaypointRangeRingsStep(pwaypoint->RangeRingSpace); - prp->SetWaypointRangeRingsColour(pwaypoint->RangeRingColor); - prp->SetScaMin(pwaypoint->scamin); - prp->SetUseSca(pwaypoint->b_useScamin); - prp->SetNameShown(pwaypoint->IsNameVisible); - - prp->SetShowWaypointRangeRings(pwaypoint->nrange_rings > 0); - - if (prp) prp->ReLoadIcon(); - - auto canvas = gFrame->GetPrimaryCanvas(); - SelectCtx ctx(canvas->m_bShowNavobjects, canvas->GetCanvasTrueScale(), - canvas->GetScaleValue()); - SelectItem* pFind = - pSelect->FindSelection(ctx, lat_save, lon_save, SELTYPE_ROUTEPOINT); - if (pFind) { - pFind->m_slat = pwaypoint->m_lat; // update the SelectList entry - pFind->m_slon = pwaypoint->m_lon; + case wxCDRF_FAILED: { + result = OCPN_DL_FAILED; + break; } - - if (!prp->m_btemp) pConfig->UpdateWayPoint(prp); - - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateWptListCtrl(); + case wxCDRF_USER_ABORTED: { + result = OCPN_DL_ABORTED; + break; + } + default: + wxASSERT(false); // This should never happen because we handle all + // possible cases of ret } + if (wxFileExists(tfn.GetFullPath())) wxRemoveFile(tfn.GetFullPath()); + return result; - return b_found; +#else + return OCPN_DL_FAILED; +#endif } -bool AddPlugInRouteEx(PlugIn_Route_Ex* proute, bool b_permanent) { - Route* route = new Route(); +// Non-Blocking download of single file +_OCPN_DLStatus OCPN_downloadFileBackground(const wxString& url, + const wxString& outputFile, + wxEvtHandler* handler, + long* handle) { +#ifdef __ANDROID__ + wxString msg = _T("Downloading file asynchronously: "); + msg += url; + msg += _T(" to: "); + msg += outputFile; + wxLogMessage(msg); - PlugIn_Waypoint_Ex* pwaypointex; - RoutePoint *pWP, *pWP_src; - int ip = 0; - wxDateTime plannedDeparture; + // Create a single event handler to receive status events - wxPlugin_WaypointExListNode* pwpnode = proute->pWaypointList->GetFirst(); - while (pwpnode) { - pwaypointex = pwpnode->GetData(); + if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler; - pWP = pWayPointMan->FindRoutePointByGUID(pwaypointex->m_GUID); - if (!pWP) { - pWP = CreateNewPoint(pwaypointex, b_permanent); - pWP->m_bIsolatedMark = false; - } + long dl_ID = -1; - route->AddPoint(pWP); + int res = startAndroidFileDownload(url, outputFile, NULL /*g_piEventHandler*/, + &dl_ID); + // Started OK? + if (res) { + finishAndroidFileDownload(); + return OCPN_DL_FAILED; + } - pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP); + // configure the local event handler for background transfer + g_piEventHandler->setBackgroundMode(dl_ID, handler); - if (ip > 0) - pSelect->AddSelectableRouteSegment(pWP_src->m_lat, pWP_src->m_lon, - pWP->m_lat, pWP->m_lon, pWP_src, pWP, - route); + if (handle) *handle = dl_ID; - plannedDeparture = pwaypointex->m_CreateTime; - ip++; - pWP_src = pWP; + return OCPN_DL_STARTED; - pwpnode = pwpnode->GetNext(); // PlugInWaypoint +#elif defined(OCPN_USE_CURL) + if (g_pi_manager->m_pCurlThread) // We allow just one download at a time. Do + // we want more? Or at least return some + // other status in this case? + return OCPN_DL_FAILED; + g_pi_manager->m_pCurlThread = + new wxCurlDownloadThread(g_pi_manager, CurlThreadId); + bool http = (url.StartsWith(wxS("http:")) || url.StartsWith(wxS("https:"))); + bool keep = false; + if (http && g_pi_manager->m_pCurl && + dynamic_cast(g_pi_manager->m_pCurl.get())) { + keep = true; + } + if (!keep) { + g_pi_manager->m_pCurl = 0; } - route->m_PlannedDeparture = plannedDeparture; - - route->m_RouteNameString = proute->m_NameString; - route->m_RouteStartString = proute->m_StartString; - route->m_RouteEndString = proute->m_EndString; - if (!proute->m_GUID.IsEmpty()) { - route->m_GUID = proute->m_GUID; + bool failed = false; + if (!g_pi_manager->HandleCurlThreadError( + g_pi_manager->m_pCurlThread->SetURL(url, g_pi_manager->m_pCurl), + g_pi_manager->m_pCurlThread, url)) + failed = true; + if (!failed) { + g_pi_manager->m_pCurl = g_pi_manager->m_pCurlThread->GetCurlSharedPtr(); + if (!g_pi_manager->HandleCurlThreadError( + g_pi_manager->m_pCurlThread->SetOutputStream( + new wxFileOutputStream(outputFile)), + g_pi_manager->m_pCurlThread)) + failed = true; } - route->m_btemp = (b_permanent == false); - route->SetVisible(proute->m_isVisible); - route->m_RouteDescription = proute->m_Description; + if (!failed) { + g_pi_manager->m_download_evHandler = handler; + g_pi_manager->m_downloadHandle = handle; - pRouteList->Append(route); + wxCurlThreadError err = g_pi_manager->m_pCurlThread->Download(); + if (err != wxCTE_NO_ERROR) { + g_pi_manager->HandleCurlThreadError( + err, g_pi_manager->m_pCurlThread); // shows a message to the user + g_pi_manager->m_pCurlThread->Abort(); + failed = true; + } + } - if (b_permanent) pConfig->AddNewRoute(route); + if (!failed) return OCPN_DL_STARTED; - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateRouteListCtrl(); + if (g_pi_manager->m_pCurlThread) { + if (g_pi_manager->m_pCurlThread->IsAlive()) + g_pi_manager->m_pCurlThread->Abort(); + if (g_pi_manager->m_pCurlThread->GetOutputStream()) + delete (g_pi_manager->m_pCurlThread->GetOutputStream()); + wxDELETE(g_pi_manager->m_pCurlThread); + g_pi_manager->m_download_evHandler = NULL; + g_pi_manager->m_downloadHandle = NULL; + return OCPN_DL_STARTED; + } + g_pi_manager->m_pCurl = 0; + return OCPN_DL_FAILED; - return true; +#else + return OCPN_DL_FAILED; +#endif } -bool UpdatePlugInRouteEx(PlugIn_Route_Ex* proute) { - bool b_found = false; - - // Find the Route - Route* pRoute = g_pRouteMan->FindRouteByGUID(proute->m_GUID); - if (pRoute) b_found = true; - - if (b_found) { - bool b_permanent = !pRoute->m_btemp; - g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance()); +void OCPN_cancelDownloadFileBackground(long handle) { +#ifdef OCPN_USE_CURL - b_found = AddPlugInRouteEx(proute, b_permanent); +#ifdef __ANDROID__ + cancelAndroidFileDownload(handle); + finishAndroidFileDownload(); + if (g_piEventHandler) g_piEventHandler->clearBackgroundMode(); +#else + if (g_pi_manager->m_pCurlThread) { + g_pi_manager->m_pCurlThread->Abort(); + delete (g_pi_manager->m_pCurlThread->GetOutputStream()); + wxDELETE(g_pi_manager->m_pCurlThread); + g_pi_manager->m_download_evHandler = NULL; + g_pi_manager->m_downloadHandle = NULL; } - - return b_found; + g_pi_manager->m_pCurl = 0; +#endif +#endif } -// std::unique_ptr GetWaypointEx_Plugin(const wxString &) -// { -// } +_OCPN_DLStatus OCPN_postDataHttp(const wxString& url, + const wxString& parameters, wxString& result, + int timeout_secs) { +#ifdef __ANDROID__ + wxString lparms = parameters; + wxString postResult = doAndroidPOST(url, lparms, timeout_secs * 1000); + if (postResult.IsSameAs(_T("NOK"))) return OCPN_DL_FAILED; -// std::unique_ptr GetRouteEx_Plugin(const wxString &) -// { -// } + result = postResult; + return OCPN_DL_NO_ERROR; -std::unique_ptr GetWaypointEx_Plugin(const wxString& GUID) { - std::unique_ptr w(new PlugIn_Waypoint_Ex); - GetSingleWaypointEx(GUID, w.get()); - return w; -} +#elif defined(OCPN_USE_CURL) + wxCurlHTTP post; + post.SetOpt(CURLOPT_TIMEOUT, timeout_secs); + size_t res = post.Post(parameters.ToAscii(), parameters.Len(), url); -std::unique_ptr GetRouteEx_Plugin(const wxString& GUID) { - std::unique_ptr r; - Route* route = g_pRouteMan->FindRouteByGUID(GUID); - if (route == nullptr) return r; + if (res) { + result = wxString(post.GetResponseBody().c_str(), wxConvUTF8); + return OCPN_DL_NO_ERROR; + } else + result = wxEmptyString; - r = std::unique_ptr(new PlugIn_Route_Ex); - PlugIn_Route_Ex* dst_route = r.get(); + return OCPN_DL_FAILED; - // PlugIn_Waypoint *pwp; - RoutePoint* src_wp; - wxRoutePointListNode* node = route->pRoutePointList->GetFirst(); +#else + return OCPN_DL_FAILED; +#endif +} - while (node) { - src_wp = node->GetData(); +bool OCPN_isOnline() { +#ifdef __ANDROID__ + return androidCheckOnline(); +#endif - PlugIn_Waypoint_Ex* dst_wp = new PlugIn_Waypoint_Ex(); - PlugInExFromRoutePoint(dst_wp, src_wp); +#if !defined(__ANDROID__) && defined(OCPN_USE_CURL) + if (wxDateTime::GetTimeNow() > + g_pi_manager->m_last_online_chk + ONLINE_CHECK_RETRY) { + wxCurlHTTP get; + get.Head(_T("http://yahoo.com/")); + g_pi_manager->m_last_online = get.GetResponseCode() > 0; - dst_route->pWaypointList->Append(dst_wp); + g_pi_manager->m_last_online_chk = wxDateTime::GetTimeNow(); + } + return g_pi_manager->m_last_online; +#else + return false; +#endif +} - node = node->GetNext(); +#if !defined(__ANDROID__) && defined(OCPN_USE_CURL) +void PlugInManager::OnEndPerformCurlDownload(wxCurlEndPerformEvent& ev) { + OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0); + if (ev.IsSuccessful()) { + event.setDLEventStatus(OCPN_DL_NO_ERROR); + } else { + g_pi_manager->m_pCurl = 0; + event.setDLEventStatus(OCPN_DL_FAILED); } - dst_route->m_NameString = route->m_RouteNameString; - dst_route->m_StartString = route->m_RouteStartString; - dst_route->m_EndString = route->m_RouteEndString; - dst_route->m_GUID = route->m_GUID; - dst_route->m_isActive = g_pRouteMan->GetpActiveRoute() == route; - dst_route->m_isVisible = route->IsVisible(); - dst_route->m_Description = route->m_RouteDescription; + event.setDLEventCondition(OCPN_DL_EVENT_TYPE_END); + event.setComplete(true); - return r; -} + if (m_download_evHandler) { + m_download_evHandler->AddPendingEvent(event); + m_download_evHandler = NULL; + m_downloadHandle = NULL; + } -wxString GetActiveWaypointGUID( - void) { // if no active waypoint, returns wxEmptyString - RoutePoint* rp = g_pRouteMan->GetpActivePoint(); - if (!rp) - return wxEmptyString; - else - return rp->m_GUID; + if (m_pCurlThread) { + m_pCurlThread->Wait(); + if (!m_pCurlThread->IsAborting()) { + delete (m_pCurlThread->GetOutputStream()); + wxDELETE(m_pCurlThread); + } + } } -wxString GetActiveRouteGUID( - void) { // if no active route, returns wxEmptyString - Route* rt = g_pRouteMan->GetpActiveRoute(); - if (!rt) - return wxEmptyString; - else - return rt->m_GUID; +void PlugInManager::OnCurlDownload(wxCurlDownloadEvent& ev) { + OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0); + event.setDLEventStatus(OCPN_DL_UNKNOWN); + event.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS); + event.setTotal(ev.GetTotalBytes()); + event.setTransferred(ev.GetDownloadedBytes()); + event.setComplete(false); + + if (m_download_evHandler) { + m_download_evHandler->AddPendingEvent(event); + } } -/** Comm Global Watchdog Query */ -int GetGlobalWatchdogTimoutSeconds() { return gps_watchdog_timeout_ticks; } +bool PlugInManager::HandleCurlThreadError(wxCurlThreadError err, + wxCurlBaseThread* p, + const wxString& url) { + switch (err) { + case wxCTE_NO_ERROR: + return true; // ignore this -/** Comm Priority query support methods */ -std::vector GetPriorityMaps() { - MyApp& app = wxGetApp(); - return (app.m_comm_bridge.GetPriorityMaps()); -} + case wxCTE_NO_RESOURCE: + wxLogError( + wxS("Insufficient resources for correct execution of the program.")); + break; -std::vector GetActivePriorityIdentifiers() { - std::vector result; + case wxCTE_ALREADY_RUNNING: + wxFAIL; // should never happen! + break; - MyApp& app = wxGetApp(); + case wxCTE_INVALID_PROTOCOL: + wxLogError(wxS("The URL '%s' uses an unsupported protocol."), + url.c_str()); + break; - std::string id = - app.m_comm_bridge.GetPriorityContainer("position").active_source; - result.push_back(id); - id = app.m_comm_bridge.GetPriorityContainer("velocity").active_source; - result.push_back(id); - id = app.m_comm_bridge.GetPriorityContainer("heading").active_source; - result.push_back(id); - id = app.m_comm_bridge.GetPriorityContainer("variation").active_source; - result.push_back(id); - id = app.m_comm_bridge.GetPriorityContainer("satellites").active_source; - result.push_back(id); + case wxCTE_NO_VALID_STREAM: + wxFAIL; // should never happen - the user streams should always be valid! + break; - return result; -} + case wxCTE_ABORTED: + return true; // ignore this -double OCPN_GetDisplayContentScaleFactor() { - double rv = 1.0; -#if defined(__WXOSX__) || defined(__WXGTK3__) - // Support scaled HDPI displays. - if (gFrame) rv = gFrame->GetContentScaleFactor(); -#endif - return rv; + case wxCTE_CURL_ERROR: { + wxString ws = wxS("unknown"); + if (p->GetCurlSession()) + ws = + wxString(p->GetCurlSession()->GetErrorString().c_str(), wxConvUTF8); + wxLogError(wxS("Network error: %s"), ws.c_str()); + } break; + } + + // stop the thread + if (p->IsAlive()) p->Abort(); + + // this is an unrecoverable error: + return false; } -double OCPN_GetWinDIPScaleFactor() { - double scaler = 1.0; -#ifdef __WXMSW__ - if (gFrame) scaler = (double)(gFrame->ToDIP(100)) / 100.; #endif - return scaler; -} diff --git a/gui/src/s57chart.cpp b/gui/src/s57chart.cpp index 77a66663f4..3a9b09b577 100644 --- a/gui/src/s57chart.cpp +++ b/gui/src/s57chart.cpp @@ -5772,6 +5772,21 @@ wxString s57chart::CreateObjDescriptions(ListOfObjRazRules *rule_list) { file.Assign(GetFullPath()); file.Assign(file.GetPath(), value); file.Normalize(); + // Make the filecheck case-unsensitive (linux) + if (file.IsCaseSensitive()) { + wxDir dir(file.GetPath()); + wxString filename; + bool cont = dir.GetFirst(&filename, "", wxDIR_FILES); + while (cont) { + if (filename.IsSameAs(value, false)) { + value = filename; + file.Assign(file.GetPath(), value); + break; + } + cont = dir.GetNext(&filename); + } + } + if (file.IsOk()) { if (file.Exists()) value = diff --git a/gui/src/shapefile_basemap.cpp b/gui/src/shapefile_basemap.cpp index e05c4f063e..346d1c5806 100644 --- a/gui/src/shapefile_basemap.cpp +++ b/gui/src/shapefile_basemap.cpp @@ -32,6 +32,8 @@ #include "chartbase.h" #include "glChartCanvas.h" +#include "model/logger.h" + #ifdef ocpnUSE_GL #include "shaders.h" #endif @@ -249,6 +251,10 @@ void ShapeBaseChartSet::LoadBasemaps(const std::string &dir) { bool ShapeBaseChart::LoadSHP() { _reader = new shp::ShapefileReader(_filename); + if (!_reader->isOpen()) { + MESSAGE_LOG << "Shapefile " << _filename << " is not opened"; + return false; + } auto bounds = _reader->getBounds(); _is_usable = _reader->getCount() > 1 && bounds.getMaxX() <= 180 && bounds.getMinX() >= -180 && bounds.getMinY() >= -90 && diff --git a/gui/src/udev_rule_mgr.cpp b/gui/src/udev_rule_mgr.cpp index 318addaa11..9c1e2b802a 100644 --- a/gui/src/udev_rule_mgr.cpp +++ b/gui/src/udev_rule_mgr.cpp @@ -17,7 +17,11 @@ * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -/** \file udev_rule_mgr.cpp Implement udev_rule_mgr.h */ + +/** + * \file + * Implement udev_rule_mgr.h + */ #include "config.h" diff --git a/gui/src/undo.cpp b/gui/src/undo.cpp index c4648a8936..8b4d1af174 100644 --- a/gui/src/undo.cpp +++ b/gui/src/undo.cpp @@ -132,6 +132,8 @@ void doUndoDeleteWaypoint(UndoAction* action, ChartCanvas* cc) { RoutePoint* point = (RoutePoint*)action->before[0]; pSelect->AddSelectableRoutePoint(point->m_lat, point->m_lon, point); pConfig->AddNewWayPoint(point, -1); + // transfer ownership to WayPointman which eventually deletes it. + // This is certainly not how things should be and needs an overhaul. if (NULL != pWayPointMan) pWayPointMan->AddRoutePoint(point); if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) pRouteManagerDialog->UpdateWptListCtrl(); diff --git a/include/ocpn_plugin.h b/include/ocpn_plugin.h index a14952ab49..90ca209589 100644 --- a/include/ocpn_plugin.h +++ b/include/ocpn_plugin.h @@ -1,11 +1,5 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: PlugIn Object Definition/API - * Author: David Register - * - *************************************************************************** - * Copyright (C) 2010 by David S. Register * +/************************************************************************** + * Copyright (C) 2010 - 2024 by David S. Register * * * * 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 * @@ -23,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file ocpn_plugin.h. */ +/** + * \file + * PlugIn Object Definition/API + */ #ifndef _PLUGIN_H_ #define _PLUGIN_H_ @@ -105,6 +102,7 @@ class wxGLCanvas; #define WANTS_MOUSE_EVENTS 0x00080000 #define WANTS_VECTOR_CHART_OBJECT_INFO 0x00100000 #define WANTS_KEYBOARD_EVENTS 0x00200000 +#define WANTS_PRESHUTDOWN_HOOK 0x00400000 //--------------------------------------------------------------------------------------------------------- // @@ -635,6 +633,14 @@ class DECL_EXP opencpn_plugin_118 : public opencpn_plugin_117 { } #endif }; + +class DECL_EXP opencpn_plugin_119 : public opencpn_plugin_118 { +public: + opencpn_plugin_119(void *pmgr); + + virtual void PreShutdownHook(); +}; + //------------------------------------------------------------------ // Route and Waypoint PlugIn support // @@ -1730,7 +1736,7 @@ extern DECL_EXP std::vector GetN2000Payload(NMEA2000Id id, * - "Context": string, message context * - "ContextSelf": string, own ship context. */ -std::shared_ptr GetSignalkPayload(ObservedEvt ev); +extern DECL_EXP std::shared_ptr GetSignalkPayload(ObservedEvt ev); /** * Return source identifier (iface) of a received n2000 message of type id @@ -1850,14 +1856,68 @@ extern DECL_EXP CommDriverResult RegisterTXPGNs(DriverHandle handle, // API 1.19 // +// Navigation mode +typedef enum _PI_NavMode { + PI_NORTH_UP_MODE = 0, + PI_COURSE_UP_MODE, + PI_HEAD_UP_MODE, +} PI_NavMode; + /** Facade for NavAddrPluginMsg. */ struct PluginMsgId { const std::string id; PluginMsgId(const std::string &s) : id(s) {}; }; +/** Return listener for plugin messages received on the REST interface. */ extern DECL_EXP std::shared_ptr GetListener( PluginMsgId id, wxEventType ev, wxEvtHandler *handler); +/** Retrieve the string in a plugin message received on the REST insterface. */ extern DECL_EXP std::string GetPluginMsgPayload(PluginMsgId id, ObservedEvt ev); + +// Assorted GUI utility functions +void ExitOCPN(); + +bool GetFullScreen(); +void SetFullScreen(bool full_screen_on); + +void EnableMUIBar(bool enable); +void EnableCompassGPSIcon(bool enable); +void EnableStatusBar(bool enable); +void EnableChartBar(bool enable); +void EnableMenu(bool enable); + +void SetGlobalColor(std::string table, std::string name, wxColor color); + +/* + * Allow plugin control of "Chart Panel Options" dialog + */ + +void EnableLatLonGrid(bool enable); + +void EnableChartOutlines(bool enable); + +void EnableDepthUnitDisplay(bool enable); + +void EnableAisTargetDisplay(bool enable); + +void EnableTideStationsDisplay(bool enable); + +void EnableCurrentStationsDisplay(bool enable); + +void EnableENCTextDisplay(bool enable); + +void EnableENCDepthSoundingsDisplay(bool enable); + +void EnableBuoyLightLabelsDisplay(bool enable); + +void EnableLightsDisplay(bool enable); + +void EnableLightDescriptionsDisplay(bool enable); + +void SetENCDisplayCategory(PI_DisCat cat); + +void SetNavigationMode(PI_NavMode mode); + #endif //_PLUGIN_H_ diff --git a/libs/ShapefileCpp/lib/include/ShapefileReader.hpp b/libs/ShapefileCpp/lib/include/ShapefileReader.hpp index 1135f9a486..141ebe9102 100644 --- a/libs/ShapefileCpp/lib/include/ShapefileReader.hpp +++ b/libs/ShapefileCpp/lib/include/ShapefileReader.hpp @@ -62,6 +62,9 @@ namespace shp { ~ShapefileReader(); + /** Return true if the reader has opened and loaded the shapefile successfully. */ + bool isOpen(); + int getCount(); GeometryType getGeometryType(); diff --git a/libs/ShapefileCpp/lib/src/Point.cpp b/libs/ShapefileCpp/lib/src/Point.cpp index a921ce88f7..4d61853cda 100644 --- a/libs/ShapefileCpp/lib/src/Point.cpp +++ b/libs/ShapefileCpp/lib/src/Point.cpp @@ -2,9 +2,9 @@ namespace shp { - Point::Point(double xCoord, double yCoord) : x(xCoord), y(yCoord), z(NAN), m(NAN) {} + Point::Point(double xCoord, double yCoord) : x(xCoord), y(yCoord), m(NAN), z(NAN) {} - Point::Point(double xCoord, double yCoord, double zValue, double mValue) : x(xCoord), y(yCoord), z(zValue), m(mValue) {} + Point::Point(double xCoord, double yCoord, double zValue, double mValue) : x(xCoord), y(yCoord), m(mValue), z(zValue) {} std::unique_ptr Point::clone() const { return std::make_unique(x,y); @@ -33,11 +33,11 @@ namespace shp { } else { str << "POINT "; if (!isnan(z) && !isnan(m)) { - str << "ZM "; + str << "ZM "; } else if (!isnan(z)) { - str << "Z "; + str << "Z "; } else if (!isnan(m)) { - str << "M "; + str << "M "; } str << "("; str << x << " "; @@ -49,7 +49,7 @@ namespace shp { str << " " << m; } str << ")"; - + } return str.str(); } @@ -74,4 +74,4 @@ namespace shp { return Point{NAN, NAN}; } -} \ No newline at end of file +} diff --git a/libs/ShapefileCpp/lib/src/ShapefileReader.cpp b/libs/ShapefileCpp/lib/src/ShapefileReader.cpp index 724fcf7291..5bb63fbc66 100644 --- a/libs/ShapefileCpp/lib/src/ShapefileReader.cpp +++ b/libs/ShapefileCpp/lib/src/ShapefileReader.cpp @@ -13,6 +13,10 @@ namespace shp { DBFClose(dbf); } + bool ShapefileReader::isOpen() { + return shp && dbf; + } + void ShapefileReader::getShapeInfo() { if (count == -1) { SHPGetInfo(shp, &count, &shapeType, min, max); diff --git a/libs/android_jvm/include/android_jvm.h b/libs/android_jvm/include/android_jvm.h index 45c97de7bc..9d2f6dcd69 100644 --- a/libs/android_jvm/include/android_jvm.h +++ b/libs/android_jvm/include/android_jvm.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file android_jvm.h Singleton Android Java JVM interface */ +/** + * \file + * Singleton Android Java JVM interface + */ #ifndef ANDROID_JVM_H #define ANDROID_JVM_H diff --git a/libs/android_jvm/src/android_jvm.cpp b/libs/android_jvm/src/android_jvm.cpp index bc1ed760ef..b2069ac9da 100644 --- a/libs/android_jvm/src/android_jvm.cpp +++ b/libs/android_jvm/src/android_jvm.cpp @@ -17,7 +17,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file android_jvm.cpp Singleton Android Java JVM interface */ +/** + * \file + * Singleton Android Java JVM interface implementation + */ + #include #include #include diff --git a/libs/manual/include/manual.h b/libs/manual/include/manual.h index c0271c793b..575be411cf 100644 --- a/libs/manual/include/manual.h +++ b/libs/manual/include/manual.h @@ -1,4 +1,3 @@ - /*************************************************************************** * Copyright (C) 2024 Alec Leamas * * * @@ -18,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file manual.h Tools to invoke the manual provided by manual_pi. */ +/** + * \file + * Tools to invoke the manual provided by manual_pi. + */ #ifndef MANUAL_H_ #define MANUAL_H_ diff --git a/libs/manual/include/manual_dlg.h b/libs/manual/include/manual_dlg.h index e27326be98..72c4dbf232 100644 --- a/libs/manual/include/manual_dlg.h +++ b/libs/manual/include/manual_dlg.h @@ -1,4 +1,3 @@ - /*************************************************************************** * Copyright (C) 2024 Alec Leamas * * * @@ -18,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file manual_dlg.h The "manual not found" dialog. */ +/** + * \file + * The "manual not found" dialog. + */ #ifndef MANUAL_DLG_H_ #define MANUAL_DLG_H_ diff --git a/libs/manual/src/manual.cpp b/libs/manual/src/manual.cpp index 798970ad81..c3e65a46aa 100644 --- a/libs/manual/src/manual.cpp +++ b/libs/manual/src/manual.cpp @@ -18,7 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file manual.cpp Implement manual.h */ +/** + * \file + * Implement manual.h + */ #include #include diff --git a/libs/manual/src/manual_dlg.cpp b/libs/manual/src/manual_dlg.cpp index be22c9dc78..c4b6b41d06 100644 --- a/libs/manual/src/manual_dlg.cpp +++ b/libs/manual/src/manual_dlg.cpp @@ -1,4 +1,3 @@ - /*************************************************************************** * Copyright (C) 2024 Alec Leamas * * * @@ -18,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file manual_dlg.cpp Implement manual_dlg.h */ +/** + * \file + * Implement manual_dlg.h + */ #include #include diff --git a/libs/s52plib/src/chartsymbols.cpp b/libs/s52plib/src/chartsymbols.cpp index b53a81e454..b5a6ec34e4 100644 --- a/libs/s52plib/src/chartsymbols.cpp +++ b/libs/s52plib/src/chartsymbols.cpp @@ -841,6 +841,25 @@ int ChartSymbols::FindColorTable(const wxString &tableName) { return 0; } +bool ChartSymbols::UpdateTableColor( std::string table_name, std::string color_name, wxColor& new_color){ + int table_index = FindColorTable( wxString(table_name.c_str())); + + colTable *colortable = (colTable *)m_colorTables.Item(table_index); + wxString key(color_name.c_str(), wxConvUTF8, 5); + + colortable->wxColors[key] = new_color; + if (colortable->colors.find(key) != colortable->colors.end()) { + S52color *color = &colortable->colors[key]; + color->R = new_color.Red(); + color->G = new_color.Green(); + color->B = new_color.Blue(); + return true; + } + else + return false; +} + + wxString ChartSymbols::HashKey(const char *symbolName) { char key[9]; key[8] = 0; diff --git a/libs/s52plib/src/chartsymbols.h b/libs/s52plib/src/chartsymbols.h index 8ecee46fad..f6261063ae 100755 --- a/libs/s52plib/src/chartsymbols.h +++ b/libs/s52plib/src/chartsymbols.h @@ -179,6 +179,7 @@ class ChartSymbols { wxString configFileDirectory; int ColorTableIndex; symbolGraphicsHashMap m_symbolGraphicLocations; + bool UpdateTableColor( std::string table_name, std::string color_name, wxColor& new_color); private: void BuildLineStyle(LineStyle &lineStyle); diff --git a/libs/wxservdisc/1035.c b/libs/wxservdisc/1035.c index dd0955e0af..5806053a2c 100644 --- a/libs/wxservdisc/1035.c +++ b/libs/wxservdisc/1035.c @@ -89,7 +89,7 @@ void _label(struct message *m, unsigned char **bufp, unsigned char **namep) *name = '\0'; for(x = 0; x <= 19 && m->_labels[x]; x++) { - if(strcmp(*namep,m->_labels[x])) continue; + if(strcmp((const char *)*namep,(const char *)m->_labels[x])) continue; *namep = m->_labels[x]; return; } @@ -212,7 +212,7 @@ int _rrparse(struct message *m, struct resource *rr, int count, unsigned char ** { case 1: if(m->_len + 16 > MAX_PACKET_LEN) return 0; - rr[i].known.a.name = m->_packet + m->_len; + rr[i].known.a.name = (char *)(m->_packet + m->_len); m->_len += 16; sprintf(rr[i].known.a.name,"%d.%d.%d.%d",(*bufp)[0],(*bufp)[1],(*bufp)[2],(*bufp)[3]); rr[i].known.a.ip = net2long(bufp); diff --git a/libs/wxservdisc/mdnsd.c b/libs/wxservdisc/mdnsd.c index 63489047fe..edb575cece 100644 --- a/libs/wxservdisc/mdnsd.c +++ b/libs/wxservdisc/mdnsd.c @@ -10,6 +10,7 @@ #include #endif +#undef bzero #define bzero(b,len) (memset((b), '\0', (len)), (void) 0) // size of query/publish hashes @@ -168,7 +169,7 @@ struct cached *_c_next(mdnsd d, struct cached *c, char *host, int type) if(c == 0) c = d->cache[_namehash(host) % LPRIME]; else c = c->next; for(;c != 0; c = c->next) - if((type == c->rr.type || type == 255) && strcmp(c->rr.name, host) == 0) + if((type == c->rr.type || type == 255) && strcmp((const char *)c->rr.name, host) == 0) return c; return 0; } @@ -177,7 +178,7 @@ mdnsdr _r_next(mdnsd d, mdnsdr r, char *host, int type) if(r == 0) r = d->published[_namehash(host) % SPRIME]; else r = r->next; for(;r != 0; r = r->next) - if(type == r->rr.type && strcmp(r->rr.name, host) == 0) + if(type == r->rr.type && strcmp((const char *)r->rr.name, host) == 0) return r; return 0; } @@ -186,7 +187,7 @@ int _rr_len(mdnsda rr) { int len = 12; // name is always compressed (dup of earlier), plus normal stuff if(rr->rdata) len += rr->rdlen; - if(rr->rdname) len += strlen(rr->rdname); // worst case + if(rr->rdname) len += strlen((const char *)rr->rdname); // worst case if(rr->ip) len += 4; if(rr->type == QTYPE_PTR) len += 6; // srv record stuff return len; @@ -194,9 +195,9 @@ int _rr_len(mdnsda rr) int _a_match(struct resource *r, mdnsda a) { // compares new rdata with known a, painfully - if(strcmp(r->name,a->name) || r->type != a->type) return 0; - if(r->type == QTYPE_SRV && !strcmp(r->known.srv.name,a->rdname) && a->srv.port == r->known.srv.port && a->srv.weight == r->known.srv.weight && a->srv.priority == r->known.srv.priority) return 1; - if((r->type == QTYPE_PTR || r->type == QTYPE_NS || r->type == QTYPE_CNAME) && !strcmp(a->rdname,r->known.ns.name)) return 1; + if(strcmp((const char *)r->name,(const char *)a->name) || r->type != a->type) return 0; + if(r->type == QTYPE_SRV && !strcmp((const char *)r->known.srv.name,(const char *)a->rdname) && a->srv.port == r->known.srv.port && a->srv.weight == r->known.srv.weight && a->srv.priority == r->known.srv.priority) return 1; + if((r->type == QTYPE_PTR || r->type == QTYPE_NS || r->type == QTYPE_CNAME) && !strcmp((const char *)a->rdname,(const char *)r->known.ns.name)) return 1; if(r->rdlength == a->rdlen && !memcmp(r->rdata,a->rdata,r->rdlength)) return 1; return 0; } @@ -271,7 +272,7 @@ void _q_reset(mdnsd d, struct query *q) struct cached *cur = 0; q->nexttry = 0; q->tries = 0; - while(cur = _c_next(d,cur,q->name,q->type)) + while((cur = _c_next(d,cur,q->name,q->type))) if(q->nexttry == 0 || cur->rr.ttl - 7 < q->nexttry) q->nexttry = cur->rr.ttl - 7; if(q->nexttry != 0 && q->nexttry < d->checkqlist) d->checkqlist = q->nexttry; } @@ -281,7 +282,7 @@ void _q_done(mdnsd d, struct query *q) struct cached *c = 0; struct query *cur; int i = _namehash(q->name) % LPRIME; - while(c = _c_next(d,c,q->name,q->type)) c->q = 0; + while((c = _c_next(d,c,q->name,q->type))) c->q = 0; if(d->qlist == q) d->qlist = q->list; else { for(cur=d->qlist;cur->list != q;cur = cur->list); @@ -299,7 +300,7 @@ void _q_done(mdnsd d, struct query *q) void _r_done(mdnsd d, mdnsdr r) { // buh-bye, remove from hash and free mdnsdr cur = 0; - int i = _namehash(r->rr.name) % SPRIME; + int i = _namehash((const char *)r->rr.name) % SPRIME; if(d->published[i] == r) d->published[i] = r->next; else { for(cur=d->published[i];cur && cur->next != r;cur = cur->next); @@ -319,7 +320,7 @@ void _q_answer(mdnsd d, struct cached *c) void _conflict(mdnsd d, mdnsdr r) { - r->conflict(r->rr.name,r->rr.type,r->arg); + r->conflict((char *)r->rr.name,r->rr.type,r->arg); mdnsd_done(d,r); } @@ -357,17 +358,17 @@ void _gc(mdnsd d) void _cache(mdnsd d, struct resource *r) { struct cached *c = 0; - int i = _namehash(r->name) % LPRIME; + int i = _namehash((const char *)r->name) % LPRIME; if(r->rr_class == 32768 + d->class) { // cache flush - while(c = _c_next(d,c,r->name,r->type)) c->rr.ttl = 0; + while((c = _c_next(d,c,(char *)r->name,r->type))) c->rr.ttl = 0; _c_expire(d,&d->cache[i]); } if(r->ttl == 0) { // process deletes - while(c = _c_next(d,c,r->name,r->type)) + while((c = _c_next(d,c,(char *)r->name,r->type))) if(_a_match(r,&c->rr)) c->rr.ttl = 0; @@ -378,7 +379,7 @@ void _cache(mdnsd d, struct resource *r) c = (struct cached *)malloc(sizeof(struct cached)); bzero(c,sizeof(struct cached)); - c->rr.name = strdup(r->name); + c->rr.name = (unsigned char *)strdup((const char *)r->name); c->rr.type = r->type; c->rr.ttl = d->now.tv_sec + (r->ttl / 2) + 8; // XXX hack for now, BAD SPEC, start retrying just after half-waypoint, then expire c->rr.rdlen = r->rdlength; @@ -392,10 +393,10 @@ void _cache(mdnsd d, struct resource *r) case QTYPE_NS: case QTYPE_CNAME: case QTYPE_PTR: - c->rr.rdname = strdup(r->known.ns.name); + c->rr.rdname = (unsigned char *)strdup((const char *)r->known.ns.name); break; case QTYPE_SRV: - c->rr.rdname = strdup(r->known.srv.name); + c->rr.rdname = (unsigned char *)strdup((const char *)r->known.srv.name); c->rr.srv.port = r->known.srv.port; c->rr.srv.weight = r->known.srv.weight; c->rr.srv.priority = r->known.srv.priority; @@ -403,7 +404,7 @@ void _cache(mdnsd d, struct resource *r) } c->next = d->cache[i]; d->cache[i] = c; - if(c->q = _q_next(d, 0, r->name, r->type)) + if((c->q = _q_next(d, 0, (char *)r->name, r->type))) _q_answer(d,c); } @@ -493,25 +494,25 @@ void mdnsd_in(mdnsd d, struct message *m, unsigned long int ip, unsigned short i { for(i=0;iqdcount;i++) { // process each query - if(m->qd[i].rr_class != d->class || (r = _r_next(d,0,m->qd[i].name,m->qd[i].type)) == 0) continue; + if(m->qd[i].rr_class != d->class || (r = _r_next(d,0,(char *)m->qd[i].name,m->qd[i].type)) == 0) continue; // send the matching unicast reply if(port != 5353) _u_push(d,r,m->id,ip,port); - for(;r != 0; r = _r_next(d,r,m->qd[i].name,m->qd[i].type)) + for(;r != 0; r = _r_next(d,r,(char *)m->qd[i].name,m->qd[i].type)) { // check all of our potential answers if(r->unique && r->unique < 5) { // probing state, check for conflicts for(j=0;jnscount;j++) { // check all to-be answers against our own - if(m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name,m->an[j].name)) continue; + if(m->qd[i].type != m->an[j].type || strcmp((const char *)m->qd[i].name,(const char *)m->an[j].name)) continue; if(!_a_match(&m->an[j],&r->rr)) _conflict(d,r); // this answer isn't ours, conflict! } continue; } for(j=0;jancount;j++) { // check the known answers for this question - if(m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name,m->an[j].name)) continue; + if(m->qd[i].type != m->an[j].type || strcmp((const char *)m->qd[i].name,(const char *)m->an[j].name)) continue; if(_a_match(&m->an[j],&r->rr)) break; // they already have this answer } if(j == m->ancount) _r_send(d,r); @@ -522,7 +523,7 @@ void mdnsd_in(mdnsd d, struct message *m, unsigned long int ip, unsigned short i for(i=0;iancount;i++) { // process each answer, check for a conflict, and cache - if((r = _r_next(d,0,m->an[i].name,m->an[i].type)) != 0 && r->unique && _a_match(&m->an[i],&r->rr) == 0) _conflict(d,r); + if((r = _r_next(d,0,(char *)m->an[i].name,m->an[i].type)) != 0 && r->unique && _a_match(&m->an[i],&r->rr) == 0) _conflict(d,r); _cache(d,&m->an[i]); } } @@ -647,7 +648,7 @@ int mdnsd_out(mdnsd d, struct message *m, unsigned long int *ip, unsigned short // ask questions first, track nextbest time for(q = d->qlist; q != 0; q = q->list) if(q->nexttry > 0 && q->nexttry <= d->now.tv_sec && q->tries < 3) - message_qd(m,q->name,q->type,d->class); + message_qd(m,(unsigned char *)q->name,q->type,d->class); else if(q->nexttry > 0 && (nextbest == 0 || q->nexttry < nextbest)) nextbest = q->nexttry; @@ -669,7 +670,7 @@ int mdnsd_out(mdnsd d, struct message *m, unsigned long int *ip, unsigned short c = 0; while((c = _c_next(d,c,q->name,q->type)) != 0 && c->rr.ttl > d->now.tv_sec + 8 && message_packet_len(m) + _rr_len(&c->rr) < d->frame) { - message_an(m,q->name,q->type,d->class,c->rr.ttl - d->now.tv_sec); + message_an(m,(unsigned char *)q->name,q->type,d->class,c->rr.ttl - d->now.tv_sec); _a_copy(m,&c->rr); } } @@ -738,7 +739,7 @@ void mdnsd_query(mdnsd d, char *host, int type, int (*answer)(mdnsda a, void *ar q->next = d->queries[i]; q->list = d->qlist; d->qlist = d->queries[i] = q; - while(cur = _c_next(d,cur,q->name,q->type)) + while((cur = _c_next(d,cur,q->name,q->type))) cur->q = q; // any cached entries should be associated _q_reset(d,q); q->nexttry = d->checkqlist = d->now.tv_sec; // new questin, immediately send out @@ -763,7 +764,7 @@ mdnsdr mdnsd_shared(mdnsd d, char *host, int type, long int ttl) mdnsdr r; r = (mdnsdr)malloc(sizeof(struct mdnsdr_struct)); bzero(r,sizeof(struct mdnsdr_struct)); - r->rr.name = strdup(host); + r->rr.name = (unsigned char *)strdup((const char *)host); r->rr.type = type; r->rr.ttl = ttl; r->next = d->published[i]; @@ -813,7 +814,7 @@ void mdnsd_set_raw(mdnsd d, mdnsdr r, char *data, int len) void mdnsd_set_host(mdnsd d, mdnsdr r, char *name) { free(r->rr.rdname); - r->rr.rdname = strdup(name); + r->rr.rdname = (unsigned char *)strdup((const char *)name); _r_publish(d,r); } @@ -830,4 +831,3 @@ void mdnsd_set_srv(mdnsd d, mdnsdr r, int priority, int weight, int port, char * r->rr.srv.port = port; mdnsd_set_host(d,r,name); } - diff --git a/manual/modules/ROOT/nav.adoc b/manual/modules/ROOT/nav.adoc index c1d014fd2a..f092a475ed 100644 --- a/manual/modules/ROOT/nav.adoc +++ b/manual/modules/ROOT/nav.adoc @@ -13,7 +13,7 @@ * xref:plugin-messaging.adoc[New Message Plugin API] * xref:gui-model.adoc[Model - Gui code organization] * xref:comm-overview.adoc[Communication Overview] -* xref:code-formatting.adoc[Code Formatting] +* xref:coding-guidelines.adoc[Coding Guidelines] * xref:user-interface-styling.adoc[User Interface Styling] * xref:troubleshooting.adoc[Troubleshooting] * xref:stacktrace.adoc[Creating a stack trace] diff --git a/manual/modules/ROOT/pages/code-formatting.adoc b/manual/modules/ROOT/pages/code-formatting.adoc deleted file mode 100644 index 1949529bf2..0000000000 --- a/manual/modules/ROOT/pages/code-formatting.adoc +++ /dev/null @@ -1,57 +0,0 @@ -= Code Formatting - -== C/C++ - -The sources have a uniform coding formatting based on the -https://google.github.io/styleguide/cppguide.html#Formatting[Google Style]. - -The style is defined in the _.clang-format_ file which when used -by https://clang.llvm.org/docs/ClangFormat.html[clang-format] produces a -correctly formatted source file. - -Since the 5.10 release all source file modifications must be processed by -clang-format to be accepted. The recommended way to do this is using a git -pre-commit hook which applies formatting when committing changes - -To do this, just install the pre-commit program from https://pre-commit.com/: - - $ pip install pre-commit - -and then, in the project top directory - - $ pre-commit install - -which will pick up the configuration from _.pre-commit-config.yaml_ which is -part of the project. - -== CMake - -Cmake file uses formatting defined by the _.cmake-format.yaml_ which when used -with https://github.com/cheshirekow/cmake_format[cmake-format] produces -correctly formatted files. This has been applied to all _cmake/*.cmake_ files -and CMakeLists.txt. - -== Using clang-format - -clang is part of the llvm tools. These are usually installed using package -managers like choco (Windows), brew (MacOS) or apt (Debian/Ubuntu). - -To format a C, C++ or header file with clang-format with the project's default -configuration use: - - clang-format -i - -== Using cmake-format - -Install cmake-format as described in -https://github.com/cheshirekow/cmake_format[]. To format a single file use - - cmake-format -i - -== Editor and IDE configuration - -There is a _.editorconfig_ file in the top directory. -This file could be used by most editors to define basic settings. - -There are no standard configurations available for IDEs like Visual Studio -Code, Eclipse or CLion. diff --git a/manual/modules/ROOT/pages/coding-guidelines.adoc b/manual/modules/ROOT/pages/coding-guidelines.adoc new file mode 100644 index 0000000000..f717186418 --- /dev/null +++ b/manual/modules/ROOT/pages/coding-guidelines.adoc @@ -0,0 +1,158 @@ += Coding Guidelines + +== Guidelines for Submitters + +=== General Principles + +Follow the https://google.github.io/styleguide/cppguide.html[Google Style Guide] +besides exceptions found in this document for all `C++` code. + +Be aware that existing code may not always reflect current best practices or project preferences. +The codebase evolves, and what was once standard may now be outdated. Refer to this document for up-to-date guidelines. + +=== Naming Conventions + +. File names: use `snake_case`. Use `.h` for headers. Keep reasonably short. +. Variable names: use `lower_case_names`. +. Class member variables: use `m_lower_case`. +. Class and methods names: use `PascalCase`. Avoid upper case acronyms, use OcpnFoo and NmeaFoo rather than OCPNFoo and NMEAFoo. +. Constants: Pascal case + a 'k' prefix: `kSomeConstantValue` + +=== C++ Practices + +Use C++ standard library components over wxWidgets and old style C equivalents: + +[options="header"] +|=== +| Avoid (wxWidgets, C) | Use Instead (C++ Standard Library) | +| `wxMutex` | `std::mutex` and `std::lock_guard` | +| `wxHashTable` | `std::unordered_map` | +| `wxList`, `wxArray` | `std::vector` or other STL containers | +| `wxThread` | `std::thread`, `std::async` | +| `wxString` | `std::string` or `std::wstring` | +| `open()` | `std::ifstream`, `std::ofstream`, etc | +|=== + +=== Memory Management + +. Do not use raw pointers and `new`/`delete`. + The exception is when raw pointers are required by external libraries + like wxWidgets. +. Use `std::unique_ptr` or `std::shared_ptr` for better memory management. + +=== String Handling + +. Use double-quoted strings for unicode strings. +. Do not use `_T()` or `wxT()` macros. +See link:++https://docs.wxwidgets.org%2F3.2%2Fgroup__group__funcmacro__string.html%23ga437ea6ba615b75dac8603e96ec864160++[wxWidgets string guidelines]. + +=== Exception handling +OpenCPN is compiled with exceptions enabled to handle third party libraries. +However, most of OpenCPN is not exception safe. Thus: + +. Do not throw exceptions. +. When using code which throws exceptions these should be caught as soon as possible + +=== Code Documentation + +. Document code in doxygen format, + see https://www.doxygen.nl/manual/docblocks.html[doxygen manual] +. Almost everything is documented in the header files. +. Omit `@brief` and `@details` tags. The first sentence automatically becomes the brief description. +. Avoid redundant phrases like "This class is..." +. Test generated documentation locally. See `manual` sub-directory. + +For an extensive example see +https://github.com/OpenCPN/OpenCPN/blob/master/model/include/model/rest_server.h[rest_server.h], +results in the http://opencpn.github.io/OpenCPN/api-docs/classAbstractRestServer.html[API docs] + +=== Error Handling and Logging + +. Use appropriate logging levels (DEBUG, INFO, WARNING, ERROR) consistently. +. Don't silently fail or ignore errors. +. Provide meaningful i18n error messages and context. + +=== Code Refactoring + +. When working with existing code, gradually refactor to comply with coding guidelines. +. Do not submit large PRs that refactor a lot of code changes. + +== Code Formatting + +=== C/C++ + +The sources have a uniform coding formatting based on the +https://google.github.io/styleguide/cppguide.html#Formatting[Google Style]. + +The style is defined in the _.clang-format_ file which when used +by https://clang.llvm.org/docs/ClangFormat.html[clang-format] produces a +correctly formatted source file. + +Since the 5.10 release all source file modifications must be processed by +clang-format to be accepted. The recommended way to do this is using a git +pre-commit hook which applies formatting when committing changes + +To do this, just install the pre-commit program from https://pre-commit.com/: + + $ pip install pre-commit + +and then, in the project top directory + + $ pre-commit install + +which will pick up the configuration from _.pre-commit-config.yaml_ which is +part of the project. + +=== CMake + +Cmake file uses formatting defined by the _.cmake-format.yaml_ which when used +with https://github.com/cheshirekow/cmake_format[cmake-format] produces +correctly formatted files. This has been applied to all _cmake/*.cmake_ files +and CMakeLists.txt. + +=== Using clang-format + +clang is part of the llvm tools. These are usually installed using package +managers like choco (Windows), brew (MacOS) or apt (Debian/Ubuntu). + +To format a C, C++ or header file with clang-format with the project's default +configuration use: + + clang-format -i + +=== Using cmake-format + +Install cmake-format as described in +https://github.com/cheshirekow/cmake_format[]. To format a single file use + + cmake-format -i + +=== Editor and IDE configuration + +There is a _.editorconfig_ file in the top directory. +This file could be used by most editors to define basic settings. + +There are no standard configurations available for IDEs like Visual Studio +Code, Eclipse or CLion. + +== Guidelines for Reviewers + +=== Performance and Compatibility + +. Ensure changes do not reduce performance. +. Verify existing functionality and plugin compatibility are maintained. + +=== Cross-Platform Considerations + +. Check for consistency across different platforms (Windows, Linux, macOS). +. Be aware of platform-specific behaviors, especially for UI elements. + +=== Display Settings + +. Consider different display settings (e.g., scaled displays, dark modes). +. Plugins should be High DPI Display Responsive. + +=== Version Control Best Practices + +. Write clear, concise commit messages. +. Keep commits focused and atomic (one logical change per commit). diff --git a/manual/modules/ROOT/pages/gui-model.adoc b/manual/modules/ROOT/pages/gui-model.adoc index 138040e04e..5b86d2b8e0 100644 --- a/manual/modules/ROOT/pages/gui-model.adoc +++ b/manual/modules/ROOT/pages/gui-model.adoc @@ -36,16 +36,8 @@ and used without paying attention to the Gui as long as the interface is kept stable. This considerably simplifies code maintenance. - - -== Definition of the Gui and Model parts - -At the time of writing, the Model is defined by the variable MODEL_SRC -in the top level CMakeLists.txt. -Remaining source file makes up the Gui. -Work to make this more visible is likely to be done, for example -by creating separate subdirectories like src/gui and src/model instead -of a single src/ directory. +The model files indeed lives in the _model/_ subdirectory and the gui parts +in _gui/_ == Model -> Gui communication. diff --git a/manual/package-lock.json b/manual/package-lock.json index 3b29974e7d..dc7f7352bb 100644 --- a/manual/package-lock.json +++ b/manual/package-lock.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@antora/cli": "3.1.3", - "npm": "8.11.0" + "npm": "^10.8.3" } }, "node_modules/@antora/asciidoc-loader": { @@ -1452,27 +1452,28 @@ } }, "node_modules/npm": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.11.0.tgz", - "integrity": "sha512-4qmtwHa28J4SPmwCNoQI07KIF/ljmBhhuqG+xNXsIIRpwdKB5OXkMIGfH6KlThR6kzusxlkgR7t1haFDB88dcQ==", + "version": "10.8.3", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.3.tgz", + "integrity": "sha512-0IQlyAYvVtQ7uOhDFYZCGK8kkut2nh8cpAdA9E6FvRSJaTgtZRZgNjlC5ZCct//L73ygrpY93CxXpRJDtNqPVg==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", - "@npmcli/ci-detect", "@npmcli/config", "@npmcli/fs", "@npmcli/map-workspaces", "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", "@npmcli/run-script", + "@sigstore/tuf", "abbrev", "archy", "cacache", "chalk", - "chownr", + "ci-info", "cli-columns", - "cli-table3", - "columnify", "fastest-levenshtein", + "fs-minipass", "glob", "graceful-fs", "hosted-git-info", @@ -1492,13 +1493,13 @@ "libnpmteam", "libnpmversion", "make-fetch-happen", + "minimatch", "minipass", "minipass-pipeline", - "mkdirp", - "mkdirp-infer-owner", "ms", "node-gyp", "nopt", + "normalize-package-data", "npm-audit-report", "npm-install-checks", "npm-package-arg", @@ -1506,19 +1507,16 @@ "npm-profile", "npm-registry-fetch", "npm-user-validate", - "npmlog", - "opener", + "p-map", "pacote", "parse-conflict-json", "proc-log", "qrcode-terminal", "read", - "read-package-json", - "read-package-json-fast", - "readdir-scoped-modules", - "rimraf", "semver", + "spdx-expression-parse", "ssri", + "supports-color", "tar", "text-table", "tiny-relative-date", @@ -1528,381 +1526,539 @@ "write-file-atomic" ], "dev": true, + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.0.4", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/config": "^4.1.0", - "@npmcli/fs": "^2.1.0", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^3.0.1", - "abbrev": "~1.1.1", + "@npmcli/arborist": "^7.5.4", + "@npmcli/config": "^8.3.4", + "@npmcli/fs": "^3.1.1", + "@npmcli/map-workspaces": "^3.0.6", + "@npmcli/package-json": "^5.2.0", + "@npmcli/promise-spawn": "^7.0.2", + "@npmcli/redact": "^2.0.1", + "@npmcli/run-script": "^8.1.0", + "@sigstore/tuf": "^2.3.4", + "abbrev": "^2.0.0", "archy": "~1.0.0", - "cacache": "^16.1.0", - "chalk": "^4.1.2", - "chownr": "^2.0.0", + "cacache": "^18.0.4", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", "cli-columns": "^4.0.0", - "cli-table3": "^0.6.2", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.12", - "glob": "^8.0.1", - "graceful-fs": "^4.2.10", - "hosted-git-info": "^5.0.0", - "ini": "^3.0.0", - "init-package-json": "^3.0.2", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^6.0.2", - "libnpmdiff": "^4.0.2", - "libnpmexec": "^4.0.2", - "libnpmfund": "^3.0.1", - "libnpmhook": "^8.0.2", - "libnpmorg": "^4.0.2", - "libnpmpack": "^4.0.2", - "libnpmpublish": "^6.0.2", - "libnpmsearch": "^5.0.2", - "libnpmteam": "^4.0.2", - "libnpmversion": "^3.0.1", - "make-fetch-happen": "^10.1.5", - "minipass": "^3.1.6", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^7.0.2", + "ini": "^4.1.3", + "init-package-json": "^6.0.3", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^3.0.2", + "libnpmaccess": "^8.0.6", + "libnpmdiff": "^6.1.4", + "libnpmexec": "^8.1.4", + "libnpmfund": "^5.0.12", + "libnpmhook": "^10.0.5", + "libnpmorg": "^6.0.6", + "libnpmpack": "^7.0.4", + "libnpmpublish": "^9.0.9", + "libnpmsearch": "^7.0.6", + "libnpmteam": "^6.0.5", + "libnpmversion": "^6.0.3", + "make-fetch-happen": "^13.0.1", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", "ms": "^2.1.2", - "node-gyp": "^9.0.0", - "nopt": "^5.0.0", - "npm-audit-report": "^3.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.2", - "npm-pick-manifest": "^7.0.1", - "npm-profile": "^6.0.3", - "npm-registry-fetch": "^13.1.1", - "npm-user-validate": "^1.0.1", - "npmlog": "^6.0.2", - "opener": "^1.5.2", - "pacote": "^13.4.1", - "parse-conflict-json": "^2.0.2", - "proc-log": "^2.0.1", + "node-gyp": "^10.2.0", + "nopt": "^7.2.1", + "normalize-package-data": "^6.0.2", + "npm-audit-report": "^5.0.0", + "npm-install-checks": "^6.3.0", + "npm-package-arg": "^11.0.3", + "npm-pick-manifest": "^9.1.0", + "npm-profile": "^10.0.0", + "npm-registry-fetch": "^17.1.0", + "npm-user-validate": "^2.0.1", + "p-map": "^4.0.0", + "pacote": "^18.0.6", + "parse-conflict-json": "^3.0.1", + "proc-log": "^4.2.0", "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^5.0.1", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.1", - "tar": "^6.1.11", + "read": "^3.0.1", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^10.0.6", + "supports-color": "^9.4.0", + "tar": "^6.2.1", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", - "treeverse": "^2.0.0", - "validate-npm-package-name": "^4.0.0", - "which": "^2.0.2", - "write-file-atomic": "^4.0.1" + "treeverse": "^3.0.0", + "validate-npm-package-name": "^5.0.1", + "which": "^4.0.0", + "write-file-atomic": "^5.0.1" }, "bin": { "npm": "bin/npm-cli.js", "npx": "bin/npx-cli.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { - "node": ">=0.1.90" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/npm/node_modules/@gar/promisify": { - "version": "1.1.3", + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", "dev": true, "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", "dev": true, "inBundle": true, "license": "ISC" }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "2.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "5.2.0", + "version": "7.5.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^3.0.0", - "bin-links": "^3.0.0", - "cacache": "^16.0.6", + "@npmcli/fs": "^3.1.1", + "@npmcli/installed-package-contents": "^2.1.0", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/metavuln-calculator": "^7.1.1", + "@npmcli/name-from-folder": "^2.0.0", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.1.0", + "@npmcli/query": "^3.1.0", + "@npmcli/redact": "^2.0.0", + "@npmcli/run-script": "^8.1.0", + "bin-links": "^4.0.4", + "cacache": "^18.0.3", "common-ancestor-path": "^1.0.1", - "json-parse-even-better-errors": "^2.3.1", + "hosted-git-info": "^7.0.2", + "json-parse-even-better-errors": "^3.0.2", "json-stringify-nice": "^1.1.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^5.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.0.5", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^7.2.1", + "npm-install-checks": "^6.2.0", + "npm-package-arg": "^11.0.2", + "npm-pick-manifest": "^9.0.1", + "npm-registry-fetch": "^17.0.1", + "pacote": "^18.0.6", + "parse-conflict-json": "^3.0.0", + "proc-log": "^4.2.0", + "proggy": "^2.0.0", "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", - "walk-up-path": "^1.0.0" + "ssri": "^10.0.6", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" }, "bin": { "arborist": "bin/index.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/ci-detect": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "4.1.0", + "version": "8.3.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/map-workspaces": "^2.0.2", - "ini": "^3.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^5.0.0", - "proc-log": "^2.0.0", - "read-package-json-fast": "^2.0.3", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/package-json": "^5.1.1", + "ci-info": "^4.0.0", + "ini": "^4.1.2", + "nopt": "^7.2.1", + "proc-log": "^4.2.0", "semver": "^7.3.5", - "walk-up-path": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ansi-styles": "^4.3.0" + "walk-up-path": "^3.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/fs": { - "version": "2.1.0", + "version": "3.1.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@gar/promisify": "^1.1.3", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/git": { - "version": "3.0.1", + "version": "5.0.8", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", - "which": "^2.0.2" + "which": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", + "version": "2.1.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" }, "bin": { - "installed-package-contents": "index.js" + "installed-package-contents": "bin/index.js" }, "engines": { - "node": ">= 10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "2.0.3", + "version": "3.0.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" + "@npmcli/name-from-folder": "^2.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0", + "read-package-json-fast": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "3.1.0", + "version": "7.1.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", + "cacache": "^18.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^18.0.0", + "proc-log": "^4.1.0", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/move-file": { + "node_modules/npm/node_modules/@npmcli/name-from-folder": { "version": "2.0.0", "dev": true, "inBundle": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, + "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "2.0.0", + "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "2.0.0", + "version": "5.2.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "json-parse-even-better-errors": "^2.3.1" + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "3.0.0", + "version": "7.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "infer-owner": "^1.0.4" + "which": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "3.0.2", + "version": "8.1.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3" + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@tootallnate/once": { - "version": "2.0.0", + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", "dev": true, "inBundle": true, "license": "MIT", + "optional": true, "engines": { - "node": ">= 10" + "node": ">=14" } }, - "node_modules/npm/node_modules/abbrev": { - "version": "1.1.1", + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "2.3.2", "dev": true, "inBundle": true, - "license": "ISC" + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", + "node_modules/npm/node_modules/@sigstore/core": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "2.3.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "2.3.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "1.2.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "2.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "debug": "4" + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" }, "engines": { - "node": ">= 6.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/agentkeepalive": { - "version": "4.2.1", + "node_modules/npm/node_modules/abbrev": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" + "debug": "^4.3.4" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 14" } }, "node_modules/npm/node_modules/aggregate-error": { @@ -1928,15 +2084,12 @@ } }, "node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", + "version": "6.2.1", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -1954,25 +2107,6 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/are-we-there-yet": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/npm/node_modules/asap": { - "version": "2.0.6", - "dev": true, - "inBundle": true, - "license": "MIT" - }, "node_modules/npm/node_modules/balanced-match": { "version": "1.0.2", "dev": true, @@ -1980,29 +2114,30 @@ "license": "MIT" }, "node_modules/npm/node_modules/bin-links": { - "version": "3.0.1", + "version": "4.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", + "version": "2.3.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm/node_modules/brace-expansion": { @@ -2014,55 +2149,36 @@ "balanced-match": "^1.0.0" } }, - "node_modules/npm/node_modules/builtins": { - "version": "5.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "semver": "^7.0.0" - } - }, "node_modules/npm/node_modules/cacache": { - "version": "16.1.0", + "version": "18.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", + "ssri": "^10.0.0", "tar": "^6.1.11", - "unique-filename": "^1.1.1" + "unique-filename": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/chalk": { - "version": "4.1.2", + "version": "5.3.0", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -2077,16 +2193,31 @@ "node": ">=10" } }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.0.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", + "version": "4.1.1", "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "ip-regex": "^4.1.0" + "ip-regex": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/npm/node_modules/clean-stack": { @@ -2111,44 +2242,17 @@ "node": ">= 10" } }, - "node_modules/npm/node_modules/cli-table3": { - "version": "0.6.2", + "node_modules/npm/node_modules/cmd-shim": { + "version": "6.0.3", "dev": true, "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, + "license": "ISC", "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "mkdirp-infer-owner": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", "dev": true, "inBundle": true, "license": "MIT", @@ -2165,48 +2269,55 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/color-support": { - "version": "1.1.3", + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", "dev": true, "inBundle": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } + "license": "ISC" }, - "node_modules/npm/node_modules/columnify": { - "version": "1.6.0", + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=8.0.0" + "node": ">= 8" } }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", "dev": true, "inBundle": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } }, - "node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", "dev": true, "inBundle": true, - "license": "ISC" + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } }, "node_modules/npm/node_modules/debug": { - "version": "4.3.4", + "version": "4.3.6", "dev": true, "inBundle": true, "license": "MIT", @@ -2228,58 +2339,21 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/debuglog": { - "version": "1.0.1", + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", "dev": true, "inBundle": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/defaults": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" + "node": ">=0.3.1" } }, - "node_modules/npm/node_modules/delegates": { - "version": "1.0.0", + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", "dev": true, "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/depd": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/dezalgo": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/diff": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/npm/node_modules/emoji-regex": { "version": "8.0.0", "dev": true, @@ -2311,160 +2385,117 @@ "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.12", + "version": "1.0.16", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "2.1.0", + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "minipass": "^3.0.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">= 8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/gauge": { - "version": "4.0.4", + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "minipass": "^7.0.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/glob": { - "version": "8.0.1", + "version": "10.4.5", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.10", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/has": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/npm/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", + "version": "4.2.11", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/hosted-git-info": { - "version": "5.0.0", + "version": "7.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "lru-cache": "^7.5.1" + "lru-cache": "^10.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.0", + "version": "4.1.1", "dev": true, "inBundle": true, "license": "BSD-2-Clause" }, "node_modules/npm/node_modules/http-proxy-agent": { - "version": "5.0.0", + "version": "7.0.2", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.1", + "version": "7.0.5", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" + "node": ">= 14" } }, "node_modules/npm/node_modules/iconv-lite": { @@ -2481,15 +2512,15 @@ } }, "node_modules/npm/node_modules/ignore-walk": { - "version": "5.0.1", + "version": "6.0.5", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "minimatch": "^5.0.1" + "minimatch": "^9.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/imurmurhash": { @@ -2510,92 +2541,68 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/infer-owner": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "inBundle": true, - "license": "ISC" - }, "node_modules/npm/node_modules/ini": { - "version": "3.0.0", + "version": "4.1.3", "dev": true, "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/init-package-json": { - "version": "3.0.2", + "version": "6.0.3", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", + "@npmcli/package-json": "^5.0.0", + "npm-package-arg": "^11.0.0", + "promzard": "^1.0.0", + "read": "^3.0.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/ip": { - "version": "1.1.8", + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } }, "node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", + "version": "5.0.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", + "version": "5.1.0", "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "cidr-regex": "^3.1.1" + "cidr-regex": "^4.1.1" }, "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/is-core-module": { - "version": "2.9.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14" } }, "node_modules/npm/node_modules/is-fullwidth-code-point": { @@ -2619,12 +2626,36 @@ "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", "dev": true, "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/json-stringify-nice": { "version": "1.1.4", "dev": true, @@ -2644,221 +2675,214 @@ "license": "MIT" }, "node_modules/npm/node_modules/just-diff": { - "version": "5.0.2", + "version": "6.0.2", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.2.0", + "version": "5.5.0", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { - "version": "6.0.3", + "version": "8.0.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0" + "npm-package-arg": "^11.0.2", + "npm-registry-fetch": "^17.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "4.0.3", + "version": "6.1.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/disparity-colors": "^2.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", - "diff": "^5.0.0", - "minimatch": "^5.0.1", - "npm-package-arg": "^9.0.1", - "pacote": "^13.0.5", - "tar": "^6.1.0" + "@npmcli/arborist": "^7.5.4", + "@npmcli/installed-package-contents": "^2.1.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.6", + "tar": "^6.2.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "4.0.5", + "version": "8.1.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^5.0.0", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/run-script": "^3.0.0", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^9.0.1", - "npmlog": "^6.0.2", - "pacote": "^13.0.5", - "proc-log": "^2.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", - "walk-up-path": "^1.0.0" + "@npmcli/arborist": "^7.5.4", + "@npmcli/run-script": "^8.1.0", + "ci-info": "^4.0.0", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.6", + "proc-log": "^4.2.0", + "read": "^3.0.1", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "3.0.2", + "version": "5.0.12", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^5.0.0" + "@npmcli/arborist": "^7.5.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmhook": { - "version": "8.0.3", + "version": "10.0.5", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^17.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmorg": { - "version": "4.0.3", + "version": "6.0.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^17.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "4.1.0", + "version": "7.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/run-script": "^3.0.0", - "npm-package-arg": "^9.0.1", - "pacote": "^13.5.0" + "@npmcli/arborist": "^7.5.4", + "@npmcli/run-script": "^8.1.0", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.6" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "6.0.4", + "version": "9.0.9", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "normalize-package-data": "^4.0.0", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0", + "ci-info": "^4.0.0", + "normalize-package-data": "^6.0.1", + "npm-package-arg": "^11.0.2", + "npm-registry-fetch": "^17.0.1", + "proc-log": "^4.2.0", "semver": "^7.3.7", - "ssri": "^9.0.0" + "sigstore": "^2.2.0", + "ssri": "^10.0.6" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmsearch": { - "version": "5.0.3", + "version": "7.0.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^17.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmteam": { - "version": "4.0.3", + "version": "6.0.5", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^17.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmversion": { - "version": "3.0.4", + "version": "6.0.3", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/run-script": "^3.0.0", - "json-parse-even-better-errors": "^2.3.1", - "proc-log": "^2.0.0", + "@npmcli/git": "^5.0.7", + "@npmcli/run-script": "^8.1.0", + "json-parse-even-better-errors": "^3.0.2", + "proc-log": "^4.2.0", "semver": "^7.3.7" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/lru-cache": { - "version": "7.9.0", + "version": "10.4.3", "dev": true, "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=12" - } + "license": "ISC" }, "node_modules/npm/node_modules/make-fetch-happen": { - "version": "10.1.5", + "version": "13.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", + "proc-log": "^4.2.0", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.1.1", - "ssri": "^9.0.0" + "ssri": "^10.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/minimatch": { - "version": "5.0.1", + "version": "9.0.5", "dev": true, "inBundle": true, "license": "ISC", @@ -2866,45 +2890,45 @@ "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/minipass": { - "version": "3.1.6", + "version": "7.1.2", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/npm/node_modules/minipass-collect": { - "version": "1.0.2", + "version": "2.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "minipass": "^3.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">= 8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/npm/node_modules/minipass-fetch": { - "version": "2.1.0", + "version": "3.0.5", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^3.1.6", + "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "optionalDependencies": { "encoding": "^0.1.13" @@ -2922,14 +2946,16 @@ "node": ">= 8" } }, - "node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/npm/node_modules/minipass-pipeline": { @@ -2944,6 +2970,18 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/npm/node_modules/minipass-sized": { "version": "1.0.3", "dev": true, @@ -2956,6 +2994,18 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/npm/node_modules/minizlib": { "version": "2.1.2", "dev": true, @@ -2969,27 +3019,25 @@ "node": ">= 8" } }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", "dev": true, "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/npm/node_modules/mkdirp-infer-owner": { - "version": "2.0.0", + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", "dev": true, "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { "node": ">=10" @@ -3002,10 +3050,13 @@ "license": "MIT" }, "node_modules/npm/node_modules/mute-stream": { - "version": "0.0.8", + "version": "1.0.0", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/npm/node_modules/negotiator": { "version": "0.6.3", @@ -3017,124 +3068,81 @@ } }, "node_modules/npm/node_modules/node-gyp": { - "version": "9.0.0", + "version": "10.2.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", - "glob": "^7.1.4", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" + "tar": "^6.2.1", + "which": "^4.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.22 || ^14.13 || >=16" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "7.2.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/nopt": { - "version": "5.0.0", + "version": "7.2.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "abbrev": "1" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": ">=6" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/normalize-package-data": { - "version": "4.0.0", + "version": "6.0.2", "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", + "hosted-git-info": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-audit-report": { - "version": "3.0.0", + "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "chalk": "^4.0.0" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-bundled": { - "version": "1.1.2", + "version": "3.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-normalize-package-bin": "^1.0.1" + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-install-checks": { - "version": "5.0.0", + "version": "6.3.0", "dev": true, "inBundle": true, "license": "BSD-2-Clause", @@ -3142,130 +3150,99 @@ "semver": "^7.1.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", + "version": "3.0.1", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/npm/node_modules/npm-package-arg": { - "version": "9.0.2", + "version": "11.0.3", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "hosted-git-info": "^5.0.0", + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-packlist": { - "version": "5.1.0", + "version": "8.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^1.1.2", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "npm-packlist": "bin/index.js" + "ignore-walk": "^6.0.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "7.0.1", + "version": "9.1.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^9.0.0", + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-profile": { - "version": "6.0.3", + "version": "10.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0" + "npm-registry-fetch": "^17.0.1", + "proc-log": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18.0.0" } }, "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "13.1.1", + "version": "17.1.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-user-validate": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/npmlog": { - "version": "6.0.2", + "version": "2.0.1", "dev": true, "inBundle": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, + "license": "BSD-2-Clause", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/once": { - "version": "1.4.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/opener": { - "version": "1.5.2", - "dev": true, - "inBundle": true, - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/p-map": { @@ -3283,71 +3260,111 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, "node_modules/npm/node_modules/pacote": { - "version": "13.5.0", + "version": "18.0.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^3.0.1", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^8.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", "tar": "^6.1.11" }, "bin": { - "pacote": "lib/bin.js" + "pacote": "bin/index.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/parse-conflict-json": { - "version": "2.0.2", + "version": "3.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", "dev": true, "inBundle": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, "node_modules/npm/node_modules/proc-log": { - "version": "2.0.1", + "version": "4.2.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/promise-all-reject-late": { @@ -3360,7 +3377,7 @@ } }, "node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.1", + "version": "3.0.1", "dev": true, "inBundle": true, "license": "ISC", @@ -3388,12 +3405,15 @@ } }, "node_modules/npm/node_modules/promzard": { - "version": "0.3.0", + "version": "1.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "read": "1" + "read": "^3.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/qrcode-terminal": { @@ -3405,78 +3425,37 @@ } }, "node_modules/npm/node_modules/read": { - "version": "1.0.7", + "version": "3.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "mute-stream": "~0.0.4" + "mute-stream": "^1.0.0" }, "engines": { - "node": ">=0.8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/read-cmd-shim": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/read-package-json": { - "version": "5.0.1", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^1.0.1" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/read-package-json-fast": { - "version": "2.0.3", + "version": "3.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/readable-stream": { - "version": "3.6.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/retry": { @@ -3488,129 +3467,75 @@ "node": ">= 4" } }, - "node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } + "optional": true }, - "node_modules/npm/node_modules/rimraf/node_modules/glob": { - "version": "7.2.0", + "node_modules/npm/node_modules/semver": { + "version": "7.6.3", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10" } }, - "node_modules/npm/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "shebang-regex": "^3.0.0" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", "dev": true, "inBundle": true, "license": "MIT", - "optional": true + "engines": { + "node": ">=8" + } }, - "node_modules/npm/node_modules/semver": { - "version": "7.3.7", + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", + "node_modules/npm/node_modules/sigstore": { + "version": "2.3.1", "dev": true, "inBundle": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "yallist": "^4.0.0" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" }, "engines": { - "node": ">=10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "inBundle": true, - "license": "ISC" - }, "node_modules/npm/node_modules/smart-buffer": { "version": "4.2.0", "dev": true, @@ -3622,35 +3547,35 @@ } }, "node_modules/npm/node_modules/socks": { - "version": "2.6.2", + "version": "2.8.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "ip": "^1.1.5", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "6.2.0", + "version": "8.0.4", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/npm/node_modules/spdx-correct": { - "version": "3.1.1", + "version": "3.2.0", "dev": true, "inBundle": true, "license": "Apache-2.0", @@ -3659,14 +3584,24 @@ "spdx-license-ids": "^3.0.0" } }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", + "version": "2.5.0", "dev": true, "inBundle": true, "license": "CC-BY-3.0" }, "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "MIT", @@ -3676,33 +3611,45 @@ } }, "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.11", + "version": "3.0.18", "dev": true, "inBundle": true, "license": "CC0-1.0" }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, "node_modules/npm/node_modules/ssri": { - "version": "9.0.1", + "version": "10.0.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "minipass": "^3.1.1" + "minipass": "^7.0.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/npm/node_modules/string-width": { + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "dev": true, "inBundle": true, @@ -3720,41 +3667,87 @@ "version": "6.0.1", "dev": true, "inBundle": true, - "license": "MIT", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "ansi-regex": "^5.0.1" + "minipass": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/npm/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/npm/node_modules/tar": { - "version": "6.1.11", + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, "engines": { - "node": ">= 10" + "node": ">=8" } }, "node_modules/npm/node_modules/text-table": { @@ -3770,30 +3763,50 @@ "license": "MIT" }, "node_modules/npm/node_modules/treeverse": { - "version": "2.0.0", + "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/unique-filename": { - "version": "1.1.1", + "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "unique-slug": "^2.0.0" + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/unique-slug": { - "version": "2.0.2", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/util-deprecate": { @@ -3812,74 +3825,166 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "builtins": "^5.0.0" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/walk-up-path": { - "version": "1.0.0", + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": ">=16" + } }, - "node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "defaults": "^1.0.3" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/npm/node_modules/which": { - "version": "2.0.2", + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, - "bin": { - "node-which": "bin/node-which" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", "dev": true, "inBundle": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } }, "node_modules/npm/node_modules/write-file-atomic": { - "version": "4.0.1", + "version": "5.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "signal-exit": "^4.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/yallist": { @@ -5828,296 +5933,393 @@ } }, "npm": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.11.0.tgz", - "integrity": "sha512-4qmtwHa28J4SPmwCNoQI07KIF/ljmBhhuqG+xNXsIIRpwdKB5OXkMIGfH6KlThR6kzusxlkgR7t1haFDB88dcQ==", + "version": "10.8.3", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.3.tgz", + "integrity": "sha512-0IQlyAYvVtQ7uOhDFYZCGK8kkut2nh8cpAdA9E6FvRSJaTgtZRZgNjlC5ZCct//L73ygrpY93CxXpRJDtNqPVg==", "dev": true, "requires": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.0.4", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/config": "^4.1.0", - "@npmcli/fs": "^2.1.0", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^3.0.1", - "abbrev": "~1.1.1", + "@npmcli/arborist": "^7.5.4", + "@npmcli/config": "^8.3.4", + "@npmcli/fs": "^3.1.1", + "@npmcli/map-workspaces": "^3.0.6", + "@npmcli/package-json": "^5.2.0", + "@npmcli/promise-spawn": "^7.0.2", + "@npmcli/redact": "^2.0.1", + "@npmcli/run-script": "^8.1.0", + "@sigstore/tuf": "^2.3.4", + "abbrev": "^2.0.0", "archy": "~1.0.0", - "cacache": "^16.1.0", - "chalk": "^4.1.2", - "chownr": "^2.0.0", + "cacache": "^18.0.4", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", "cli-columns": "^4.0.0", - "cli-table3": "^0.6.2", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.12", - "glob": "^8.0.1", - "graceful-fs": "^4.2.10", - "hosted-git-info": "^5.0.0", - "ini": "^3.0.0", - "init-package-json": "^3.0.2", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^6.0.2", - "libnpmdiff": "^4.0.2", - "libnpmexec": "^4.0.2", - "libnpmfund": "^3.0.1", - "libnpmhook": "^8.0.2", - "libnpmorg": "^4.0.2", - "libnpmpack": "^4.0.2", - "libnpmpublish": "^6.0.2", - "libnpmsearch": "^5.0.2", - "libnpmteam": "^4.0.2", - "libnpmversion": "^3.0.1", - "make-fetch-happen": "^10.1.5", - "minipass": "^3.1.6", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^7.0.2", + "ini": "^4.1.3", + "init-package-json": "^6.0.3", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^3.0.2", + "libnpmaccess": "^8.0.6", + "libnpmdiff": "^6.1.4", + "libnpmexec": "^8.1.4", + "libnpmfund": "^5.0.12", + "libnpmhook": "^10.0.5", + "libnpmorg": "^6.0.6", + "libnpmpack": "^7.0.4", + "libnpmpublish": "^9.0.9", + "libnpmsearch": "^7.0.6", + "libnpmteam": "^6.0.5", + "libnpmversion": "^6.0.3", + "make-fetch-happen": "^13.0.1", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", "ms": "^2.1.2", - "node-gyp": "^9.0.0", - "nopt": "^5.0.0", - "npm-audit-report": "^3.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.2", - "npm-pick-manifest": "^7.0.1", - "npm-profile": "^6.0.3", - "npm-registry-fetch": "^13.1.1", - "npm-user-validate": "^1.0.1", - "npmlog": "^6.0.2", - "opener": "^1.5.2", - "pacote": "^13.4.1", - "parse-conflict-json": "^2.0.2", - "proc-log": "^2.0.1", + "node-gyp": "^10.2.0", + "nopt": "^7.2.1", + "normalize-package-data": "^6.0.2", + "npm-audit-report": "^5.0.0", + "npm-install-checks": "^6.3.0", + "npm-package-arg": "^11.0.3", + "npm-pick-manifest": "^9.1.0", + "npm-profile": "^10.0.0", + "npm-registry-fetch": "^17.1.0", + "npm-user-validate": "^2.0.1", + "p-map": "^4.0.0", + "pacote": "^18.0.6", + "parse-conflict-json": "^3.0.1", + "proc-log": "^4.2.0", "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^5.0.1", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.1", - "tar": "^6.1.11", + "read": "^3.0.1", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^10.0.6", + "supports-color": "^9.4.0", + "tar": "^6.2.1", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", - "treeverse": "^2.0.0", - "validate-npm-package-name": "^4.0.0", - "which": "^2.0.2", - "write-file-atomic": "^4.0.1" + "treeverse": "^3.0.0", + "validate-npm-package-name": "^5.0.1", + "which": "^4.0.0", + "write-file-atomic": "^5.0.1" }, "dependencies": { - "@colors/colors": { - "version": "1.5.0", + "@isaacs/cliui": { + "version": "8.0.2", "bundled": true, "dev": true, - "optional": true - }, - "@gar/promisify": { - "version": "1.1.3", - "bundled": true, - "dev": true + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "bundled": true, + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } }, "@isaacs/string-locale-compare": { "version": "1.1.0", "bundled": true, "dev": true }, + "@npmcli/agent": { + "version": "2.2.2", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + } + }, "@npmcli/arborist": { - "version": "5.2.0", + "version": "7.5.4", "bundled": true, "dev": true, "requires": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^3.0.0", - "bin-links": "^3.0.0", - "cacache": "^16.0.6", + "@npmcli/fs": "^3.1.1", + "@npmcli/installed-package-contents": "^2.1.0", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/metavuln-calculator": "^7.1.1", + "@npmcli/name-from-folder": "^2.0.0", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.1.0", + "@npmcli/query": "^3.1.0", + "@npmcli/redact": "^2.0.0", + "@npmcli/run-script": "^8.1.0", + "bin-links": "^4.0.4", + "cacache": "^18.0.3", "common-ancestor-path": "^1.0.1", - "json-parse-even-better-errors": "^2.3.1", + "hosted-git-info": "^7.0.2", + "json-parse-even-better-errors": "^3.0.2", "json-stringify-nice": "^1.1.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^5.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.0.5", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^7.2.1", + "npm-install-checks": "^6.2.0", + "npm-package-arg": "^11.0.2", + "npm-pick-manifest": "^9.0.1", + "npm-registry-fetch": "^17.0.1", + "pacote": "^18.0.6", + "parse-conflict-json": "^3.0.0", + "proc-log": "^4.2.0", + "proggy": "^2.0.0", "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", - "walk-up-path": "^1.0.0" + "ssri": "^10.0.6", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" } }, - "@npmcli/ci-detect": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, "@npmcli/config": { - "version": "4.1.0", + "version": "8.3.4", "bundled": true, "dev": true, "requires": { - "@npmcli/map-workspaces": "^2.0.2", - "ini": "^3.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^5.0.0", - "proc-log": "^2.0.0", - "read-package-json-fast": "^2.0.3", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/package-json": "^5.1.1", + "ci-info": "^4.0.0", + "ini": "^4.1.2", + "nopt": "^7.2.1", + "proc-log": "^4.2.0", "semver": "^7.3.5", - "walk-up-path": "^1.0.0" - } - }, - "@npmcli/disparity-colors": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.3.0" + "walk-up-path": "^3.0.1" } }, "@npmcli/fs": { - "version": "2.1.0", + "version": "3.1.1", "bundled": true, "dev": true, "requires": { - "@gar/promisify": "^1.1.3", "semver": "^7.3.5" } }, "@npmcli/git": { - "version": "3.0.1", + "version": "5.0.8", "bundled": true, "dev": true, "requires": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", - "which": "^2.0.2" + "which": "^4.0.0" } }, "@npmcli/installed-package-contents": { - "version": "1.0.7", + "version": "2.1.0", "bundled": true, "dev": true, "requires": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" } }, "@npmcli/map-workspaces": { - "version": "2.0.3", + "version": "3.0.6", "bundled": true, "dev": true, "requires": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" + "@npmcli/name-from-folder": "^2.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0", + "read-package-json-fast": "^3.0.0" } }, "@npmcli/metavuln-calculator": { - "version": "3.1.0", + "version": "7.1.1", "bundled": true, "dev": true, "requires": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", + "cacache": "^18.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^18.0.0", + "proc-log": "^4.1.0", "semver": "^7.3.5" } }, - "@npmcli/move-file": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - } - }, "@npmcli/name-from-folder": { - "version": "1.0.1", + "version": "2.0.0", "bundled": true, "dev": true }, "@npmcli/node-gyp": { - "version": "2.0.0", + "version": "3.0.0", "bundled": true, "dev": true }, "@npmcli/package-json": { - "version": "2.0.0", + "version": "5.2.0", "bundled": true, "dev": true, "requires": { - "json-parse-even-better-errors": "^2.3.1" + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" } }, "@npmcli/promise-spawn": { - "version": "3.0.0", + "version": "7.0.2", + "bundled": true, + "dev": true, + "requires": { + "which": "^4.0.0" + } + }, + "@npmcli/query": { + "version": "3.1.0", "bundled": true, "dev": true, "requires": { - "infer-owner": "^1.0.4" + "postcss-selector-parser": "^6.0.10" } }, + "@npmcli/redact": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, "@npmcli/run-script": { - "version": "3.0.2", + "version": "8.1.0", "bundled": true, "dev": true, "requires": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3" + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" } }, - "@tootallnate/once": { - "version": "2.0.0", + "@pkgjs/parseargs": { + "version": "0.11.0", + "bundled": true, + "dev": true, + "optional": true + }, + "@sigstore/bundle": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "requires": { + "@sigstore/protobuf-specs": "^0.3.2" + } + }, + "@sigstore/core": { + "version": "1.1.0", "bundled": true, "dev": true }, - "abbrev": { - "version": "1.1.1", + "@sigstore/protobuf-specs": { + "version": "0.3.2", "bundled": true, "dev": true }, - "agent-base": { - "version": "6.0.2", + "@sigstore/sign": { + "version": "2.3.2", "bundled": true, "dev": true, "requires": { - "debug": "4" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + } + }, + "@sigstore/tuf": { + "version": "2.3.4", + "bundled": true, + "dev": true, + "requires": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + } + }, + "@sigstore/verify": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + } + }, + "@tufjs/canonical-json": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "@tufjs/models": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" } }, - "agentkeepalive": { - "version": "4.2.1", + "abbrev": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "agent-base": { + "version": "7.1.1", "bundled": true, "dev": true, "requires": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" + "debug": "^4.3.4" } }, "aggregate-error": { @@ -6135,12 +6337,9 @@ "dev": true }, "ansi-styles": { - "version": "4.3.0", + "version": "6.2.1", "bundled": true, - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } + "dev": true }, "aproba": { "version": "2.0.0", @@ -6152,40 +6351,24 @@ "bundled": true, "dev": true }, - "are-we-there-yet": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, - "asap": { - "version": "2.0.6", - "bundled": true, - "dev": true - }, "balanced-match": { "version": "1.0.2", "bundled": true, "dev": true }, "bin-links": { - "version": "3.0.1", + "version": "4.0.4", "bundled": true, "dev": true, "requires": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" } }, "binary-extensions": { - "version": "2.2.0", + "version": "2.3.0", "bundled": true, "dev": true }, @@ -6197,59 +6380,46 @@ "balanced-match": "^1.0.0" } }, - "builtins": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "semver": "^7.0.0" - } - }, "cacache": { - "version": "16.1.0", + "version": "18.0.4", "bundled": true, "dev": true, "requires": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", + "ssri": "^10.0.0", "tar": "^6.1.11", - "unique-filename": "^1.1.1" + "unique-filename": "^3.0.0" } }, "chalk": { - "version": "4.1.2", + "version": "5.3.0", "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "dev": true }, "chownr": { "version": "2.0.0", "bundled": true, "dev": true }, + "ci-info": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, "cidr-regex": { - "version": "3.1.1", + "version": "4.1.1", "bundled": true, "dev": true, "requires": { - "ip-regex": "^4.1.0" + "ip-regex": "^5.0.0" } }, "clean-stack": { @@ -6266,27 +6436,10 @@ "strip-ansi": "^6.0.1" } }, - "cli-table3": { - "version": "0.6.2", - "bundled": true, - "dev": true, - "requires": { - "@colors/colors": "1.5.0", - "string-width": "^4.2.0" - } - }, - "clone": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, "cmd-shim": { - "version": "5.0.0", + "version": "6.0.3", "bundled": true, - "dev": true, - "requires": { - "mkdirp-infer-owner": "^2.0.0" - } + "dev": true }, "color-convert": { "version": "2.0.1", @@ -6301,37 +6454,38 @@ "bundled": true, "dev": true }, - "color-support": { - "version": "1.1.3", + "common-ancestor-path": { + "version": "1.0.1", "bundled": true, "dev": true }, - "columnify": { - "version": "1.6.0", + "cross-spawn": { + "version": "7.0.3", "bundled": true, "dev": true, "requires": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, - "common-ancestor-path": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", + "cssesc": { + "version": "3.0.0", "bundled": true, "dev": true }, "debug": { - "version": "4.3.4", + "version": "4.3.6", "bundled": true, "dev": true, "requires": { @@ -6345,40 +6499,13 @@ } } }, - "debuglog": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "defaults": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "depd": { - "version": "1.1.2", + "diff": { + "version": "5.2.0", "bundled": true, "dev": true }, - "dezalgo": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "diff": { - "version": "5.0.0", + "eastasianwidth": { + "version": "0.2.0", "bundled": true, "dev": true }, @@ -6406,120 +6533,82 @@ "bundled": true, "dev": true }, - "fastest-levenshtein": { - "version": "1.0.12", - "bundled": true, - "dev": true - }, - "fs-minipass": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", + "exponential-backoff": { + "version": "3.1.1", "bundled": true, "dev": true }, - "function-bind": { - "version": "1.1.1", + "fastest-levenshtein": { + "version": "1.0.16", "bundled": true, "dev": true }, - "gauge": { - "version": "4.0.4", + "foreground-child": { + "version": "3.3.0", "bundled": true, "dev": true, "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" } }, - "glob": { - "version": "8.0.1", + "fs-minipass": { + "version": "3.0.3", "bundled": true, "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minipass": "^7.0.3" } }, - "graceful-fs": { - "version": "4.2.10", - "bundled": true, - "dev": true - }, - "has": { - "version": "1.0.3", + "glob": { + "version": "10.4.5", "bundled": true, "dev": true, "requires": { - "function-bind": "^1.1.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" } }, - "has-flag": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "has-unicode": { - "version": "2.0.1", + "graceful-fs": { + "version": "4.2.11", "bundled": true, "dev": true }, "hosted-git-info": { - "version": "5.0.0", + "version": "7.0.2", "bundled": true, "dev": true, "requires": { - "lru-cache": "^7.5.1" + "lru-cache": "^10.0.1" } }, "http-cache-semantics": { - "version": "4.1.0", + "version": "4.1.1", "bundled": true, "dev": true }, "http-proxy-agent": { - "version": "5.0.0", + "version": "7.0.2", "bundled": true, "dev": true, "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" } }, "https-proxy-agent": { - "version": "5.0.1", + "version": "7.0.5", "bundled": true, "dev": true, "requires": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" } }, - "humanize-ms": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, "iconv-lite": { "version": "0.6.3", "bundled": true, @@ -6530,11 +6619,11 @@ } }, "ignore-walk": { - "version": "5.0.1", + "version": "6.0.5", "bundled": true, "dev": true, "requires": { - "minimatch": "^5.0.1" + "minimatch": "^9.0.0" } }, "imurmurhash": { @@ -6547,68 +6636,45 @@ "bundled": true, "dev": true }, - "infer-owner": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "bundled": true, - "dev": true - }, "ini": { - "version": "3.0.0", + "version": "4.1.3", "bundled": true, "dev": true }, "init-package-json": { - "version": "3.0.2", + "version": "6.0.3", "bundled": true, "dev": true, "requires": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", + "@npmcli/package-json": "^5.0.0", + "npm-package-arg": "^11.0.0", + "promzard": "^1.0.0", + "read": "^3.0.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" + "validate-npm-package-name": "^5.0.0" } }, - "ip": { - "version": "1.1.8", + "ip-address": { + "version": "9.0.5", "bundled": true, - "dev": true + "dev": true, + "requires": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + } }, "ip-regex": { - "version": "4.3.0", + "version": "5.0.0", "bundled": true, "dev": true }, "is-cidr": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "cidr-regex": "^3.1.1" - } - }, - "is-core-module": { - "version": "2.9.0", + "version": "5.1.0", "bundled": true, "dev": true, "requires": { - "has": "^1.0.3" + "cidr-regex": "^4.1.1" } }, "is-fullwidth-code-point": { @@ -6626,8 +6692,22 @@ "bundled": true, "dev": true }, + "jackspeak": { + "version": "3.4.3", + "bundled": true, + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "jsbn": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, "json-parse-even-better-errors": { - "version": "2.3.1", + "version": "3.0.2", "bundled": true, "dev": true }, @@ -6642,167 +6722,163 @@ "dev": true }, "just-diff": { - "version": "5.0.2", + "version": "6.0.2", "bundled": true, "dev": true }, "just-diff-apply": { - "version": "5.2.0", + "version": "5.5.0", "bundled": true, "dev": true }, "libnpmaccess": { - "version": "6.0.3", + "version": "8.0.6", "bundled": true, "dev": true, "requires": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0" + "npm-package-arg": "^11.0.2", + "npm-registry-fetch": "^17.0.1" } }, "libnpmdiff": { - "version": "4.0.3", + "version": "6.1.4", "bundled": true, "dev": true, "requires": { - "@npmcli/disparity-colors": "^2.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", - "diff": "^5.0.0", - "minimatch": "^5.0.1", - "npm-package-arg": "^9.0.1", - "pacote": "^13.0.5", - "tar": "^6.1.0" + "@npmcli/arborist": "^7.5.4", + "@npmcli/installed-package-contents": "^2.1.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.6", + "tar": "^6.2.1" } }, "libnpmexec": { - "version": "4.0.5", + "version": "8.1.4", "bundled": true, "dev": true, "requires": { - "@npmcli/arborist": "^5.0.0", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/run-script": "^3.0.0", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^9.0.1", - "npmlog": "^6.0.2", - "pacote": "^13.0.5", - "proc-log": "^2.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", - "walk-up-path": "^1.0.0" + "@npmcli/arborist": "^7.5.4", + "@npmcli/run-script": "^8.1.0", + "ci-info": "^4.0.0", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.6", + "proc-log": "^4.2.0", + "read": "^3.0.1", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" } }, "libnpmfund": { - "version": "3.0.2", + "version": "5.0.12", "bundled": true, "dev": true, "requires": { - "@npmcli/arborist": "^5.0.0" + "@npmcli/arborist": "^7.5.4" } }, "libnpmhook": { - "version": "8.0.3", + "version": "10.0.5", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^17.0.1" } }, "libnpmorg": { - "version": "4.0.3", + "version": "6.0.6", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^17.0.1" } }, "libnpmpack": { - "version": "4.1.0", + "version": "7.0.4", "bundled": true, "dev": true, "requires": { - "@npmcli/run-script": "^3.0.0", - "npm-package-arg": "^9.0.1", - "pacote": "^13.5.0" + "@npmcli/arborist": "^7.5.4", + "@npmcli/run-script": "^8.1.0", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.6" } }, "libnpmpublish": { - "version": "6.0.4", + "version": "9.0.9", "bundled": true, "dev": true, "requires": { - "normalize-package-data": "^4.0.0", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0", + "ci-info": "^4.0.0", + "normalize-package-data": "^6.0.1", + "npm-package-arg": "^11.0.2", + "npm-registry-fetch": "^17.0.1", + "proc-log": "^4.2.0", "semver": "^7.3.7", - "ssri": "^9.0.0" + "sigstore": "^2.2.0", + "ssri": "^10.0.6" } }, "libnpmsearch": { - "version": "5.0.3", + "version": "7.0.6", "bundled": true, "dev": true, "requires": { - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^17.0.1" } }, "libnpmteam": { - "version": "4.0.3", + "version": "6.0.5", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^17.0.1" } }, "libnpmversion": { - "version": "3.0.4", + "version": "6.0.3", "bundled": true, "dev": true, "requires": { - "@npmcli/git": "^3.0.0", - "@npmcli/run-script": "^3.0.0", - "json-parse-even-better-errors": "^2.3.1", - "proc-log": "^2.0.0", + "@npmcli/git": "^5.0.7", + "@npmcli/run-script": "^8.1.0", + "json-parse-even-better-errors": "^3.0.2", + "proc-log": "^4.2.0", "semver": "^7.3.7" } }, "lru-cache": { - "version": "7.9.0", + "version": "10.4.3", "bundled": true, "dev": true }, "make-fetch-happen": { - "version": "10.1.5", + "version": "13.0.1", "bundled": true, "dev": true, "requires": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", + "proc-log": "^4.2.0", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.1.1", - "ssri": "^9.0.0" + "ssri": "^10.0.0" } }, "minimatch": { - "version": "5.0.1", + "version": "9.0.5", "bundled": true, "dev": true, "requires": { @@ -6810,28 +6886,25 @@ } }, "minipass": { - "version": "3.1.6", + "version": "7.1.2", "bundled": true, - "dev": true, - "requires": { - "yallist": "^4.0.0" - } + "dev": true }, "minipass-collect": { - "version": "1.0.2", + "version": "2.0.1", "bundled": true, "dev": true, "requires": { - "minipass": "^3.0.0" + "minipass": "^7.0.3" } }, "minipass-fetch": { - "version": "2.1.0", + "version": "3.0.5", "bundled": true, "dev": true, "requires": { "encoding": "^0.1.13", - "minipass": "^3.1.6", + "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" } @@ -6842,15 +6915,16 @@ "dev": true, "requires": { "minipass": "^3.0.0" - } - }, - "minipass-json-stream": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } } }, "minipass-pipeline": { @@ -6859,6 +6933,16 @@ "dev": true, "requires": { "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } } }, "minipass-sized": { @@ -6867,6 +6951,16 @@ "dev": true, "requires": { "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } } }, "minizlib": { @@ -6876,22 +6970,22 @@ "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } } }, "mkdirp": { "version": "1.0.4", "bundled": true, - "dev": true - }, - "mkdirp-infer-owner": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" - } + "dev": true }, "ms": { "version": "2.1.3", @@ -6899,7 +6993,7 @@ "dev": true }, "mute-stream": { - "version": "0.0.8", + "version": "1.0.0", "bundled": true, "dev": true }, @@ -6909,91 +7003,55 @@ "dev": true }, "node-gyp": { - "version": "9.0.0", + "version": "10.2.0", "bundled": true, "dev": true, "requires": { "env-paths": "^2.2.0", - "glob": "^7.1.4", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.0", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "tar": "^6.2.1", + "which": "^4.0.0" } }, "nopt": { - "version": "5.0.0", + "version": "7.2.1", "bundled": true, "dev": true, "requires": { - "abbrev": "1" + "abbrev": "^2.0.0" } }, "normalize-package-data": { - "version": "4.0.0", + "version": "6.0.2", "bundled": true, "dev": true, "requires": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", + "hosted-git-info": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "npm-audit-report": { - "version": "3.0.0", + "version": "5.0.0", "bundled": true, - "dev": true, - "requires": { - "chalk": "^4.0.0" - } + "dev": true }, "npm-bundled": { - "version": "1.1.2", + "version": "3.0.1", "bundled": true, "dev": true, "requires": { - "npm-normalize-package-bin": "^1.0.1" + "npm-normalize-package-bin": "^3.0.0" } }, "npm-install-checks": { - "version": "5.0.0", + "version": "6.3.0", "bundled": true, "dev": true, "requires": { @@ -7001,147 +7059,146 @@ } }, "npm-normalize-package-bin": { - "version": "1.0.1", + "version": "3.0.1", "bundled": true, "dev": true }, "npm-package-arg": { - "version": "9.0.2", + "version": "11.0.3", "bundled": true, "dev": true, "requires": { - "hosted-git-info": "^5.0.0", + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" + "validate-npm-package-name": "^5.0.0" } }, "npm-packlist": { - "version": "5.1.0", + "version": "8.0.2", "bundled": true, "dev": true, "requires": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^1.1.2", - "npm-normalize-package-bin": "^1.0.1" + "ignore-walk": "^6.0.4" } }, "npm-pick-manifest": { - "version": "7.0.1", + "version": "9.1.0", "bundled": true, "dev": true, "requires": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^9.0.0", + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", "semver": "^7.3.5" } }, "npm-profile": { - "version": "6.0.3", + "version": "10.0.0", "bundled": true, "dev": true, "requires": { - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0" + "npm-registry-fetch": "^17.0.1", + "proc-log": "^4.0.0" } }, "npm-registry-fetch": { - "version": "13.1.1", + "version": "17.1.0", "bundled": true, "dev": true, "requires": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" } }, "npm-user-validate": { - "version": "1.0.1", + "version": "2.0.1", "bundled": true, "dev": true }, - "npmlog": { - "version": "6.0.2", + "p-map": { + "version": "4.0.0", "bundled": true, "dev": true, "requires": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" + "aggregate-error": "^3.0.0" } }, - "once": { - "version": "1.4.0", + "package-json-from-dist": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "pacote": { + "version": "18.0.6", "bundled": true, "dev": true, "requires": { - "wrappy": "1" + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^8.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" } }, - "opener": { - "version": "1.5.2", - "bundled": true, - "dev": true - }, - "p-map": { - "version": "4.0.0", + "parse-conflict-json": { + "version": "3.0.1", "bundled": true, "dev": true, "requires": { - "aggregate-error": "^3.0.0" + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" } }, - "pacote": { - "version": "13.5.0", + "path-key": { + "version": "3.1.1", + "bundled": true, + "dev": true + }, + "path-scurry": { + "version": "1.11.1", "bundled": true, "dev": true, "requires": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^3.0.1", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, - "parse-conflict-json": { - "version": "2.0.2", + "postcss-selector-parser": { + "version": "6.1.2", "bundled": true, "dev": true, "requires": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", - "just-diff-apply": "^5.2.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" } }, - "path-is-absolute": { - "version": "1.0.1", + "proc-log": { + "version": "4.2.0", "bundled": true, "dev": true }, - "proc-log": { - "version": "2.0.1", + "proggy": { + "version": "2.0.0", "bundled": true, "dev": true }, @@ -7151,7 +7208,7 @@ "dev": true }, "promise-call-limit": { - "version": "1.0.1", + "version": "3.0.1", "bundled": true, "dev": true }, @@ -7170,11 +7227,11 @@ } }, "promzard": { - "version": "0.3.0", + "version": "1.0.2", "bundled": true, "dev": true, "requires": { - "read": "1" + "read": "^3.0.1" } }, "qrcode-terminal": { @@ -7183,57 +7240,25 @@ "dev": true }, "read": { - "version": "1.0.7", + "version": "3.0.1", "bundled": true, "dev": true, "requires": { - "mute-stream": "~0.0.4" + "mute-stream": "^1.0.0" } }, "read-cmd-shim": { - "version": "3.0.0", + "version": "4.0.0", "bundled": true, "dev": true }, - "read-package-json": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^1.0.1" - } - }, "read-package-json-fast": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", + "version": "3.0.2", "bundled": true, "dev": true, "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" } }, "retry": { @@ -7241,51 +7266,6 @@ "bundled": true, "dev": true }, - "rimraf": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.3" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.0", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "safe-buffer": { - "version": "5.2.1", - "bundled": true, - "dev": true - }, "safer-buffer": { "version": "2.1.2", "bundled": true, @@ -7293,73 +7273,92 @@ "optional": true }, "semver": { - "version": "7.3.7", + "version": "7.6.3", + "bundled": true, + "dev": true + }, + "shebang-command": { + "version": "2.0.0", "bundled": true, "dev": true, "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - } + "shebang-regex": "^3.0.0" } }, - "set-blocking": { - "version": "2.0.0", + "shebang-regex": { + "version": "3.0.0", "bundled": true, "dev": true }, "signal-exit": { - "version": "3.0.7", + "version": "4.1.0", "bundled": true, "dev": true }, + "sigstore": { + "version": "2.3.1", + "bundled": true, + "dev": true, + "requires": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + } + }, "smart-buffer": { "version": "4.2.0", "bundled": true, "dev": true }, "socks": { - "version": "2.6.2", + "version": "2.8.3", "bundled": true, "dev": true, "requires": { - "ip": "^1.1.5", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" } }, "socks-proxy-agent": { - "version": "6.2.0", + "version": "8.0.4", "bundled": true, "dev": true, "requires": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" } }, "spdx-correct": { - "version": "3.1.1", + "version": "3.2.0", "bundled": true, "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" + }, + "dependencies": { + "spdx-expression-parse": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + } } }, "spdx-exceptions": { - "version": "2.3.0", + "version": "2.5.0", "bundled": true, "dev": true }, "spdx-expression-parse": { - "version": "3.0.1", + "version": "4.0.0", "bundled": true, "dev": true, "requires": { @@ -7368,28 +7367,35 @@ } }, "spdx-license-ids": { - "version": "3.0.11", + "version": "3.0.18", + "bundled": true, + "dev": true + }, + "sprintf-js": { + "version": "1.1.3", "bundled": true, "dev": true }, "ssri": { - "version": "9.0.1", + "version": "10.0.6", "bundled": true, "dev": true, "requires": { - "minipass": "^3.1.1" + "minipass": "^7.0.3" } }, - "string_decoder": { - "version": "1.3.0", + "string-width": { + "version": "4.2.3", "bundled": true, "dev": true, "requires": { - "safe-buffer": "~5.2.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, - "string-width": { - "version": "4.2.3", + "string-width-cjs": { + "version": "npm:string-width@4.2.3", "bundled": true, "dev": true, "requires": { @@ -7406,25 +7412,55 @@ "ansi-regex": "^5.0.1" } }, - "supports-color": { - "version": "7.2.0", + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", "bundled": true, "dev": true, "requires": { - "has-flag": "^4.0.0" + "ansi-regex": "^5.0.1" } }, + "supports-color": { + "version": "9.4.0", + "bundled": true, + "dev": true + }, "tar": { - "version": "6.1.11", + "version": "6.2.1", "bundled": true, "dev": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" + }, + "dependencies": { + "fs-minipass": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass": { + "version": "5.0.0", + "bundled": true, + "dev": true + } } }, "text-table": { @@ -7438,20 +7474,30 @@ "dev": true }, "treeverse": { - "version": "2.0.0", + "version": "3.0.0", "bundled": true, "dev": true }, + "tuf-js": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + } + }, "unique-filename": { - "version": "1.1.1", + "version": "3.0.0", "bundled": true, "dev": true, "requires": { - "unique-slug": "^2.0.0" + "unique-slug": "^4.0.0" } }, "unique-slug": { - "version": "2.0.2", + "version": "4.0.0", "bundled": true, "dev": true, "requires": { @@ -7470,57 +7516,111 @@ "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" + }, + "dependencies": { + "spdx-expression-parse": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + } } }, "validate-npm-package-name": { - "version": "4.0.0", + "version": "5.0.1", "bundled": true, - "dev": true, - "requires": { - "builtins": "^5.0.0" - } + "dev": true }, "walk-up-path": { - "version": "1.0.0", + "version": "3.0.1", "bundled": true, "dev": true }, - "wcwidth": { - "version": "1.0.1", + "which": { + "version": "4.0.0", "bundled": true, "dev": true, "requires": { - "defaults": "^1.0.3" + "isexe": "^3.1.1" + }, + "dependencies": { + "isexe": { + "version": "3.1.1", + "bundled": true, + "dev": true + } } }, - "which": { - "version": "2.0.2", + "wrap-ansi": { + "version": "8.1.0", "bundled": true, "dev": true, "requires": { - "isexe": "^2.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "bundled": true, + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } } }, - "wide-align": { - "version": "1.1.5", + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", "bundled": true, "dev": true, "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + } } }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "write-file-atomic": { - "version": "4.0.1", + "version": "5.0.1", "bundled": true, "dev": true, "requires": { "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "signal-exit": "^4.0.1" } }, "yallist": { diff --git a/manual/package.json b/manual/package.json index d358c9cd37..ae161416f1 100644 --- a/manual/package.json +++ b/manual/package.json @@ -21,7 +21,7 @@ "homepage": "https://github.com/leamas/manuals-v2#readme", "devDependencies": { "@antora/cli": "3.1.3", - "npm": "8.11.0" + "npm": "^10.8.3" }, "dependencies": { "@antora/logger": "^3.1.3", @@ -30,8 +30,8 @@ "isomorphic-git": "^1.12.0" }, "overrides": { - "vinyl-fs": { - "glob-stream": "~7.0" + "vinyl-fs": { + "glob-stream": "~7.0" + } } } -} diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index 148b79f7bb..acfa8e1835 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -166,6 +166,7 @@ set(SRC ${MODEL_SRC_DIR}/conn_params.cpp ${MODEL_SRC_DIR}/cutil.cpp ${MODEL_SRC_DIR}/downloader.cpp + ${MODEL_SRC_DIR}/ds_porttype.cpp ${MODEL_SRC_DIR}/garmin_protocol_mgr.cpp ${MODEL_SRC_DIR}/geodesic.cpp ${MODEL_SRC_DIR}/georef.cpp diff --git a/model/include/model/ais_state_vars.h b/model/include/model/ais_state_vars.h index 6b117430e9..a1bf7e2841 100644 --- a/model/include/model/ais_state_vars.h +++ b/model/include/model/ais_state_vars.h @@ -18,7 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file ais_state_vars.h Global state for AIS decoder */ +/** + * \file + * Global state for AIS decoder + */ extern bool g_bAIS_ACK_Timeout; extern bool g_bAIS_CPA_Alert; diff --git a/model/include/model/catalog_handler.h b/model/include/model/catalog_handler.h index a0e96d28e8..272548f1bb 100644 --- a/model/include/model/catalog_handler.h +++ b/model/include/model/catalog_handler.h @@ -1,8 +1,4 @@ -/****************************************************************************** - * - * Project: OpenCPN - * - *************************************************************************** +/************************************************************************** * Copyright (C) 2019 Alec Leamas * * * * This program is free software; you can redistribute it and/or modify * @@ -19,12 +15,12 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** - */ + **************************************************************************/ /** - * Plugin catalog management: Check for available versions and branches, - * download as required. + * \file + * Plugin catalog management: Build the runtime catalog, handling downloads + * as required. */ #ifndef CATALOG_HANDLER_H__ @@ -38,11 +34,17 @@ #include "model/catalog_parser.h" /** - * A local proxy for the catalog server. The server has a number - * of branches, some of which containing a plugin catalog. + * Local proxy for the catalog server and other catalog sources. * - * Backend code for channel management (which catalog to get) and - * the important download function. + * The active catalog is exported by GetActiveCatalogContext(). This + * catalog is the merged results of + * - The active downloaded catalog, + * - Possible meta-urls reflecting developers' personal catalogs. + * - Imported metadata living in the data directory, created at import. + * + * The server has a number of branches, some of which containing a + * plugin catalog. Backend code for channel management (which catalog to + * get) and the important download function. * * Also: CatalogData handling, basically version and date for * various ocpn-plugins.xml. @@ -80,7 +82,7 @@ class CatalogHandler { /** Set a custom url, overrides also channel settings. */ void SetCustomUrl(const char* url); - /** Set a custom url, overrides also channel settings. */ + /** Get the custom url set by SetCustomUrl. */ std::string GetCustomUrl(); /** Get the default URL, with actual channel included */ @@ -125,6 +127,10 @@ class CatalogHandler { /** Last error message, free format. */ std::string LastErrorMsg(); + /** + * Parse the catalog by merging data from imported metadata, meta-urls and + * the standard url. + */ ServerStatus DoParseCatalog(const std::string xml, CatalogCtx* ctx); protected: diff --git a/model/include/model/catalog_parser.h b/model/include/model/catalog_parser.h index 35d0276870..7f7f8d93dd 100644 --- a/model/include/model/catalog_parser.h +++ b/model/include/model/catalog_parser.h @@ -1,8 +1,4 @@ -/****************************************************************************** - * - * Project: OpenCPN - * - *************************************************************************** +/************************************************************************** * Copyright (C) 2019 Alec Leamas * * * * This program is free software; you can redistribute it and/or modify * @@ -19,8 +15,7 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** - */ + **************************************************************************/ #ifndef CATALOG_PARSER_H__ #define CATALOG_PARSER_H__ @@ -28,8 +23,9 @@ #include /** - * Datatypes and methods to parse ocpn-plugins.xml XML data, - * either complete catalog or a single plugin. + * \file + * Datatypes and methods to parse ocpn-plugins.xml XML data, either complete + * catalog or a single plugin. */ /** Overall metadata for the set of plugins used. */ diff --git a/model/include/model/comm_buffers.h b/model/include/model/comm_buffers.h index 33298874d4..cca263b83a 100644 --- a/model/include/model/comm_buffers.h +++ b/model/include/model/comm_buffers.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file comm_buffers.h Line-oriented input/output buffers. */ +/** + * \file + * Line-oriented input/output buffers. + */ #ifndef _COMM_BUFFERS_H__ #define _COMM_BUFFERS_H__ @@ -70,4 +73,27 @@ class LineBuffer { uint8_t m_last_ch; }; +/** Assemble characters to NMEA0183 sentences. */ +class N0183Buffer { +public: + /** Add a single character, possibly making a sentence available */ + void Put(uint8_t ch); + + /** Return true if a sentence is available to be returned by GetSentence() */ + bool HasSentence() const { return !m_lines.empty(); } + + /** + * Retrieve a sentence from buffer + * @return Next available sentence in buffer or "" if none available + */ + std::string GetSentence(); + + N0183Buffer() : m_state(State::PrefixWait) {} + +private: + std::deque m_lines; + std::vector m_line; + enum class State { PrefixWait, Data, CsDigit1, CsDigit2 } m_state; +}; + #endif // _COMM_BUFFERS_H__ diff --git a/model/include/model/comm_drv_n0183.h b/model/include/model/comm_drv_n0183.h index 228ced88e9..3fcb63376f 100644 --- a/model/include/model/comm_drv_n0183.h +++ b/model/include/model/comm_drv_n0183.h @@ -1,11 +1,6 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: - * Author: David Register, Alec Leamas - * - *************************************************************************** - * Copyright (C) 2022 by David Register, Alec Leamas * +/************************************************************************** + * Copyright (C) 2022 David Register * + * Copyright (C) 2022 Alec Leamas * * * * 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 * @@ -22,6 +17,12 @@ * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ + +/** + * \file + * NMEA0183 drivers common base. + */ + #ifndef _COMMDRIVERN0183_H__ #define _COMMDRIVERN0183_H__ @@ -30,6 +31,7 @@ #include "model/comm_driver.h" +/** NMEA0183 drivers common part. */ class CommDriverN0183 : public AbstractCommDriver { public: CommDriverN0183(); @@ -49,4 +51,4 @@ class CommDriverN0183 : public AbstractCommDriver { void Activate() override; }; -#endif // guarstring +#endif // guardstring diff --git a/model/include/model/comm_drv_n0183_net.h b/model/include/model/comm_drv_n0183_net.h index b5af7a31f7..36396e2033 100644 --- a/model/include/model/comm_drv_n0183_net.h +++ b/model/include/model/comm_drv_n0183_net.h @@ -1,11 +1,6 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: - * Author: David Register, Alec Leamas - * - *************************************************************************** - * Copyright (C) 2022 by David Register, Alec Leamas * +/************************************************************************** + * Copyright (C) 2022 David Register * + * Copyright (C) 2022 Alec Leamas * * * * 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 * @@ -23,9 +18,15 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -#ifndef _COMMDRIVERN0183NET_H -#define _COMMDRIVERN0183NET_H +/** + * \file + * NMEA0183 over IP driver + */ +#ifndef COMMDRIVERN0183NET_H_ +#define COMMDRIVERN0183NET_H_ + +#include #include #include @@ -33,14 +34,13 @@ #ifndef WX_PRECOMP #include -#endif // precompiled header +#endif -#include #include #include #ifdef __WXGTK__ -// newer versions of glib define its own GSocket but we unfortunately use this +// newer versions of glib define its own GSocket, but we unfortunately use this // name in our own (semi-)public header and so can't change it -- rename glib // one instead // #include @@ -56,101 +56,82 @@ #include #endif +#include "model/comm_buffers.h" #include "model/comm_drv_n0183.h" #include "model/conn_params.h" +#include "model/ocpn_utils.h" #include "observable.h" -class CommDriverN0183NetEvent; // Internal class MrqContainer; class CommDriverN0183Net : public CommDriverN0183, public wxEvtHandler { public: - CommDriverN0183Net(); CommDriverN0183Net(const ConnectionParams* params, DriverListener& listener); - virtual ~CommDriverN0183Net(); + ~CommDriverN0183Net() override; - void Open(); - void Close(); ConnectionParams GetParams() const { return m_params; } - bool SetOutputSocketOptions(wxSocketBase* tsock); - bool SendSentenceNetwork(const wxString& payload); - void OnServerSocketEvent(wxSocketEvent& event); // The listener - void OnTimerSocket(wxTimerEvent& event) { OnTimerSocket(); } - void OnTimerSocket(); - void OnSocketEvent(wxSocketEvent& event); - void OpenNetworkGPSD(); - void OpenNetworkTCP(unsigned int addr); - void OpenNetworkUDP(unsigned int addr); - void OnSocketReadWatchdogTimer(wxTimerEvent& event); - void HandleResume(); + const wxSocketBase* GetSock() const { return m_sock; } bool SendMessage(std::shared_ptr msg, std::shared_ptr addr) override; - wxSocketBase* GetSock() const { return m_sock; } private: - ConnectionParams m_params; - DriverListener& m_listener; + class SocketTimer : public wxTimer { + public: + SocketTimer(CommDriverN0183Net& owner) : wxTimer(), m_owner(owner) {} + void Notify() override { m_owner.OnTimerSocket(); } + + private: + CommDriverN0183Net& m_owner; + }; + + class SocketReadWatchdogTimer : public wxTimer { + public: + SocketReadWatchdogTimer(CommDriverN0183Net& owner) + : wxTimer(), m_owner(owner) {} + void Notify() override { m_owner.OnSocketReadWatchdogTimer(); } - void handle_N0183_MSG(CommDriverN0183NetEvent& event); - wxString GetNetPort() const { return m_net_port; } - wxIPV4address GetAddr() const { return m_addr; } - wxTimer* GetSocketThreadWatchdogTimer() { - return &m_socketread_watchdog_timer; - } - wxTimer* GetSocketTimer() { return &m_socket_timer; } - void SetSock(wxSocketBase* sock) { m_sock = sock; } - void SetTSock(wxSocketBase* sock) { m_tsock = sock; } - wxSocketBase* GetTSock() const { return m_tsock; } - void SetSockServer(wxSocketServer* sock) { m_socket_server = sock; } - wxSocketServer* GetSockServer() const { return m_socket_server; } - void SetMulticast(bool multicast) { m_is_multicast = multicast; } - bool GetMulticast() const { return m_is_multicast; } - - NetworkProtocol GetProtocol() { return m_net_protocol; } - void SetBrxConnectEvent(bool event) { m_brx_connect_event = event; } - bool GetBrxConnectEvent() { return m_brx_connect_event; } - - void SetConnectTime(wxDateTime time) { m_connect_time = time; } - wxDateTime GetConnectTime() { return m_connect_time; } - - dsPortType GetPortType() const { return m_io_select; } - wxString GetPort() const { return m_portstring; } - - ConnectionType GetConnectionType() const { return m_connection_type; } - - bool ChecksumOK(const std::string& sentence); - void SetOk(bool ok) { m_bok = ok; }; - - wxString m_net_port; - NetworkProtocol m_net_protocol; + private: + CommDriverN0183Net& m_owner; + }; + + void Open(); + void Close(); + void HandleResume(); + void OnServerSocketEvent(wxSocketEvent& event); // The listener + void OnTimerSocket(); + void OnSocketEvent(wxSocketEvent& event); + void OnSocketReadWatchdogTimer(); + void OpenNetworkGpsd(); + void OpenNetworkTcp(unsigned int addr); + void OpenNetworkUdp(unsigned int addr); + void HandleN0183Msg(const std::string& sentence); + bool SendSentenceNetwork(const wxString& payload); + + const ConnectionParams m_params; + DriverListener& m_listener; + N0183Buffer n0183_buffer; wxIPV4address m_addr; wxSocketBase* m_sock; wxSocketBase* m_tsock; wxSocketServer* m_socket_server; bool m_is_multicast; - MrqContainer* m_mrq_container; + std::unique_ptr m_mrq_container; int m_txenter; int m_dog_value; - std::string m_sock_buffer; - wxString m_portstring; - dsPortType m_io_select; - wxDateTime m_connect_time; - bool m_brx_connect_event; - bool m_bchecksumCheck; - ConnectionType m_connection_type; + std::chrono::time_point m_connect_time; + bool m_rx_connect_event; - wxTimer m_socket_timer; - wxTimer m_socketread_watchdog_timer; + SocketTimer m_socket_timer; + SocketReadWatchdogTimer m_socketread_watchdog_timer; - bool m_bok; + bool m_ok; + bool m_is_conn_err_reported; ObsListener resume_listener; - - DECLARE_EVENT_TABLE() }; -#endif // guard +#endif // COMMDRIVERN0183NET_H_ diff --git a/model/include/model/comm_drv_n0183_serial.h b/model/include/model/comm_drv_n0183_serial.h index 82cb414a34..baff070047 100644 --- a/model/include/model/comm_drv_n0183_serial.h +++ b/model/include/model/comm_drv_n0183_serial.h @@ -18,7 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file comm_drv_n0183_serial.h NMEA0183 serial driver */ +/** + * \file + * NMEA0183 serial driver + */ #ifndef _COMMDRIVERN0183SERIAL_H #define _COMMDRIVERN0183SERIAL_H diff --git a/model/include/model/comm_navmsg.h b/model/include/model/comm_navmsg.h index dd178751eb..20441c0bb3 100644 --- a/model/include/model/comm_navmsg.h +++ b/model/include/model/comm_navmsg.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file navmsg.h Raw, undecoded messages definitions. */ +/** + * \file + * Raw, undecoded messages definitions + */ #ifndef _DRIVER_NAVMSG_H #define _DRIVER_NAVMSG_H diff --git a/model/include/model/conn_params.h b/model/include/model/conn_params.h index ce8bbff844..5212663b68 100644 --- a/model/include/model/conn_params.h +++ b/model/include/model/conn_params.h @@ -122,8 +122,8 @@ class ConnectionParams { NavAddr::Bus GetLastCommProtocol(); wxString GetPortStr() const { return Port; } void SetPortStr(wxString str) { Port = str; } - std::string GetStrippedDSPort(); - NavAddr::Bus GetCommProtocol(); + std::string GetStrippedDSPort() const; + NavAddr::Bus GetCommProtocol() const; bool SentencePassesFilter(const wxString &sentence, FilterDirection direction) const; diff --git a/model/include/model/dbus_server.h b/model/include/model/dbus_server.h index 077f9e6c9e..48a8db5426 100644 --- a/model/include/model/dbus_server.h +++ b/model/include/model/dbus_server.h @@ -15,7 +15,11 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** + **************************************************************************/ + +/** + * \file + * DBus interface header file. */ #ifndef DBUS_HANDLER_H__ @@ -30,12 +34,6 @@ #include "model/local_api.h" #include "observable_evtvar.h" -/** - * \file - * - * DBus interface header file. - */ - /** The name registered on the session bus. */ static const char* const kDbusName = "org.opencpn.OpenCPN"; diff --git a/model/include/model/ds_porttype.h b/model/include/model/ds_porttype.h index 820fc380e1..1d1945fa16 100644 --- a/model/include/model/ds_porttype.h +++ b/model/include/model/ds_porttype.h @@ -1,8 +1,4 @@ -/*************************************************************************** - * - * Project: OpenCPN - * - *************************************************************************** +/************************************************************************** * Copyright (C) 2013 by David S. Register * * * * This program is free software; you can redistribute it and/or modify * @@ -19,13 +15,17 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** - */ + ***************************************************************************/ #ifndef __DSPORTTYPE_H__ #define __DSPORTTYPE_H__ +#include + // Port I/O type typedef enum { DS_TYPE_INPUT, DS_TYPE_INPUT_OUTPUT, DS_TYPE_OUTPUT } dsPortType; +/** Return textual representation for use in driver ioDirection attribute. */ +std::string DsPortTypeToString(dsPortType type); + #endif diff --git a/model/include/model/garmin_protocol_mgr.h b/model/include/model/garmin_protocol_mgr.h index 187bb70de1..21d75c8b8e 100644 --- a/model/include/model/garmin_protocol_mgr.h +++ b/model/include/model/garmin_protocol_mgr.h @@ -74,8 +74,6 @@ #define PI 3.1415926535897931160E0 /* pi */ #endif -#define TIMER_SOCKET 7006 - //---------------------------------------------------------------------------- // Garmin Device Management // Handle USB and Serial Port Garmin PVT protocol data interface. diff --git a/model/include/model/gui.h b/model/include/model/gui.h index c2030b08ea..e3c9ebf5b8 100644 --- a/model/include/model/gui.h +++ b/model/include/model/gui.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file gui.h Hooks int gui available in model. */ +/** + * \file + * Hooks into gui available in model. + */ #include diff --git a/model/include/model/ipc_api.h b/model/include/model/ipc_api.h index 1c643f3a4c..626ab6b0c9 100644 --- a/model/include/model/ipc_api.h +++ b/model/include/model/ipc_api.h @@ -17,6 +17,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ +/** + * \file + * Server and client CLI api implementations. + */ + #ifndef _IPC_API_H__ #define _IPC_API_H__ @@ -26,11 +31,6 @@ #include "model/local_api.h" -/** - * \file ipc-api.h - * Server and client CLI api implementations. - */ - std::string GetSocketPath(); /** diff --git a/model/include/model/linux_devices.h b/model/include/model/linux_devices.h index a661e3a264..507e7b9288 100644 --- a/model/include/model/linux_devices.h +++ b/model/include/model/linux_devices.h @@ -1,10 +1,4 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: Low-level USB device management - * Author: Alec Leamas - * - *************************************************************************** +/************************************************************************** * Copyright (C) 2021 Alec Leamas * * * * This program is free software; you can redistribute it and/or modify * @@ -23,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file linux_devices.h Low level udev usb device management. */ +/** + * \file + * Low level udev usb device management. + */ #ifndef LINUX_DEVICES_H #define LINUX_DEVICES_H diff --git a/model/include/model/linux_usb_watch.h b/model/include/model/linux_usb_watch.h index 63f86cb056..37931164d3 100644 --- a/model/include/model/linux_usb_watch.h +++ b/model/include/model/linux_usb_watch.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file linux_usb_watch.h Linux specific hardware events DBus interface */ +/** + * \file + * Linux specific hardware events DBus interface + */ #ifndef __linux__ #error "This file can only be compiled on linux. " diff --git a/model/include/model/logger.h b/model/include/model/logger.h index 496f78b50e..061afa195f 100644 --- a/model/include/model/logger.h +++ b/model/include/model/logger.h @@ -1,9 +1,7 @@ -/****************************************************************************** - * - * Project: OpenCPN - * - *************************************************************************** - * Copyright (C) 2013 by David S. Register * + +/************************************************************************** + * Copyright (C) 2013 David S. Register * + * Copyright (C) 2022 - 2024 Alec Leamas * * * * 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 * @@ -19,21 +17,11 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** - */ - -#ifndef LOGGER_H -#define LOGGER_H - -#include -#include -#include -#include - -#include + ***************************************************************************/ /** - * Logging interface. + * \file + * Enhanced logging interface on top of wx/log.h. * * The OcpnLog acts as the active wxLog target: it formats and prints * log messages, overriding the default setup. @@ -53,6 +41,44 @@ * level <= wxLog::GetLogLevel(). */ +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include +#include +#include + +#include + +#define DO_LOG_MESSAGE(level, fmt, ...) \ + { \ + if (level <= wxLog::GetLogLevel()) { \ + Logger::logMessage(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ + } \ + } + +#define _LOG(level) \ + if (level > wxLog::GetLogLevel()) \ + ; \ + else \ + Logger().get(level, __FILE__, __LINE__) + +#define TRACE_LOG _LOG(wxLOG_Trace) +#define DEBUG_LOG _LOG(wxLOG_Debug) +#define INFO_LOG _LOG(wxLOG_Info) +#define MESSAGE_LOG _LOG(wxLOG_Message) +#define WARNING_LOG _LOG(wxLOG_Warning) +#define ERROR_LOG _LOG(wxLOG_Error) + +#define LOG_TRACE(fmt, ...) DO_LOG_MESSAGE(wxLOG_Trace, fmt, ##__VA_ARGS__); +#define LOG_DEBUG(fmt, ...) DO_LOG_MESSAGE(wxLOG_Debug, fmt, ##__VA_ARGS__); +#define LOG_INFO(fmt, ...) DO_LOG_MESSAGE(wxLOG_Info, fmt, ##__VA_ARGS__); +#define LOG_MESSAGE(fmt, ...) DO_LOG_MESSAGE(wxLOG_Message, fmt, ##__VA_ARGS__); +#define LOG_WARNING(fmt, ...) DO_LOG_MESSAGE(wxLOG_Warning, fmt, ##__VA_ARGS__); +#define LOG_ERROR(fmt, ...) DO_LOG_MESSAGE(wxLOG_Error, fmt, ##__VA_ARGS__); + /** * Customized logger class appending to a file providing: * - Millisecond timestamps @@ -100,31 +126,36 @@ class Logger { wxLogLevel level; }; -#define DO_LOG_MESSAGE(level, fmt, ...) \ - { \ - if (level <= wxLog::GetLogLevel()) { \ - Logger::logMessage(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - } \ - } +/** Filter logging every nth message */ +class CountedLogFilter { +public: + CountedLogFilter(unsigned n, wxLogLevel level = wxLOG_Message) + : m_count(n), m_level(level), m_not_logged(0) {} -#define _LOG(level) \ - if (level > wxLog::GetLogLevel()) \ - ; \ - else \ - Logger().get(level, __FILE__, __LINE__) + /** Log a repeated message after suppressing n ones. */ + void Log(const std::string& message); -#define TRACE_LOG _LOG(wxLOG_Trace) -#define DEBUG_LOG _LOG(wxLOG_Debug) -#define INFO_LOG _LOG(wxLOG_Info) -#define MESSAGE_LOG _LOG(wxLOG_Message) -#define WARNING_LOG _LOG(wxLOG_Warning) -#define ERROR_LOG _LOG(wxLOG_Error) +private: + const unsigned m_count; + const wxLogLevel m_level; + unsigned m_not_logged; +}; -#define LOG_TRACE(fmt, ...) DO_LOG_MESSAGE(wxLOG_Trace, fmt, ##__VA_ARGS__); -#define LOG_DEBUG(fmt, ...) DO_LOG_MESSAGE(wxLOG_Debug, fmt, ##__VA_ARGS__); -#define LOG_INFO(fmt, ...) DO_LOG_MESSAGE(wxLOG_Info, fmt, ##__VA_ARGS__); -#define LOG_MESSAGE(fmt, ...) DO_LOG_MESSAGE(wxLOG_Message, fmt, ##__VA_ARGS__); -#define LOG_WARNING(fmt, ...) DO_LOG_MESSAGE(wxLOG_Warning, fmt, ##__VA_ARGS__); -#define LOG_ERROR(fmt, ...) DO_LOG_MESSAGE(wxLOG_Error, fmt, ##__VA_ARGS__); +/** Filter logging repeated message with specified interval. */ +class TimedLogFilter { +public: + TimedLogFilter(const std::chrono::seconds& interval, + wxLogLevel level = wxLOG_Message) + : m_interval(interval), m_level(level), m_not_logged(0) {} + + /** Log a repeated message after interval seconds. */ + void Log(const std::string& message); + +private: + const std::chrono::seconds m_interval; + const wxLogLevel m_level; + std::chrono::time_point m_last_logged; + unsigned m_not_logged; +}; #endif // LOGGER_H diff --git a/model/include/model/ocpn_utils.h b/model/include/model/ocpn_utils.h index 97dee05a26..076b71058d 100644 --- a/model/include/model/ocpn_utils.h +++ b/model/include/model/ocpn_utils.h @@ -58,6 +58,13 @@ bool replace(std::string& str, const std::string& from, const std::string& to); void copy_file(const std::string& src_path, const std::string& dest_path); +/** + * Check if checksum in a NMEA0183 sentence is correct + * @param sentence complete NMEA01832 message + * @return true if checksum is OK, else false. + */ +bool N0183CheckSumOk(const std::string& sentence); + } // namespace ocpn #endif // _OCPN_UTILS_H__ diff --git a/model/include/model/plugin_loader.h b/model/include/model/plugin_loader.h index ce6342009e..2e7d16f4f1 100644 --- a/model/include/model/plugin_loader.h +++ b/model/include/model/plugin_loader.h @@ -54,6 +54,7 @@ enum class PluginStatus { ManagedInstalledUpdateAvailable, ManagedInstalledCurrentVersion, ManagedInstalledDowngradeAvailable, + Imported, PendingListRemoval }; diff --git a/model/include/model/routeman.h b/model/include/model/routeman.h index 2fd85213a0..90476c9258 100644 --- a/model/include/model/routeman.h +++ b/model/include/model/routeman.h @@ -241,12 +241,17 @@ class WayPointman { public: WayPointman(GlobalColourFunc colour_func); ~WayPointman(); - wxBitmap *GetIconBitmap(const wxString &icon_key); - bool GetIconPrescaled(const wxString &icon_key); - int GetIconIndex(const wxBitmap *pbm); - int GetIconImageListIndex(const wxBitmap *pbm); - int GetXIconImageListIndex(const wxBitmap *pbm); - int GetFIconImageListIndex(const wxBitmap *pbm); + wxBitmap *GetIconBitmap(const wxString &icon_key) const; + bool GetIconPrescaled(const wxString &icon_key) const; + int GetIconIndex(const wxBitmap *pbm) const; + int GetIconImageListIndex(const wxBitmap *pbm) const; + + /** index of "X-ed out" icon in the image list */ + int GetXIconImageListIndex(const wxBitmap *pbm) const; + + /** index of "fixed viz" icon in the image list */ + int GetFIconImageListIndex(const wxBitmap *pbm) const; + int GetNumIcons(void) { return m_pIconArray->Count(); } wxString CreateGUID(RoutePoint *pRP); RoutePoint *FindWaypointByGuid(const std::string &guid); @@ -262,16 +267,28 @@ class WayPointman { void ClearRoutePointFonts(void); bool DoesIconExist(const wxString &icon_key) const; - wxBitmap GetIconBitmapForList(int index, int height); - wxString *GetIconDescription(int index); - wxString *GetIconKey(int index); - wxString GetIconDescription(wxString icon_key); + wxBitmap GetIconBitmapForList(int index, int height) const; + wxString *GetIconDescription(int index) const; + wxString *GetIconKey(int index) const; + wxString GetIconDescription(wxString icon_key) const; wxImageList *Getpmarkicon_image_list(int nominal_height); + /** + * Add a point to list which owns it. + * @param prp RoutePoint allocated by caller. + * @return true if successfully added. + */ bool AddRoutePoint(RoutePoint *prp); + + /** + * Remove a routepoint from list if present, deallocate it all cases. + * @param prp RoutePoint possibly part of list. + * @return true if prp != nullptr. + */ bool RemoveRoutePoint(RoutePoint *prp); - RoutePointList *GetWaypointList(void) { return m_pWayPointList; } + + const RoutePointList *GetWaypointList(void) { return m_pWayPointList; } private: wxImage CreateDimImage(wxImage &image, double factor); diff --git a/model/include/model/serial_io.h b/model/include/model/serial_io.h index 62dc7091a5..1fccfa3836 100644 --- a/model/include/model/serial_io.h +++ b/model/include/model/serial_io.h @@ -18,7 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file serial_io.h Abstract N0183 serial communications interface */ +/** + * \file + * Abstract N0183 serial communications interface + */ #ifndef _N0183_PROTOL_MGR__ #define _N0183_PROTOL_MGR__ @@ -32,9 +35,12 @@ #include #include "comm_buffers.h" +#include "model/logger.h" #include "model/thread_ctrl.h" #include "model/ocpn_utils.h" +using namespace std::literals::chrono_literals; + /** Remove possible Serial: prefix. */ static std::string NormalizePort(const std::string& port) { return port.find("Serial:") == 0 ? port.substr(strlen("Serial:")) : port; @@ -65,11 +71,13 @@ class SerialIo : public ThreadCtrl { const wxString m_portname; const unsigned m_baud; const SendMsgFunc m_send_msg_func; + TimedLogFilter m_open_log_filter; SerialIo(SendMsgFunc send_msg_func, const std::string& port, unsigned baud) : m_portname(NormalizePort(port)), m_baud(baud), - m_send_msg_func(send_msg_func) {} + m_send_msg_func(send_msg_func), + m_open_log_filter(5min) {} }; #ifdef __clang__ diff --git a/model/include/model/sys_events.h b/model/include/model/sys_events.h index 3eff3e9621..70a17cb8e3 100644 --- a/model/include/model/sys_events.h +++ b/model/include/model/sys_events.h @@ -17,9 +17,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file sys_event.h Suspend/resum and new devices events exchange point */ +/** + * \file + * Suspend/resume and new devices events exchange point + */ -# #ifndef SYS__EVENTS_H_ #define SYS__EVENTS_H_ #include "observable_evtvar.h" diff --git a/model/include/model/thread_ctrl.h b/model/include/model/thread_ctrl.h index d7a35c2895..aabc970cb4 100644 --- a/model/include/model/thread_ctrl.h +++ b/model/include/model/thread_ctrl.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file thread_ctrl.h ThreadCtrl mixin class definition */ +/** + * \file + * ThreadCtrl mixin class definition + */ #ifndef THREAD_CTRL_H__ #define THREAD_CTRL_H__ diff --git a/model/include/model/usb_watch_daemon.h b/model/include/model/usb_watch_daemon.h index be8a70a8db..c0623bf7c4 100644 --- a/model/include/model/usb_watch_daemon.h +++ b/model/include/model/usb_watch_daemon.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file usb_watch_daemon.h Generic hardware events interface */ +/** + * \file + * Generic hardware events interface + */ #ifndef USB_WATCH_DAEMON__H #define USB_WATCH_DAEMON__H diff --git a/model/include/model/wait_continue.h b/model/include/model/wait_continue.h index a5e39cf46c..985f8e141f 100644 --- a/model/include/model/wait_continue.h +++ b/model/include/model/wait_continue.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file wait_continue.h Basic synchronization primitive */ +/** + * \file + * Basic synchronization primitive + */ #ifndef WAIT_COND__ #define WAIT_COND__ diff --git a/model/include/model/win_usb_watch.h b/model/include/model/win_usb_watch.h index 89c8c2d35c..5908fbc04c 100644 --- a/model/include/model/win_usb_watch.h +++ b/model/include/model/win_usb_watch.h @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file win_usb_watch.h Windows specific hardware events interface */ +/** + * \file + * Windows specific hardware events interface + */ #ifndef _WIN32 #error "This file can only be compiled on windows. " diff --git a/model/src/ais_state_vars.cpp b/model/src/ais_state_vars.cpp index 6fced3a723..f1939e8035 100644 --- a/model/src/ais_state_vars.cpp +++ b/model/src/ais_state_vars.cpp @@ -18,7 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file ais_state_vars.cpp Implement ais_state_vars.h */ +/** + * \file + * Implement ais_state_vars.h + */ #include diff --git a/model/src/android_serial_io.cpp b/model/src/android_serial_io.cpp index 2cb797b27a..26f1076642 100644 --- a/model/src/android_serial_io.cpp +++ b/model/src/android_serial_io.cpp @@ -18,8 +18,9 @@ **************************************************************************/ /** - * \file android_serial_io.cpp Android SerialIo synchronous implementation - * based on the native Android serial interface. + * \file + * Android SerialIo synchronous implementation based on the native + * Android serial interface. */ #include diff --git a/model/src/comm_buffers.cpp b/model/src/comm_buffers.cpp index 12ca30b178..00575aa866 100644 --- a/model/src/comm_buffers.cpp +++ b/model/src/comm_buffers.cpp @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file comm_buffers.cpp Implement comm_buffers.h */ +/** + * \file + * Implement comm_buffers.h + */ #include "model/comm_buffers.h" @@ -67,3 +70,50 @@ std::vector LineBuffer::GetLine() { m_line_count -= 1; return line; } + +std::string N0183Buffer::GetSentence() { + if (m_lines.empty()) return ""; + auto sentence = m_lines.front(); + m_lines.pop_front(); + return sentence; +} + +void N0183Buffer::Put(uint8_t ch) { + switch (m_state) { + case State::PrefixWait: + // Wait until start of message + if (ch == '$' || ch == '!') { + m_line.clear(); + m_line.push_back(ch); + m_state = State::Data; + } + break; + case State::Data: + // Collect data into m_line until a '*' is found + if (std::isprint(ch)) { + m_line.push_back(ch); + if (ch == '*') m_state = State::CsDigit1; + } else { + // Malformed? Garbage input? + m_state = State::PrefixWait; + } + break; + case State::CsDigit1: + // Collect first checksum digit + if (std::isxdigit(ch)) { + m_line.push_back(ch); + m_state = State::CsDigit2; + } else { + m_state = State::PrefixWait; + } + break; + case State::CsDigit2: + // Collect last checksum digit, push m_line to m_lines if OK + if (std::isxdigit(ch)) { + m_line.push_back(ch); + m_lines.emplace_back(m_line.begin(), m_line.end()); + } + m_state = State::PrefixWait; + break; + } +} diff --git a/model/src/comm_drv_n0183_android_bt.cpp b/model/src/comm_drv_n0183_android_bt.cpp index a518edca58..6137959a5c 100644 --- a/model/src/comm_drv_n0183_android_bt.cpp +++ b/model/src/comm_drv_n0183_android_bt.cpp @@ -173,8 +173,7 @@ class circular_buffer { CommDriverN0183AndroidBT::CommDriverN0183AndroidBT( const ConnectionParams* params, DriverListener& listener) - : CommDriverN0183(NavAddr::Bus::N0183, - ((ConnectionParams*)params)->GetStrippedDSPort()), + : CommDriverN0183(NavAddr::Bus::N0183, params->GetStrippedDSPort()), m_bok(false), m_portstring(params->GetDSPort()), m_params(*params), @@ -183,14 +182,7 @@ CommDriverN0183AndroidBT::CommDriverN0183AndroidBT( // SetSecThreadInActive(); this->attributes["commPort"] = params->Port.ToStdString(); this->attributes["userComment"] = params->UserComment.ToStdString(); - dsPortType iosel = params->IOSelect; - std::string s_iosel = std::string("IN"); - if (iosel == DS_TYPE_INPUT_OUTPUT) { - s_iosel = "OUT"; - } else if (iosel == DS_TYPE_INPUT_OUTPUT) { - s_iosel = "IN/OUT"; - } - this->attributes["ioDirection"] = s_iosel; + this->attributes["ioDirection"] = DsPortTypeToString(params->IOSelect); // Prepare the wxEventHandler to accept events from the actual hardware thread Bind(wxEVT_COMMDRIVER_N0183_ANDROID_BT, diff --git a/model/src/comm_drv_n0183_android_int.cpp b/model/src/comm_drv_n0183_android_int.cpp index d8c4ed29f8..80ed2458da 100644 --- a/model/src/comm_drv_n0183_android_int.cpp +++ b/model/src/comm_drv_n0183_android_int.cpp @@ -173,22 +173,14 @@ class circular_buffer { CommDriverN0183AndroidInt::CommDriverN0183AndroidInt( const ConnectionParams* params, DriverListener& listener) - : CommDriverN0183(NavAddr::Bus::N0183, - ((ConnectionParams*)params)->GetStrippedDSPort()), + : CommDriverN0183(NavAddr::Bus::N0183, params->GetStrippedDSPort()), m_bok(false), m_portstring(params->GetDSPort()), m_params(*params), m_listener(listener) { this->attributes["commPort"] = params->Port.ToStdString(); this->attributes["userComment"] = params->UserComment.ToStdString(); - dsPortType iosel = params->IOSelect; - std::string s_iosel = std::string("IN"); - if (iosel == DS_TYPE_INPUT_OUTPUT) { - s_iosel = "OUT"; - } else if (iosel == DS_TYPE_INPUT_OUTPUT) { - s_iosel = "IN/OUT"; - } - this->attributes["ioDirection"] = s_iosel; + this->attributes["ioDirection"] = DsPortTypeToString(params->IOSelect); // Prepare the wxEventHandler to accept events from the actual hardware thread Bind(wxEVT_COMMDRIVER_N0183_ANDROID_INT, diff --git a/model/src/comm_drv_n0183_net.cpp b/model/src/comm_drv_n0183_net.cpp index 696b32207d..1524f63fb0 100644 --- a/model/src/comm_drv_n0183_net.cpp +++ b/model/src/comm_drv_n0183_net.cpp @@ -1,11 +1,6 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: Implement comm_drv_n0183_net.h -- network nmea0183 driver - * Author: David Register, Alec Leamas - * - *************************************************************************** - * Copyright (C) 2022 by David Register, Alec Leamas * +/************************************************************************** + * Copyright (C) 2022 David Register * + * Copyright (C) 2022 Alec Leamas * * * * 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 * @@ -23,11 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -#ifdef __MINGW32__ -#undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq -#include -#include -#endif +/** + * \file + * Implement comm_drv_n0183_net.h. + */ #ifdef __MSVC__ #include "winsock2.h" @@ -39,55 +33,36 @@ #ifndef WX_PRECOMP #include -#endif // precompiled headers - -#include -#include +#endif -#include -#include -#include +#include +#include #ifndef __WXMSW__ #include #include #endif -#include +#include #include #include #include #include -#include #include #include "model/comm_drv_n0183_net.h" +#include "model/comm_drv_registry.h" #include "model/comm_navmsg_bus.h" #include "model/garmin_protocol_mgr.h" #include "model/idents.h" +#include "model/logger.h" #include "model/sys_events.h" #include "observable.h" -#define N_DOG_TIMEOUT 8 - -// FIXME (dave) This should be in some more "common" space, but where? -bool CheckSumCheck(const std::string& sentence) { - size_t check_start = sentence.find('*'); - if (check_start == wxString::npos || check_start > sentence.size() - 3) - return false; // * not found, or it didn't have 2 characters following it. - - std::string check_str = sentence.substr(check_start + 1, 2); - unsigned long checksum = strtol(check_str.c_str(), 0, 16); - if (checksum == 0L && check_str != "00") return false; +using namespace std::literals::chrono_literals; - unsigned char calculated_checksum = 0; - for (std::string::const_iterator i = sentence.begin() + 1; - i != sentence.end() && *i != '*'; ++i) - calculated_checksum ^= static_cast(*i); - - return calculated_checksum == checksum; -} +#define N_DOG_TIMEOUT 8 class MrqContainer { public: @@ -98,268 +73,226 @@ class MrqContainer { } }; -wxDEFINE_EVENT(wxEVT_COMMDRIVER_N0183_NET, CommDriverN0183NetEvent); - -class CommDriverN0183NetEvent; -wxDECLARE_EVENT(wxEVT_COMMDRIVER_N0183_NET, CommDriverN0183NetEvent); - -class CommDriverN0183NetEvent : public wxEvent { -public: - CommDriverN0183NetEvent(wxEventType commandType = wxEVT_NULL, int id = 0) - : wxEvent(id, commandType) {}; - ~CommDriverN0183NetEvent() {}; +static bool SetOutputSocketOptions(wxSocketBase* tsock) { + int ret; - // accessors - void SetPayload(std::shared_ptr> data) { - m_payload = data; - } - std::shared_ptr> GetPayload() { return m_payload; } + // Disable nagle algorithm on outgoing connection + // Doing this here rather than after the accept() is + // pointless on platforms where TCP_NODELAY is + // not inherited. However, none of OpenCPN's currently + // supported platforms fall into that category. - // required for sending with wxPostEvent() - wxEvent* Clone() const { - CommDriverN0183NetEvent* newevent = new CommDriverN0183NetEvent(*this); - newevent->m_payload = this->m_payload; - return newevent; - }; + int nagleDisable = 1; + ret = tsock->SetOption(IPPROTO_TCP, TCP_NODELAY, &nagleDisable, + sizeof(nagleDisable)); -private: - std::shared_ptr> m_payload; -}; + // Drastically reduce the size of the socket output buffer + // so that when client goes away without properly closing, the stream will + // quickly fill the output buffer, and thus fail the write() call + // within a few seconds. + unsigned long outbuf_size = 1024; // Smallest allowable value on Linux + return (tsock->SetOption(SOL_SOCKET, SO_SNDBUF, &outbuf_size, + sizeof(outbuf_size)) && + ret); +} //======================================================================== -/* commdriverN0183Net implementation - * */ - -BEGIN_EVENT_TABLE(CommDriverN0183Net, wxEvtHandler) -EVT_TIMER(TIMER_SOCKET, CommDriverN0183Net::OnTimerSocket) -EVT_SOCKET(DS_SOCKET_ID, CommDriverN0183Net::OnSocketEvent) -EVT_SOCKET(DS_SERVERSOCKET_ID, CommDriverN0183Net::OnServerSocketEvent) -EVT_TIMER(TIMER_SOCKET + 1, CommDriverN0183Net::OnSocketReadWatchdogTimer) -END_EVENT_TABLE() - -// CommDriverN0183Net::CommDriverN0183Net() : CommDriverN0183() {} - +/* + * CommDriverN0183Net implementation + */ CommDriverN0183Net::CommDriverN0183Net(const ConnectionParams* params, DriverListener& listener) - : CommDriverN0183(NavAddr::Bus::N0183, - ((ConnectionParams*)params)->GetStrippedDSPort()), + : CommDriverN0183(NavAddr::Bus::N0183, params->GetStrippedDSPort()), m_params(*params), m_listener(listener), - m_net_port(wxString::Format("%i", params->NetworkPort)), - m_net_protocol(params->NetProtocol), - m_sock(NULL), - m_tsock(NULL), - m_socket_server(NULL), + m_sock(nullptr), + m_tsock(nullptr), + m_socket_server(nullptr), m_is_multicast(false), m_txenter(0), - m_portstring(params->GetDSPort()), - m_io_select(params->IOSelect), - m_connection_type(params->Type), - m_bok(false) - -{ + m_dog_value(0), + m_rx_connect_event(false), + m_socket_timer(*this), + m_socketread_watchdog_timer(*this), + m_ok(false), + m_is_conn_err_reported(false) { m_addr.Hostname(params->NetworkAddress); m_addr.Service(params->NetworkPort); - m_socket_timer.SetOwner(this, TIMER_SOCKET); - m_socketread_watchdog_timer.SetOwner(this, TIMER_SOCKET + 1); this->attributes["netAddress"] = params->NetworkAddress.ToStdString(); - char port_char[10]; - sprintf(port_char, "%d", params->NetworkPort); - this->attributes["netPort"] = std::string(port_char); + this->attributes["netPort"] = std::to_string(params->NetworkPort); this->attributes["userComment"] = params->UserComment.ToStdString(); - dsPortType iosel = params->IOSelect; - std::string s_iosel = std::string("IN"); - if (iosel == DS_TYPE_INPUT_OUTPUT) { - s_iosel = "OUT"; - } else if (iosel == DS_TYPE_INPUT_OUTPUT) { - s_iosel = "IN/OUT"; - } - this->attributes["ioDirection"] = s_iosel; + this->attributes["ioDirection"] = DsPortTypeToString(params->IOSelect); - // Prepare the wxEventHandler to accept events from the actual hardware thread - Bind(wxEVT_COMMDRIVER_N0183_NET, &CommDriverN0183Net::handle_N0183_MSG, this); + m_mrq_container = std::make_unique(); - m_mrq_container = new MrqContainer; - - // Establish the power events response + // Establish event listeners resume_listener.Init(SystemEvents::GetInstance().evt_resume, [&](ObservedEvt&) { HandleResume(); }); - Open(); -} + Bind(wxEVT_SOCKET, &CommDriverN0183Net::OnSocketEvent, this, DS_SOCKET_ID); + Bind(wxEVT_SOCKET, &CommDriverN0183Net::OnServerSocketEvent, this, + DS_SERVERSOCKET_ID); -CommDriverN0183Net::~CommDriverN0183Net() { - delete m_mrq_container; - Close(); + Open(); } -void CommDriverN0183Net::handle_N0183_MSG(CommDriverN0183NetEvent& event) { - auto p = event.GetPayload(); - std::vector* payload = p.get(); - - // Extract the NMEA0183 sentence - std::string full_sentence = std::string(payload->begin(), payload->end()); +CommDriverN0183Net::~CommDriverN0183Net() { Close(); } - if ((full_sentence[0] == '$') || (full_sentence[0] == '!')) { // Sanity check +void CommDriverN0183Net::HandleN0183Msg(const std::string& sentence) { + // Sanity check + if ((sentence[0] == '$' || sentence[0] == '!') && sentence.size() > 5) { std::string identifier; // We notify based on full message, including the Talker ID - identifier = full_sentence.substr(1, 5); + identifier = sentence.substr(1, 5); // notify message listener and also "ALL" N0183 messages, to support plugin // API using original talker id - auto msg = std::make_shared(identifier, full_sentence, - GetAddress()); + auto msg = + std::make_shared(identifier, sentence, GetAddress()); auto msg_all = std::make_shared(*msg, "ALL"); - if (m_params.SentencePassesFilter(full_sentence, FILTER_INPUT)) + if (m_params.SentencePassesFilter(sentence, FILTER_INPUT)) m_listener.Notify(std::move(msg)); m_listener.Notify(std::move(msg_all)); } } -void CommDriverN0183Net::Open(void) { +void CommDriverN0183Net::Open() { #ifdef __UNIX__ -#if wxCHECK_VERSION(3, 0, 0) - in_addr_t addr = - ((struct sockaddr_in*)GetAddr().GetAddressData())->sin_addr.s_addr; -#else in_addr_t addr = - ((struct sockaddr_in*)GetAddr().GetAddress()->m_addr)->sin_addr.s_addr; -#endif + ((struct sockaddr_in*)m_addr.GetAddressData())->sin_addr.s_addr; #else - unsigned int addr = inet_addr(GetAddr().IPAddress().mb_str()); + unsigned int addr = inet_addr(m_addr.IPAddress().mb_str()); #endif // Create the socket - switch (m_net_protocol) { + switch (m_params.NetProtocol) { case GPSD: { - OpenNetworkGPSD(); + OpenNetworkGpsd(); break; } case TCP: { - OpenNetworkTCP(addr); + OpenNetworkTcp(addr); break; } case UDP: { - OpenNetworkUDP(addr); + OpenNetworkUdp(addr); break; } default: break; } - SetOk(true); + m_ok = true; } -void CommDriverN0183Net::OpenNetworkUDP(unsigned int addr) { - if (GetPortType() != DS_TYPE_OUTPUT) { +void CommDriverN0183Net::OpenNetworkUdp(unsigned int addr) { + if (m_params.IOSelect != DS_TYPE_OUTPUT) { // We need a local (bindable) address to create the Datagram receive socket - // Set up the receive socket + // Set up the reception socket wxIPV4address conn_addr; - conn_addr.Service(GetNetPort()); + conn_addr.Service(std::to_string(m_params.NetworkPort)); + conn_addr.AnyAddress(); conn_addr.AnyAddress(); - SetSock( - new wxDatagramSocket(conn_addr, wxSOCKET_NOWAIT | wxSOCKET_REUSEADDR)); + m_sock = + new wxDatagramSocket(conn_addr, wxSOCKET_NOWAIT | wxSOCKET_REUSEADDR); // Test if address is IPv4 multicast if ((ntohl(addr) & 0xf0000000) == 0xe0000000) { - SetMulticast(true); + m_is_multicast = true; m_mrq_container->SetMrqAddr(addr); - GetSock()->SetOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, - &m_mrq_container->m_mrq, - sizeof(m_mrq_container->m_mrq)); + m_sock->SetOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, &m_mrq_container->m_mrq, + sizeof(m_mrq_container->m_mrq)); } - GetSock()->SetEventHandler(*this, DS_SOCKET_ID); + m_sock->SetEventHandler(*this, DS_SOCKET_ID); - GetSock()->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | - wxSOCKET_LOST_FLAG); - GetSock()->Notify(TRUE); - GetSock()->SetTimeout(1); // Short timeout + m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | + wxSOCKET_LOST_FLAG); + m_sock->Notify(TRUE); + m_sock->SetTimeout(1); // Short timeout } // Set up another socket for transmit - if (GetPortType() != DS_TYPE_INPUT) { + if (m_params.IOSelect != DS_TYPE_INPUT) { wxIPV4address tconn_addr; tconn_addr.Service(0); // use ephemeral out port tconn_addr.AnyAddress(); - SetTSock( - new wxDatagramSocket(tconn_addr, wxSOCKET_NOWAIT | wxSOCKET_REUSEADDR)); + m_tsock = + new wxDatagramSocket(tconn_addr, wxSOCKET_NOWAIT | wxSOCKET_REUSEADDR); // Here would be the place to disable multicast loopback // but for consistency with broadcast behaviour, we will // instead rely on setting priority levels to ignore // sentences read back that have just been transmitted - if ((!GetMulticast()) && (GetAddr().IPAddress().EndsWith(_T("255")))) { + if ((!m_is_multicast) && (m_addr.IPAddress().EndsWith("255"))) { int broadcastEnable = 1; - bool bam = GetTSock()->SetOption( - SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable)); + m_tsock->SetOption(SOL_SOCKET, SO_BROADCAST, &broadcastEnable, + sizeof(broadcastEnable)); } } // In case the connection is lost before acquired.... - SetConnectTime(wxDateTime::Now()); + m_connect_time = std::chrono::steady_clock::now(); } -void CommDriverN0183Net::OpenNetworkTCP(unsigned int addr) { - int isServer = ((addr == INADDR_ANY) ? 1 : 0); - wxLogMessage(wxString::Format(_T("Opening TCP Server %d"), isServer)); - - if (isServer) { - SetSockServer(new wxSocketServer(GetAddr(), wxSOCKET_REUSEADDR)); +void CommDriverN0183Net::OpenNetworkTcp(unsigned int addr) { + if (addr == INADDR_ANY) { + MESSAGE_LOG << "Listening for TCP connections on " << INADDR_ANY; + m_socket_server = new wxSocketServer(m_addr, wxSOCKET_REUSEADDR); + m_socket_server->SetEventHandler(*this, DS_SERVERSOCKET_ID); + m_socket_server->SetNotify(wxSOCKET_CONNECTION_FLAG); + m_socket_server->Notify(TRUE); + m_socket_server->SetTimeout(1); // Short timeout } else { - SetSock(new wxSocketClient()); - } - - if (isServer) { - GetSockServer()->SetEventHandler(*this, DS_SERVERSOCKET_ID); - GetSockServer()->SetNotify(wxSOCKET_CONNECTION_FLAG); - GetSockServer()->Notify(TRUE); - GetSockServer()->SetTimeout(1); // Short timeout - } else { - GetSock()->SetEventHandler(*this, DS_SOCKET_ID); + MESSAGE_LOG << "Opening TCP connection to " << m_params.NetworkAddress + << ":" << m_params.NetworkPort; + m_sock = new wxSocketClient(); + m_sock->SetEventHandler(*this, DS_SOCKET_ID); int notify_flags = (wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG); - if (GetPortType() != DS_TYPE_INPUT) notify_flags |= wxSOCKET_OUTPUT_FLAG; - if (GetPortType() != DS_TYPE_OUTPUT) notify_flags |= wxSOCKET_INPUT_FLAG; - GetSock()->SetNotify(notify_flags); - GetSock()->Notify(TRUE); - GetSock()->SetTimeout(1); // Short timeout - - SetBrxConnectEvent(false); - GetSocketTimer()->Start(100, wxTIMER_ONE_SHOT); // schedule a connection + if (m_params.IOSelect != DS_TYPE_INPUT) + notify_flags |= wxSOCKET_OUTPUT_FLAG; + if (m_params.IOSelect != DS_TYPE_OUTPUT) + notify_flags |= wxSOCKET_INPUT_FLAG; + m_sock->SetNotify(notify_flags); + m_sock->Notify(true); + m_sock->SetTimeout(1); // Short timeout + + m_rx_connect_event = false; + m_socket_timer.Start(100, wxTIMER_ONE_SHOT); // schedule a connection } // In case the connection is lost before acquired.... - SetConnectTime(wxDateTime::Now()); + m_connect_time = std::chrono::steady_clock::now(); } -void CommDriverN0183Net::OpenNetworkGPSD() { - SetSock(new wxSocketClient()); - GetSock()->SetEventHandler(*this, DS_SOCKET_ID); - GetSock()->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | - wxSOCKET_LOST_FLAG); - GetSock()->Notify(TRUE); - GetSock()->SetTimeout(1); // Short timeout - - wxSocketClient* tcp_socket = static_cast(GetSock()); - tcp_socket->Connect(GetAddr(), FALSE); - SetBrxConnectEvent(false); +void CommDriverN0183Net::OpenNetworkGpsd() { + m_sock = new wxSocketClient(); + m_sock->SetEventHandler(*this, DS_SOCKET_ID); + m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | + wxSOCKET_LOST_FLAG); + m_sock->Notify(TRUE); + m_sock->SetTimeout(1); // Short timeout + + auto* tcp_socket = dynamic_cast(m_sock); + tcp_socket->Connect(m_addr, false); + m_rx_connect_event = false; } -void CommDriverN0183Net::OnSocketReadWatchdogTimer(wxTimerEvent& event) { +void CommDriverN0183Net::OnSocketReadWatchdogTimer() { m_dog_value--; if (m_dog_value <= 0) { // No receive in n seconds if (GetParams().NoDataReconnect) { // Reconnect on NO DATA is true, so try to reconnect now. - if (GetProtocol() == TCP) { - wxSocketClient* tcp_socket = dynamic_cast(GetSock()); + if (m_params.NetProtocol == TCP) { + auto* tcp_socket = dynamic_cast(m_sock); if (tcp_socket) tcp_socket->Close(); int n_reconnect_delay = wxMax(N_DOG_TIMEOUT - 2, 2); - wxLogMessage(wxString::Format(" Reconnection scheduled in %d seconds.", - n_reconnect_delay)); - GetSocketTimer()->Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT); + wxLogMessage("Reconnection scheduled in %d seconds.", + n_reconnect_delay); + m_socket_timer.Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT); // Stop DATA watchdog, will be restarted on successful connection. - GetSocketThreadWatchdogTimer()->Stop(); + m_socketread_watchdog_timer.Stop(); } } } @@ -367,55 +300,67 @@ void CommDriverN0183Net::OnSocketReadWatchdogTimer(wxTimerEvent& event) { void CommDriverN0183Net::OnTimerSocket() { // Attempt a connection - wxSocketClient* tcp_socket = dynamic_cast(GetSock()); + using namespace std::chrono; + auto* tcp_socket = dynamic_cast(m_sock); if (tcp_socket) { if (tcp_socket->IsDisconnected()) { - wxLogDebug(" Attempting reconnection..."); - SetBrxConnectEvent(false); + wxLogDebug("Attempting reconnection..."); + m_rx_connect_event = false; // Stop DATA watchdog, may be restarted on successful connection. - GetSocketThreadWatchdogTimer()->Stop(); - tcp_socket->Connect(GetAddr(), FALSE); + m_socketread_watchdog_timer.Stop(); + tcp_socket->Connect(m_addr, false); // schedule another connection attempt, in case this one fails int n_reconnect_delay = N_DOG_TIMEOUT; - GetSocketTimer()->Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT); + m_socket_timer.Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT); + + // Possibly report connect error to GUI. + if (m_connect_time == time_point()) return; + auto since_connect = steady_clock::now() - m_connect_time; + if (since_connect > 10s && !m_is_conn_err_reported) { + std::stringstream ss; + ss << "Cannot connect to remote server " << m_params.NetworkAddress + << ":" << m_params.NetworkPort; + CommDriverRegistry::GetInstance().evt_driver_msg.Notify(ss.str()); + m_is_conn_err_reported = true; + } } } } void CommDriverN0183Net::HandleResume() { // Attempt a stop and restart of connection - wxSocketClient* tcp_socket = dynamic_cast(GetSock()); + auto* tcp_socket = dynamic_cast(m_sock); if (tcp_socket) { - GetSocketThreadWatchdogTimer()->Stop(); + m_socketread_watchdog_timer.Stop(); tcp_socket->Close(); // schedule reconnect attempt int n_reconnect_delay = wxMax(N_DOG_TIMEOUT - 2, 2); - wxLogMessage(wxString::Format(" Reconnection scheduled in %d seconds.", - n_reconnect_delay)); + wxLogMessage("Reconnection scheduled in %d seconds.", n_reconnect_delay); - GetSocketTimer()->Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT); + m_socket_timer.Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT); } } bool CommDriverN0183Net::SendMessage(std::shared_ptr msg, std::shared_ptr addr) { auto msg_0183 = std::dynamic_pointer_cast(msg); - return SendSentenceNetwork(msg_0183->payload.c_str()); + std::string payload(msg_0183->payload); + if (!ocpn::endswith(payload, "\r\n")) payload += "\r\n"; + return SendSentenceNetwork(payload.c_str()); } void CommDriverN0183Net::OnSocketEvent(wxSocketEvent& event) { - // #define RD_BUF_SIZE 200 -#define RD_BUF_SIZE \ - 4096 // Allows handling of high volume data streams, such as a National AIS - // stream with 100s of msgs a second. +#define RD_BUF_SIZE 4096 + // Allows handling of high volume data streams, such as a National AIS + // stream with 100s of msgs a second. switch (event.GetSocketEvent()) { case wxSOCKET_INPUT: // from gpsd Daemon { - // TODO determine if the follwing SetFlags needs to be done at every + // TODO determine if the following SetFlags needs to be done at every // socket event or only once when socket is created, it it needs to be // done at all! // m_sock->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK); // was @@ -427,149 +372,78 @@ void CommDriverN0183Net::OnSocketEvent(wxSocketEvent& event) { // Disable input event notifications to preclude re-entrancy on // non-blocking socket // m_sock->SetNotify(wxSOCKET_LOST_FLAG); - - std::vector data(RD_BUF_SIZE + 1); - event.GetSocket()->Read(&data.front(), RD_BUF_SIZE); + uint8_t buff[RD_BUF_SIZE + 1]; + event.GetSocket()->Read(buff, RD_BUF_SIZE); if (!event.GetSocket()->Error()) { - size_t count = event.GetSocket()->LastCount(); - if (count) { - if (1 /*FIXME !g_benableUDPNullHeader*/) { - data[count] = 0; - m_sock_buffer += (&data.front()); - } else { - // XXX FIXME: is it reliable? - // copy all received bytes - // there's 0 in furuno UDP tags before NMEA sentences. - m_sock_buffer.append(&data.front(), count); - } + unsigned count = event.GetSocket()->LastCount(); + for (unsigned i = 0; i < count; i += 1) n0183_buffer.Put(buff[i]); + while (n0183_buffer.HasSentence()) { + HandleN0183Msg(n0183_buffer.GetSentence() + "\r\n"); } } - - bool done = false; - - while (!done) { - int nmea_tail = 2; - size_t nmea_end = m_sock_buffer.find_first_of( - "*\r\n"); // detect the potential end of a NMEA string by finding - // the checkum marker or EOL - - if (nmea_end == - wxString::npos) // No termination characters: continue reading - break; - - if (m_sock_buffer[nmea_end] != '*') nmea_tail = -1; - - if (nmea_end < m_sock_buffer.size() - nmea_tail) { - nmea_end += - nmea_tail + - 1; // move to the char after the 2 checksum digits, if present - if (nmea_end == 0) // The first character in the buffer is a - // terminator, skip it to avoid infinite loop - nmea_end = 1; - std::string nmea_line = m_sock_buffer.substr(0, nmea_end); - // If, due to some logic error, the {nmea_end} parameter is larger - // than the length of the socket buffer, then std::string::substr() - // will throw an exception. We don't want that, so test for it. If - // found, the simple solution is to clear the socket buffer, and - // carry on This has been seen on high volume TCP feeds, Windows - // only. Hard to catch..... - if (nmea_end > m_sock_buffer.size()) - m_sock_buffer.clear(); - else - m_sock_buffer = m_sock_buffer.substr(nmea_end); - - size_t nmea_start = nmea_line.find_last_of( - "$!"); // detect the potential start of a NMEA string, skipping - // preceding chars that may look like the start of a - // string. - if (nmea_start != wxString::npos) { - nmea_line = nmea_line.substr(nmea_start); - nmea_line += "\r\n"; // Add cr/lf, possibly superfluous - if (ChecksumOK(nmea_line)) { - CommDriverN0183NetEvent Nevent(wxEVT_COMMDRIVER_N0183_NET, 0); - if (nmea_line.size()) { - // Copy the message into a vector for tranmittal upstream - auto buffer = std::make_shared>(); - std::vector* vec = buffer.get(); - std::copy(nmea_line.begin(), nmea_line.end(), - std::back_inserter(*vec)); - - Nevent.SetPayload(buffer); - AddPendingEvent(Nevent); - } - } - } - } else - done = true; - } - - // Prevent non-nmea junk from consuming to much memory by limiting - // carry-over buffer size. - if (m_sock_buffer.size() > RD_BUF_SIZE) - m_sock_buffer = - m_sock_buffer.substr(m_sock_buffer.size() - RD_BUF_SIZE); - m_dog_value = N_DOG_TIMEOUT; // feed the dog break; } case wxSOCKET_LOST: { - if (GetProtocol() == TCP || GetProtocol() == GPSD) { - if (GetBrxConnectEvent()) - wxLogMessage(wxString::Format( - _T("NetworkDataStream connection lost: %s"), GetPort().c_str())); - if (GetSockServer()) { - GetSock()->Destroy(); - SetSock(NULL); + using namespace std::chrono; + if (m_params.NetProtocol == TCP || m_params.NetProtocol == GPSD) { + if (m_rx_connect_event) { + MESSAGE_LOG << "NetworkDataStream connection lost: " + << m_params.GetDSPort(); + } + if (m_socket_server) { + m_sock->Destroy(); + m_sock = nullptr; break; } - wxDateTime now = wxDateTime::Now(); - wxTimeSpan since_connect( - 0, 0, 10); // ten secs assumed, if connect time is uninitialized - if (GetConnectTime().IsValid()) since_connect = now - GetConnectTime(); - - int retry_time = 5000; // default + auto since_connect = 10s; + // ten secs assumed, if connect time is uninitialized + auto now = steady_clock::now(); + if (m_connect_time != time_point()) + since_connect = duration_cast(now - m_connect_time); + auto retry_time = 5s; // default // If the socket has never connected, and it is a short interval since // the connect request then stretch the time a bit. This happens on - // Windows if there is no dafault IP on any interface + // Windows if there is no default IP on any interface + if (!m_rx_connect_event && (since_connect < 5s)) retry_time = 10s; - if (!GetBrxConnectEvent() && (since_connect.GetSeconds() < 5)) - retry_time = 10000; // 10 secs + m_socketread_watchdog_timer.Stop(); - GetSocketThreadWatchdogTimer()->Stop(); - GetSocketTimer()->Start( - retry_time, wxTIMER_ONE_SHOT); // Schedule a re-connect attempt + // Schedule a re-connect attempt + m_socket_timer.Start(duration_cast(retry_time).count(), + wxTIMER_ONE_SHOT); } break; } case wxSOCKET_CONNECTION: { - if (GetProtocol() == GPSD) { + if (m_params.NetProtocol == GPSD) { // Sign up for watcher mode, Cooked NMEA // Note that SIRF devices will be converted by gpsd into // pseudo-NMEA - char cmd[] = "?WATCH={\"class\":\"WATCH\", \"nmea\":true}"; - GetSock()->Write(cmd, strlen(cmd)); - } else if (GetProtocol() == TCP) { - wxLogMessage(wxString::Format( - _T("TCP NetworkDataStream connection established: %s"), - GetPort().c_str())); + + char cmd[] = R"--(?WATCH={"class":"WATCH", "nmea":true})--"; + m_sock->Write(cmd, strlen(cmd)); + } else if (m_params.NetProtocol == TCP) { + MESSAGE_LOG << "TCP NetworkDataStream connection established: " + << m_params.GetDSPort(); m_dog_value = N_DOG_TIMEOUT; // feed the dog - if (GetPortType() != DS_TYPE_OUTPUT) { - /// start the DATA watchdog only if NODATA Reconnect is desired + if (m_params.IOSelect != DS_TYPE_OUTPUT) { + // start the DATA watchdog only if NODATA Reconnect is desired if (GetParams().NoDataReconnect) - GetSocketThreadWatchdogTimer()->Start(1000); + m_socketread_watchdog_timer.Start(1000); } - if (GetPortType() != DS_TYPE_INPUT && GetSock()->IsOk()) - (void)SetOutputSocketOptions(GetSock()); - GetSocketTimer()->Stop(); - SetBrxConnectEvent(true); + if (m_params.IOSelect != DS_TYPE_INPUT && GetSock()->IsOk()) + (void)SetOutputSocketOptions(m_sock); + m_socket_timer.Stop(); + m_rx_connect_event = true; } - SetConnectTime(wxDateTime::Now()); + m_connect_time = std::chrono::steady_clock::now(); break; } @@ -581,26 +455,24 @@ void CommDriverN0183Net::OnSocketEvent(wxSocketEvent& event) { void CommDriverN0183Net::OnServerSocketEvent(wxSocketEvent& event) { switch (event.GetSocketEvent()) { case wxSOCKET_CONNECTION: { - SetSock(GetSockServer()->Accept(false)); + m_sock = m_socket_server->Accept(false); if (GetSock()) { - GetSock()->SetTimeout(2); + m_sock->SetTimeout(2); // GetSock()->SetFlags(wxSOCKET_BLOCK); - GetSock()->SetEventHandler(*this, DS_SOCKET_ID); + m_sock->SetEventHandler(*this, DS_SOCKET_ID); int notify_flags = (wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG); - if (GetPortType() != DS_TYPE_INPUT) { + if (m_params.IOSelect != DS_TYPE_INPUT) { notify_flags |= wxSOCKET_OUTPUT_FLAG; - (void)SetOutputSocketOptions(GetSock()); + (void)SetOutputSocketOptions(m_sock); } - if (GetPortType() != DS_TYPE_OUTPUT) + if (m_params.IOSelect != DS_TYPE_OUTPUT) notify_flags |= wxSOCKET_INPUT_FLAG; - GetSock()->SetNotify(notify_flags); - GetSock()->Notify(true); + m_sock->SetNotify(notify_flags); + m_sock->Notify(true); } - break; } - default: break; } @@ -614,22 +486,21 @@ bool CommDriverN0183Net::SendSentenceNetwork(const wxString& payload) { bool ret = true; wxDatagramSocket* udp_socket; - switch (GetProtocol()) { + switch (m_params.NetProtocol) { case TCP: if (GetSock() && GetSock()->IsOk()) { - GetSock()->Write(payload.mb_str(), strlen(payload.mb_str())); + m_sock->Write(payload.mb_str(), strlen(payload.mb_str())); if (GetSock()->Error()) { - if (GetSockServer()) { - GetSock()->Destroy(); - SetSock(NULL); + if (m_socket_server) { + m_sock->Destroy(); + m_sock = nullptr; } else { - wxSocketClient* tcp_socket = - dynamic_cast(GetSock()); + auto* tcp_socket = dynamic_cast(m_sock); if (tcp_socket) tcp_socket->Close(); - if (!GetSocketTimer()->IsRunning()) - GetSocketTimer()->Start( - 5000, wxTIMER_ONE_SHOT); // schedule a reconnect - GetSocketThreadWatchdogTimer()->Stop(); + if (!m_socket_timer.IsRunning()) + m_socket_timer.Start(5000, wxTIMER_ONE_SHOT); + // schedule a reconnect + m_socketread_watchdog_timer.Stop(); } ret = false; } @@ -638,12 +509,13 @@ bool CommDriverN0183Net::SendSentenceNetwork(const wxString& payload) { ret = false; break; case UDP: - udp_socket = dynamic_cast(GetTSock()); + udp_socket = dynamic_cast(m_tsock); if (udp_socket && udp_socket->IsOk()) { - udp_socket->SendTo(GetAddr(), payload.mb_str(), payload.size()); + udp_socket->SendTo(m_addr, payload.mb_str(), payload.size()); if (udp_socket->Error()) ret = false; - } else + } else { ret = false; + } break; case GPSD: @@ -656,8 +528,7 @@ bool CommDriverN0183Net::SendSentenceNetwork(const wxString& payload) { } void CommDriverN0183Net::Close() { - wxLogMessage(wxString::Format(_T("Closing NMEA NetworkDataStream %s"), - GetNetPort().c_str())); + MESSAGE_LOG << "Closing NMEA NetworkDataStream " << m_params.NetworkPort; // Kill off the TCP Socket if alive if (m_sock) { if (m_is_multicast) @@ -680,32 +551,3 @@ void CommDriverN0183Net::Close() { m_socket_timer.Stop(); m_socketread_watchdog_timer.Stop(); } - -bool CommDriverN0183Net::SetOutputSocketOptions(wxSocketBase* tsock) { - int ret; - - // Disable nagle algorithm on outgoing connection - // Doing this here rather than after the accept() is - // pointless on platforms where TCP_NODELAY is - // not inherited. However, none of OpenCPN's currently - // supported platforms fall into that category. - - int nagleDisable = 1; - ret = tsock->SetOption(IPPROTO_TCP, TCP_NODELAY, &nagleDisable, - sizeof(nagleDisable)); - - // Drastically reduce the size of the socket output buffer - // so that when client goes away without properly closing, the stream will - // quickly fill the output buffer, and thus fail the write() call - // within a few seconds. - unsigned long outbuf_size = 1024; // Smallest allowable value on Linux - return (tsock->SetOption(SOL_SOCKET, SO_SNDBUF, &outbuf_size, - sizeof(outbuf_size)) && - ret); -} - -bool CommDriverN0183Net::ChecksumOK(const std::string& sentence) { - if (!m_bchecksumCheck) return true; - - return CheckSumCheck(sentence); -} diff --git a/model/src/comm_drv_n0183_serial.cpp b/model/src/comm_drv_n0183_serial.cpp index 8fb3c55ff0..23d060e5d2 100644 --- a/model/src/comm_drv_n0183_serial.cpp +++ b/model/src/comm_drv_n0183_serial.cpp @@ -18,7 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file comm_drv_n0183_serial.cpp Implement comm_drv_n0183_serial.h */ +/** + * \file + * Implement comm_drv_n0183_serial.h + */ // For compilers that support precompilation, includes "wx.h". #include @@ -45,15 +48,9 @@ using namespace std::literals::chrono_literals; -typedef enum DS_ENUM_BUFFER_STATE { - DS_RX_BUFFER_EMPTY, - DS_RX_BUFFER_FULL -} _DS_ENUM_BUFFER_STATE; - CommDriverN0183Serial::CommDriverN0183Serial(const ConnectionParams* params, DriverListener& listener) - : CommDriverN0183(NavAddr::Bus::N0183, - ((ConnectionParams*)params)->GetStrippedDSPort()), + : CommDriverN0183(NavAddr::Bus::N0183, params->GetStrippedDSPort()), m_portstring(params->GetDSPort()), m_baudrate(params->Baudrate), m_serial_io(SerialIo::Create( @@ -64,14 +61,8 @@ CommDriverN0183Serial::CommDriverN0183Serial(const ConnectionParams* params, m_garmin_handler = nullptr; this->attributes["commPort"] = params->Port.ToStdString(); this->attributes["userComment"] = params->UserComment.ToStdString(); - dsPortType iosel = params->IOSelect; - std::string s_iosel = std::string("IN"); - if (iosel == DS_TYPE_INPUT_OUTPUT) { - s_iosel = "OUT"; - } else if (iosel == DS_TYPE_INPUT_OUTPUT) { - s_iosel = "IN/OUT"; - } - this->attributes["ioDirection"] = s_iosel; + this->attributes["ioDirection"] = DsPortTypeToString(params->IOSelect); + Open(); } diff --git a/model/src/comm_drv_n2k_net.cpp b/model/src/comm_drv_n2k_net.cpp index 15ed0cbbd9..74a38e85f8 100644 --- a/model/src/comm_drv_n2k_net.cpp +++ b/model/src/comm_drv_n2k_net.cpp @@ -184,7 +184,7 @@ END_EVENT_TABLE() CommDriverN2KNet::CommDriverN2KNet(const ConnectionParams* params, DriverListener& listener) - : CommDriverN2K(((ConnectionParams*)params)->GetStrippedDSPort()), + : CommDriverN2K(params->GetStrippedDSPort()), m_params(*params), m_listener(listener), m_net_port(wxString::Format("%i", params->NetworkPort)), diff --git a/model/src/comm_drv_n2k_serial.cpp b/model/src/comm_drv_n2k_serial.cpp index 9993d13e21..e8f0e15782 100644 --- a/model/src/comm_drv_n2k_serial.cpp +++ b/model/src/comm_drv_n2k_serial.cpp @@ -207,7 +207,7 @@ wxDEFINE_EVENT(wxEVT_COMMDRIVER_N2K_SERIAL, CommDriverN2KSerialEvent); CommDriverN2KSerial::CommDriverN2KSerial(const ConnectionParams* params, DriverListener& listener) - : CommDriverN2K(((ConnectionParams*)params)->GetStrippedDSPort()), + : CommDriverN2K(params->GetStrippedDSPort()), m_Thread_run_flag(-1), m_bok(false), m_portstring(params->GetDSPort()), diff --git a/model/src/comm_drv_n2k_socketcan.cpp b/model/src/comm_drv_n2k_socketcan.cpp index c4932fd26b..30428e59cc 100644 --- a/model/src/comm_drv_n2k_socketcan.cpp +++ b/model/src/comm_drv_n2k_socketcan.cpp @@ -374,7 +374,7 @@ bool CommDriverN2KSocketCanImpl::SendMessage( CommDriverN2KSocketCAN::CommDriverN2KSocketCAN(const ConnectionParams* params, DriverListener& listener) - : CommDriverN2K(((ConnectionParams*)params)->GetStrippedDSPort()), + : CommDriverN2K(params->GetStrippedDSPort()), m_params(*params), m_listener(listener), m_ok(false), diff --git a/model/src/comm_drv_signalk_net.cpp b/model/src/comm_drv_signalk_net.cpp index 7e8eb6f451..cbede04393 100644 --- a/model/src/comm_drv_signalk_net.cpp +++ b/model/src/comm_drv_signalk_net.cpp @@ -218,7 +218,7 @@ wxDEFINE_EVENT(wxEVT_COMMDRIVER_SIGNALK_NET, CommDriverSignalKNetEvent); CommDriverSignalKNet::CommDriverSignalKNet(const ConnectionParams* params, DriverListener& listener) - : CommDriverSignalK(((ConnectionParams*)params)->GetStrippedDSPort()), + : CommDriverSignalK(params->GetStrippedDSPort()), m_Thread_run_flag(-1), m_params(*params), m_listener(listener) { diff --git a/model/src/comm_navmsg.cpp b/model/src/comm_navmsg.cpp index c2ec41b70a..0b20c42364 100644 --- a/model/src/comm_navmsg.cpp +++ b/model/src/comm_navmsg.cpp @@ -18,7 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file comm_navmsg.cpp Implement comm_navmsg.h */ +/** + * \file + * Implement comm_navmsg.h + */ // For compilers that support precompilation, includes "wx.h". #include diff --git a/model/src/conn_params.cpp b/model/src/conn_params.cpp index 6ca655ab24..0ba6f5c42b 100644 --- a/model/src/conn_params.cpp +++ b/model/src/conn_params.cpp @@ -295,7 +295,7 @@ wxString ConnectionParams::GetDSPort() const { return _T(""); } -std::string ConnectionParams::GetStrippedDSPort() { +std::string ConnectionParams::GetStrippedDSPort() const { if (Type == SERIAL) { wxString t = wxString::Format(_T("Serial:%s"), Port.c_str()); wxString comx = t.AfterFirst(':').BeforeFirst(' '); @@ -370,7 +370,7 @@ bool ConnectionParams::SentencePassesFilter(const wxString& sentence, return !listype; } -NavAddr::Bus ConnectionParams::GetCommProtocol() { +NavAddr::Bus ConnectionParams::GetCommProtocol() const { if (Type == NETWORK) { if (NetProtocol == SIGNALK) return NavAddr::Bus::Signalk; diff --git a/model/src/ds_porttype.cpp b/model/src/ds_porttype.cpp new file mode 100644 index 0000000000..ad4bbb5853 --- /dev/null +++ b/model/src/ds_porttype.cpp @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2014 ALec Leamas * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include + +#include "model/ds_porttype.h" + +std::string DsPortTypeToString(dsPortType type) { + switch (type) { + case DS_TYPE_INPUT_OUTPUT: + return "IN/OUT"; + break; + case DS_TYPE_OUTPUT: + return "OUT"; + break; + case DS_TYPE_INPUT: + return "IN"; + break; + }; + assert(false && "Compiler error (undefined dsPortType)"); + return ""; // for the compiler +} diff --git a/model/src/gui.cpp b/model/src/gui.cpp index fdce46f969..1407a9b7d5 100644 --- a/model/src/gui.cpp +++ b/model/src/gui.cpp @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file gui.cpp Implement gui.h. */ +/** + * \file + * Implement gui.h. + */ #include "model/gui.h" diff --git a/model/src/ipc_factories.cpp b/model/src/ipc_factories.cpp index 2400fc338b..7c9f2c17f4 100644 --- a/model/src/ipc_factories.cpp +++ b/model/src/ipc_factories.cpp @@ -17,7 +17,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file Local communications factories +/** + * \file + * Local communications factories. * * Factory methods which returns dbus or wxwidgets based implementations * of instance checkers, servers and clients. diff --git a/model/src/linux_devices.cpp b/model/src/linux_devices.cpp index 1ddacd8115..b27ac936a3 100644 --- a/model/src/linux_devices.cpp +++ b/model/src/linux_devices.cpp @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file linux_devices.cpp Implement linux_devices.h. */ +/** + * \file + * Implement linux_devices.h. + */ #include "config.h" diff --git a/model/src/linux_usb_watch.cpp b/model/src/linux_usb_watch.cpp index 21fd7d9981..af09b511d2 100644 --- a/model/src/linux_usb_watch.cpp +++ b/model/src/linux_usb_watch.cpp @@ -18,7 +18,7 @@ **************************************************************************/ /** - * \file linux_watch_daemon.h + * \file * Listen for Linux DBus events like suspend/resume and new devices and notify * SystemEvents */ diff --git a/model/src/logger.cpp b/model/src/logger.cpp index 61cbba4972..9572c161f9 100644 --- a/model/src/logger.cpp +++ b/model/src/logger.cpp @@ -1,9 +1,6 @@ -/****************************************************************************** - * - * Project: OpenCPN - * - *************************************************************************** +/*************************************************************************** * Copyright (C) 2013 by David S. Register * + * Copyright (C) 2022 Alec Leamas * * * * 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 * @@ -19,7 +16,11 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** + ***************************************************************************/ + +/** + * \file + * Implement logger.h */ #include @@ -138,3 +139,23 @@ void Logger::logMessage(wxLogLevel level, const char* path, int line, log->LogRecord(level, buf, info); } } + +void CountedLogFilter::Log(const std::string& message) { + m_not_logged += 1; + if (m_not_logged < m_count) return; + + wxLogGeneric(m_level, message.c_str()); + wxLogGeneric(m_level, "Previous message suppressed %d times", m_count); + m_not_logged = 0; +} + +void TimedLogFilter::Log(const std::string& message) { + auto now = std::chrono::steady_clock::now(); + m_not_logged += 1; + if (now - m_last_logged < m_interval) return; + + wxLogGeneric(m_level, message.c_str()); + wxLogGeneric(m_level, "Previous message suppressed %d times", m_not_logged); + m_not_logged = 0; + m_last_logged = now; +} diff --git a/model/src/mDNS_query.cpp b/model/src/mDNS_query.cpp index e778e58cda..6377455ee5 100644 --- a/model/src/mDNS_query.cpp +++ b/model/src/mDNS_query.cpp @@ -166,7 +166,7 @@ static int sk_query_callback(int sock, const struct sockaddr* from, std::string hostname = srv.substr(0, rh); // Remove non-printable characters as seen in names returned by macOS hostname.erase(remove_if(hostname.begin(), hostname.end(), - [](char c) { return !(c >= 0 && c < 128); }), + [](char c) { return (c < 0); }), hostname.end()); bool found = false; for (const auto& sks : g_sk_servers) { diff --git a/model/src/mDNS_service.cpp b/model/src/mDNS_service.cpp index 956a35fb90..c754949545 100644 --- a/model/src/mDNS_service.cpp +++ b/model/src/mDNS_service.cpp @@ -164,7 +164,7 @@ int ocpn_service_callback(int sock, const struct sockaddr* from, size_t addrlen, // ".<_service-name>._tcp.local." mdns_record_t answer = service->record_ptr; - mdns_record_t additional[5] = {0}; + mdns_record_t additional[5]{{}}; size_t additional_count = 0; // SRV record mapping ".<_service-name>._tcp.local." to @@ -213,7 +213,7 @@ int ocpn_service_callback(int sock, const struct sockaddr* from, size_t addrlen, // ".<_service-name>._tcp.local." mdns_record_t answer = service->record_srv; - mdns_record_t additional[5] = {0}; + mdns_record_t additional[5]{{}}; size_t additional_count = 0; // A/AAAA records mapping ".local." to IPv4/IPv6 addresses @@ -257,7 +257,7 @@ int ocpn_service_callback(int sock, const struct sockaddr* from, size_t addrlen, // Answer A records mapping ".local." to IPv4 address mdns_record_t answer = service->record_a; - mdns_record_t additional[5] = {0}; + mdns_record_t additional[5]{{}}; size_t additional_count = 0; // AAAA record mapping ".local." to IPv6 addresses @@ -300,7 +300,7 @@ int ocpn_service_callback(int sock, const struct sockaddr* from, size_t addrlen, // Answer AAAA records mapping ".local." to IPv6 address mdns_record_t answer = service->record_aaaa; - mdns_record_t additional[5] = {0}; + mdns_record_t additional[5]{{}}; size_t additional_count = 0; // A record mapping ".local." to IPv4 addresses @@ -394,7 +394,7 @@ void service_mdns(const char* hostname, const char* service_name, hostname_qualified_string.str = qualified_hostname_buffer; hostname_qualified_string.length = strlen(qualified_hostname_buffer); - service_t service = {0}; + service_t service{}; service.service = service_string; service.hostname = hostname_string; service.service_instance = service_instance_string; @@ -459,7 +459,7 @@ void service_mdns(const char* hostname, const char* service_name, // Send an announcement on startup of service { printf("Sending announce\n"); - mdns_record_t additional[5] = {0}; + mdns_record_t additional[5]{{}}; size_t additional_count = 0; additional[additional_count++] = service.record_srv; if (service.address_ipv4.sin_family == AF_INET) @@ -505,7 +505,7 @@ void service_mdns(const char* hostname, const char* service_name, // Send a goodbye on end of service { printf("Sending goodbye\n"); - mdns_record_t additional[5] = {0}; + mdns_record_t additional[5]{{}}; size_t additional_count = 0; additional[additional_count++] = service.record_srv; if (service.address_ipv4.sin_family == AF_INET) diff --git a/model/src/mdns_cache.cpp b/model/src/mdns_cache.cpp index 08cb058643..eed407f0fa 100644 --- a/model/src/mdns_cache.cpp +++ b/model/src/mdns_cache.cpp @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file mdns_cache.cpp Implement mdns_cache.h */ +/** + * \file + * Implement mdns_cache.h + */ #include diff --git a/model/src/ocpn_plugin.cpp b/model/src/ocpn_plugin.cpp index a8eb43841a..bf9dd3bc59 100644 --- a/model/src/ocpn_plugin.cpp +++ b/model/src/ocpn_plugin.cpp @@ -289,3 +289,8 @@ bool opencpn_plugin_118::RenderOverlayMultiCanvas(wxDC& dc, PlugIn_ViewPort* vp, int priority) { return false; } + +// Opencpn_Plugin_119 Implementation +opencpn_plugin_119::opencpn_plugin_119(void* pmgr) : opencpn_plugin_118(pmgr) {} + +void opencpn_plugin_119::PreShutdownHook() { return; } diff --git a/model/src/ocpn_utils.cpp b/model/src/ocpn_utils.cpp index 429c12e139..cacc0155db 100644 --- a/model/src/ocpn_utils.cpp +++ b/model/src/ocpn_utils.cpp @@ -145,4 +145,21 @@ void copy_file(const std::string& src_path, const std::string& dest_path) { dest.close(); } +bool N0183CheckSumOk(const std::string& sentence) { + size_t check_start = sentence.find('*'); + if (check_start == std::string::npos || check_start > sentence.size() - 3) + return false; // * not found, or it didn't have 2 characters following it. + + std::string check_str = sentence.substr(check_start + 1, 2); + unsigned long checksum = strtol(check_str.c_str(), 0, 16); + if (checksum == 0L && check_str != "00") return false; + + unsigned char calculated_checksum = 0; + for (std::string::const_iterator i = sentence.begin() + 1; + i != sentence.end() && *i != '*'; ++i) + calculated_checksum ^= static_cast(*i); + + return calculated_checksum == checksum; +} + } // namespace ocpn diff --git a/model/src/plugin_api.cpp b/model/src/plugin_api.cpp index c8ec7b017a..638733e128 100644 --- a/model/src/plugin_api.cpp +++ b/model/src/plugin_api.cpp @@ -1,11 +1,6 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: Implement communications defined in ocpn_plugin.h - * Author: David Register, Alec Leamas - * - *************************************************************************** - * Copyright (C) 2022 by David Register, Alec Leamas * +/************************************************************************** + * Copyright (C) 2022 by David Register * + * Copyright (C) 2022 Alec Leamas * * * * 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 * @@ -23,7 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file plugin_api.cpp Implement various ocpn_plugin.h methods. */ +/** + * \file + * Implement various ocpn_plugin.h methods. + */ #include #include diff --git a/model/src/plugin_loader.cpp b/model/src/plugin_loader.cpp index 4bbd3b8571..6783cda516 100644 --- a/model/src/plugin_loader.cpp +++ b/model/src/plugin_loader.cpp @@ -70,6 +70,7 @@ #include "model/plugin_loader.h" #include "model/plugin_paths.h" #include "model/safe_mode.h" +#include "model/semantic_vers.h" #include "observable_confvar.h" #ifdef __ANDROID__ @@ -849,8 +850,10 @@ PluginMetadata PluginLoader::MetadataByName(const std::string& name) { ss << f.rdbuf(); PluginMetadata pd; ParsePlugin(ss.str(), pd); + pd.is_imported = true; return pd; } + auto available = PluginHandler::getInstance()->getCompatiblePlugins(); vector matches; copy_if(available.begin(), available.end(), back_inserter(matches), @@ -880,6 +883,8 @@ void PluginLoader::UpdatePlugin(PlugInContainer* plugin, if (is_system) plugin->m_status = PluginStatus::System; + else if (plugin->m_status == PluginStatus::Imported) + ; // plugin->m_status = PluginStatus::Imported; else if (installedVersion < metaVersion) plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable; else if (installedVersion == metaVersion) @@ -925,7 +930,9 @@ void PluginLoader::UpdateManagedPlugins(bool keep_orphans) { if (!md.name.empty()) { auto import_path = PluginHandler::ImportedMetadataPath(md.name.c_str()); md.is_imported = isRegularFile(import_path.c_str()); - if (isRegularFile(PluginHandler::fileListPath(md.name).c_str())) { + if (md.is_imported) { + plugin->m_status = PluginStatus::Imported; + } else if (isRegularFile(PluginHandler::fileListPath(md.name).c_str())) { // This is an installed plugin PluginLoader::UpdatePlugin(plugin, md); } else if (IsSystemPluginName(md.name)) { @@ -1548,6 +1555,7 @@ PlugInContainer* PluginLoader::LoadPlugIn(const wxString& plugin_file, case 114: pic->m_pplugin = dynamic_cast(plug_in); break; + case 115: pic->m_pplugin = dynamic_cast(plug_in); break; @@ -1558,29 +1566,29 @@ PlugInContainer* PluginLoader::LoadPlugIn(const wxString& plugin_file, case 117: pic->m_pplugin = dynamic_cast(plug_in); - do /* force a local scope */ { - auto p = dynamic_cast(plug_in); - pi_ver = - SemanticVersion(pi_major, pi_minor, p->GetPlugInVersionPatch(), - p->GetPlugInVersionPost(), p->GetPlugInVersionPre(), - p->GetPlugInVersionBuild()); - } while (false); // NOLINT break; + case 118: pic->m_pplugin = dynamic_cast(plug_in); - do /* force a local scope */ { - auto p = dynamic_cast(plug_in); - pi_ver = - SemanticVersion(pi_major, pi_minor, p->GetPlugInVersionPatch(), - p->GetPlugInVersionPost(), p->GetPlugInVersionPre(), - p->GetPlugInVersionBuild()); - } while (false); // NOLINT + break; + + case 119: + pic->m_pplugin = dynamic_cast(plug_in); break; default: break; } + if (auto p = dynamic_cast(plug_in)) { + // For API 1.17+ use the version info in the plugin API in favor of + // the version file created when installing plugin. + pi_ver = + SemanticVersion(pi_major, pi_minor, p->GetPlugInVersionPatch(), + p->GetPlugInVersionPost(), p->GetPlugInVersionPre(), + p->GetPlugInVersionBuild()); + } + if (!pic->m_pplugin) { INFO_LOG << _("Incompatible plugin detected: ") << plugin_file << "\n"; INFO_LOG << _(" API Version detected: "); diff --git a/model/src/rest_server.cpp b/model/src/rest_server.cpp index b7d9a5bb29..3fe19cf423 100644 --- a/model/src/rest_server.cpp +++ b/model/src/rest_server.cpp @@ -1,4 +1,3 @@ - /************************************************************************** * Copyright (C) 2022 David Register * * Copyright (C) 2022-2023 Alec Leamas * @@ -19,7 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file rest_server.cpp Implement rest_server.h */ +/** + * \file + * Implement rest_server.h + */ #include #include diff --git a/model/src/routeman.cpp b/model/src/routeman.cpp index 6071eb4bc6..f56dd97c74 100644 --- a/model/src/routeman.cpp +++ b/model/src/routeman.cpp @@ -1095,7 +1095,7 @@ bool WayPointman::DoesIconExist(const wxString &icon_key) const { return false; } -wxBitmap *WayPointman::GetIconBitmap(const wxString &icon_key) { +wxBitmap *WayPointman::GetIconBitmap(const wxString &icon_key) const { wxBitmap *pret = NULL; MarkIcon *pmi = NULL; unsigned int i; @@ -1131,7 +1131,7 @@ wxBitmap *WayPointman::GetIconBitmap(const wxString &icon_key) { return pret; } -bool WayPointman::GetIconPrescaled(const wxString &icon_key) { +bool WayPointman::GetIconPrescaled(const wxString &icon_key) const { MarkIcon *pmi = NULL; unsigned int i; @@ -1159,7 +1159,7 @@ bool WayPointman::GetIconPrescaled(const wxString &icon_key) { return false; } -wxBitmap WayPointman::GetIconBitmapForList(int index, int height) { +wxBitmap WayPointman::GetIconBitmapForList(int index, int height) const { wxBitmap pret; MarkIcon *pmi; @@ -1201,7 +1201,7 @@ wxBitmap WayPointman::GetIconBitmapForList(int index, int height) { return pret; } -wxString *WayPointman::GetIconDescription(int index) { +wxString *WayPointman::GetIconDescription(int index) const { wxString *pret = NULL; if (index >= 0) { @@ -1211,7 +1211,7 @@ wxString *WayPointman::GetIconDescription(int index) { return pret; } -wxString WayPointman::GetIconDescription(wxString icon_key) { +wxString WayPointman::GetIconDescription(wxString icon_key) const { MarkIcon *pmi; unsigned int i; @@ -1224,7 +1224,7 @@ wxString WayPointman::GetIconDescription(wxString icon_key) { return wxEmptyString; } -wxString *WayPointman::GetIconKey(int index) { +wxString *WayPointman::GetIconKey(int index) const { wxString *pret = NULL; if ((index >= 0) && ((unsigned int)index < m_pIconArray->GetCount())) { @@ -1234,7 +1234,7 @@ wxString *WayPointman::GetIconKey(int index) { return pret; } -int WayPointman::GetIconIndex(const wxBitmap *pbm) { +int WayPointman::GetIconIndex(const wxBitmap *pbm) const { unsigned int ret = 0; MarkIcon *pmi; @@ -1250,7 +1250,7 @@ int WayPointman::GetIconIndex(const wxBitmap *pbm) { return ret; } -int WayPointman::GetIconImageListIndex(const wxBitmap *pbm) { +int WayPointman::GetIconImageListIndex(const wxBitmap *pbm) const { MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(GetIconIndex(pbm)); // Build a "list - sized" image @@ -1344,28 +1344,16 @@ int WayPointman::GetIconImageListIndex(const wxBitmap *pbm) { return pmi->listIndex; } -int WayPointman::GetXIconImageListIndex(const wxBitmap *pbm) { - return GetIconImageListIndex(pbm) + - 1; // index of "X-ed out" icon in the image list +int WayPointman::GetXIconImageListIndex(const wxBitmap *pbm) const { + return GetIconImageListIndex(pbm) + 1; } -int WayPointman::GetFIconImageListIndex(const wxBitmap *pbm) { - return GetIconImageListIndex(pbm) + - 2; // index of "fixed viz" icon in the image list +int WayPointman::GetFIconImageListIndex(const wxBitmap *pbm) const { + return GetIconImageListIndex(pbm) + 2; } // Create the unique identifier wxString WayPointman::CreateGUID(RoutePoint *pRP) { - // FIXME: this method is not needed at all (if GetUUID works...) - /*wxDateTime now = wxDateTime::Now(); - time_t ticks = now.GetTicks(); - wxString GUID; - GUID.Printf(_T("%d-%d-%d-%d"), ((int)fabs(pRP->m_lat * 1e4)), - ((int)fabs(pRP->m_lon * 1e4)), (int)ticks, m_nGUID); - - m_nGUID++; - - return GUID;*/ return GpxDocument::GetUUID(); } diff --git a/model/src/std_serial_io.cpp b/model/src/std_serial_io.cpp index 402968c31f..252dc045c8 100644 --- a/model/src/std_serial_io.cpp +++ b/model/src/std_serial_io.cpp @@ -19,9 +19,9 @@ **************************************************************************/ /** - * \file std_serial_io.cpp SerialIo asynchronous implementation - * based on the serial/serial.h header. Used on all platforms - * besides Android. + * \file + * SerialIo asynchronous implementation based on the serial/serial.h header. + * Used on all platforms besides Android. */ #include @@ -67,7 +67,7 @@ class StdSerialIo : public SerialIo { bool OpenComPortPhysical(const wxString& com_name, unsigned baud_rate); void CloseComPortPhysical(); ssize_t WriteComPortPhysical(const char* msg); - void RequestStop() { ThreadCtrl::RequestStop(); } + void RequestStop() override { ThreadCtrl::RequestStop(); } }; std::unique_ptr SerialIo::Create(SendMsgFunc send_msg_func, @@ -159,8 +159,8 @@ bool StdSerialIo::OpenComPortPhysical(const wxString& com_name, m_serial.open(); m_serial.setTimeout(250, 250, 0, 250, 0); } catch (std::exception& e) { - MESSAGE_LOG << "Unhandled Exception while opening serial port: " - << e.what(); + auto msg = std::string("Unhandled Exception while opening serial port: "); + m_open_log_filter.Log(msg + e.what()); } return m_serial.isOpen(); } diff --git a/model/src/thread_ctrl.cpp b/model/src/thread_ctrl.cpp index 39e71c6cf4..3599024804 100644 --- a/model/src/thread_ctrl.cpp +++ b/model/src/thread_ctrl.cpp @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file thread_ctrl.cpp Implement thread_ctrl.h. */ +/** + * \file + * Implement thread_ctrl.h. + */ #include "model/thread_ctrl.h" diff --git a/model/src/usb_watch_factory.cpp b/model/src/usb_watch_factory.cpp index b3fe29bd9a..6627ec91ff 100644 --- a/model/src/usb_watch_factory.cpp +++ b/model/src/usb_watch_factory.cpp @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file usb_watch_factory.cpp UsbWatchDaemon factory */ +/** + * \file + * UsbWatchDaemon factory + */ #if defined(__linux__) && !defined(__ANDROID__) #include "model/linux_usb_watch.h" diff --git a/model/src/win_usb_watch.cpp b/model/src/win_usb_watch.cpp index d70101b6ba..ad0792b7af 100644 --- a/model/src/win_usb_watch.cpp +++ b/model/src/win_usb_watch.cpp @@ -17,7 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file win_watch_daemon.cpp Implement win_watch_daemon.h */ +/** + * \file + * Implement win_watch_daemon.h + */ #include #include diff --git a/plugins/chartdldr_pi/data/chart_sources.xml b/plugins/chartdldr_pi/data/chart_sources.xml index 3511ebf9c4..eb37bb681c 100644 --- a/plugins/chartdldr_pi/data/chart_sources.xml +++ b/plugins/chartdldr_pi/data/chart_sources.xml @@ -914,6 +914,12 @@ https://raw.githubusercontent.com/chartcatalogs/catalogs/master/BR_RNC_Catalog.xml {USERDATA}/RNC/BRASIL + + Brasil Inland ENC Charts + RNC + https://raw.githubusercontent.com/chartcatalogs/catalogs/master/BR_IENC_Catalog.xml + {USERDATA}/ENC/BRASIL_INLAND + Bulgaria Inland ENC Charts ENC diff --git a/plugins/dashboard_pi/src/dashboard_pi.cpp b/plugins/dashboard_pi/src/dashboard_pi.cpp index bf6a3baeb1..8d43ff8f62 100644 --- a/plugins/dashboard_pi/src/dashboard_pi.cpp +++ b/plugins/dashboard_pi/src/dashboard_pi.cpp @@ -1622,14 +1622,19 @@ void dashboard_pi::SetNMEASentence(wxString &sentence) { mHDx_Watchdog = gps_watchdog_timeout_ticks; } } - if (!std::isnan(m_NMEA0183.Vhw.Knots)) { - if (mPriSTW >= 3) { - mPriSTW = 3; + if (mPriSTW >= 3) { + double stw_kn = NAN; + if (!std::isnan(m_NMEA0183.Vhw.Knots)) + stw_kn = m_NMEA0183.Vhw.Knots; + else if (!std::isnan(m_NMEA0183.Vhw.KilometersPerHour)) + stw_kn = m_NMEA0183.Vhw.KilometersPerHour * 0.53995; + + if (!std::isnan(stw_kn)) { SendSentenceToAllInstruments( - OCPN_DBP_STC_STW, - toUsrSpeed_Plugin(m_NMEA0183.Vhw.Knots, g_iDashSpeedUnit), + OCPN_DBP_STC_STW, toUsrSpeed_Plugin(stw_kn, g_iDashSpeedUnit), getUsrSpeedUnit_Plugin(g_iDashSpeedUnit)); mSTW_Watchdog = gps_watchdog_timeout_ticks; + mPriSTW = 3; } } } @@ -5591,7 +5596,12 @@ void DashboardWindow::OnContextMenuSelect(wxCommandEvent &event) { switch (event.GetId()) { case ID_DASH_PREFS: { + // Capture the dashboard's floating_pos before update. + wxPoint fp = m_pauimgr->GetPane(this).floating_pos; m_plugin->ShowPreferencesDialog(this); + // This method sets the correct size of the edited dashboard, + // but if it's not specified, also a default floating_pos. + ChangePaneOrientation(GetSizerOrientation(), true, fp.x, fp.y); return; // Does it's own save. } case ID_DASH_RESIZE: { @@ -5635,7 +5645,8 @@ void DashboardWindow::SetColorScheme(PI_ColorScheme cs) { Refresh(false); } -void DashboardWindow::ChangePaneOrientation(int orient, bool updateAUImgr) { +void DashboardWindow::ChangePaneOrientation(int orient, bool updateAUImgr, + int fpx, int fpy) { m_pauimgr->DetachPane(this); SetSizerOrientation(orient); bool vertical = orient == wxVERTICAL; @@ -5654,7 +5665,7 @@ void DashboardWindow::ChangePaneOrientation(int orient, bool updateAUImgr) { .MinSize(sz) .BestSize(sz) .FloatingSize(sz) - .FloatingPosition(100, 100) + .FloatingPosition(fpx, fpy) .Float() .Show(m_Container->m_bIsVisible)); diff --git a/plugins/dashboard_pi/src/dashboard_pi.h b/plugins/dashboard_pi/src/dashboard_pi.h index 4cebf2d22f..898e824cfe 100644 --- a/plugins/dashboard_pi/src/dashboard_pi.h +++ b/plugins/dashboard_pi/src/dashboard_pi.h @@ -454,7 +454,10 @@ class DashboardWindow : public wxWindow { void SendSatInfoToAllInstruments(int cnt, int seq, wxString talk, SAT_INFO sats[4]); void SendUtcTimeToAllInstruments(wxDateTime value); - void ChangePaneOrientation(int orient, bool updateAUImgr); + + // Default FloatingPosition (100.100) included. + void ChangePaneOrientation(int orient, bool updateAUImgr, int fpx = 100, + int fpy = 100); /*TODO: OnKeyPress pass event to main window or disable focus*/ DashboardWindowContainer *m_Container; diff --git a/plugins/demo_pi_sample/CMakeLists.txt b/plugins/demo_pi_sample/CMakeLists.txt index 8cbb0523ba..76f3ca7144 100644 --- a/plugins/demo_pi_sample/CMakeLists.txt +++ b/plugins/demo_pi_sample/CMakeLists.txt @@ -1,145 +1,161 @@ -##--------------------------------------------------------------------------- -## Author: Dave Register -##*************************************************************************** -## * Copyright (C) 2010 by David S. Register * -## * 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 * -## * the Free Software Foundation; either version 2 of the License, or * -## * (at your option) any later version. * -## * * -## * This program is distributed in the hope that it will be useful, * -## * but WITHOUT ANY WARRANTY; without even the implied warranty of * -## * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -## * GNU General Public License for more details. * -## * * -## * You should have received a copy of the GNU General Public License * -## * along with this program; if not, write to the * -## * Free Software Foundation, Inc., * -## * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * -## *************************************************************************** - +# --------------------------------------------------------------------------- +# Author: Dave Register +# *************************************************************************** +# - Copyright (C) 2010 by David S. Register * +# - 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 * +# - the Free Software Foundation; either version 2 of the License, or * +# - (at your option) any later version. * +# - * +# - This program is distributed in the hope that it will be useful, * +# - but WITHOUT ANY WARRANTY; without even the implied warranty of * +# - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# - GNU General Public License for more details. * +# - * +# - You should have received a copy of the GNU General Public License * +# - along with this program; if not, write to the * +# - Free Software Foundation, Inc., * +# - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * +# *************************************************************************** # define minimum cmake version -CMAKE_MINIMUM_REQUIRED(VERSION 3.1.1) +cmake_minimum_required(VERSION 3.1.1) + +project(demo_pi) + +set(PACKAGE_NAME demo_pi) +set(VERBOSE_NAME demo) +set(TITLE_NAME demo) +set(CPACK_PACKAGE_CONTARCT "Pavel Kalian") + +message(STATUS "*** Building ${PACKAGE_NAME} ***") + +set(VERSION_MAJOR "1") +set(VERSION_MINOR "0") +set(VERSION_PATCH "3") +set(VERSION_DATE "07/01/2018") + +if (NOT CMAKE_BUILD_TYPE) + set( + CMAKE_BUILD_TYPE Release CACHE STRING + "Choose build type [None Debug Release RelWithDebInfo MinSizeRel]: " FORCE + ) +endif () + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +include("cmake/PluginConfigure.cmake") + +if (PREFIX) + set(CMAKE_INSTALL_PREFIX ${PREFIX}) +endif () + +set(PARENT opencpn) +set(PREFIX_BIN bin) +set(PREFIX_INCLUDE include) +set(PREFIX_DATA share) +set(PREFIX_PARENTDATA ${PREFIX_DATA}/${PARENT}) + +include_directories(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src) -PROJECT(demo_pi) +# require proper c++ ADD_DEFINITIONS( "-Wall -ansi -pedantic +# -Wno-variadic-macros" ) TODO: Should we use -fno-stack-protector IF NOT +# DEBUGGING CFLAGS="-O2 -march=native" -SET(PACKAGE_NAME demo_pi) -SET(VERBOSE_NAME demo) -SET(TITLE_NAME demo) -SET(CPACK_PACKAGE_CONTARCT "Pavel Kalian") +if (WIN32) + add_definitions(-D__MSVC__) + add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE) +elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|AppleClang") + string(APPEND + CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS " -undefined dynamic_lookup" + ) + add_compile_options("-Wall" "-g" "-fexceptions" "-fvisibility=hidden") +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL GNU) + add_compile_options("-Wl,-Bsymbolic") + add_compile_options("-Wl,--unresolved-symbols=ignore-in-shared-libs") + add_compile_options("-Wall" "-g" "-fexceptions" "-fvisibility=hidden") +endif () -MESSAGE (STATUS "*** Building ${PACKAGE_NAME} ***") +set(BUILD_SHARED_LIBS TRUE) -SET(VERSION_MAJOR "1") -SET(VERSION_MINOR "0") -SET(VERSION_PATCH "3") -SET(VERSION_DATE "07/01/2018") +if ("$ENV{CI}" STREQUAL "") + set(wxWidgets_USE_LIBS base core net xml html adv) + find_package(wxWidgets REQUIRED) + include(${wxWidgets_USE_FILE}) +endif () -IF( NOT CMAKE_BUILD_TYPE ) - SET( CMAKE_BUILD_TYPE Release CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." - FORCE ) -ENDIF( NOT CMAKE_BUILD_TYPE ) +find_package(Gettext REQUIRED) -MESSAGE (STATUS "Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") -INCLUDE("cmake/PluginConfigure.cmake") +include("cmake/PluginConfigure.cmake") -IF (PREFIX) - SET(CMAKE_INSTALL_PREFIX ${PREFIX}) -ENDIF (PREFIX) +if (PREFIX) + set(CMAKE_INSTALL_PREFIX ${PREFIX}) +endif () -SET(PARENT opencpn) -SET(PREFIX_BIN bin) -SET(PREFIX_INCLUDE include) -SET(PREFIX_DATA share) -SET(PREFIX_PARENTDATA ${PREFIX_DATA}/${PARENT}) +set(PARENT opencpn) +set(PREFIX_BIN bin) +set(PREFIX_INCLUDE include) +set(PREFIX_DATA share) +set(PREFIX_PARENTDATA ${PREFIX_DATA}/${PARENT}) -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src) +include_directories(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src) # require proper c++ -#ADD_DEFINITIONS( "-Wall -ansi -pedantic -Wno-variadic-macros" ) -#TODO: Should we use -fno-stack-protector -# IF NOT DEBUGGING CFLAGS="-O2 -march=native" -IF(NOT WIN32) - ADD_COMPILE_OPTIONS("-Wall" "-g" "-fexceptions" "-fvisibility=hidden") - - IF(NOT APPLE) - SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic") - ELSE(NOT APPLE) - SET(CMAKE_SHARED_LINKER_FLAGS "-Wl") - ENDIF(NOT APPLE) - -ENDIF(NOT WIN32) - -# Add some definitions to satisfy MS -IF(WIN32) - ADD_DEFINITIONS(-D__MSVC__) - ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE) -ENDIF(WIN32) - - -SET(wxWidgets_USE_LIBS base core net xml html adv) -SET(BUILD_SHARED_LIBS TRUE) -FIND_PACKAGE(wxWidgets REQUIRED) - -INCLUDE(${wxWidgets_USE_FILE}) - -FIND_PACKAGE(Gettext REQUIRED) - -# For convenience we define the sources as a variable. You can add -# header files and cpp/c files and CMake will sort them out - - -SET(SRC_DEMO - src/demo_pi.h - src/demo_pi.cpp - ) - -SET(SRC_NMEA0183 - src/nmea0183/LatLong.hpp - src/nmea0183/latlong.cpp - src/nmea0183/long.cpp - src/nmea0183/nmea0183.cpp - src/nmea0183/nmea0183.hpp - src/nmea0183/Response.hpp - src/nmea0183/response.cpp - src/nmea0183/RMB.hpp - src/nmea0183/rmb.cpp - src/nmea0183/Sentence.hpp - src/nmea0183/sentence.cpp - src/nmea0183/talkerid.cpp - src/nmea0183/RMC.HPP - src/nmea0183/rmc.cpp - src/nmea0183/hexvalue.cpp - src/nmea0183/lat.cpp - src/nmea0183/expid.cpp - src/nmea0183/wpl.hpp - src/nmea0183/wpl.cpp - src/nmea0183/rte.hpp - src/nmea0183/rte.cpp - src/nmea0183/hdt.hpp - src/nmea0183/hdt.cpp - src/nmea0183/hdg.hpp - src/nmea0183/hdg.cpp - src/nmea0183/hdm.hpp - src/nmea0183/hdm.cpp - src/nmea0183/gll.hpp - src/nmea0183/gll.cpp - src/nmea0183/vtg.hpp - src/nmea0183/vtg.cpp - src/nmea0183/gga.hpp - src/nmea0183/gga.cpp - src/nmea0183/gsv.hpp - src/nmea0183/gsv.cpp - ) -INCLUDE_DIRECTORIES(nmea0183) - -ADD_LIBRARY(${PACKAGE_NAME} SHARED ${SRC_DEMO} ${SRC_NMEA0183} ) +# ADD_DEFINITIONS( "-Wall -ansi -pedantic -Wno-variadic-macros" ) +# TODO: Should we use -fno-stack-protector IF NOT +# DEBUGGING CFLAGS="-O2 -march=native" + + +# For convenience we define the sources as a variable. You can add header files +# and cpp/c files and CMake will sort them out + +set(SRC_DEMO src/demo_pi.h src/demo_pi.cpp) + +set(SRC_NMEA0183 + src/nmea0183/LatLong.hpp + src/nmea0183/latlong.cpp + src/nmea0183/long.cpp + src/nmea0183/nmea0183.cpp + src/nmea0183/nmea0183.hpp + src/nmea0183/Response.hpp + src/nmea0183/response.cpp + src/nmea0183/RMB.hpp + src/nmea0183/rmb.cpp + src/nmea0183/Sentence.hpp + src/nmea0183/sentence.cpp + src/nmea0183/talkerid.cpp + src/nmea0183/RMC.HPP + src/nmea0183/rmc.cpp + src/nmea0183/hexvalue.cpp + src/nmea0183/lat.cpp + src/nmea0183/expid.cpp + src/nmea0183/wpl.hpp + src/nmea0183/wpl.cpp + src/nmea0183/rte.hpp + src/nmea0183/rte.cpp + src/nmea0183/hdt.hpp + src/nmea0183/hdt.cpp + src/nmea0183/hdg.hpp + src/nmea0183/hdg.cpp + src/nmea0183/hdm.hpp + src/nmea0183/hdm.cpp + src/nmea0183/gll.hpp + src/nmea0183/gll.cpp + src/nmea0183/vtg.hpp + src/nmea0183/vtg.cpp + src/nmea0183/gga.hpp + src/nmea0183/gga.cpp + src/nmea0183/gsv.hpp + src/nmea0183/gsv.cpp +) +include_directories(nmea0183) + +add_library(${PACKAGE_NAME} SHARED ${SRC_DEMO} ${SRC_NMEA0183}) target_link_libraries(${PACKAGE_NAME} ocpn::opencpn) -INCLUDE("cmake/PluginInstall.cmake") -INCLUDE("cmake/PluginLocalization.cmake") -INCLUDE("cmake/PluginPackage.cmake") +include("cmake/PluginInstall.cmake") +include("cmake/PluginLocalization.cmake") +include("cmake/PluginPackage.cmake") diff --git a/plugins/demo_pi_sample/src/demo_pi.cpp b/plugins/demo_pi_sample/src/demo_pi.cpp index db5b907ba5..34aac9efd2 100644 --- a/plugins/demo_pi_sample/src/demo_pi.cpp +++ b/plugins/demo_pi_sample/src/demo_pi.cpp @@ -1,11 +1,5 @@ -/****************************************************************************** - * updated: 4-5-2012 - * Project: OpenCPN - * Purpose: demo Plugin - * Author: David Register - * - *************************************************************************** - * Copyright (C) 2010 by David S. Register * +/*************************************************************************** + * Copyright (C) 2010 by David S. Register * * * * 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 * @@ -21,233 +15,150 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** - */ + **************************************************************************/ +/** + * \file demo_pi.h + * Minimal demonstration plugin. + */ #include "wx/wxprec.h" -#ifndef WX_PRECOMP - #include "wx/wx.h" -#endif //precompiled headers +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif // precompiled headers #include #include "demo_pi.h" -#include "wxWTranslateCatalog.h" - - -// the class factories, used to create and destroy instances of the PlugIn - -extern "C" DECL_EXP opencpn_plugin* create_pi(void *ppimgr) -{ - return new demo_pi(ppimgr); -} -extern "C" DECL_EXP void destroy_pi(opencpn_plugin* p) -{ - delete p; +/** Class factory used to create instances of the PlugIn */ +extern "C" DECL_EXP opencpn_plugin* create_pi(void* ppimgr) { + return new DemoPi(ppimgr); } +/** Class destructor. */ +extern "C" DECL_EXP void destroy_pi(opencpn_plugin* p) { delete p; } +/** Basic plugin initialization. */ +int DemoPi::Init(void) { + AddLocaleCatalog("../plugins/demo_pi_sample/src/"); + m_demo_window = nullptr; + // Get a pointer to the opencpn display canvas, to use as a parent for windows + // created + m_parent_window = GetOCPNCanvasWindow(); -//--------------------------------------------------------------------------------------------------------- -// -// demo PlugIn Implementation -// -//--------------------------------------------------------------------------------------------------------- - - - -//--------------------------------------------------------------------------------------------------------- -// -// PlugIn initialization and de-init -// -//--------------------------------------------------------------------------------------------------------- - - -int demo_pi::Init(void) -{ - AddLocaleCatalog( PLUGIN_CATALOG_NAME ); - - m_pdemo_window = NULL; - - // Get a pointer to the opencpn display canvas, to use as a parent for windows created - m_parent_window = GetOCPNCanvasWindow(); + // Create the Context Menu Items - // Create the Context Menu Items + // In order to avoid an ASSERT on msw debug builds, + // we need to create a dummy menu to act as a surrogate parent of the + // created MenuItems The Items will be re-parented when added to the real + // context meenu + wxMenu dummy_menu; - // In order to avoid an ASSERT on msw debug builds, - // we need to create a dummy menu to act as a surrogate parent of the created MenuItems - // The Items will be re-parented when added to the real context meenu - wxMenu dummy_menu; + wxMenuItem* pmi = + new wxMenuItem(&dummy_menu, -1, _("Show PlugIn DemoWindow")); + m_show_id = AddCanvasContextMenuItem(pmi, this); + SetCanvasContextMenuItemViz(m_show_id, true); - wxMenuItem *pmi = new wxMenuItem(&dummy_menu, -1, _("Show PlugIn DemoWindow")); - m_show_id = AddCanvasContextMenuItem(pmi, this ); - SetCanvasContextMenuItemViz(m_show_id, true); + wxMenuItem* pmih = + new wxMenuItem(&dummy_menu, -1, _("Hide PlugIn DemoWindow")); + m_hide_id = AddCanvasContextMenuItem(pmih, this); + SetCanvasContextMenuItemViz(m_hide_id, false); - wxMenuItem *pmih = new wxMenuItem(&dummy_menu, -1, _("Hide PlugIn DemoWindow")); - m_hide_id = AddCanvasContextMenuItem(pmih, this ); - SetCanvasContextMenuItemViz(m_hide_id, false); + m_demo_window = std::make_unique(m_parent_window, wxID_ANY); - m_pdemo_window = new demoWindow(m_parent_window, wxID_ANY); + m_aui_mgr = GetFrameAuiManager(); + m_aui_mgr->AddPane(m_demo_window.get()); + m_aui_mgr->GetPane(m_demo_window.get()).Name("Demo Window Name"); - m_AUImgr = GetFrameAuiManager(); - m_AUImgr->AddPane(m_pdemo_window); - m_AUImgr->GetPane(m_pdemo_window).Name(_T("Demo Window Name")); + m_aui_mgr->GetPane(m_demo_window.get()).Float(); + m_aui_mgr->GetPane(m_demo_window.get()).FloatingPosition(300, 30); - m_AUImgr->GetPane(m_pdemo_window).Float(); - m_AUImgr->GetPane(m_pdemo_window).FloatingPosition(300,30); + m_aui_mgr->GetPane(m_demo_window.get()).Caption("AUI Managed Demo Window"); + m_aui_mgr->GetPane(m_demo_window.get()).CaptionVisible(true); + m_aui_mgr->GetPane(m_demo_window.get()).GripperTop(true); + m_aui_mgr->GetPane(m_demo_window.get()).CloseButton(true); + m_aui_mgr->GetPane(m_demo_window.get()).Show(false); + m_aui_mgr->Update(); - m_AUImgr->GetPane(m_pdemo_window).Caption(_T("AUI Managed Demo Window")); - m_AUImgr->GetPane(m_pdemo_window).CaptionVisible(true); - m_AUImgr->GetPane(m_pdemo_window).GripperTop(true); - m_AUImgr->GetPane(m_pdemo_window).CloseButton(true); - m_AUImgr->GetPane(m_pdemo_window).Show(false); - m_AUImgr->Update(); - - return ( - INSTALLS_CONTEXTMENU_ITEMS | - WANTS_NMEA_SENTENCES | - USES_AUI_MANAGER - ); + return (INSTALLS_CONTEXTMENU_ITEMS | WANTS_NMEA_SENTENCES | USES_AUI_MANAGER); } -bool demo_pi::DeInit(void) -{ - m_AUImgr->DetachPane(m_pdemo_window); - if(m_pdemo_window) - { - m_pdemo_window->Close(); -// m_pdemo_window->Destroy(); //Gives a Segmentation fault - } - return true; +bool DemoPi::DeInit(void) { + m_aui_mgr->DetachPane(m_demo_window.get()); + return true; } -int demo_pi::GetAPIVersionMajor() -{ - return MY_API_VERSION_MAJOR; -} +int DemoPi::GetAPIVersionMajor() { return MY_API_VERSION_MAJOR; } -int demo_pi::GetAPIVersionMinor() -{ - return MY_API_VERSION_MINOR; -} +int DemoPi::GetAPIVersionMinor() { return MY_API_VERSION_MINOR; } -int demo_pi::GetPlugInVersionMajor() -{ - return PLUGIN_VERSION_MAJOR; -} +int DemoPi::GetPlugInVersionMajor() { return PLUGIN_VERSION_MAJOR; } -int demo_pi::GetPlugInVersionMinor() -{ - return PLUGIN_VERSION_MINOR; -} +int DemoPi::GetPlugInVersionMinor() { return PLUGIN_VERSION_MINOR; } +int GetPlugInVersionPatch() { return PLUGIN_VERSION_PATCH; } +int GetPlugInVersionPost() { return PLUGIN_VERSION_TWEAK; } +const char* GetPlugInVersionPre() { return PKG_PRERELEASE; } +const char* GetPlugInVersionBuild() { return PKG_BUILD_INFO; } -wxString demo_pi::GetCommonName() -{ - return _("Demo"); -} +wxString DemoPi::GetCommonName() { return _("Demo"); } -wxString demo_pi::GetShortDescription() -{ - return _("Demo PlugIn for OpenCPN"); -} +wxString DemoPi::GetShortDescription() { return _("Demo PlugIn for OpenCPN"); } -wxString demo_pi::GetLongDescription() -{ - return _("Demo PlugIn for OpenCPN\n\ +wxString DemoPi::GetLongDescription() { + return _( + "Demo PlugIn for OpenCPN\n\ demonstrates PlugIn processing of NMEA messages."); - } -void demo_pi::SetNMEASentence(wxString &sentence) -{ -// printf("demo_pi::SetNMEASentence\n"); - if(m_pdemo_window) - { - m_pdemo_window->SetSentence(sentence); - } -} - - -void demo_pi::OnContextMenuItemCallback(int id) -{ - wxLogMessage(_T("demo_pi OnContextMenuCallBack()")); - ::wxBell(); - - // Note carefully that this is a "reference to a wxAuiPaneInfo classs instance" - // Copy constructor (i.e. wxAuiPaneInfo pane = m_AUImgr->GetPane(m_pdemo_window);) will not work - - wxAuiPaneInfo &pane = m_AUImgr->GetPane(m_pdemo_window); - if(!pane.IsOk()) - return; - - if(!pane.IsShown()) - { -// printf("show\n"); - SetCanvasContextMenuItemViz(m_hide_id, true); - SetCanvasContextMenuItemViz(m_show_id, false); - - pane.Show(true); - m_AUImgr->Update(); - - } - else - { -// printf("hide\n"); - SetCanvasContextMenuItemViz(m_hide_id, false); - SetCanvasContextMenuItemViz(m_show_id, true); - - pane.Show(false); - m_AUImgr->Update(); - } - -/* - if(NULL == m_pdemo_window) - { - m_pdemo_window = new demoWindow(m_parent_window, wxID_ANY); - - SetCanvasContextMenuItemViz(m_hide_id, true); - SetCanvasContextMenuItemViz(m_show_id, false); - } - else - { - m_pdemo_window->Close(); - m_pdemo_window->Destroy(); - m_pdemo_window = NULL; - - SetCanvasContextMenuItemViz(m_hide_id, false); - SetCanvasContextMenuItemViz(m_show_id, true); - } -*/ +void DemoPi::OnContextMenuItemCallback(int id) { + wxLogMessage("demo_pi OnContextMenuCallBack()"); + ::wxBell(); + + // Note carefully that this is a "reference to a wxAuiPaneInfo classs + // instance" Copy constructor (i.e. wxAuiPaneInfo pane = + // m_aui_mgr->GetPane(m_demo_window);) will not work + + wxAuiPaneInfo& pane = m_aui_mgr->GetPane(m_demo_window.get()); + if (!pane.IsOk()) return; + + if (!pane.IsShown()) { + SetCanvasContextMenuItemViz(m_hide_id, true); + SetCanvasContextMenuItemViz(m_show_id, false); + pane.Show(true); + m_aui_mgr->Update(); + } else { + SetCanvasContextMenuItemViz(m_hide_id, false); + SetCanvasContextMenuItemViz(m_show_id, true); + + pane.Show(false); + m_aui_mgr->Update(); + } } -void demo_pi::UpdateAuiStatus(void) -{ - // This method is called after the PlugIn is initialized - // and the frame has done its initial layout, possibly from a saved wxAuiManager "Perspective" - // It is a chance for the PlugIn to syncronize itself internally with the state of any Panes that - // were added to the frame in the PlugIn ctor. +void DemoPi::UpdateAuiStatus(void) { + // This method is called after the PlugIn is initialized + // and the frame has done its initial layout, possibly from a saved + // wxAuiManager "Perspective" It is a chance for the PlugIn to syncronize + // itself internally with the state of any Panes that were added to the + // frame in the PlugIn ctor. - // We use this callback here to keep the context menu selection in sync with the window state + // We use this callback here to keep the context menu selection in sync + // with the window state + wxAuiPaneInfo& pane = m_aui_mgr->GetPane(m_demo_window.get()); + if (!pane.IsOk()) return; - wxAuiPaneInfo &pane = m_AUImgr->GetPane(m_pdemo_window); - if(!pane.IsOk()) - return; - - printf("update %d\n",pane.IsShown()); - - SetCanvasContextMenuItemViz(m_hide_id, pane.IsShown()); - SetCanvasContextMenuItemViz(m_show_id, !pane.IsShown()); + printf("update %d\n", pane.IsShown()); + SetCanvasContextMenuItemViz(m_hide_id, pane.IsShown()); + SetCanvasContextMenuItemViz(m_show_id, !pane.IsShown()); } -bool demo_pi::RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp) -{ +bool DemoPi::RenderOverlay(wxDC& dc, PlugIn_ViewPort* vp) { /* if(m_pGribDialog && m_pGRIBOverlayFactory) { if(m_pGRIBOverlayFactory->IsReadyToRender()) @@ -259,48 +170,28 @@ bool demo_pi::RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp) return false; } else*/ - return false; -} -void demo_pi::SetCursorLatLon(double lat, double lon) -{ - -} -bool demo_pi::RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp) -{ - /* if(m_pGribDialog && m_pGRIBOverlayFactory) - { - if(m_pGRIBOverlayFactory->IsReadyToRender()) - { - m_pGRIBOverlayFactory->RenderGLGribOverlay ( pcontext, vp ); - return true; - } - else - return false; - } - else*/ - return false; - + return false; } -int demo_pi::GetToolbarToolCount(void) -{ - return 1; -} -void demo_pi::ShowPreferencesDialog( wxWindow* parent ) -{ - -} -void demo_pi::OnToolbarToolCallback(int id) -{ - -} -void demo_pi::SetPluginMessage(wxString &message_id, wxString &message_body) -{ - -} -void demo_pi::SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix) -{ - +void DemoPi::SetCursorLatLon(double lat, double lon) {} +bool DemoPi::RenderGLOverlay(wxGLContext* pcontext, PlugIn_ViewPort* vp) { + /* if(m_pGribDialog && m_pGRIBOverlayFactory) + { + if(m_pGRIBOverlayFactory->IsReadyToRender()) + { + m_pGRIBOverlayFactory->RenderGLGribOverlay ( pcontext, vp ); + return true; + } + else + return false; + } + else*/ + return false; } +int DemoPi::GetToolbarToolCount(void) { return 1; } +void DemoPi::ShowPreferencesDialog(wxWindow* parent) {} +void DemoPi::OnToolbarToolCallback(int id) {} +void DemoPi::SetPluginMessage(wxString& message_id, wxString& message_body) {} +void DemoPi::SetPositionFixEx(PlugIn_Position_Fix_Ex& pfix) {} //---------------------------------------------------------------- // @@ -308,109 +199,75 @@ void demo_pi::SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix) // //---------------------------------------------------------------- -BEGIN_EVENT_TABLE(demoWindow, wxWindow) - EVT_PAINT ( demoWindow::OnPaint ) - EVT_SIZE(demoWindow::OnSize) - - -END_EVENT_TABLE() - -demoWindow::demoWindow(wxWindow *pparent, wxWindowID id) - :wxWindow(pparent, id, wxPoint(10,10), wxSize(200,200), - wxSIMPLE_BORDER, _T("OpenCPN PlugIn")) -{ - mLat = 0.0; - mLon = 1.0; - mSog = 2.0; - mCog = 3.0; - mVar = 4.0; +DemoWindow::DemoWindow(wxWindow* parent, wxWindowID id) + : wxWindow(parent, id, wxPoint(10, 10), wxSize(200, 200), wxSIMPLE_BORDER, + "OpenCPN PlugIn"), + m_lat(0.0), + m_lon(1.0), + m_sog(2.0), + m_cog(3.0), + m_var(4.0), + m_utc("No GGA data") { + Bind(wxEVT_PAINT, [&](wxPaintEvent& ev) { OnPaint(ev); }); + Bind(wxEVT_SIZE, [&](wxSizeEvent& ev) { OnSize(ev); }); + + wxDEFINE_EVENT(EVT_DEMO_NAVDATA, ObservedEvt); + m_navdata_listener = GetListener(NavDataId(), EVT_DEMO_NAVDATA, this); + Bind(EVT_DEMO_NAVDATA, [&](ObservedEvt ev) { SetNavdata(ev); }); + + wxDEFINE_EVENT(EVT_DEMO_GGA, ObservedEvt); + m_gga_listener = GetListener(NMEA0183Id("GGA"), EVT_DEMO_GGA, this); + Bind(EVT_DEMO_GGA, [&](ObservedEvt ev) { HandleGga(ev); }); } -demoWindow::~demoWindow() -{ -} +void DemoWindow::OnSize(wxSizeEvent&) { printf("demoWindow OnSize()\n"); } -void demoWindow::OnSize(wxSizeEvent& event) -{ - printf("demoWindow OnSize()\n"); +void DemoWindow::SetNavdata(ObservedEvt ev) { + const PluginNavdata nav_data = GetEventNavdata(ev); + m_lat = nav_data.lat; + m_lon = nav_data.lon; + m_cog = nav_data.cog; + m_sog = nav_data.sog; + m_var = nav_data.var; + Refresh(false); } - -void demoWindow::SetSentence(wxString &sentence) -{ - m_NMEA0183 << sentence; - - bool bGoodData = false; - - if(m_NMEA0183.PreParse()) - { - if(m_NMEA0183.LastSentenceIDReceived == _T("RMC")) - { - if(m_NMEA0183.Parse()) - { - if(m_NMEA0183.Rmc.IsDataValid == NTrue) - { - float llt = m_NMEA0183.Rmc.Position.Latitude.Latitude; - int lat_deg_int = (int)(llt / 100); - float lat_deg = lat_deg_int; - float lat_min = llt - (lat_deg * 100); - mLat = lat_deg + (lat_min/60.); - if(m_NMEA0183.Rmc.Position.Latitude.Northing == South) - mLat = -mLat; - - float lln = m_NMEA0183.Rmc.Position.Longitude.Longitude; - int lon_deg_int = (int)(lln / 100); - float lon_deg = lon_deg_int; - float lon_min = lln - (lon_deg * 100); - mLon = lon_deg + (lon_min/60.); - if(m_NMEA0183.Rmc.Position.Longitude.Easting == West) - mLon = -mLon; - - mSog = m_NMEA0183.Rmc.SpeedOverGroundKnots; - mCog = m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue; - - if(m_NMEA0183.Rmc.MagneticVariationDirection == East) - mVar = m_NMEA0183.Rmc.MagneticVariation; - else if(m_NMEA0183.Rmc.MagneticVariationDirection == West) - mVar = -m_NMEA0183.Rmc.MagneticVariation; - - bGoodData = true; - - } - } - } - } - - // Got the data, now do something with it - - if(bGoodData) - { - Refresh(false); - } +void DemoWindow::HandleGga(ObservedEvt ev) { + auto payload = GetN0183Payload(NMEA0183Id("GGA"), ev); + wxString payload_clone(payload); // Missing const in << operator def + m_utc = "parse error"; + m_nmea0183 << payload_clone; + if (!m_nmea0183.PreParse()) return; + if (!m_nmea0183.Parse()) return; + if (m_nmea0183.LastSentenceIDReceived != "GGA") return; + m_utc = m_nmea0183.Gga.UTCTime; + Refresh(false); } -void demoWindow::OnPaint(wxPaintEvent& event) -{ - wxLogMessage(_T("demo_pi onpaint")); +void DemoWindow::OnPaint(wxPaintEvent&) { + wxLogMessage("demo_pi onpaint"); - wxPaintDC dc ( this ); + wxPaintDC dc(this); -// printf("onpaint\n"); + // printf("onpaint\n"); - { - dc.Clear(); + { + dc.Clear(); - wxString data; - data.Printf(_T("Lat: %g "), mLat); - dc.DrawText(data, 10, 10); + wxString data; + data.Printf("Lat: %g ", m_lat); + dc.DrawText(data, 10, 10); - data.Printf(_T("Lon: %g"), mLon); - dc.DrawText(data, 10, 40); + data.Printf("Lon: %g", m_lon); + dc.DrawText(data, 10, 40); - data.Printf(_T("Sog: %g"), mSog); - dc.DrawText(data, 10, 70); + data.Printf("Sog: %g", m_sog); + dc.DrawText(data, 10, 70); - data.Printf(_T("Cog: %g"), mCog); - dc.DrawText(data, 10, 100); - } + data.Printf("Cog: %g", m_cog); + dc.DrawText(data, 10, 100); + + data = "Utc: "; + dc.DrawText(data + m_utc, 10, 130); + } } diff --git a/plugins/demo_pi_sample/src/demo_pi.h b/plugins/demo_pi_sample/src/demo_pi.h index b5231b773d..9ce588e539 100644 --- a/plugins/demo_pi_sample/src/demo_pi.h +++ b/plugins/demo_pi_sample/src/demo_pi.h @@ -1,10 +1,5 @@ -/***************************************************************************** - * - * Project: OpenCPN - * Purpose: demo Plugin - * - *************************************************************************** - * Copyright (C) 2010 by David S. Register * +/*************************************************************************** + * Copyright (C) 2010 by David S. Register * * * * 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 * @@ -20,7 +15,11 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** + ***************************************************************************/ + +/** + * \file + * Minimal example plugin header */ #ifndef _DEMOPI_H_ @@ -28,95 +27,91 @@ #include "wx/wxprec.h" -#ifndef WX_PRECOMP - #include "wx/wx.h" -#endif //precompiled headers +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif // precompiled headers #include "version.h" -#define MY_API_VERSION_MAJOR 1 -#define MY_API_VERSION_MINOR 8 +#define MY_API_VERSION_MAJOR 1 +#define MY_API_VERSION_MINOR 8 -#include "../../../include/ocpn_plugin.h" +#define PLUGIN_VERSION_MAJOR 0 +#define PLUGIN_VERSION_MINOR 1 -#include "nmea0183/nmea0183.h" +#include "ocpn_plugin.h" +#include "nmea0183/nmea0183.h" -class demoWindow; +class DemoWindow; //---------------------------------------------------------------------------------------------------------- // The PlugIn Class Definition //---------------------------------------------------------------------------------------------------------- - -class demo_pi : public opencpn_plugin_18 -{ +class DemoPi : public opencpn_plugin_118 { public: - demo_pi(void *ppimgr):opencpn_plugin_18(ppimgr){} - -// The required PlugIn Methods - int Init(void); - bool DeInit(void); - - int GetAPIVersionMajor(); - int GetAPIVersionMinor(); - int GetPlugInVersionMajor(); - int GetPlugInVersionMinor(); - - wxString GetCommonName(); - wxString GetShortDescription(); - wxString GetLongDescription(); - -// The optional method overrides - - void SetNMEASentence(wxString &sentence); - void OnContextMenuItemCallback(int id); - void UpdateAuiStatus(void); - -// The override PlugIn Methods - bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); - void SetCursorLatLon(double lat, double lon); - bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp); - int GetToolbarToolCount(void); - void ShowPreferencesDialog( wxWindow* parent ); - void OnToolbarToolCallback(int id); - void SetPluginMessage(wxString &message_id, wxString &message_body); - void SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix); - + DemoPi(void *ppimgr) : opencpn_plugin_118(ppimgr) {} + + // The required PlugIn Methods + int Init(void); + bool DeInit(void); + + int GetAPIVersionMajor(); + int GetAPIVersionMinor(); + int GetPlugInVersionMajor(); + int GetPlugInVersionMinor(); + + wxString GetCommonName(); + wxString GetShortDescription(); + wxString GetLongDescription(); + + // The optional method overrides + void OnContextMenuItemCallback(int id); + void UpdateAuiStatus(void); + + // The override PlugIn Methods + bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); + void SetCursorLatLon(double lat, double lon); + bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp); + int GetToolbarToolCount(void); + void ShowPreferencesDialog(wxWindow *parent); + void OnToolbarToolCallback(int id); + void SetPluginMessage(wxString &message_id, wxString &message_body); + void SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix); private: - wxWindow *m_parent_window; - - demoWindow *m_pdemo_window; - wxAuiManager *m_AUImgr; - int m_show_id; - int m_hide_id; + wxWindow *m_parent_window; + std::unique_ptr m_demo_window; + wxAuiManager *m_aui_mgr; + int m_show_id; + int m_hide_id; }; - - -class demoWindow : public wxWindow -{ +class DemoWindow : public wxWindow { public: - demoWindow(wxWindow *pparent, wxWindowID id); - ~demoWindow(); - - void OnPaint(wxPaintEvent& event); - void SetSentence(wxString &sentence); - void OnSize(wxSizeEvent& event); + DemoWindow(wxWindow *pparent, wxWindowID id); + virtual ~DemoWindow() = default; - NMEA0183 m_NMEA0183; // Used to parse NMEA Sentences + void OnPaint(wxPaintEvent &event); + void OnSize(wxSizeEvent &event); + void SetNavdata(ObservedEvt ev); + void HandleGga(ObservedEvt ev); - wxString m_NMEASentence; - double mLat, mLon, mSog, mCog, mVar; + NMEA0183 m_nmea0183; // Used to parse NMEA Sentences + wxString m_nmea_sentence; + double m_lat; + double m_lon; + double m_sog; + double m_cog; + double m_var; + std::string m_utc; -DECLARE_EVENT_TABLE() +private: + std::shared_ptr m_navdata_listener; + std::shared_ptr m_gga_listener; }; - #endif - - - diff --git a/plugins/demo_pi_sample/src/nmea0183/LatLong.hpp b/plugins/demo_pi_sample/src/nmea0183/LatLong.hpp index ed0b6f7681..5f02a7b4a5 100644 --- a/plugins/demo_pi_sample/src/nmea0183/LatLong.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/LatLong.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( LATLONG_CLASS_HEADER ) +#if !defined(LATLONG_CLASS_HEADER) #define LATLONG_CLASS_HEADER /* @@ -43,100 +42,95 @@ class SENTENCE; -class LATITUDE -{ - - public: +class LATITUDE { +public: + LATITUDE(); + virtual ~LATITUDE(); - LATITUDE(); - virtual ~LATITUDE(); + /* + ** Data + */ - /* - ** Data - */ + double Latitude; - double Latitude; + NORTHSOUTH Northing; - NORTHSOUTH Northing; + /* + ** Methods + */ - /* - ** Methods - */ + virtual void Empty(void); + virtual bool IsDataValid(void); + virtual void Parse(int PositionFieldNumber, int NorthingFieldNumber, + const SENTENCE& LineToParse); + virtual void Set(double Position, const wxString& Northing); + virtual void Write(SENTENCE& sentence); - virtual void Empty( void ); - virtual bool IsDataValid( void ); - virtual void Parse( int PositionFieldNumber, int NorthingFieldNumber, const SENTENCE& LineToParse ); - virtual void Set( double Position, const wxString& Northing ); - virtual void Write( SENTENCE& sentence ); + /* + ** Operators + */ - /* - ** Operators - */ - - virtual const LATITUDE& operator = ( const LATITUDE& source ); + virtual const LATITUDE& operator=(const LATITUDE& source); }; -class LONGITUDE -{ - - public: +class LONGITUDE { +public: + LONGITUDE(); + virtual ~LONGITUDE(); - LONGITUDE(); - virtual ~LONGITUDE(); + /* + ** Data + */ - /* - ** Data - */ + double Longitude; - double Longitude; + EASTWEST Easting; - EASTWEST Easting; + /* + ** Methods + */ - /* - ** Methods - */ + virtual void Empty(void); + virtual bool IsDataValid(void); + virtual void Parse(int PositionFieldNumber, int EastingFieldNumber, + const SENTENCE& LineToParse); + virtual void Set(double Position, const wxString& Easting); + virtual void Write(SENTENCE& sentence); - virtual void Empty( void ); - virtual bool IsDataValid( void ); - virtual void Parse( int PositionFieldNumber, int EastingFieldNumber, const SENTENCE& LineToParse ); - virtual void Set( double Position, const wxString& Easting ); - virtual void Write( SENTENCE& sentence ); + /* + ** Operators + */ - /* - ** Operators - */ - - virtual const LONGITUDE& operator = ( const LONGITUDE& source ); + virtual const LONGITUDE& operator=(const LONGITUDE& source); }; -class LATLONG -{ - - public: - - LATLONG(); - virtual ~LATLONG(); +class LATLONG { +public: + LATLONG(); + virtual ~LATLONG(); - /* - ** Data - */ + /* + ** Data + */ - LATITUDE Latitude; - LONGITUDE Longitude; + LATITUDE Latitude; + LONGITUDE Longitude; - /* - ** Methods - */ + /* + ** Methods + */ - virtual void Empty( void ); - virtual bool Parse( int LatitudePostionFieldNumber, int NorthingFieldNumber, int LongitudePositionFieldNumber, int EastingFieldNumber, const SENTENCE& LineToParse ); - virtual void Write( SENTENCE& sentence ); + virtual void Empty(void); + virtual bool Parse(int LatitudePostionFieldNumber, int NorthingFieldNumber, + int LongitudePositionFieldNumber, int EastingFieldNumber, + const SENTENCE& LineToParse); + virtual void Write(SENTENCE& sentence); - /* - ** Operators - */ + /* + ** Operators + */ - virtual const LATLONG& operator = ( const LATLONG& source ); + virtual const LATLONG& operator=(const LATLONG& source); }; -#endif // LATLONG_CLASS_HEADER +#endif // LATLONG_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/RMB.hpp b/plugins/demo_pi_sample/src/nmea0183/RMB.hpp index 90c6df33ff..1ac42957b2 100644 --- a/plugins/demo_pi_sample/src/nmea0183/RMB.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/RMB.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( RMB_CLASS_HEADER ) +#if !defined(RMB_CLASS_HEADER) #define RMB_CLASS_HEADER /* @@ -41,43 +40,40 @@ ** You can use it any way you like. */ -class RMB : public RESPONSE -{ - - public: - - RMB(); - virtual ~RMB(); +class RMB : public RESPONSE { +public: + RMB(); + virtual ~RMB(); - /* - ** Data - */ + /* + ** Data + */ - NMEA0183_BOOLEAN IsDataValid; - double CrossTrackError; - LEFTRIGHT DirectionToSteer; - wxString To; - wxString From; - LATLONG DestinationPosition; - double RangeToDestinationNauticalMiles; - double BearingToDestinationDegreesTrue; - double DestinationClosingVelocityKnots; - NMEA0183_BOOLEAN IsArrivalCircleEntered; - wxString FAAModeIndicator; + NMEA0183_BOOLEAN IsDataValid; + double CrossTrackError; + LEFTRIGHT DirectionToSteer; + wxString To; + wxString From; + LATLONG DestinationPosition; + double RangeToDestinationNauticalMiles; + double BearingToDestinationDegreesTrue; + double DestinationClosingVelocityKnots; + NMEA0183_BOOLEAN IsArrivalCircleEntered; + wxString FAAModeIndicator; - /* - ** Methods - */ + /* + ** Methods + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); - /* - ** Operators - */ + /* + ** Operators + */ - virtual const RMB& operator = ( const RMB& source ); + virtual const RMB& operator=(const RMB& source); }; -#endif // RMB_CLASS_HEADER +#endif // RMB_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/RMC.HPP b/plugins/demo_pi_sample/src/nmea0183/RMC.HPP index fd69bde2e5..2791e0f490 100644 --- a/plugins/demo_pi_sample/src/nmea0183/RMC.HPP +++ b/plugins/demo_pi_sample/src/nmea0183/RMC.HPP @@ -29,7 +29,7 @@ * "It is BSD license, do with it what you will" * */ -#if ! defined( RMC_CLASS_HEADER ) +#if !defined(RMC_CLASS_HEADER) #define RMC_CLASS_HEADER /* @@ -40,41 +40,38 @@ ** You can use it any way you like. */ -class RMC : public RESPONSE -{ +class RMC : public RESPONSE { +public: + RMC(); + ~RMC(); - public: + /* + ** Data + */ - RMC(); - ~RMC(); + wxString UTCTime; + NMEA0183_BOOLEAN IsDataValid; + LATLONG Position; + double SpeedOverGroundKnots; + double TrackMadeGoodDegreesTrue; + wxString Date; + double MagneticVariation; + EASTWEST MagneticVariationDirection; + wxString FAAModeIndicator; - /* - ** Data - */ + /* + ** Methods + */ - wxString UTCTime; - NMEA0183_BOOLEAN IsDataValid; - LATLONG Position; - double SpeedOverGroundKnots; - double TrackMadeGoodDegreesTrue; - wxString Date; - double MagneticVariation; - EASTWEST MagneticVariationDirection; - wxString FAAModeIndicator; + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); - /* - ** Methods - */ + /* + ** Operators + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); - - /* - ** Operators - */ - - virtual const RMC& operator = ( const RMC& source ); + virtual const RMC& operator=(const RMC& source); }; -#endif // RMC_CLASS_HEADER +#endif // RMC_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/Response.hpp b/plugins/demo_pi_sample/src/nmea0183/Response.hpp index ac739de921..8522ffe8fd 100644 --- a/plugins/demo_pi_sample/src/nmea0183/Response.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/Response.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( RESPONSE_CLASS_HEADER ) +#if !defined(RESPONSE_CLASS_HEADER) #define RESPONSE_CLASS_HEADER /* @@ -43,38 +42,32 @@ class NMEA0183; -class RESPONSE -{ - - protected: - - NMEA0183 *container_p; +class RESPONSE { +protected: + NMEA0183* container_p; - public: +public: + RESPONSE(); + virtual ~RESPONSE(); - RESPONSE(); - virtual ~RESPONSE(); + /* + ** Data + */ - /* - ** Data - */ + wxString ErrorMessage; + wxString Mnemonic; + wxString Talker; - wxString ErrorMessage; - wxString Mnemonic; - wxString Talker; + /* + ** Methods + */ - /* - ** Methods - */ - - virtual void Empty( void ) = 0; - virtual bool Parse( const SENTENCE& sentence ) = 0; - virtual const wxString& PlainEnglish( void ); - virtual void SetErrorMessage( const wxString& ); - virtual void SetContainer( NMEA0183 *container ); - virtual bool Write( SENTENCE& sentence ); + virtual void Empty(void) = 0; + virtual bool Parse(const SENTENCE& sentence) = 0; + virtual const wxString& PlainEnglish(void); + virtual void SetErrorMessage(const wxString&); + virtual void SetContainer(NMEA0183* container); + virtual bool Write(SENTENCE& sentence); }; - - -#endif // RESPONSE_CLASS_HEADER +#endif // RESPONSE_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/Sentence.hpp b/plugins/demo_pi_sample/src/nmea0183/Sentence.hpp index 5dffea0c5b..dc0892930a 100644 --- a/plugins/demo_pi_sample/src/nmea0183/Sentence.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/Sentence.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( SENTENCE_CLASS_HEADER ) +#if !defined(SENTENCE_CLASS_HEADER) #define SENTENCE_CLASS_HEADER /* @@ -43,57 +42,56 @@ class LATLONG; -class SENTENCE -{ -// DECLARE_DYNAMIC( SENTENCE ) - - public: +class SENTENCE { + // DECLARE_DYNAMIC( SENTENCE ) - SENTENCE(); - virtual ~SENTENCE(); +public: + SENTENCE(); + virtual ~SENTENCE(); - /* - ** Data - */ + /* + ** Data + */ - wxString Sentence; + wxString Sentence; - /* - ** Methods - */ + /* + ** Methods + */ - virtual NMEA0183_BOOLEAN Boolean( int field_number ) const; - virtual unsigned char ComputeChecksum( void ) const; - virtual COMMUNICATIONS_MODE CommunicationsMode( int field_number ) const; - virtual double Double( int field_number ) const; - virtual EASTWEST EastOrWest( int field_number ) const; - virtual const wxString& Field( int field_number ) const; - virtual void Finish( void ); - virtual int GetNumberOfDataFields( void ) const; - virtual int Integer( int field_number ) const; - virtual NMEA0183_BOOLEAN IsChecksumBad( int checksum_field_number ) const; - virtual LEFTRIGHT LeftOrRight( int field_number ) const; - virtual NORTHSOUTH NorthOrSouth( int field_number ) const; - virtual REFERENCE Reference( int field_number ) const; - virtual TRANSDUCER_TYPE TransducerType( int field_number ) const; - virtual SENTENCE& Add ( double value, int precision); // Added to allow precision to be changed + virtual NMEA0183_BOOLEAN Boolean(int field_number) const; + virtual unsigned char ComputeChecksum(void) const; + virtual COMMUNICATIONS_MODE CommunicationsMode(int field_number) const; + virtual double Double(int field_number) const; + virtual EASTWEST EastOrWest(int field_number) const; + virtual const wxString& Field(int field_number) const; + virtual void Finish(void); + virtual int GetNumberOfDataFields(void) const; + virtual int Integer(int field_number) const; + virtual NMEA0183_BOOLEAN IsChecksumBad(int checksum_field_number) const; + virtual LEFTRIGHT LeftOrRight(int field_number) const; + virtual NORTHSOUTH NorthOrSouth(int field_number) const; + virtual REFERENCE Reference(int field_number) const; + virtual TRANSDUCER_TYPE TransducerType(int field_number) const; + virtual SENTENCE& Add( + double value, int precision); // Added to allow precision to be changed - /* - ** Operators - */ + /* + ** Operators + */ - operator wxString() const; - virtual const SENTENCE& operator = ( const SENTENCE& source ); - virtual const SENTENCE& operator = ( const wxString& source ); - virtual const SENTENCE& operator += ( const wxString& source ); - virtual const SENTENCE& operator += ( double value ); - virtual const SENTENCE& operator += ( NORTHSOUTH northing ); - virtual const SENTENCE& operator += ( COMMUNICATIONS_MODE mode ); - virtual const SENTENCE& operator += ( int value ); - virtual const SENTENCE& operator += ( EASTWEST easting ); - virtual const SENTENCE& operator += ( TRANSDUCER_TYPE transducer ); - virtual const SENTENCE& operator += ( NMEA0183_BOOLEAN boolean ); - virtual const SENTENCE& operator += ( LATLONG& source ); + operator wxString() const; + virtual const SENTENCE& operator=(const SENTENCE& source); + virtual const SENTENCE& operator=(const wxString& source); + virtual const SENTENCE& operator+=(const wxString& source); + virtual const SENTENCE& operator+=(double value); + virtual const SENTENCE& operator+=(NORTHSOUTH northing); + virtual const SENTENCE& operator+=(COMMUNICATIONS_MODE mode); + virtual const SENTENCE& operator+=(int value); + virtual const SENTENCE& operator+=(EASTWEST easting); + virtual const SENTENCE& operator+=(TRANSDUCER_TYPE transducer); + virtual const SENTENCE& operator+=(NMEA0183_BOOLEAN boolean); + virtual const SENTENCE& operator+=(LATLONG& source); }; -#endif // SENTENCE_CLASS_HEADER +#endif // SENTENCE_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/expid.cpp b/plugins/demo_pi_sample/src/nmea0183/expid.cpp index e4b6789479..6995ea49cf 100644 --- a/plugins/demo_pi_sample/src/nmea0183/expid.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/expid.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,390 +39,377 @@ ** You can use it any way you like. */ -wxString& expand_talker_id( const wxString &identifier ) -{ - static wxString expanded_identifier; - - char first_character = 0x00; - char second_character = 0x00; - - first_character = identifier[ 0 ]; - second_character = identifier[ 1 ]; +wxString& expand_talker_id(const wxString& identifier) { + static wxString expanded_identifier; - /* - ** Set up our default return value - */ + char first_character = 0x00; + char second_character = 0x00; - expanded_identifier = _T("Unknown - "); - expanded_identifier += identifier; + first_character = identifier[0]; + second_character = identifier[1]; - switch( first_character ) - { - case 'A': // AG, AP + /* + ** Set up our default return value + */ - switch( second_character ) - { - case 'G': // AG + expanded_identifier = _T("Unknown - "); + expanded_identifier += identifier; - expanded_identifier = _T("Autopilot - General"); - break; + switch (first_character) { + case 'A': // AG, AP - case 'P': // AP + switch (second_character) { + case 'G': // AG - expanded_identifier = _T("Autopilot - Magnetic"); - break; - } + expanded_identifier = _T("Autopilot - General"); + break; - break; + case 'P': // AP - case 'C': // CD, CS, CT, CV, CX and CC,CM + expanded_identifier = _T("Autopilot - Magnetic"); + break; + } - switch( second_character ) - { - case 'C': // CC + break; - expanded_identifier = _T("Commputer - Programmed Calculator (outdated)"); - break; + case 'C': // CD, CS, CT, CV, CX and CC,CM - case 'D': // CD + switch (second_character) { + case 'C': // CC - expanded_identifier = _T("Communications - Digital Selective Calling (DSC)"); - break; + expanded_identifier = + _T("Commputer - Programmed Calculator (outdated)"); + break; - case 'M': // CM + case 'D': // CD - expanded_identifier = _T("Computer - Memory Data (outdated)"); - break; + expanded_identifier = + _T("Communications - Digital Selective Calling (DSC)"); + break; - case 'S': // CS + case 'M': // CM - expanded_identifier = _T("Communications - Satellite"); - break; + expanded_identifier = _T("Computer - Memory Data (outdated)"); + break; - case 'T': // CT + case 'S': // CS - expanded_identifier = _T("Communications - Radio-Telephone (MF/HF)"); - break; + expanded_identifier = _T("Communications - Satellite"); + break; - case 'V': // CV + case 'T': // CT - expanded_identifier = _T("Communications - Radio-Telephone (VHF)"); - break; + expanded_identifier = _T("Communications - Radio-Telephone (MF/HF)"); + break; - case 'X': // CX + case 'V': // CV - expanded_identifier = _T("Communications - Scanning Receiver"); - break; - } + expanded_identifier = _T("Communications - Radio-Telephone (VHF)"); + break; - break; + case 'X': // CX - case 'D': // DE, DF + expanded_identifier = _T("Communications - Scanning Receiver"); + break; + } - switch( second_character ) - { - case 'E': // DE + break; - expanded_identifier = _T("DECCA Navigation"); - break; + case 'D': // DE, DF - case 'F': // DF + switch (second_character) { + case 'E': // DE - expanded_identifier = _T("Direction Finder"); - break; - } + expanded_identifier = _T("DECCA Navigation"); + break; - break; + case 'F': // DF - case 'E': // EC, EP, ER + expanded_identifier = _T("Direction Finder"); + break; + } - switch( second_character ) - { - case 'C': // EC + break; - expanded_identifier = _T("Electronic Chart Display & Information System (ECDIS)"); - break; + case 'E': // EC, EP, ER - case 'P': // EP + switch (second_character) { + case 'C': // EC - expanded_identifier = _T("Emergency Position Indicating Beacon (EPIRB)"); - break; + expanded_identifier = + _T("Electronic Chart Display & Information System (ECDIS)"); + break; - case 'R': // ER + case 'P': // EP - expanded_identifier = _T("Engine Room Monitoring Systems"); - break; - } + expanded_identifier = + _T("Emergency Position Indicating Beacon (EPIRB)"); + break; - break; + case 'R': // ER - case 'G': // GP + expanded_identifier = _T("Engine Room Monitoring Systems"); + break; + } - switch( second_character ) - { - case 'P': // GP + break; - expanded_identifier = _T("Global Positioning System (GPS)"); - break; - } + case 'G': // GP - break; + switch (second_character) { + case 'P': // GP - case 'H': // HC, HE, HN + expanded_identifier = _T("Global Positioning System (GPS)"); + break; + } - switch( second_character ) - { - case 'C': // HC + break; - expanded_identifier = _T("Heading - Magnetic Compass"); - break; + case 'H': // HC, HE, HN - case 'E': // HE + switch (second_character) { + case 'C': // HC - expanded_identifier = _T("Heading - North Seeking Gyro"); - break; + expanded_identifier = _T("Heading - Magnetic Compass"); + break; - case 'N': // HN + case 'E': // HE - expanded_identifier = _T("Heading - Non North Seeking Gyro"); - break; - } + expanded_identifier = _T("Heading - North Seeking Gyro"); + break; - break; + case 'N': // HN - case 'I': // II, IN + expanded_identifier = _T("Heading - Non North Seeking Gyro"); + break; + } - switch( second_character ) - { - case 'I': // II + break; - expanded_identifier = _T("Integrated Instrumentation"); - break; + case 'I': // II, IN - case 'N': // IN + switch (second_character) { + case 'I': // II - expanded_identifier = _T("Integrated Navigation"); - break; - } + expanded_identifier = _T("Integrated Instrumentation"); + break; - break; + case 'N': // IN - case 'L': // LA, LC + expanded_identifier = _T("Integrated Navigation"); + break; + } - switch( second_character ) - { - case 'A': // LA + break; - expanded_identifier = _T("Loran A"); - break; + case 'L': // LA, LC - case 'C': // LC + switch (second_character) { + case 'A': // LA - expanded_identifier = _T("Loran C"); - break; - } + expanded_identifier = _T("Loran A"); + break; - break; + case 'C': // LC - case 'M': // MP + expanded_identifier = _T("Loran C"); + break; + } - switch( second_character ) - { - case 'P': // MP + break; - expanded_identifier = _T("Microwave Positioning System (outdated)"); - break; - } + case 'M': // MP - break; + switch (second_character) { + case 'P': // MP - case 'O': // OM, OS + expanded_identifier = _T("Microwave Positioning System (outdated)"); + break; + } - switch( second_character ) - { - case 'M': // OM + break; - expanded_identifier = _T("OMEGA Navigation System"); - break; + case 'O': // OM, OS - case 'S': // OS + switch (second_character) { + case 'M': // OM - expanded_identifier = _T("Distress Alarm System (outdated)"); - break; - } + expanded_identifier = _T("OMEGA Navigation System"); + break; - break; + case 'S': // OS - case 'P': // P + expanded_identifier = _T("Distress Alarm System (outdated)"); + break; + } - break; + break; - case 'R': // RA + case 'P': // P - switch( second_character ) - { - case 'A': // RA + break; - expanded_identifier = _T("RADAR and/or ARPA"); - break; - } + case 'R': // RA - break; + switch (second_character) { + case 'A': // RA - case 'S': // SD, SN, SS + expanded_identifier = _T("RADAR and/or ARPA"); + break; + } - switch( second_character ) - { - case 'D': // SD + break; - expanded_identifier = _T("Sounder, Depth"); - break; + case 'S': // SD, SN, SS - case 'N': // SN + switch (second_character) { + case 'D': // SD - expanded_identifier = _T("Electronic Positioning System, other/general"); - break; + expanded_identifier = _T("Sounder, Depth"); + break; - case 'S': // SS + case 'N': // SN - expanded_identifier = _T("Sounder, Scanning"); - break; - } + expanded_identifier = + _T("Electronic Positioning System, other/general"); + break; - break; + case 'S': // SS - case 'T': // TI, TR + expanded_identifier = _T("Sounder, Scanning"); + break; + } - switch( second_character ) - { - case 'I': // TI + break; - expanded_identifier = _T("Turn Rate Indicator"); - break; + case 'T': // TI, TR - case 'R': // TR + switch (second_character) { + case 'I': // TI - expanded_identifier = _T("TRANSIT Navigation System"); - break; - } + expanded_identifier = _T("Turn Rate Indicator"); + break; - break; + case 'R': // TR - case 'V': // VD, VM, VW + expanded_identifier = _T("TRANSIT Navigation System"); + break; + } - switch( second_character ) - { - case 'D': // VD + break; - expanded_identifier = _T("Velocity Sensor, Doppler, other/general"); - break; + case 'V': // VD, VM, VW - case 'M': // VM + switch (second_character) { + case 'D': // VD - expanded_identifier = _T("Velocity Sensor, Speed Log, Water, Magnetic"); - break; + expanded_identifier = _T("Velocity Sensor, Doppler, other/general"); + break; - case 'W': // VW + case 'M': // VM - expanded_identifier = _T("Velocity Sensor, Speed Log, Water, Mechanical"); - break; - } + expanded_identifier = + _T("Velocity Sensor, Speed Log, Water, Magnetic"); + break; - break; + case 'W': // VW + expanded_identifier = + _T("Velocity Sensor, Speed Log, Water, Mechanical"); + break; + } - case 'W': // WI + break; - switch( second_character ) - { - case 'I': // WI + case 'W': // WI - expanded_identifier = _T("Weather Instruments"); - break; - } + switch (second_character) { + case 'I': // WI - break; + expanded_identifier = _T("Weather Instruments"); + break; + } - case 'Y': // YC, YD, YF, YL, YP, YR, YT, YV, YX + break; - switch( second_character ) - { - case 'C': // YC + case 'Y': // YC, YD, YF, YL, YP, YR, YT, YV, YX - expanded_identifier = _T("Transducer - Temperature (outdated)"); - break; + switch (second_character) { + case 'C': // YC - case 'D': // YD + expanded_identifier = _T("Transducer - Temperature (outdated)"); + break; - expanded_identifier = _T("Transducer - Displacement, Angular or Linear (outdated)"); - break; + case 'D': // YD - case 'F': // YF + expanded_identifier = + _T("Transducer - Displacement, Angular or Linear (outdated)"); + break; - expanded_identifier = _T("Transducer - Frequency (outdated)"); - break; + case 'F': // YF - case 'L': // YL + expanded_identifier = _T("Transducer - Frequency (outdated)"); + break; - expanded_identifier = _T("Transducer - Level (outdated)"); - break; + case 'L': // YL - case 'P': // YP + expanded_identifier = _T("Transducer - Level (outdated)"); + break; - expanded_identifier = _T("Transducer - Pressure (outdated)"); - break; + case 'P': // YP - case 'R': // YR + expanded_identifier = _T("Transducer - Pressure (outdated)"); + break; - expanded_identifier = _T("Transducer - Flow Rate (outdated)"); - break; + case 'R': // YR - case 'T': // YT + expanded_identifier = _T("Transducer - Flow Rate (outdated)"); + break; - expanded_identifier = _T("Transducer - Tachometer (outdated)"); - break; + case 'T': // YT - case 'V': // YV + expanded_identifier = _T("Transducer - Tachometer (outdated)"); + break; - expanded_identifier = _T("Transducer - Volume (outdated)"); - break; + case 'V': // YV - case 'X': // YX + expanded_identifier = _T("Transducer - Volume (outdated)"); + break; - expanded_identifier = _T("Transducer"); - break; + case 'X': // YX - } + expanded_identifier = _T("Transducer"); + break; + } - break; + break; - case 'Z': // ZA, ZC, ZQ, ZV + case 'Z': // ZA, ZC, ZQ, ZV - switch( second_character ) - { - case 'A': // ZA + switch (second_character) { + case 'A': // ZA - expanded_identifier = _T("Timekeeper - Atomic Clock"); - break; + expanded_identifier = _T("Timekeeper - Atomic Clock"); + break; - case 'C': // ZC + case 'C': // ZC - expanded_identifier = _T("Timekeeper - Chronometer"); - break; + expanded_identifier = _T("Timekeeper - Chronometer"); + break; - case 'Q': // ZQ + case 'Q': // ZQ - expanded_identifier = _T("Timekeeper - Quartz"); - break; + expanded_identifier = _T("Timekeeper - Quartz"); + break; - case 'V': // ZV + case 'V': // ZV - expanded_identifier = _T("Timekeeper - Radio Update, WWV or WWVH"); - break; - } + expanded_identifier = _T("Timekeeper - Radio Update, WWV or WWVH"); + break; + } - break; - } + break; + } - return( expanded_identifier ); + return (expanded_identifier); } diff --git a/plugins/demo_pi_sample/src/nmea0183/gga.cpp b/plugins/demo_pi_sample/src/nmea0183/gga.cpp index 0e4164c988..37fc84a7b3 100644 --- a/plugins/demo_pi_sample/src/nmea0183/gga.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/gga.cpp @@ -29,9 +29,8 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" -//#pragma hdrstop +// #pragma hdrstop /* ** Author: Samuel R. Blackburn @@ -41,135 +40,128 @@ ** You can use it any way you like. */ -//IMPLEMENT_DYNAMIC( GGA, RESPONSE ) +// IMPLEMENT_DYNAMIC( GGA, RESPONSE ) -GGA::GGA() -{ - Mnemonic = _T("GGA"); - Empty(); +GGA::GGA() { + Mnemonic = _T("GGA"); + Empty(); } -GGA::~GGA() -{ - Mnemonic.Empty(); - Empty(); +GGA::~GGA() { + Mnemonic.Empty(); + Empty(); } -void GGA::Empty( void ) -{ -// ASSERT_VALID( this ); - - UTCTime.Empty(); - Position.Empty(); - GPSQuality = 0; - NumberOfSatellitesInUse = 0; - HorizontalDilutionOfPrecision = 0.0; - AntennaAltitudeMeters = 0.0; - GeoidalSeparationMeters = 0.0; - AgeOfDifferentialGPSDataSeconds = 0.0; - DifferentialReferenceStationID = 0; +void GGA::Empty(void) { + // ASSERT_VALID( this ); + + UTCTime.Empty(); + Position.Empty(); + GPSQuality = 0; + NumberOfSatellitesInUse = 0; + HorizontalDilutionOfPrecision = 0.0; + AntennaAltitudeMeters = 0.0; + GeoidalSeparationMeters = 0.0; + AgeOfDifferentialGPSDataSeconds = 0.0; + DifferentialReferenceStationID = 0; } -bool GGA::Parse( const SENTENCE& sentence ) -{ -// ASSERT_VALID( this ); - - /* - ** GGA - Global Positioning System Fix Data - ** Time, Position and fix related data fora GPS receiver. - ** - ** 11 - ** 1 2 3 4 5 6 7 8 9 10 | 12 13 14 15 - ** | | | | | | | | | | | | | | | - ** $--GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh - ** - ** Field Number: - ** 1) Universal Time Coordinated (UTC) - ** 2) Latitude - ** 3) N or S (North or South) - ** 4) Longitude - ** 5) E or W (East or West) - ** 6) GPS Quality Indicator, - ** 0 - fix not available, - ** 1 - GPS fix, - ** 2 - Differential GPS fix - ** 7) Number of satellites in view, 00 - 12 - ** 8) Horizontal Dilution of precision - ** 9) Antenna Altitude above/below mean-sea-level (geoid) - ** 10) Units of antenna altitude, meters - ** 11) Geoidal separation, the difference between the WGS-84 earth - ** ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level - ** below ellipsoid - ** 12) Units of geoidal separation, meters - ** 13) Age of differential GPS data, time in seconds since last SC104 - ** type 1 or 9 update, null field when DGPS is not used - ** 14) Differential reference station ID, 0000-1023 - ** 15) Checksum - */ - - /* - ** First we check the checksum... - */ - - if ( sentence.IsChecksumBad( 15 ) ==NTrue ) - { - SetErrorMessage( _T("Invalid Checksum" )); - return( FALSE ); - } - - UTCTime = sentence.Field( 1 ); - Position.Parse( 2, 3, 4, 5, sentence ); - GPSQuality = sentence.Integer( 6 ); - NumberOfSatellitesInUse = sentence.Integer( 7 ); - HorizontalDilutionOfPrecision = sentence.Double( 8 ); - AntennaAltitudeMeters = sentence.Double( 9 ); - GeoidalSeparationMeters = sentence.Double( 11 ); - AgeOfDifferentialGPSDataSeconds = sentence.Double( 13 ); - DifferentialReferenceStationID = sentence.Integer( 14 ); - - return( TRUE ); +bool GGA::Parse(const SENTENCE& sentence) { + // ASSERT_VALID( this ); + + /* + ** GGA - Global Positioning System Fix Data + ** Time, Position and fix related data fora GPS receiver. + ** + ** 11 + ** 1 2 3 4 5 6 7 8 9 10 | 12 13 14 15 + ** | | | | | | | | | | | | | | | + ** $--GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh + ** + ** Field Number: + ** 1) Universal Time Coordinated (UTC) + ** 2) Latitude + ** 3) N or S (North or South) + ** 4) Longitude + ** 5) E or W (East or West) + ** 6) GPS Quality Indicator, + ** 0 - fix not available, + ** 1 - GPS fix, + ** 2 - Differential GPS fix + ** 7) Number of satellites in view, 00 - 12 + ** 8) Horizontal Dilution of precision + ** 9) Antenna Altitude above/below mean-sea-level (geoid) + ** 10) Units of antenna altitude, meters + ** 11) Geoidal separation, the difference between the WGS-84 earth + ** ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level + ** below ellipsoid + ** 12) Units of geoidal separation, meters + ** 13) Age of differential GPS data, time in seconds since last SC104 + ** type 1 or 9 update, null field when DGPS is not used + ** 14) Differential reference station ID, 0000-1023 + ** 15) Checksum + */ + + /* + ** First we check the checksum... + */ + + if (sentence.IsChecksumBad(15) == NTrue) { + SetErrorMessage(_T("Invalid Checksum" )); + return (FALSE); + } + + UTCTime = sentence.Field(1); + Position.Parse(2, 3, 4, 5, sentence); + GPSQuality = sentence.Integer(6); + NumberOfSatellitesInUse = sentence.Integer(7); + HorizontalDilutionOfPrecision = sentence.Double(8); + AntennaAltitudeMeters = sentence.Double(9); + GeoidalSeparationMeters = sentence.Double(11); + AgeOfDifferentialGPSDataSeconds = sentence.Double(13); + DifferentialReferenceStationID = sentence.Integer(14); + + return (TRUE); } -bool GGA::Write( SENTENCE& sentence ) -{ -// ASSERT_VALID( this ); +bool GGA::Write(SENTENCE& sentence) { + // ASSERT_VALID( this ); - /* - ** Let the parent do its thing - */ + /* + ** Let the parent do its thing + */ - RESPONSE::Write( sentence ); + RESPONSE::Write(sentence); - sentence += UTCTime; - sentence += Position; - sentence += GPSQuality; - sentence += NumberOfSatellitesInUse; - sentence += HorizontalDilutionOfPrecision; - sentence += AntennaAltitudeMeters; - sentence += _T("M"); - sentence += GeoidalSeparationMeters; - sentence += _T("M"); - sentence += AgeOfDifferentialGPSDataSeconds; - sentence += DifferentialReferenceStationID; + sentence += UTCTime; + sentence += Position; + sentence += GPSQuality; + sentence += NumberOfSatellitesInUse; + sentence += HorizontalDilutionOfPrecision; + sentence += AntennaAltitudeMeters; + sentence += _T("M"); + sentence += GeoidalSeparationMeters; + sentence += _T("M"); + sentence += AgeOfDifferentialGPSDataSeconds; + sentence += DifferentialReferenceStationID; - sentence.Finish(); + sentence.Finish(); - return( TRUE ); + return (TRUE); } -const GGA& GGA::operator = ( const GGA& source ) -{ -// ASSERT_VALID( this ); - - UTCTime = source.UTCTime; - Position = source.Position; - GPSQuality = source.GPSQuality; - NumberOfSatellitesInUse = source.NumberOfSatellitesInUse; - HorizontalDilutionOfPrecision = source.HorizontalDilutionOfPrecision; - AntennaAltitudeMeters = source.AntennaAltitudeMeters; - GeoidalSeparationMeters = source.GeoidalSeparationMeters; - AgeOfDifferentialGPSDataSeconds = source.AgeOfDifferentialGPSDataSeconds; - DifferentialReferenceStationID = source.DifferentialReferenceStationID; - - return( *this ); +const GGA& GGA::operator=(const GGA& source) { + // ASSERT_VALID( this ); + + UTCTime = source.UTCTime; + Position = source.Position; + GPSQuality = source.GPSQuality; + NumberOfSatellitesInUse = source.NumberOfSatellitesInUse; + HorizontalDilutionOfPrecision = source.HorizontalDilutionOfPrecision; + AntennaAltitudeMeters = source.AntennaAltitudeMeters; + GeoidalSeparationMeters = source.GeoidalSeparationMeters; + AgeOfDifferentialGPSDataSeconds = source.AgeOfDifferentialGPSDataSeconds; + DifferentialReferenceStationID = source.DifferentialReferenceStationID; + + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/gga.hpp b/plugins/demo_pi_sample/src/nmea0183/gga.hpp index f989cb83f3..035a507167 100644 --- a/plugins/demo_pi_sample/src/nmea0183/gga.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/gga.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( GGA_CLASS_HEADER ) +#if !defined(GGA_CLASS_HEADER) #define GGA_CLASS_HEADER /* @@ -41,41 +40,38 @@ ** You can use it any way you like. */ -class GGA : public RESPONSE -{ - - public: - - GGA(); - ~GGA(); +class GGA : public RESPONSE { +public: + GGA(); + ~GGA(); - /* - ** Data - */ + /* + ** Data + */ - wxString UTCTime; - LATLONG Position; - int GPSQuality; - int NumberOfSatellitesInUse; - double HorizontalDilutionOfPrecision; - double AntennaAltitudeMeters; - double GeoidalSeparationMeters; - double AgeOfDifferentialGPSDataSeconds; - int DifferentialReferenceStationID; + wxString UTCTime; + LATLONG Position; + int GPSQuality; + int NumberOfSatellitesInUse; + double HorizontalDilutionOfPrecision; + double AntennaAltitudeMeters; + double GeoidalSeparationMeters; + double AgeOfDifferentialGPSDataSeconds; + int DifferentialReferenceStationID; - /* - ** Methods - */ + /* + ** Methods + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); - /* - ** Operators - */ + /* + ** Operators + */ - virtual const GGA& operator = ( const GGA& source ); + virtual const GGA& operator=(const GGA& source); }; -#endif // GGA_CLASS_HEADER +#endif // GGA_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/gll.cpp b/plugins/demo_pi_sample/src/nmea0183/gll.cpp index f779953b57..f7bc6d68b4 100644 --- a/plugins/demo_pi_sample/src/nmea0183/gll.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/gll.cpp @@ -29,9 +29,8 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" -//#pragma hdrstop +// #pragma hdrstop /* ** Author: Samuel R. Blackburn @@ -41,154 +40,140 @@ ** You can use it any way you like. */ -//IMPLEMENT_DYNAMIC( GLL, RESPONSE ) +// IMPLEMENT_DYNAMIC( GLL, RESPONSE ) -GLL::GLL() -{ - Mnemonic = _T("GLL"); - Empty(); +GLL::GLL() { + Mnemonic = _T("GLL"); + Empty(); } -GLL::~GLL() -{ - Mnemonic.Empty(); - Empty(); +GLL::~GLL() { + Mnemonic.Empty(); + Empty(); } -void GLL::Empty( void ) -{ -// ASSERT_VALID( this ); +void GLL::Empty(void) { + // ASSERT_VALID( this ); - Position.Empty(); - UTCTime.Empty(); - IsDataValid = Unknown0183; + Position.Empty(); + UTCTime.Empty(); + IsDataValid = Unknown0183; } -bool GLL::Parse( const SENTENCE& sentence ) -{ -// ASSERT_VALID( this ); - - /* - ** GLL - Geographic Position - Latitude/Longitude - ** Latitude, N/S, Longitude, E/W, UTC, Status - ** - ** +-------------------------------- 1) Latitude - ** | +------------------------ 2) N or S (North or South) - ** | | +---------------------- 3) Longitude - ** | | | +------------- 4) E or W (East or West) - ** | | | | +----------- 5) Universal Time Coordinated (UTC) - ** | | | | | +- 6) Status A - Data Valid, V - Data Invalid - ** | | | | | | +7) Checksum - ** $--GLL,llll.ll,a,yyyyy.yy,a,hhmmss.ss,A*hh - */ - - /* - ** First we check the checksum... - */ - - int target_field_count = 6; - - NMEA0183_BOOLEAN check = sentence.IsChecksumBad( 7 ); +bool GLL::Parse(const SENTENCE& sentence) { + // ASSERT_VALID( this ); - if ( check == NTrue ) - { + /* + ** GLL - Geographic Position - Latitude/Longitude + ** Latitude, N/S, Longitude, E/W, UTC, Status + ** + ** +-------------------------------- 1) Latitude + ** | +------------------------ 2) N or S (North or South) + ** | | +---------------------- 3) Longitude + ** | | | +------------- 4) E or W (East or West) + ** | | | | +----------- 5) Universal Time Coordinated + *(UTC) + ** | | | | | +- 6) Status A - Data Valid, V - Data + *Invalid + ** | | | | | | +7) Checksum + ** $--GLL,llll.ll,a,yyyyy.yy,a,hhmmss.ss,A*hh + */ /* - ** This may be an NMEA Version 2.3 sentence, with "Mode" field + ** First we check the checksum... */ - wxString checksum_in_sentence = sentence.Field( 7 ); - if(checksum_in_sentence.StartsWith(_T("*"))) // Field is a valid erroneous checksum - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - - else - { - target_field_count = 7; - check = sentence.IsChecksumBad( 8 ); - if( check == NTrue) - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - } + + int target_field_count = 6; + + NMEA0183_BOOLEAN check = sentence.IsChecksumBad(7); + + if (check == NTrue) { + /* + ** This may be an NMEA Version 2.3 sentence, with "Mode" field + */ + wxString checksum_in_sentence = sentence.Field(7); + if (checksum_in_sentence.StartsWith( + _T("*"))) // Field is a valid erroneous checksum + { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); + } + + else { + target_field_count = 7; + check = sentence.IsChecksumBad(8); + if (check == NTrue) { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); } + } + } + if (sentence.GetNumberOfDataFields() == target_field_count) { + Position.Parse(1, 2, 3, 4, sentence); + UTCTime = sentence.Field(5); + IsDataValid = sentence.Boolean(6); - if ( sentence.GetNumberOfDataFields() == target_field_count ) - { - Position.Parse( 1, 2, 3, 4, sentence ); - UTCTime = sentence.Field( 5 ); - IsDataValid = sentence.Boolean( 6 ); + return (TRUE); + } - return( TRUE ); - } + // May be old style GLL sentence + if (sentence.GetNumberOfDataFields() == 4) { + Position.Parse(1, 2, 3, 4, sentence); + IsDataValid = NTrue; - // May be old style GLL sentence - if ( sentence.GetNumberOfDataFields() == 4 ) - { - Position.Parse( 1, 2, 3, 4, sentence ); - IsDataValid = NTrue; + return (TRUE); + } - return( TRUE ); - } - - // A real error... - SetErrorMessage( _T("Invalid FieldCount") ); - return( FALSE ); + // A real error... + SetErrorMessage(_T("Invalid FieldCount")); + return (FALSE); } +const wxString& GLL::PlainEnglish(void) { + // ASSERT_VALID( this ); -const wxString& GLL::PlainEnglish( void ) -{ -// ASSERT_VALID( this ); - - static wxString return_string; + static wxString return_string; - return_string.Empty(); -/* - char temp_string[ 128 ]; + return_string.Empty(); + /* + char temp_string[ 128 ]; - sprintf( temp_string, "At %d, you were at Latitude %ld %s, Longitude %ld %s.", - (const char *) UTCTime, - Position.Latitude.Latitude, - ( Position.Latitude.Northing == North ) ? "North" : "South", - Position.Longitude.Longitude, - ( Position.Longitude.Easting == East ) ? "East" : "West" ); + sprintf( temp_string, "At %d, you were at Latitude %ld %s, Longitude %ld + %s.", (const char *) UTCTime, Position.Latitude.Latitude, ( + Position.Latitude.Northing == North ) ? "North" : "South", + Position.Longitude.Longitude, + ( Position.Longitude.Easting == East ) ? "East" : "West" ); - return_string = temp_string; -*/ - return( return_string ); + return_string = temp_string; + */ + return (return_string); } +bool GLL::Write(SENTENCE& sentence) { + // ASSERT_VALID( this ); -bool GLL::Write( SENTENCE& sentence ) -{ -// ASSERT_VALID( this ); - - /* - ** Let the parent do its thing - */ + /* + ** Let the parent do its thing + */ - RESPONSE::Write( sentence ); + RESPONSE::Write(sentence); - sentence += Position; - sentence += UTCTime; - sentence += IsDataValid; + sentence += Position; + sentence += UTCTime; + sentence += IsDataValid; - sentence.Finish(); + sentence.Finish(); - return( TRUE ); + return (TRUE); } -const GLL& GLL::operator = ( const GLL& source ) -{ -// ASSERT_VALID( this ); +const GLL& GLL::operator=(const GLL& source) { + // ASSERT_VALID( this ); - Position = source.Position; - UTCTime = source.UTCTime; - IsDataValid = source.IsDataValid; + Position = source.Position; + UTCTime = source.UTCTime; + IsDataValid = source.IsDataValid; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/gll.hpp b/plugins/demo_pi_sample/src/nmea0183/gll.hpp index 6e1c6f9665..c9c73e2726 100644 --- a/plugins/demo_pi_sample/src/nmea0183/gll.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/gll.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( GLL_CLASS_HEADER ) +#if !defined(GLL_CLASS_HEADER) #define GLL_CLASS_HEADER /* @@ -41,36 +40,33 @@ ** You can use it any way you like. */ -class GLL : public RESPONSE -{ - - public: - - GLL(); - ~GLL(); +class GLL : public RESPONSE { +public: + GLL(); + ~GLL(); - /* - ** Data - */ + /* + ** Data + */ - wxString UTCTime; - NMEA0183_BOOLEAN IsDataValid; - LATLONG Position; + wxString UTCTime; + NMEA0183_BOOLEAN IsDataValid; + LATLONG Position; - /* - ** Methods - */ + /* + ** Methods + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual const wxString& PlainEnglish( void ); - virtual bool Write( SENTENCE& sentence ); + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual const wxString& PlainEnglish(void); + virtual bool Write(SENTENCE& sentence); - /* - ** Operators - */ + /* + ** Operators + */ - virtual const GLL& operator = ( const GLL& source ); + virtual const GLL& operator=(const GLL& source); }; -#endif // GLL_CLASS_HEADER +#endif // GLL_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/gsv.cpp b/plugins/demo_pi_sample/src/nmea0183/gsv.cpp index fa9b466e48..18da2f1f8a 100644 --- a/plugins/demo_pi_sample/src/nmea0183/gsv.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/gsv.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,94 +39,86 @@ ** You can use it any way you like. */ - -GSV::GSV() -{ - Mnemonic = _T("GSV"); - Empty(); +GSV::GSV() { + Mnemonic = _T("GSV"); + Empty(); } -GSV::~GSV() -{ - Mnemonic.Empty(); - Empty(); +GSV::~GSV() { + Mnemonic.Empty(); + Empty(); } -void GSV::Empty( void ) -{ - MessageNumber = 0; - SatsInView = 0; +void GSV::Empty(void) { + MessageNumber = 0; + SatsInView = 0; } -bool GSV::Parse( const SENTENCE& sentence ) -{ -/* - $GPGSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75 - -Where: - GSV Satellites in view - 2 Number of sentences for full data - 1 sentence 1 of 2 - 08 Number of satellites in view - - 01 Satellite PRN number - 40 Elevation, degrees - 083 Azimuth, degrees - 46 SNR - higher is better - for up to 4 satellites per sentence - *75 the checksum data, always begins with * -*/ - - - /* - ** GSV - GPS satellite Status - ** - ** 1 2 3 4 5 6 7 n - ** | | | | | | | | - ** $--GSV,x,x,x,x,x,x,x.........*hh - ** - ** Field Number: - ** 1) Number of sentences for full data - ** 2) sentence number - ** 3) Number of satellites in view - ** 4) Satellite PRN number - ** 5) Elevation, degrees - ** 6) Azimuth, degrees - ** 7) SNR - higher is better - ** Fields 4-7 may repeat up to 4 times per sentence - ** n) Checksum - */ - - /* - ** Ignore the checksum... - */ - - MessageNumber = sentence.Integer( 2 ); - SatsInView = sentence.Integer( 3 ); - - return( TRUE ); +bool GSV::Parse(const SENTENCE& sentence) { + /* + $GPGSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75 + + Where: + GSV Satellites in view + 2 Number of sentences for full data + 1 sentence 1 of 2 + 08 Number of satellites in view + + 01 Satellite PRN number + 40 Elevation, degrees + 083 Azimuth, degrees + 46 SNR - higher is better + for up to 4 satellites per sentence + *75 the checksum data, always begins with * + */ + + /* + ** GSV - GPS satellite Status + ** + ** 1 2 3 4 5 6 7 n + ** | | | | | | | | + ** $--GSV,x,x,x,x,x,x,x.........*hh + ** + ** Field Number: + ** 1) Number of sentences for full data + ** 2) sentence number + ** 3) Number of satellites in view + ** 4) Satellite PRN number + ** 5) Elevation, degrees + ** 6) Azimuth, degrees + ** 7) SNR - higher is better + ** Fields 4-7 may repeat up to 4 times per sentence + ** n) Checksum + */ + + /* + ** Ignore the checksum... + */ + + MessageNumber = sentence.Integer(2); + SatsInView = sentence.Integer(3); + + return (TRUE); } -bool GSV::Write( SENTENCE& sentence ) -{ - /* - ** Let the parent do its thing - */ +bool GSV::Write(SENTENCE& sentence) { + /* + ** Let the parent do its thing + */ - RESPONSE::Write( sentence ); + RESPONSE::Write(sentence); - sentence += 1; - sentence += 1; - sentence += SatsInView; + sentence += 1; + sentence += 1; + sentence += SatsInView; - sentence.Finish(); + sentence.Finish(); - return( TRUE ); + return (TRUE); } -const GSV& GSV::operator = ( const GSV& source ) -{ - SatsInView = source.SatsInView; +const GSV& GSV::operator=(const GSV& source) { + SatsInView = source.SatsInView; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/gsv.hpp b/plugins/demo_pi_sample/src/nmea0183/gsv.hpp index a9dc35f48d..c953466e55 100644 --- a/plugins/demo_pi_sample/src/nmea0183/gsv.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/gsv.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( GSV_CLASS_HEADER ) +#if !defined(GSV_CLASS_HEADER) #define GSV_CLASS_HEADER /* @@ -41,34 +40,31 @@ ** You can use it any way you like. */ -class GSV : public RESPONSE -{ - - public: - - GSV(); - ~GSV(); +class GSV : public RESPONSE { +public: + GSV(); + ~GSV(); - /* - ** Data - */ + /* + ** Data + */ - int MessageNumber; - int SatsInView; + int MessageNumber; + int SatsInView; - /* - ** Methods - */ + /* + ** Methods + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); - /* - ** Operators - */ + /* + ** Operators + */ - virtual const GSV& operator = ( const GSV& source ); + virtual const GSV& operator=(const GSV& source); }; -#endif // GSV_CLASS_HEADER +#endif // GSV_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/hdg.cpp b/plugins/demo_pi_sample/src/nmea0183/hdg.cpp index 4c4dd7a07a..ac692d4e57 100644 --- a/plugins/demo_pi_sample/src/nmea0183/hdg.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/hdg.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,90 +39,83 @@ ** You can use it any way you like. */ -HDG::HDG() -{ - Mnemonic = _T("HDG"); - Empty(); +HDG::HDG() { + Mnemonic = _T("HDG"); + Empty(); } -HDG::~HDG() -{ - Mnemonic.Empty(); - Empty(); +HDG::~HDG() { + Mnemonic.Empty(); + Empty(); } -void HDG::Empty( void ) -{ - MagneticSensorHeadingDegrees = 0.0; - MagneticDeviationDegrees = 0.0; - MagneticDeviationDirection = EW_Unknown; - MagneticVariationDegrees = 0.0; - MagneticVariationDirection = EW_Unknown; +void HDG::Empty(void) { + MagneticSensorHeadingDegrees = 0.0; + MagneticDeviationDegrees = 0.0; + MagneticDeviationDirection = EW_Unknown; + MagneticVariationDegrees = 0.0; + MagneticVariationDirection = EW_Unknown; } -bool HDG::Parse( const SENTENCE& sentence ) -{ - /* - ** HDG - Heading - Deviation & Variation - ** - ** 1 2 3 4 5 6 - ** | | | | | | - ** $--HDG,x.x,x.x,a,x.x,a*hh - ** - ** Field Number: - ** 1) Magnetic Sensor heading in degrees - ** 2) Magnetic Deviation, degrees - ** 3) Magnetic Deviation direction, E = Easterly, W = Westerly - ** 4) Magnetic Variation degrees - ** 5) Magnetic Variation direction, E = Easterly, W = Westerly - ** 6) Checksum - */ - - /* - ** First we check the checksum... - */ - - if ( sentence.IsChecksumBad( 6 ) == TRUE ) - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - - MagneticSensorHeadingDegrees = sentence.Double( 1 ); - MagneticDeviationDegrees = sentence.Double( 2 ); - MagneticDeviationDirection = sentence.EastOrWest( 3 ); - MagneticVariationDegrees = sentence.Double( 4 ); - MagneticVariationDirection = sentence.EastOrWest( 5 ); - - return( TRUE ); +bool HDG::Parse(const SENTENCE& sentence) { + /* + ** HDG - Heading - Deviation & Variation + ** + ** 1 2 3 4 5 6 + ** | | | | | | + ** $--HDG,x.x,x.x,a,x.x,a*hh + ** + ** Field Number: + ** 1) Magnetic Sensor heading in degrees + ** 2) Magnetic Deviation, degrees + ** 3) Magnetic Deviation direction, E = Easterly, W = Westerly + ** 4) Magnetic Variation degrees + ** 5) Magnetic Variation direction, E = Easterly, W = Westerly + ** 6) Checksum + */ + + /* + ** First we check the checksum... + */ + + if (sentence.IsChecksumBad(6) == TRUE) { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); + } + + MagneticSensorHeadingDegrees = sentence.Double(1); + MagneticDeviationDegrees = sentence.Double(2); + MagneticDeviationDirection = sentence.EastOrWest(3); + MagneticVariationDegrees = sentence.Double(4); + MagneticVariationDirection = sentence.EastOrWest(5); + + return (TRUE); } -bool HDG::Write( SENTENCE& sentence ) -{ - /* - ** Let the parent do its thing - */ +bool HDG::Write(SENTENCE& sentence) { + /* + ** Let the parent do its thing + */ - RESPONSE::Write( sentence ); + RESPONSE::Write(sentence); - sentence += MagneticSensorHeadingDegrees; - sentence += MagneticDeviationDegrees; - sentence += MagneticDeviationDirection; - sentence += MagneticVariationDegrees; - sentence += MagneticVariationDirection; + sentence += MagneticSensorHeadingDegrees; + sentence += MagneticDeviationDegrees; + sentence += MagneticDeviationDirection; + sentence += MagneticVariationDegrees; + sentence += MagneticVariationDirection; - sentence.Finish(); + sentence.Finish(); - return( TRUE ); + return (TRUE); } -const HDG& HDG::operator = ( const HDG& source ) -{ - MagneticSensorHeadingDegrees = source.MagneticSensorHeadingDegrees; - MagneticDeviationDegrees = source.MagneticDeviationDegrees; - MagneticDeviationDirection = source.MagneticDeviationDirection; - MagneticVariationDegrees = source.MagneticVariationDegrees; - MagneticVariationDirection = source.MagneticVariationDirection; +const HDG& HDG::operator=(const HDG& source) { + MagneticSensorHeadingDegrees = source.MagneticSensorHeadingDegrees; + MagneticDeviationDegrees = source.MagneticDeviationDegrees; + MagneticDeviationDirection = source.MagneticDeviationDirection; + MagneticVariationDegrees = source.MagneticVariationDegrees; + MagneticVariationDirection = source.MagneticVariationDirection; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/hdg.hpp b/plugins/demo_pi_sample/src/nmea0183/hdg.hpp index 6eaf84847a..79cc9e299d 100644 --- a/plugins/demo_pi_sample/src/nmea0183/hdg.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/hdg.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( HDG_CLASS_HEADER ) +#if !defined(HDG_CLASS_HEADER) #define HDG_CLASS_HEADER /* @@ -41,37 +40,34 @@ ** You can use it any way you like. */ -class HDG : public RESPONSE -{ - - public: - - HDG(); - ~HDG(); +class HDG : public RESPONSE { +public: + HDG(); + ~HDG(); - /* - ** Data - */ + /* + ** Data + */ - double MagneticSensorHeadingDegrees; - double MagneticDeviationDegrees; - EASTWEST MagneticDeviationDirection; - double MagneticVariationDegrees; - EASTWEST MagneticVariationDirection; + double MagneticSensorHeadingDegrees; + double MagneticDeviationDegrees; + EASTWEST MagneticDeviationDirection; + double MagneticVariationDegrees; + EASTWEST MagneticVariationDirection; - /* - ** Methods - */ + /* + ** Methods + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); - /* - ** Operators - */ + /* + ** Operators + */ - virtual const HDG& operator = ( const HDG& source ); + virtual const HDG& operator=(const HDG& source); }; -#endif // HDG_CLASS_HEADER +#endif // HDG_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/hdm.cpp b/plugins/demo_pi_sample/src/nmea0183/hdm.cpp index 0f5a60a908..96e274dfdd 100644 --- a/plugins/demo_pi_sample/src/nmea0183/hdm.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/hdm.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,74 +39,63 @@ ** You can use it any way you like. */ - -HDM::HDM() -{ - Mnemonic = _T("HDM"); - Empty(); -} - -HDM::~HDM() -{ - Mnemonic.Empty(); - Empty(); +HDM::HDM() { + Mnemonic = _T("HDM"); + Empty(); } -void HDM::Empty( void ) -{ - DegreesMagnetic = 0.0; +HDM::~HDM() { + Mnemonic.Empty(); + Empty(); } -bool HDM::Parse( const SENTENCE& sentence ) -{ - - /* - ** HDM - Heading - Magnetic - ** - ** 1 2 3 - ** | | | - ** $--HDM,x.x,M*hh - ** - ** Field Number: - ** 1) Heading Degrees, Magnetic - ** 2) M = Magnetic - ** 3) Checksum - */ - - /* - ** First we check the checksum... - */ - - if ( sentence.IsChecksumBad( 3 ) == TRUE ) - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - - DegreesMagnetic = sentence.Double( 1 ); - - return( TRUE ); +void HDM::Empty(void) { DegreesMagnetic = 0.0; } + +bool HDM::Parse(const SENTENCE& sentence) { + /* + ** HDM - Heading - Magnetic + ** + ** 1 2 3 + ** | | | + ** $--HDM,x.x,M*hh + ** + ** Field Number: + ** 1) Heading Degrees, Magnetic + ** 2) M = Magnetic + ** 3) Checksum + */ + + /* + ** First we check the checksum... + */ + + if (sentence.IsChecksumBad(3) == TRUE) { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); + } + + DegreesMagnetic = sentence.Double(1); + + return (TRUE); } -bool HDM::Write( SENTENCE& sentence ) -{ - /* - ** Let the parent do its thing - */ +bool HDM::Write(SENTENCE& sentence) { + /* + ** Let the parent do its thing + */ - RESPONSE::Write( sentence ); + RESPONSE::Write(sentence); - sentence += DegreesMagnetic; - sentence += _T("M"); + sentence += DegreesMagnetic; + sentence += _T("M"); - sentence.Finish(); + sentence.Finish(); - return( TRUE ); + return (TRUE); } -const HDM& HDM::operator = ( const HDM& source ) -{ - DegreesMagnetic = source.DegreesMagnetic; +const HDM& HDM::operator=(const HDM& source) { + DegreesMagnetic = source.DegreesMagnetic; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/hdm.hpp b/plugins/demo_pi_sample/src/nmea0183/hdm.hpp index ed550ae439..18769185b7 100644 --- a/plugins/demo_pi_sample/src/nmea0183/hdm.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/hdm.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( HDM_CLASS_HEADER ) +#if !defined(HDM_CLASS_HEADER) #define HDM_CLASS_HEADER /* @@ -41,33 +40,30 @@ ** You can use it any way you like. */ -class HDM : public RESPONSE -{ - - public: - - HDM(); - ~HDM(); +class HDM : public RESPONSE { +public: + HDM(); + ~HDM(); - /* - ** Data - */ + /* + ** Data + */ - double DegreesMagnetic; + double DegreesMagnetic; - /* - ** Methods - */ + /* + ** Methods + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); - /* - ** Operators - */ + /* + ** Operators + */ - virtual const HDM& operator = ( const HDM& source ); + virtual const HDM& operator=(const HDM& source); }; -#endif // HDM_CLASS_HEADER +#endif // HDM_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/hdt.cpp b/plugins/demo_pi_sample/src/nmea0183/hdt.cpp index 7ff923c42d..e101015141 100644 --- a/plugins/demo_pi_sample/src/nmea0183/hdt.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/hdt.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,74 +39,63 @@ ** You can use it any way you like. */ - -HDT::HDT() -{ - Mnemonic = _T("HDT"); - Empty(); -} - -HDT::~HDT() -{ - Mnemonic.Empty(); - Empty(); +HDT::HDT() { + Mnemonic = _T("HDT"); + Empty(); } -void HDT::Empty( void ) -{ - DegreesTrue = 0.0; +HDT::~HDT() { + Mnemonic.Empty(); + Empty(); } -bool HDT::Parse( const SENTENCE& sentence ) -{ - - /* - ** HDT - Heading - True - ** - ** 1 2 3 - ** | | | - ** $--HDT,x.x,T*hh - ** - ** Field Number: - ** 1) Heading Degrees, TRUE - ** 2) T = True - ** 3) Checksum - */ - - /* - ** First we check the checksum... - */ - - if ( sentence.IsChecksumBad( 3 ) == TRUE ) - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - - DegreesTrue = sentence.Double( 1 ); - - return( TRUE ); +void HDT::Empty(void) { DegreesTrue = 0.0; } + +bool HDT::Parse(const SENTENCE& sentence) { + /* + ** HDT - Heading - True + ** + ** 1 2 3 + ** | | | + ** $--HDT,x.x,T*hh + ** + ** Field Number: + ** 1) Heading Degrees, TRUE + ** 2) T = True + ** 3) Checksum + */ + + /* + ** First we check the checksum... + */ + + if (sentence.IsChecksumBad(3) == TRUE) { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); + } + + DegreesTrue = sentence.Double(1); + + return (TRUE); } -bool HDT::Write( SENTENCE& sentence ) -{ - /* - ** Let the parent do its thing - */ +bool HDT::Write(SENTENCE& sentence) { + /* + ** Let the parent do its thing + */ - RESPONSE::Write( sentence ); + RESPONSE::Write(sentence); - sentence += DegreesTrue; - sentence += _T("T"); + sentence += DegreesTrue; + sentence += _T("T"); - sentence.Finish(); + sentence.Finish(); - return( TRUE ); + return (TRUE); } -const HDT& HDT::operator = ( const HDT& source ) -{ - DegreesTrue = source.DegreesTrue; +const HDT& HDT::operator=(const HDT& source) { + DegreesTrue = source.DegreesTrue; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/hdt.hpp b/plugins/demo_pi_sample/src/nmea0183/hdt.hpp index ed78ab8b33..806bedf51e 100644 --- a/plugins/demo_pi_sample/src/nmea0183/hdt.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/hdt.hpp @@ -28,36 +28,33 @@ * More recent (2010) license statement: * * "It is BSD license, do with it what you will" * */ -#if ! defined( HDT_CLASS_HEADER ) +#if !defined(HDT_CLASS_HEADER) #define HDT_CLASS_HEADER -class HDT : public RESPONSE -{ +class HDT : public RESPONSE { +public: + HDT(); + ~HDT(); - public: + /* + ** Data + */ - HDT(); - ~HDT(); + double DegreesTrue; - /* - ** Data - */ + /* + ** Methods + */ - double DegreesTrue; + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); - /* - ** Methods - */ + /* + ** Operators + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); - - /* - ** Operators - */ - - virtual const HDT& operator = ( const HDT& source ); + virtual const HDT& operator=(const HDT& source); }; -#endif // HDT_CLASS_HEADER +#endif // HDT_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/hexvalue.cpp b/plugins/demo_pi_sample/src/nmea0183/hexvalue.cpp index f775cffe3d..6177605d46 100644 --- a/plugins/demo_pi_sample/src/nmea0183/hexvalue.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/hexvalue.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,18 +39,17 @@ ** You can use it any way you like. */ -int HexValue( const wxString& hex_string ) -{ - int return_value = 0; +int HexValue(const wxString& hex_string) { + int return_value = 0; - long scan_value = 0; + long scan_value = 0; - wxCharBuffer abuf = hex_string.ToUTF8(); - if( !abuf.data() ) // badly formed sentence? - return 0; + wxCharBuffer abuf = hex_string.ToUTF8(); + if (!abuf.data()) // badly formed sentence? + return 0; - sscanf( abuf.data(), "%lx", &scan_value ); + sscanf(abuf.data(), "%lx", &scan_value); - return_value = (int)scan_value; - return( return_value ); + return_value = (int)scan_value; + return (return_value); } diff --git a/plugins/demo_pi_sample/src/nmea0183/lat.cpp b/plugins/demo_pi_sample/src/nmea0183/lat.cpp index 0a4ce110b6..5032723b88 100644 --- a/plugins/demo_pi_sample/src/nmea0183/lat.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/lat.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,105 +39,78 @@ ** You can use it any way you like. */ +LATITUDE::LATITUDE() { Empty(); } -LATITUDE::LATITUDE() -{ - Empty(); -} +LATITUDE::~LATITUDE() { Empty(); } -LATITUDE::~LATITUDE() -{ - Empty(); +void LATITUDE::Empty(void) { + Latitude = 0.0; + Northing = NS_Unknown; } -void LATITUDE::Empty( void ) -{ +bool LATITUDE::IsDataValid(void) { + if (Northing != North && Northing != South) { + return (FALSE); + } - Latitude = 0.0; - Northing = NS_Unknown; + return (TRUE); } -bool LATITUDE::IsDataValid( void ) -{ - if ( Northing != North && Northing != South ) - { - return( FALSE ); - } - - return( TRUE ); +void LATITUDE::Parse(int position_field_number, int north_or_south_field_number, + const SENTENCE& sentence) { + wxString n_or_s = sentence.Field(north_or_south_field_number); + Set(sentence.Double(position_field_number), n_or_s); } -void LATITUDE::Parse( int position_field_number, int north_or_south_field_number, const SENTENCE& sentence ) -{ - wxString n_or_s = sentence.Field( north_or_south_field_number ); - Set( sentence.Double( position_field_number ), n_or_s ); -} +void LATITUDE::Set(double position, const wxString& north_or_south) { + // assert( north_or_south != NULL ); -void LATITUDE::Set( double position, const wxString& north_or_south ) -{ -// assert( north_or_south != NULL ); - - Latitude = position; - wxString ts = north_or_south; - - if ( !ts.IsEmpty() && ts.Trim(false)[ 0 ] == _T('N') ) - { - Northing = North; - } - else if ( !ts.IsEmpty( ) && ts.Trim(false)[ 0 ] == _T('S') ) - { - Northing = South; - } - else - { - Northing = NS_Unknown; - } -} + Latitude = position; + wxString ts = north_or_south; -void LATITUDE::Write( SENTENCE& sentence ) -{ - - wxString temp_string; - int neg = 0; - int d; - int m; - - if (Latitude < 0.0) { - Latitude = -Latitude; - neg = 1; - } - d = (int) Latitude; - double m0 = (Latitude - (double) d) * 60000.0; - m = (int)wxRound(m0); - - if (neg) - d = -d; - - temp_string.Printf(_T("%02d%02d.%03d"), d, m / 1000, m % 1000); - - sentence += temp_string; - - if ( Northing == North ) - { - sentence += _T("N"); - } - else if ( Northing == South ) - { - sentence += _T("S"); - } - else - { - /* - ** Add Nothing - */ - } + if (!ts.IsEmpty() && ts.Trim(false)[0] == _T('N')) { + Northing = North; + } else if (!ts.IsEmpty() && ts.Trim(false)[0] == _T('S')) { + Northing = South; + } else { + Northing = NS_Unknown; + } } -const LATITUDE& LATITUDE::operator = ( const LATITUDE& source ) -{ +void LATITUDE::Write(SENTENCE& sentence) { + wxString temp_string; + int neg = 0; + int d; + int m; + + if (Latitude < 0.0) { + Latitude = -Latitude; + neg = 1; + } + d = (int)Latitude; + double m0 = (Latitude - (double)d) * 60000.0; + m = (int)wxRound(m0); + + if (neg) d = -d; + + temp_string.Printf(_T("%02d%02d.%03d"), d, m / 1000, m % 1000); + + sentence += temp_string; + + if (Northing == North) { + sentence += _T("N"); + } else if (Northing == South) { + sentence += _T("S"); + } else { + /* + ** Add Nothing + */ + } +} - Latitude = source.Latitude; - Northing = source.Northing; +const LATITUDE& LATITUDE::operator=(const LATITUDE& source) { + Latitude = source.Latitude; + Northing = source.Northing; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/latlong.cpp b/plugins/demo_pi_sample/src/nmea0183/latlong.cpp index 6c87ea2aae..35b9257ace 100644 --- a/plugins/demo_pi_sample/src/nmea0183/latlong.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/latlong.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,52 +39,37 @@ ** You can use it any way you like. */ +LATLONG::LATLONG() { Empty(); } -LATLONG::LATLONG() -{ - Empty(); -} +LATLONG::~LATLONG() { Empty(); } -LATLONG::~LATLONG() -{ - Empty(); +void LATLONG::Empty(void) { + Latitude.Empty(); + Longitude.Empty(); } -void LATLONG::Empty( void ) -{ - - Latitude.Empty(); - Longitude.Empty(); +bool LATLONG::Parse(int LatitudePositionFieldNumber, int NorthingFieldNumber, + int LongitudePositionFieldNumber, int EastingFieldNumber, + const SENTENCE& LineToParse) { + Latitude.Parse(LatitudePositionFieldNumber, NorthingFieldNumber, LineToParse); + Longitude.Parse(LongitudePositionFieldNumber, EastingFieldNumber, + LineToParse); + + if (Latitude.IsDataValid() && Longitude.IsDataValid()) { + return (TRUE); + } else { + return (FALSE); + } } -bool LATLONG::Parse( int LatitudePositionFieldNumber, int NorthingFieldNumber, int LongitudePositionFieldNumber, int EastingFieldNumber, const SENTENCE& LineToParse ) -{ - - Latitude.Parse( LatitudePositionFieldNumber, NorthingFieldNumber, LineToParse ); - Longitude.Parse( LongitudePositionFieldNumber, EastingFieldNumber, LineToParse ); - - if ( Latitude.IsDataValid() && Longitude.IsDataValid() ) - { - return( TRUE ); - } - else - { - return( FALSE ); - } +void LATLONG::Write(SENTENCE& sentence) { + Latitude.Write(sentence); + Longitude.Write(sentence); } -void LATLONG::Write( SENTENCE& sentence ) -{ - - Latitude.Write( sentence ); - Longitude.Write( sentence ); -} - -const LATLONG& LATLONG::operator = ( const LATLONG& source ) -{ - - Latitude = source.Latitude; - Longitude = source.Longitude; +const LATLONG& LATLONG::operator=(const LATLONG& source) { + Latitude = source.Latitude; + Longitude = source.Longitude; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/long.cpp b/plugins/demo_pi_sample/src/nmea0183/long.cpp index da6a7c9ba4..f71ed9de38 100644 --- a/plugins/demo_pi_sample/src/nmea0183/long.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/long.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,105 +39,78 @@ ** You can use it any way you like. */ +LONGITUDE::LONGITUDE() { Empty(); } -LONGITUDE::LONGITUDE() -{ - Empty(); -} +LONGITUDE::~LONGITUDE() { Empty(); } -LONGITUDE::~LONGITUDE() -{ - Empty(); +void LONGITUDE::Empty(void) { + Longitude = 0.0; + Easting = EW_Unknown; } -void LONGITUDE::Empty( void ) -{ +bool LONGITUDE::IsDataValid(void) { + if (Easting != East && Easting != West) { + return (FALSE); + } - Longitude = 0.0; - Easting = EW_Unknown; + return (TRUE); } -bool LONGITUDE::IsDataValid( void ) -{ - if ( Easting != East && Easting != West ) - { - return( FALSE ); - } - - return( TRUE ); +void LONGITUDE::Parse(int position_field_number, int east_or_west_field_number, + const SENTENCE& sentence) { + wxString w_or_e = sentence.Field(east_or_west_field_number); + Set(sentence.Double(position_field_number), w_or_e); } -void LONGITUDE::Parse( int position_field_number, int east_or_west_field_number, const SENTENCE& sentence ) -{ - wxString w_or_e = sentence.Field( east_or_west_field_number ); - Set( sentence.Double( position_field_number ), w_or_e ); -} +void LONGITUDE::Set(double position, const wxString& east_or_west) { + // assert( east_or_west != NULL ); -void LONGITUDE::Set( double position, const wxString& east_or_west ) -{ -// assert( east_or_west != NULL ); - - Longitude = position; - wxString ts = east_or_west; - - if ( !ts.IsEmpty( ) && ts.Trim(false)[ 0 ] == 'E' ) - { - Easting = East; - } - else if ( !ts.IsEmpty( ) && ts.Trim(false)[ 0 ] == 'W' ) - { - Easting = West; - } - else - { - Easting = EW_Unknown; - } -} + Longitude = position; + wxString ts = east_or_west; -void LONGITUDE::Write( SENTENCE& sentence ) -{ - - wxString temp_string; - int neg = 0; - int d; - int m; - - if (Longitude < 0.0) { - Longitude = -Longitude; - neg = 1; - } - d = (int) Longitude; - double m0 = (Longitude - (double) d) * 60000.0; - m = (int)wxRound(m0); - - if (neg) - d = -d; - - temp_string.Printf(_T("%03d%02d.%03d"), d, m / 1000, m % 1000); - - sentence += temp_string; - - if ( Easting == East ) - { - sentence += _T("E"); - } - else if ( Easting == West ) - { - sentence += _T("W"); - } - else - { - /* - ** Add Nothing - */ - } + if (!ts.IsEmpty() && ts.Trim(false)[0] == 'E') { + Easting = East; + } else if (!ts.IsEmpty() && ts.Trim(false)[0] == 'W') { + Easting = West; + } else { + Easting = EW_Unknown; + } } -const LONGITUDE& LONGITUDE::operator = ( const LONGITUDE& source ) -{ +void LONGITUDE::Write(SENTENCE& sentence) { + wxString temp_string; + int neg = 0; + int d; + int m; + + if (Longitude < 0.0) { + Longitude = -Longitude; + neg = 1; + } + d = (int)Longitude; + double m0 = (Longitude - (double)d) * 60000.0; + m = (int)wxRound(m0); + + if (neg) d = -d; + + temp_string.Printf(_T("%03d%02d.%03d"), d, m / 1000, m % 1000); + + sentence += temp_string; + + if (Easting == East) { + sentence += _T("E"); + } else if (Easting == West) { + sentence += _T("W"); + } else { + /* + ** Add Nothing + */ + } +} - Longitude = source.Longitude; - Easting = source.Easting; +const LONGITUDE& LONGITUDE::operator=(const LONGITUDE& source) { + Longitude = source.Longitude; + Easting = source.Easting; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/nmea0183.cpp b/plugins/demo_pi_sample/src/nmea0183/nmea0183.cpp index 91c170cc72..003eea9aac 100644 --- a/plugins/demo_pi_sample/src/nmea0183/nmea0183.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/nmea0183.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -43,343 +42,307 @@ #include WX_DEFINE_LIST(MRL); - NMEA0183::NMEA0183() : NMEA0183(NmeaContext()) {} -NMEA0183::NMEA0183(const NmeaContext& ctx) : caller_ctx(ctx) -{ - initialize(); - -/* - response_table.Add( (RESPONSE *) &Aam ); - response_table.Add( (RESPONSE *) &Alm ); - response_table.Add( (RESPONSE *) &Apb ); - response_table.Add( (RESPONSE *) &Asd ); - response_table.Add( (RESPONSE *) &Bec ); - response_table.Add( (RESPONSE *) &Bod ); - response_table.Add( (RESPONSE *) &Bwc ); - response_table.Add( (RESPONSE *) &Bwr ); - response_table.Add( (RESPONSE *) &Bww ); - response_table.Add( (RESPONSE *) &Dbt ); - response_table.Add( (RESPONSE *) &Dcn ); - response_table.Add( (RESPONSE *) &Dpt ); - response_table.Add( (RESPONSE *) &Fsi ); - response_table.Add( (RESPONSE *) &Gga ); - response_table.Add( (RESPONSE *) &Glc ); - response_table.Add( (RESPONSE *) &Gll ); - response_table.Add( (RESPONSE *) &Gxa ); - response_table.Add( (RESPONSE *) &Hsc ); - response_table.Add( (RESPONSE *) &Lcd ); - response_table.Add( (RESPONSE *) &Mtw ); - response_table.Add( (RESPONSE *) &Oln ); - response_table.Add( (RESPONSE *) &Osd ); - response_table.Add( (RESPONSE *) &Proprietary ); - response_table.Add( (RESPONSE *) &Rma ); -*/ - response_table.Append( (RESPONSE *) &Hdm ); - response_table.Append( (RESPONSE *) &Hdg ); - response_table.Append( (RESPONSE *) &Hdt ); - response_table.Append( (RESPONSE *) &Rmb ); - response_table.Append( (RESPONSE *) &Rmc ); - response_table.Append( (RESPONSE *) &Wpl ); - response_table.Append( (RESPONSE *) &Rte ); - response_table.Append( (RESPONSE *) &Gll ); - response_table.Append( (RESPONSE *) &Vtg ); - response_table.Append( (RESPONSE *) &Gsv ); - response_table.Append( (RESPONSE *) &Gga ); - response_table.Append( (RESPONSE *) &GPwpl ); - response_table.Append( (RESPONSE *) &Apb ); - response_table.Append( (RESPONSE *) &Xte ); - response_table.Append( (RESPONSE *) &Mwd ); - response_table.Append( (RESPONSE *) &Mwv ); - - -/* - response_table.Add( (RESPONSE *) &Rot ); - response_table.Add( (RESPONSE *) &Rpm ); - response_table.Add( (RESPONSE *) &Rsa ); - response_table.Add( (RESPONSE *) &Rsd ); - response_table.Add( (RESPONSE *) &Sfi ); - response_table.Add( (RESPONSE *) &Stn ); - response_table.Add( (RESPONSE *) &Trf ); - response_table.Add( (RESPONSE *) &Ttm ); - response_table.Add( (RESPONSE *) &Vbw ); - response_table.Add( (RESPONSE *) &Vhw ); - response_table.Add( (RESPONSE *) &Vdr ); - response_table.Add( (RESPONSE *) &Vlw ); - response_table.Add( (RESPONSE *) &Vpw ); - response_table.Add( (RESPONSE *) &Vtg ); - response_table.Add( (RESPONSE *) &Wcv ); - response_table.Add( (RESPONSE *) &Wnc ); - response_table.Add( (RESPONSE *) &Xdr ); - response_table.Add( (RESPONSE *) &Xte ); - response_table.Add( (RESPONSE *) &Xtr ); - response_table.Add( (RESPONSE *) &Zda ); - response_table.Add( (RESPONSE *) &Zfo ); - response_table.Add( (RESPONSE *) &Ztg ); -*/ - sort_response_table(); - set_container_pointers(); +NMEA0183::NMEA0183(const NmeaContext &ctx) : caller_ctx(ctx) { + initialize(); + + /* + response_table.Add( (RESPONSE *) &Aam ); + response_table.Add( (RESPONSE *) &Alm ); + response_table.Add( (RESPONSE *) &Apb ); + response_table.Add( (RESPONSE *) &Asd ); + response_table.Add( (RESPONSE *) &Bec ); + response_table.Add( (RESPONSE *) &Bod ); + response_table.Add( (RESPONSE *) &Bwc ); + response_table.Add( (RESPONSE *) &Bwr ); + response_table.Add( (RESPONSE *) &Bww ); + response_table.Add( (RESPONSE *) &Dbt ); + response_table.Add( (RESPONSE *) &Dcn ); + response_table.Add( (RESPONSE *) &Dpt ); + response_table.Add( (RESPONSE *) &Fsi ); + response_table.Add( (RESPONSE *) &Gga ); + response_table.Add( (RESPONSE *) &Glc ); + response_table.Add( (RESPONSE *) &Gll ); + response_table.Add( (RESPONSE *) &Gxa ); + response_table.Add( (RESPONSE *) &Hsc ); + response_table.Add( (RESPONSE *) &Lcd ); + response_table.Add( (RESPONSE *) &Mtw ); + response_table.Add( (RESPONSE *) &Oln ); + response_table.Add( (RESPONSE *) &Osd ); + response_table.Add( (RESPONSE *) &Proprietary ); + response_table.Add( (RESPONSE *) &Rma ); + */ + response_table.Append((RESPONSE *)&Hdm); + response_table.Append((RESPONSE *)&Hdg); + response_table.Append((RESPONSE *)&Hdt); + response_table.Append((RESPONSE *)&Rmb); + response_table.Append((RESPONSE *)&Rmc); + response_table.Append((RESPONSE *)&Wpl); + response_table.Append((RESPONSE *)&Rte); + response_table.Append((RESPONSE *)&Gll); + response_table.Append((RESPONSE *)&Vtg); + response_table.Append((RESPONSE *)&Gsv); + response_table.Append((RESPONSE *)&Gga); + response_table.Append((RESPONSE *)&GPwpl); + response_table.Append((RESPONSE *)&Apb); + response_table.Append((RESPONSE *)&Xte); + response_table.Append((RESPONSE *)&Mwd); + response_table.Append((RESPONSE *)&Mwv); + + /* + response_table.Add( (RESPONSE *) &Rot ); + response_table.Add( (RESPONSE *) &Rpm ); + response_table.Add( (RESPONSE *) &Rsa ); + response_table.Add( (RESPONSE *) &Rsd ); + response_table.Add( (RESPONSE *) &Sfi ); + response_table.Add( (RESPONSE *) &Stn ); + response_table.Add( (RESPONSE *) &Trf ); + response_table.Add( (RESPONSE *) &Ttm ); + response_table.Add( (RESPONSE *) &Vbw ); + response_table.Add( (RESPONSE *) &Vhw ); + response_table.Add( (RESPONSE *) &Vdr ); + response_table.Add( (RESPONSE *) &Vlw ); + response_table.Add( (RESPONSE *) &Vpw ); + response_table.Add( (RESPONSE *) &Vtg ); + response_table.Add( (RESPONSE *) &Wcv ); + response_table.Add( (RESPONSE *) &Wnc ); + response_table.Add( (RESPONSE *) &Xdr ); + response_table.Add( (RESPONSE *) &Xte ); + response_table.Add( (RESPONSE *) &Xtr ); + response_table.Add( (RESPONSE *) &Zda ); + response_table.Add( (RESPONSE *) &Zfo ); + response_table.Add( (RESPONSE *) &Ztg ); + */ + sort_response_table(); + set_container_pointers(); } -NMEA0183::~NMEA0183() -{ - initialize(); -} +NMEA0183::~NMEA0183() { initialize(); } -void NMEA0183::initialize( void ) -{ -// ASSERT_VALID( this ); +void NMEA0183::initialize(void) { + // ASSERT_VALID( this ); - ErrorMessage.Empty(); + ErrorMessage.Empty(); } -void NMEA0183::set_container_pointers( void ) -{ -// ASSERT_VALID( this ); +void NMEA0183::set_container_pointers(void) { + // ASSERT_VALID( this ); - int index = 0; - int number_of_entries_in_table = response_table.GetCount(); + int index = 0; + int number_of_entries_in_table = response_table.GetCount(); - RESPONSE *this_response = (RESPONSE *) NULL; + RESPONSE *this_response = (RESPONSE *)NULL; - index = 0; + index = 0; - while( index < number_of_entries_in_table ) - { - this_response = (RESPONSE *) response_table[ index ]; + while (index < number_of_entries_in_table) { + this_response = (RESPONSE *)response_table[index]; - this_response->SetContainer( this ); + this_response->SetContainer(this); - index++; - } + index++; + } } -void NMEA0183::sort_response_table( void ) -{ -// ASSERT_VALID( this ); +void NMEA0183::sort_response_table(void) { + // ASSERT_VALID( this ); -/* - int index = 0; - int number_of_entries_in_table = response_table.GetSize(); + /* + int index = 0; + int number_of_entries_in_table = response_table.GetSize(); - RESPONSE *this_response = (RESPONSE *) NULL; - RESPONSE *that_response = (RESPONSE *) NULL; + RESPONSE *this_response = (RESPONSE *) NULL; + RESPONSE *that_response = (RESPONSE *) NULL; - bool sorted = FALSE; + bool sorted = FALSE; - while( sorted == FALSE ) - { - sorted = TRUE; + while( sorted == FALSE ) + { + sorted = TRUE; - index = 0; + index = 0; - while( index < number_of_entries_in_table ) - { - this_response = (RESPONSE *) response_table.Item( index ); - that_response = (RESPONSE *) response_table.Item( index + 1 ); + while( index < number_of_entries_in_table ) + { + this_response = (RESPONSE *) response_table.Item( index ); + that_response = (RESPONSE *) response_table.Item( index + 1 ); - if ( this_response->Mnemonic.Compare( that_response->Mnemonic ) > 0 ) - { - response_table[ index ] = that_response; - response_table[ index + 1 ] = this_response; + if ( this_response->Mnemonic.Compare( that_response->Mnemonic ) > 0 ) + { + response_table[ index ] = that_response; + response_table[ index + 1 ] = this_response; - sorted = FALSE; - } + sorted = FALSE; + } - index++; - } - } -*/ + index++; + } + } + */ } /* ** Public Interface */ -bool NMEA0183::IsGood( void ) const -{ -// ASSERT_VALID( this ); +bool NMEA0183::IsGood(void) const { + // ASSERT_VALID( this ); - /* - ** NMEA 0183 sentences begin with $ and and with CR LF - */ + /* + ** NMEA 0183 sentences begin with $ and and with CR LF + */ - if ( sentence.Sentence[ 0 ] != '$' ) - { - return( FALSE ); - } + if (sentence.Sentence[0] != '$') { + return (FALSE); + } - /* - ** Next to last character must be a CR - */ + /* + ** Next to last character must be a CR + */ - /* This seems too harsh for cross platform work + /* This seems too harsh for cross platform work - Relax requirement to line ending of either CR or LF + Relax requirement to line ending of either CR or LF - if ( sentence.Sentence.Mid( sentence.Sentence.Len() - 2, 1 ) != wxString(_T("\r")) ) - { - return( FALSE ); - } + if ( sentence.Sentence.Mid( sentence.Sentence.Len() - 2, 1 ) != + wxString(_T("\r")) ) + { + return( FALSE ); + } - if ( sentence.Sentence.Right( 1 ) != _T("\n") ) - { - return( FALSE ); - } - */ + if ( sentence.Sentence.Right( 1 ) != _T("\n") ) + { + return( FALSE ); + } + */ -//TODO: GPSD messages are not terminated with CR/LF if ( (sentence.Sentence.Right( 1 ) != _T("\n") ) && (sentence.Sentence.Right( 1 ) != _T("\r") )) -// return false; + // TODO: GPSD messages are not terminated with CR/LF if ( + // (sentence.Sentence.Right( 1 ) != _T("\n") ) && (sentence.Sentence.Right( 1 + // ) != _T("\r") )) + // return false; - return( TRUE ); + return (TRUE); } +bool NMEA0183::PreParse(void) { + wxCharBuffer buf = sentence.Sentence.ToUTF8(); + if (!buf.data()) // badly formed sentence? + return false; -bool NMEA0183::PreParse( void ) -{ - wxCharBuffer buf = sentence.Sentence.ToUTF8(); - if( !buf.data() ) // badly formed sentence? - return false; - - if ( IsGood() ) - { - wxString mnemonic = sentence.Field( 0 ); - - /* - ** See if this is a proprietary field - */ + if (IsGood()) { + wxString mnemonic = sentence.Field(0); - if ( mnemonic.Left( 1 ) == 'P' ) - mnemonic = _T("P"); + /* + ** See if this is a proprietary field + */ - else - mnemonic = mnemonic.Right( 3 ); + if (mnemonic.Left(1) == 'P') + mnemonic = _T("P"); + else + mnemonic = mnemonic.Right(3); - LastSentenceIDReceived = mnemonic; + LastSentenceIDReceived = mnemonic; - return true; - } - else - return false; + return true; + } else + return false; } +bool NMEA0183::Parse(void) { + bool return_value = FALSE; -bool NMEA0183::Parse( void ) -{ - bool return_value = FALSE; - - if(PreParse()) - { + if (PreParse()) { + wxString mnemonic = sentence.Field(0); - wxString mnemonic = sentence.Field( 0 ); + /* + ** See if this is a proprietary field + */ - /* - ** See if this is a proprietary field - */ - - if ( mnemonic.Left( 1 ) == 'P' ) - { - mnemonic = _T("P"); - } - else - { - mnemonic = mnemonic.Right( 3 ); - } + if (mnemonic.Left(1) == 'P') { + mnemonic = _T("P"); + } else { + mnemonic = mnemonic.Right(3); + } - /* - ** Set up our default error message - */ + /* + ** Set up our default error message + */ - ErrorMessage = mnemonic; - ErrorMessage += _T(" is an unknown type of sentence"); + ErrorMessage = mnemonic; + ErrorMessage += _T(" is an unknown type of sentence"); - LastSentenceIDReceived = mnemonic; + LastSentenceIDReceived = mnemonic; - RESPONSE *response_p = (RESPONSE *) NULL; + RESPONSE *response_p = (RESPONSE *)NULL; + // Traverse the response list to find a mnemonic match -// Traverse the response list to find a mnemonic match + wxMRLNode *node = response_table.GetFirst(); - wxMRLNode *node = response_table.GetFirst(); + int comparison = 0; - int comparison = 0; + while (node) { + RESPONSE *resp = node->GetData(); - while(node) - { - RESPONSE *resp = node->GetData(); - - comparison = mnemonic.Cmp( resp->Mnemonic ); - - if ( comparison == 0 ) - { - response_p = (RESPONSE *) resp; - return_value = response_p->Parse( sentence ); - - /* - ** Set your ErrorMessage - */ - - if ( return_value == TRUE ) - { - ErrorMessage = _T("No Error"); - LastSentenceIDParsed = response_p->Mnemonic; - TalkerID = talker_id( sentence ); - ExpandedTalkerID = expand_talker_id( TalkerID ); - } - else - { - ErrorMessage = response_p->ErrorMessage; - } - - break; - } - - node = node->GetNext(); - } + comparison = mnemonic.Cmp(resp->Mnemonic); - } - else - { - return_value = FALSE; - } + if (comparison == 0) { + response_p = (RESPONSE *)resp; + return_value = response_p->Parse(sentence); - return( return_value ); -} + /* + ** Set your ErrorMessage + */ -wxArrayString NMEA0183::GetRecognizedArray(void) -{ - wxArrayString ret; + if (return_value == TRUE) { + ErrorMessage = _T("No Error"); + LastSentenceIDParsed = response_p->Mnemonic; + TalkerID = talker_id(sentence); + ExpandedTalkerID = expand_talker_id(TalkerID); + } else { + ErrorMessage = response_p->ErrorMessage; + } - wxMRLNode *node = response_table.GetFirst(); + break; + } - while(node) - { - RESPONSE *resp = node->GetData(); - ret.Add( resp->Mnemonic ); - node = node->GetNext(); + node = node->GetNext(); } - return ret; + } else { + return_value = FALSE; + } + + return (return_value); } +wxArrayString NMEA0183::GetRecognizedArray(void) { + wxArrayString ret; + wxMRLNode *node = response_table.GetFirst(); + while (node) { + RESPONSE *resp = node->GetData(); + ret.Add(resp->Mnemonic); + node = node->GetNext(); + } + + return ret; +} -NMEA0183& NMEA0183::operator << ( wxString & source ) -{ -// ASSERT_VALID( this ); +NMEA0183 &NMEA0183::operator<<(wxString &source) { + // ASSERT_VALID( this ); - sentence = source; + sentence = source; - return( *this ); + return (*this); } -NMEA0183& NMEA0183::operator >> ( wxString& destination ) -{ -// ASSERT_VALID( this ); +NMEA0183 &NMEA0183::operator>>(wxString &destination) { + // ASSERT_VALID( this ); - destination = sentence; + destination = sentence; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/nmea0183.h b/plugins/demo_pi_sample/src/nmea0183/nmea0183.h index e30b301887..f453d49201 100644 --- a/plugins/demo_pi_sample/src/nmea0183/nmea0183.h +++ b/plugins/demo_pi_sample/src/nmea0183/nmea0183.h @@ -29,7 +29,7 @@ * "It is BSD license, do with it what you will" * */ -#if ! defined( NMEA_0183_HEADER ) +#if !defined(NMEA_0183_HEADER) #define NMEA_0183_HEADER @@ -41,14 +41,14 @@ ** You can use it any way you like. */ -#include +#include // Include wxWindows stuff -//#include "wx/wxprec.h" +// #include "wx/wxprec.h" -//#ifndef WX_PRECOMP -// #include "wx/wx.h" -//#endif //precompiled headers +// #ifndef WX_PRECOMP +// #include "wx/wx.h" +// #endif //precompiled headers #include "wx/string.h" #include "wx/list.h" #include "wx/arrstr.h" @@ -59,11 +59,11 @@ */ #ifdef __MSVC__ -#pragma warning( disable : 4699 ) +#pragma warning(disable : 4699) #endif #define CARRIAGE_RETURN 0x0D -#define LINE_FEED 0x0A +#define LINE_FEED 0x0A class NmeaContext { public: @@ -71,95 +71,70 @@ class NmeaContext { std::function get_apb_precision; NmeaContext() : get_talker_id([]() { return ""; }), - get_apb_precision( []() { return 3; }) {} + get_apb_precision([]() { return 3; }) {} }; - -typedef enum _NMEA0183_BOOLEAN -{ - Unknown0183 = 0, - NTrue, - NFalse +typedef enum _NMEA0183_BOOLEAN { + Unknown0183 = 0, + NTrue, + NFalse } NMEA0183_BOOLEAN; -typedef enum _leftright -{ - LR_Unknown = 0, - Left, - Right -} LEFTRIGHT; - -typedef enum _eastwest -{ - EW_Unknown = 0, - East, - West -} EASTWEST; - -typedef enum _northsouth -{ - NS_Unknown = 0, - North, - South -} NORTHSOUTH; - -typedef enum _reference -{ - ReferenceUnknown = 0, - BottomTrackingLog, - ManuallyEntered, - WaterReferenced, - RadarTrackingOfFixedTarget, - PositioningSystemGroundReference +typedef enum _leftright { LR_Unknown = 0, Left, Right } LEFTRIGHT; + +typedef enum _eastwest { EW_Unknown = 0, East, West } EASTWEST; + +typedef enum _northsouth { NS_Unknown = 0, North, South } NORTHSOUTH; + +typedef enum _reference { + ReferenceUnknown = 0, + BottomTrackingLog, + ManuallyEntered, + WaterReferenced, + RadarTrackingOfFixedTarget, + PositioningSystemGroundReference } REFERENCE; -typedef enum _communicationsmode -{ - CommunicationsModeUnknown = 0, - F3E_G3E_SimplexTelephone = 'd', - F3E_G3E_DuplexTelephone = 'e', - J3E_Telephone = 'm', - H3E_Telephone = 'o', - F1B_J2B_FEC_NBDP_TelexTeleprinter = 'q', - F1B_J2B_ARQ_NBDP_TelexTeleprinter = 's', - F1B_J2B_ReceiveOnlyTeleprinterDSC = 'w', - A1A_MorseTapeRecorder = 'x', - A1A_MorseKeyHeadset = '{', - F1C_F2C_F3C_FaxMachine = '|' +typedef enum _communicationsmode { + CommunicationsModeUnknown = 0, + F3E_G3E_SimplexTelephone = 'd', + F3E_G3E_DuplexTelephone = 'e', + J3E_Telephone = 'm', + H3E_Telephone = 'o', + F1B_J2B_FEC_NBDP_TelexTeleprinter = 'q', + F1B_J2B_ARQ_NBDP_TelexTeleprinter = 's', + F1B_J2B_ReceiveOnlyTeleprinterDSC = 'w', + A1A_MorseTapeRecorder = 'x', + A1A_MorseKeyHeadset = '{', + F1C_F2C_F3C_FaxMachine = '|' } COMMUNICATIONS_MODE; -typedef enum _transducertype -{ - TransducerUnknown = 0, - AngularDisplacementTransducer = 'A', - TemperatureTransducer = 'C', - LinearDisplacementTransducer = 'D', - FrequencyTransducer = 'F', - HumidityTransducer = 'H', - ForceTransducer = 'N', - PressureTransducer = 'P', - FlowRateTransducer = 'R', - TachometerTransducer = 'T', - VolumeTransducer = 'V' +typedef enum _transducertype { + TransducerUnknown = 0, + AngularDisplacementTransducer = 'A', + TemperatureTransducer = 'C', + LinearDisplacementTransducer = 'D', + FrequencyTransducer = 'F', + HumidityTransducer = 'H', + ForceTransducer = 'N', + PressureTransducer = 'P', + FlowRateTransducer = 'R', + TachometerTransducer = 'T', + VolumeTransducer = 'V' } TRANSDUCER_TYPE; -typedef enum -{ - RouteUnknown = 0, - CompleteRoute, - WorkingRoute -} ROUTE_TYPE; +typedef enum { RouteUnknown = 0, CompleteRoute, WorkingRoute } ROUTE_TYPE; /* ** Misc Function Prototypes */ -int HexValue( const wxString& hex_string ); +int HexValue(const wxString& hex_string); -wxString& expand_talker_id( const wxString & ); -wxString& Hex( int value ); -wxString& talker_id( const wxString& sentence ); +wxString& expand_talker_id(const wxString&); +wxString& Hex(int value); +wxString& talker_id(const wxString& sentence); #include "nmea0183.hpp" -#endif // NMEA0183_HEADER +#endif // NMEA0183_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/nmea0183.hpp b/plugins/demo_pi_sample/src/nmea0183/nmea0183.hpp index 20ec8d4cd1..fefea76893 100644 --- a/plugins/demo_pi_sample/src/nmea0183/nmea0183.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/nmea0183.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( NMEA_0183_CLASS_HEADER ) +#if !defined(NMEA_0183_CLASS_HEADER) #define NMEA_0183_CLASS_HEADER /* @@ -48,14 +47,14 @@ #include "Sentence.hpp" #include "Response.hpp" #include "LatLong.hpp" -//#include "LoranTD.hpp" -//#include "Manufact.hpp" -//#include "MList.hpp" -//#include "OmegaPar.hpp" -//#include "DeccaLOP.hpp" -//#include "RatioPls.hpp" -//#include "RadarDat.hpp" -//#include "FreqMode.hpp" +// #include "LoranTD.hpp" +// #include "Manufact.hpp" +// #include "MList.hpp" +// #include "OmegaPar.hpp" +// #include "DeccaLOP.hpp" +// #include "RatioPls.hpp" +// #include "RadarDat.hpp" +// #include "FreqMode.hpp" /* ** Response Classes @@ -131,121 +130,118 @@ WX_DECLARE_LIST(RESPONSE, MRL); -class NMEA0183 -{ - - private: - - SENTENCE sentence; - - void initialize( void ); - - protected: - - MRL response_table; - - void set_container_pointers( void ); - void sort_response_table( void ); - - public: - /** For use in main opencpn with access to globals. */ - NMEA0183(const NmeaContext& ctx); - - /** For use in plugins without access to globals. */ - NMEA0183(); - - virtual ~NMEA0183(); - - const NmeaContext caller_ctx; - - wxArrayString GetRecognizedArray(void); - - /* - ** NMEA 0183 Sentences we understand - */ - -/* - AAM Aam; - ALM Alm; - APB Apb; - ASD Asd; - BEC Bec; - BOD Bod; - BWC Bwc; - BWR Bwr; - BWW Bww; - DBT Dbt; - DCN Dcn; - DPT Dpt; - FSI Fsi; - GGA Gga; - GLC Glc; - GLL Gll; - GXA Gxa; - HSC Hsc; - LCD Lcd; - MTW Mtw; - MWV Mwv; - OLN Oln; - OSD Osd; - P Proprietary; - RMA Rma; -*/ - HDM Hdm; - HDG Hdg; - HDT Hdt; - RMB Rmb; - RMC Rmc; - WPL Wpl; - RTE Rte; - GLL Gll; +class NMEA0183 { +private: + SENTENCE sentence; + + void initialize(void); + +protected: + MRL response_table; + + void set_container_pointers(void); + void sort_response_table(void); + +public: + /** For use in main opencpn with access to globals. */ + NMEA0183(const NmeaContext& ctx); + + /** For use in plugins without access to globals. */ + NMEA0183(); + + virtual ~NMEA0183(); + + const NmeaContext caller_ctx; + + wxArrayString GetRecognizedArray(void); + + /* + ** NMEA 0183 Sentences we understand + */ + + /* + AAM Aam; + ALM Alm; + APB Apb; + ASD Asd; + BEC Bec; + BOD Bod; + BWC Bwc; + BWR Bwr; + BWW Bww; + DBT Dbt; + DCN Dcn; + DPT Dpt; + FSI Fsi; + GGA Gga; + GLC Glc; + GLL Gll; + GXA Gxa; + HSC Hsc; + LCD Lcd; + MTW Mtw; + MWV Mwv; + OLN Oln; + OSD Osd; + P Proprietary; + RMA Rma; + */ + HDM Hdm; + HDG Hdg; + HDT Hdt; + RMB Rmb; + RMC Rmc; + WPL Wpl; + RTE Rte; + GLL Gll; + VTG Vtg; + GSV Gsv; + GGA Gga; + GPWPL GPwpl; + APB Apb; + XTE Xte; + MWD Mwd; + MWV Mwv; + /* + ROT Rot; + RPM Rpm; + RSA Rsa; + RSD Rsd; + SFI Sfi; + STN Stn; + TRF Trf; + TTM Ttm; + VBW Vbw; + VDR Vdr; + VHW Vhw; + VLW Vlw; + VPW Vpw; VTG Vtg; - GSV Gsv; - GGA Gga; - GPWPL GPwpl; - APB Apb; + WCV Wcv; + WNC Wnc; + XDR Xdr; XTE Xte; - MWD Mwd; - MWV Mwv; - /* - ROT Rot; - RPM Rpm; - RSA Rsa; - RSD Rsd; - SFI Sfi; - STN Stn; - TRF Trf; - TTM Ttm; - VBW Vbw; - VDR Vdr; - VHW Vhw; - VLW Vlw; - VPW Vpw; - VTG Vtg; - WCV Wcv; - WNC Wnc; - XDR Xdr; - XTE Xte; - XTR Xtr; - ZDA Zda; - ZFO Zfo; - ZTG Ztg; -*/ - wxString ErrorMessage; // Filled when Parse returns FALSE - wxString LastSentenceIDParsed; // ID of the lst sentence successfully parsed - wxString LastSentenceIDReceived; // ID of the last sentence received, may not have parsed successfully + XTR Xtr; + ZDA Zda; + ZFO Zfo; + ZTG Ztg; + */ + wxString ErrorMessage; // Filled when Parse returns FALSE + wxString LastSentenceIDParsed; // ID of the lst sentence successfully parsed + wxString LastSentenceIDReceived; // ID of the last sentence received, may not + // have parsed successfully - wxString TalkerID; - wxString ExpandedTalkerID; + wxString TalkerID; + wxString ExpandedTalkerID; -// MANUFACTURER_LIST Manufacturers; + // MANUFACTURER_LIST Manufacturers; - virtual bool IsGood( void ) const; - virtual bool Parse( void ); - virtual bool PreParse( void ); + virtual bool IsGood(void) const; + virtual bool Parse(void); + virtual bool PreParse(void); - NMEA0183& operator << ( wxString& source ); - NMEA0183& operator >> ( wxString& destination ); + NMEA0183& operator<<(wxString& source); + NMEA0183& operator>>(wxString& destination); }; -#endif // NMEA_0183_CLASS_HEADER +#endif // NMEA_0183_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/response.cpp b/plugins/demo_pi_sample/src/nmea0183/response.cpp index fe1b9e1f69..78e3655023 100644 --- a/plugins/demo_pi_sample/src/nmea0183/response.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/response.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,61 +39,52 @@ ** You can use it any way you like. */ -RESPONSE::RESPONSE() -{ - Talker.Empty(); - ErrorMessage.Empty(); +RESPONSE::RESPONSE() { + Talker.Empty(); + ErrorMessage.Empty(); } -RESPONSE::~RESPONSE() -{ - Mnemonic.Empty(); - Talker.Empty(); - ErrorMessage.Empty(); +RESPONSE::~RESPONSE() { + Mnemonic.Empty(); + Talker.Empty(); + ErrorMessage.Empty(); } -void RESPONSE::SetContainer( NMEA0183 *container ) -{ - container_p = container; -} +void RESPONSE::SetContainer(NMEA0183* container) { container_p = container; } -void RESPONSE::SetErrorMessage( const wxString& error_message ) -{ - ErrorMessage = Mnemonic; - ErrorMessage += _T(", "); - ErrorMessage += error_message; +void RESPONSE::SetErrorMessage(const wxString& error_message) { + ErrorMessage = Mnemonic; + ErrorMessage += _T(", "); + ErrorMessage += error_message; } -bool RESPONSE::Write( SENTENCE& sentence ) -{ - /* - ** All NMEA0183 sentences begin with the mnemonic... - */ +bool RESPONSE::Write(SENTENCE& sentence) { + /* + ** All NMEA0183 sentences begin with the mnemonic... + */ - sentence = _T("$"); + sentence = _T("$"); - if (NULL == container_p) - sentence.Sentence.Append("--"); - else { - wxString talker_id = container_p->caller_ctx.get_talker_id(); - if ( talker_id.length() == 0) { - sentence.Sentence.Append(container_p->TalkerID); - } - else { - sentence.Sentence.Append( talker_id ); - } + if (NULL == container_p) + sentence.Sentence.Append("--"); + else { + wxString talker_id = container_p->caller_ctx.get_talker_id(); + if (talker_id.length() == 0) { + sentence.Sentence.Append(container_p->TalkerID); + } else { + sentence.Sentence.Append(talker_id); } + } - sentence.Sentence.Append(Mnemonic); + sentence.Sentence.Append(Mnemonic); - return( TRUE ); + return (TRUE); } -const wxString& RESPONSE::PlainEnglish( void ) -{ - static wxString return_string; +const wxString& RESPONSE::PlainEnglish(void) { + static wxString return_string; - return_string.Empty(); + return_string.Empty(); - return( return_string ); + return (return_string); } diff --git a/plugins/demo_pi_sample/src/nmea0183/rmb.cpp b/plugins/demo_pi_sample/src/nmea0183/rmb.cpp index 7eb7c833fc..51adbcfdbb 100644 --- a/plugins/demo_pi_sample/src/nmea0183/rmb.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/rmb.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,156 +39,146 @@ ** You can use it any way you like. */ - -RMB::RMB() -{ - Mnemonic = _T("RMB"); - Empty(); +RMB::RMB() { + Mnemonic = _T("RMB"); + Empty(); } -RMB::~RMB() -{ - Mnemonic.Empty(); - Empty(); +RMB::~RMB() { + Mnemonic.Empty(); + Empty(); } -void RMB::Empty( void ) -{ - - IsDataValid = Unknown0183; - CrossTrackError = 0.0; - DirectionToSteer = LR_Unknown; - To.Empty(); - From.Empty(); - DestinationPosition.Empty(); - RangeToDestinationNauticalMiles = 0.0; - BearingToDestinationDegreesTrue = 0.0; - DestinationClosingVelocityKnots = 0.0; - IsArrivalCircleEntered = Unknown0183; +void RMB::Empty(void) { + IsDataValid = Unknown0183; + CrossTrackError = 0.0; + DirectionToSteer = LR_Unknown; + To.Empty(); + From.Empty(); + DestinationPosition.Empty(); + RangeToDestinationNauticalMiles = 0.0; + BearingToDestinationDegreesTrue = 0.0; + DestinationClosingVelocityKnots = 0.0; + IsArrivalCircleEntered = Unknown0183; } -bool RMB::Parse( const SENTENCE& sentence ) -{ - - /* - ** RMB - Recommended Minimum Navigation Information - ** 14 - ** 1 2 3 4 5 6 7 8 9 10 11 12 13| - ** | | | | | | | | | | | | | | - ** $--RMB,A,x.x,a,c--c,c--c,llll.ll,a,yyyyy.yy,a,x.x,x.x,x.x,A*hh - ** - ** Field Number: - ** 1) Status, V = Navigation receiver warning - ** 2) Cross Track error - nautical miles - ** 3) Direction to Steer, Left or Right - ** 4) FROM Waypoint ID - ** 5) TO Waypoint ID - ** 6) Destination Waypoint Latitude - ** 7) N or S - ** 8) Destination Waypoint Longitude - ** 9) E or W - ** 10) Range to destination in nautical miles - ** 11) Bearing to destination in degrees True - ** 12) Destination closing velocity in knots - ** 13) Arrival Status, A = Arrival Circle Entered - ** Version 2.0 - ** 14) Checksum - - Version 2.3 - ** 14) FAA Mode Indicatior, The value can be A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator, optional, may be NULL - ** 15) Checksum - */ - - /* - ** First we check the checksum... - */ - int nFields = sentence.GetNumberOfDataFields(); - NMEA0183_BOOLEAN check = sentence.IsChecksumBad(nFields + 1); - - if ( check == NTrue ) - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - - // If sentence is at least Version 2.3, check the extra FAA mode indicator field - bool mode_valid = true; - if (nFields >= 14) { - wxString mode_string = sentence.Field(14); - if (!mode_string.StartsWith(_T("*"))) { - if ((mode_string == _T("N")) || (mode_string == _T("S"))) // Not valid, or simulator mode - mode_valid = false; +bool RMB::Parse(const SENTENCE& sentence) { + /* + ** RMB - Recommended Minimum Navigation Information + ** 14 + ** 1 2 3 4 5 6 7 8 9 10 11 12 13| + ** | | | | | | | | | | | | | | + ** $--RMB,A,x.x,a,c--c,c--c,llll.ll,a,yyyyy.yy,a,x.x,x.x,x.x,A*hh + ** + ** Field Number: + ** 1) Status, V = Navigation receiver warning + ** 2) Cross Track error - nautical miles + ** 3) Direction to Steer, Left or Right + ** 4) FROM Waypoint ID + ** 5) TO Waypoint ID + ** 6) Destination Waypoint Latitude + ** 7) N or S + ** 8) Destination Waypoint Longitude + ** 9) E or W + ** 10) Range to destination in nautical miles + ** 11) Bearing to destination in degrees True + ** 12) Destination closing velocity in knots + ** 13) Arrival Status, A = Arrival Circle Entered + ** Version 2.0 + ** 14) Checksum + + Version 2.3 + ** 14) FAA Mode Indicatior, The value can be A=autonomous, D=differential, + E=Estimated, N=not valid, S=Simulator, optional, may be NULL + ** 15) Checksum + */ + + /* + ** First we check the checksum... + */ + int nFields = sentence.GetNumberOfDataFields(); + NMEA0183_BOOLEAN check = sentence.IsChecksumBad(nFields + 1); + + if (check == NTrue) { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); + } + + // If sentence is at least Version 2.3, check the extra FAA mode indicator + // field + bool mode_valid = true; + if (nFields >= 14) { + wxString mode_string = sentence.Field(14); + if (!mode_string.StartsWith(_T("*"))) { + if ((mode_string == _T("N")) || + (mode_string == _T("S"))) // Not valid, or simulator mode + mode_valid = false; + } + } + + /* + if ( check == Unknown0183 ) + { + SetErrorMessage( _T("Missing Checksum") ); + return( FALSE ); } - } - -/* - if ( check == Unknown0183 ) - { - SetErrorMessage( _T("Missing Checksum") ); - return( FALSE ); - } -*/ - IsDataValid = mode_valid ? sentence.Boolean( 1 ) : NFalse; - CrossTrackError = sentence.Double( 2 ); - DirectionToSteer = sentence.LeftOrRight( 3 ); - From = sentence.Field( 4 ); - To = sentence.Field( 5 ); - DestinationPosition.Parse( 6, 7, 8, 9, sentence ); - RangeToDestinationNauticalMiles = sentence.Double( 10 ); - BearingToDestinationDegreesTrue = sentence.Double( 11 ); - DestinationClosingVelocityKnots = sentence.Double( 12 ); - IsArrivalCircleEntered = sentence.Boolean( 13 ); - - return( TRUE ); + */ + IsDataValid = mode_valid ? sentence.Boolean(1) : NFalse; + CrossTrackError = sentence.Double(2); + DirectionToSteer = sentence.LeftOrRight(3); + From = sentence.Field(4); + To = sentence.Field(5); + DestinationPosition.Parse(6, 7, 8, 9, sentence); + RangeToDestinationNauticalMiles = sentence.Double(10); + BearingToDestinationDegreesTrue = sentence.Double(11); + DestinationClosingVelocityKnots = sentence.Double(12); + IsArrivalCircleEntered = sentence.Boolean(13); + + return (TRUE); } -bool RMB::Write( SENTENCE& sentence ) -{ - - /* - ** Let the parent do its thing - */ - - RESPONSE::Write( sentence ); +bool RMB::Write(SENTENCE& sentence) { + /* + ** Let the parent do its thing + */ - sentence += IsDataValid; - sentence += CrossTrackError; - if(DirectionToSteer == Left) - sentence += _T("L"); - else - sentence += _T("R"); + RESPONSE::Write(sentence); + sentence += IsDataValid; + sentence += CrossTrackError; + if (DirectionToSteer == Left) + sentence += _T("L"); + else + sentence += _T("R"); - sentence += From; - sentence += To; - sentence += DestinationPosition; - sentence += RangeToDestinationNauticalMiles; - sentence += BearingToDestinationDegreesTrue; - sentence += DestinationClosingVelocityKnots; - sentence += IsArrivalCircleEntered; - sentence += FAAModeIndicator; + sentence += From; + sentence += To; + sentence += DestinationPosition; + sentence += RangeToDestinationNauticalMiles; + sentence += BearingToDestinationDegreesTrue; + sentence += DestinationClosingVelocityKnots; + sentence += IsArrivalCircleEntered; + sentence += FAAModeIndicator; - sentence.Finish(); + sentence.Finish(); -// NMEA0183_BOOLEAN check = sentence.IsChecksumBad( 14 ); + // NMEA0183_BOOLEAN check = sentence.IsChecksumBad( 14 ); - return( TRUE ); + return (TRUE); } -const RMB& RMB::operator = ( const RMB& source ) -{ - - IsDataValid = source.IsDataValid; - CrossTrackError = source.CrossTrackError; - DirectionToSteer = source.DirectionToSteer; - From = source.From; - To = source.To; - DestinationPosition = source.DestinationPosition; - RangeToDestinationNauticalMiles = source.RangeToDestinationNauticalMiles; - BearingToDestinationDegreesTrue = source.BearingToDestinationDegreesTrue; - DestinationClosingVelocityKnots = source.DestinationClosingVelocityKnots; - IsArrivalCircleEntered = source.IsArrivalCircleEntered; - FAAModeIndicator = source.FAAModeIndicator; - - return( *this ); +const RMB& RMB::operator=(const RMB& source) { + IsDataValid = source.IsDataValid; + CrossTrackError = source.CrossTrackError; + DirectionToSteer = source.DirectionToSteer; + From = source.From; + To = source.To; + DestinationPosition = source.DestinationPosition; + RangeToDestinationNauticalMiles = source.RangeToDestinationNauticalMiles; + BearingToDestinationDegreesTrue = source.BearingToDestinationDegreesTrue; + DestinationClosingVelocityKnots = source.DestinationClosingVelocityKnots; + IsArrivalCircleEntered = source.IsArrivalCircleEntered; + FAAModeIndicator = source.FAAModeIndicator; + + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/rmc.cpp b/plugins/demo_pi_sample/src/nmea0183/rmc.cpp index 0d186d9184..ac40979d80 100644 --- a/plugins/demo_pi_sample/src/nmea0183/rmc.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/rmc.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,157 +39,148 @@ ** You can use it any way you like. */ - -RMC::RMC() -{ - Mnemonic = _T("RMC"); - Empty(); +RMC::RMC() { + Mnemonic = _T("RMC"); + Empty(); } -RMC::~RMC() -{ - Mnemonic.Empty(); - Empty(); +RMC::~RMC() { + Mnemonic.Empty(); + Empty(); } -void RMC::Empty( void ) -{ - - UTCTime.Empty(); - IsDataValid = Unknown0183; - SpeedOverGroundKnots = 0.0; - Position.Empty(); - TrackMadeGoodDegreesTrue = 0.0; - Date.Empty(); - MagneticVariation = 0.0; - MagneticVariationDirection = EW_Unknown; +void RMC::Empty(void) { + UTCTime.Empty(); + IsDataValid = Unknown0183; + SpeedOverGroundKnots = 0.0; + Position.Empty(); + TrackMadeGoodDegreesTrue = 0.0; + Date.Empty(); + MagneticVariation = 0.0; + MagneticVariationDirection = EW_Unknown; } -bool RMC::Parse( const SENTENCE& sentence ) -{ -// ASSERT_VALID( this ); - - /* - ** RMC - Recommended Minimum Navigation Information - ** - ** Version 2.0 Format - ** 12 - ** 1 2 3 4 5 6 7 8 9 10 11| - ** | | | | | | | | | | | | - ** $--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxx,x.x,a*hh - ** - ** Field Number: - ** 1) UTC Time - ** 2) Status, V = Navigation receiver warning - ** 3) Latitude - ** 4) N or S - ** 5) Longitude - ** 6) E or W - ** 7) Speed over ground, knots - ** 8) Track made good, degrees true - ** 9) Date, ddmmyy - ** 10) Magnetic Variation, degrees - ** 11) E or W - - ** Version 2.0 - ** 12) Checksum - - ** Version 2.3 - ** 12) The value can be A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator, optional, may be NULL - ** 13) Checksum - */ - - /* - ** First we check the checksum... - */ - - int nFields = sentence.GetNumberOfDataFields( ); - - NMEA0183_BOOLEAN check = sentence.IsChecksumBad( nFields + 1 ); - - if ( check == NTrue ) - { - /* - ** This may be an NMEA Version 3+ sentence, with added fields - */ - wxString checksum_in_sentence = sentence.Field( nFields + 1 ); - if(checksum_in_sentence.StartsWith(_T("*"))) // Field is a valid erroneous checksum - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - } - - // If sentence is at least Version 2.3, check the extra mode indicator field - bool mode_valid = true; - if(nFields >= 12){ - wxString mode_string = sentence.Field( 12 ); - if(!mode_string.StartsWith(_T("*"))) { - if((mode_string == _T("N")) || (mode_string == _T("S"))) // Not valid, or simulator mode - mode_valid = false; - } - } - - - UTCTime = sentence.Field( 1 ); - - IsDataValid = sentence.Boolean( 2 ); - if( !mode_valid ) - IsDataValid = NFalse; - - Position.Parse( 3, 4, 5, 6, sentence ); - SpeedOverGroundKnots = sentence.Double( 7 ); - TrackMadeGoodDegreesTrue = sentence.Double( 8 ); - Date = sentence.Field( 9 ); - MagneticVariation = sentence.Double( 10 ); - MagneticVariationDirection = sentence.EastOrWest( 11 ); - - return( TRUE ); +bool RMC::Parse(const SENTENCE& sentence) { + // ASSERT_VALID( this ); + + /* + ** RMC - Recommended Minimum Navigation Information + ** + ** Version 2.0 Format + ** 12 + ** 1 2 3 4 5 6 7 8 9 10 11| + ** | | | | | | | | | | | | + ** $--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxx,x.x,a*hh + ** + ** Field Number: + ** 1) UTC Time + ** 2) Status, V = Navigation receiver warning + ** 3) Latitude + ** 4) N or S + ** 5) Longitude + ** 6) E or W + ** 7) Speed over ground, knots + ** 8) Track made good, degrees true + ** 9) Date, ddmmyy + ** 10) Magnetic Variation, degrees + ** 11) E or W + + ** Version 2.0 + ** 12) Checksum + + ** Version 2.3 + ** 12) The value can be A=autonomous, D=differential, E=Estimated, N=not + valid, S=Simulator, optional, may be NULL + ** 13) Checksum + */ + + /* + ** First we check the checksum... + */ + + int nFields = sentence.GetNumberOfDataFields(); + + NMEA0183_BOOLEAN check = sentence.IsChecksumBad(nFields + 1); + + if (check == NTrue) { + /* + ** This may be an NMEA Version 3+ sentence, with added fields + */ + wxString checksum_in_sentence = sentence.Field(nFields + 1); + if (checksum_in_sentence.StartsWith( + _T("*"))) // Field is a valid erroneous checksum + { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); + } + } + + // If sentence is at least Version 2.3, check the extra mode indicator field + bool mode_valid = true; + if (nFields >= 12) { + wxString mode_string = sentence.Field(12); + if (!mode_string.StartsWith(_T("*"))) { + if ((mode_string == _T("N")) || + (mode_string == _T("S"))) // Not valid, or simulator mode + mode_valid = false; + } + } + + UTCTime = sentence.Field(1); + + IsDataValid = sentence.Boolean(2); + if (!mode_valid) IsDataValid = NFalse; + + Position.Parse(3, 4, 5, 6, sentence); + SpeedOverGroundKnots = sentence.Double(7); + TrackMadeGoodDegreesTrue = sentence.Double(8); + Date = sentence.Field(9); + MagneticVariation = sentence.Double(10); + MagneticVariationDirection = sentence.EastOrWest(11); + + return (TRUE); } -bool RMC::Write( SENTENCE& sentence ) -{ -// ASSERT_VALID( this ); - - /* - ** Let the parent do its thing - */ - - RESPONSE::Write( sentence ); - - sentence += UTCTime; - sentence += IsDataValid; - sentence += Position; - sentence += SpeedOverGroundKnots; - sentence += TrackMadeGoodDegreesTrue; - sentence += Date; - - if(MagneticVariation > 360.) - sentence += _T(",,"); - else - { - sentence += MagneticVariation; - sentence += MagneticVariationDirection; - } - sentence += FAAModeIndicator; - sentence.Finish(); - - return( TRUE ); +bool RMC::Write(SENTENCE& sentence) { + // ASSERT_VALID( this ); + + /* + ** Let the parent do its thing + */ + + RESPONSE::Write(sentence); + + sentence += UTCTime; + sentence += IsDataValid; + sentence += Position; + sentence += SpeedOverGroundKnots; + sentence += TrackMadeGoodDegreesTrue; + sentence += Date; + + if (MagneticVariation > 360.) + sentence += _T(",,"); + else { + sentence += MagneticVariation; + sentence += MagneticVariationDirection; + } + sentence += FAAModeIndicator; + sentence.Finish(); + + return (TRUE); } -const RMC& RMC::operator = ( const RMC& source ) -{ -// ASSERT_VALID( this ); - - UTCTime = source.UTCTime; - IsDataValid = source.IsDataValid; - Position = source.Position; - SpeedOverGroundKnots = source.SpeedOverGroundKnots; - TrackMadeGoodDegreesTrue = source.TrackMadeGoodDegreesTrue; - Date = source.Date; - MagneticVariation = source.MagneticVariation; - MagneticVariationDirection = source.MagneticVariationDirection; - FAAModeIndicator = source.FAAModeIndicator; - - return( *this ); +const RMC& RMC::operator=(const RMC& source) { + // ASSERT_VALID( this ); + + UTCTime = source.UTCTime; + IsDataValid = source.IsDataValid; + Position = source.Position; + SpeedOverGroundKnots = source.SpeedOverGroundKnots; + TrackMadeGoodDegreesTrue = source.TrackMadeGoodDegreesTrue; + Date = source.Date; + MagneticVariation = source.MagneticVariation; + MagneticVariationDirection = source.MagneticVariationDirection; + FAAModeIndicator = source.FAAModeIndicator; + + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/rte.cpp b/plugins/demo_pi_sample/src/nmea0183/rte.cpp index a7ca1949a5..75fc7537be 100644 --- a/plugins/demo_pi_sample/src/nmea0183/rte.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/rte.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,158 +39,139 @@ ** You can use it any way you like. */ +RTE::RTE() { + m_complete_char = 'c'; + m_skip_checksum = 0; -RTE::RTE() -{ - m_complete_char = 'c'; - m_skip_checksum = 0; - - Mnemonic = _T("RTE"); - Empty(); + Mnemonic = _T("RTE"); + Empty(); } -RTE::~RTE() -{ - Mnemonic.Empty(); - Empty(); +RTE::~RTE() { + Mnemonic.Empty(); + Empty(); } -void RTE::Empty( void ) -{ - total_number_of_messages = 0; - last_message_number_received = 0; - message_number = 0; - last_waypoint_number_written = 0; +void RTE::Empty(void) { + total_number_of_messages = 0; + last_message_number_received = 0; + message_number = 0; + last_waypoint_number_written = 0; - TypeOfRoute = RouteUnknown; - RouteName.Empty(); + TypeOfRoute = RouteUnknown; + RouteName.Empty(); - delete_all_entries(); + delete_all_entries(); } -bool RTE::Parse( const SENTENCE& sentence ) -{ - /* - ** RTE - Routes - ** - ** 1 2 3 4 5 x n - ** | | | | | | | - ** $--RTE,x.x,x.x,a,c--c,c--c, ..... c--c*hh - ** - ** Field Number: - ** 1) Total number of messages being transmitted - ** 2) Message Number - ** 3) Message mode - ** c = complete route, all waypoints - ** w = working route, the waypoint you just left, the waypoint you're heading to then all the rest - ** 4) Waypoint ID - ** x) More Waypoints - ** n) Checksum - */ - - delete_all_entries(); - - int field_number = 1; - - total_number_of_messages = sentence.Integer( 1 ); -// total_number_of_messages = sentence.Double( 1 ); - - int this_message_number = sentence.Integer( 2 ); -// double this_message_number = sentence.Double( 2 ); - - if ( this_message_number == 1 ) - { - /* - ** Make sure we've got a clean list - */ - - delete_all_entries(); - } - - - if ( sentence.Field( 3 ).StartsWith(_T("c")) ) - { - TypeOfRoute = CompleteRoute; - } - else if ( sentence.Field( 3 ).StartsWith(_T("w"))) - { - TypeOfRoute = WorkingRoute; - } - else - { - TypeOfRoute = RouteUnknown; - } - - RouteName = sentence.Field( 4 ); - - int number_of_data_fields = sentence.GetNumberOfDataFields(); - field_number = 5; - - while( field_number < number_of_data_fields ) - { - Waypoints.Add( ( sentence.Field( field_number ) )) ; - field_number++; - } - - return( TRUE ); +bool RTE::Parse(const SENTENCE& sentence) { + /* + ** RTE - Routes + ** + ** 1 2 3 4 5 x n + ** | | | | | | | + ** $--RTE,x.x,x.x,a,c--c,c--c, ..... c--c*hh + ** + ** Field Number: + ** 1) Total number of messages being transmitted + ** 2) Message Number + ** 3) Message mode + ** c = complete route, all waypoints + ** w = working route, the waypoint you just left, the waypoint you're + *heading to then all the rest + ** 4) Waypoint ID + ** x) More Waypoints + ** n) Checksum + */ + + delete_all_entries(); + + int field_number = 1; + + total_number_of_messages = sentence.Integer(1); + // total_number_of_messages = sentence.Double( 1 ); + + int this_message_number = sentence.Integer(2); + // double this_message_number = sentence.Double( 2 ); + + if (this_message_number == 1) { + /* + ** Make sure we've got a clean list + */ + + delete_all_entries(); + } + + if (sentence.Field(3).StartsWith(_T("c"))) { + TypeOfRoute = CompleteRoute; + } else if (sentence.Field(3).StartsWith(_T("w"))) { + TypeOfRoute = WorkingRoute; + } else { + TypeOfRoute = RouteUnknown; + } + + RouteName = sentence.Field(4); + + int number_of_data_fields = sentence.GetNumberOfDataFields(); + field_number = 5; + + while (field_number < number_of_data_fields) { + Waypoints.Add((sentence.Field(field_number))); + field_number++; + } + + return (TRUE); } -bool RTE::Write( SENTENCE& sentence ) -{ +bool RTE::Write(SENTENCE& sentence) { + /* + ** Let the parent do its thing + */ - /* - ** Let the parent do its thing - */ + RESPONSE::Write(sentence); - RESPONSE::Write( sentence ); + sentence += total_number_of_messages; + sentence += message_number; - sentence += total_number_of_messages; - sentence += message_number; + switch (TypeOfRoute) { + case CompleteRoute: - switch( TypeOfRoute ) - { - case CompleteRoute: + // sentence += _T("C"); // uppercase required for + // GPS MLR FFX312 sentence += _T("c"); // trying + // lowercase for generic NMEA device + sentence += wxString((wxChar)m_complete_char); + break; -// sentence += _T("C"); // uppercase required for GPS MLR FFX312 -// sentence += _T("c"); // trying lowercase for generic NMEA device - sentence += wxString((wxChar)m_complete_char); - break; + case WorkingRoute: - case WorkingRoute: + sentence += _T("w"); + break; - sentence += _T("w"); - break; + default: - default: + // sentence += ""; + break; + } -// sentence += ""; - break; - } + sentence += RouteName; - sentence += RouteName; + for (unsigned int i = 0; i < Waypoints.GetCount(); i++) + sentence += Waypoints[i]; - for(unsigned int i=0 ; i < Waypoints.GetCount() ; i++) - sentence += Waypoints[i]; + if (m_skip_checksum) { + wxString temp_string; + temp_string.Printf(_T("%c%c"), CARRIAGE_RETURN, LINE_FEED); + sentence.Sentence += temp_string; + } else + sentence.Finish(); - if(m_skip_checksum){ - wxString temp_string; - temp_string.Printf(_T("%c%c"), CARRIAGE_RETURN, LINE_FEED ); - sentence.Sentence += temp_string; - } - else - sentence.Finish(); - - return( TRUE ); + return (TRUE); } -bool RTE::AddWaypoint(const wxString& name) -{ - Waypoints.Add(name); +bool RTE::AddWaypoint(const wxString& name) { + Waypoints.Add(name); - return( TRUE ); + return (TRUE); } -void RTE::delete_all_entries( void ) -{ - Waypoints.Clear(); -} +void RTE::delete_all_entries(void) { Waypoints.Clear(); } diff --git a/plugins/demo_pi_sample/src/nmea0183/rte.hpp b/plugins/demo_pi_sample/src/nmea0183/rte.hpp index 738f0535ee..dc757a1814 100644 --- a/plugins/demo_pi_sample/src/nmea0183/rte.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/rte.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( RTE_CLASS_HEADER ) +#if !defined(RTE_CLASS_HEADER) #define RTE_CLASS_HEADER /* @@ -41,37 +40,30 @@ ** You can use it any way you like. */ -class RTE : public RESPONSE -{ - - private: - - void delete_all_entries( void ); - - double last_message_number_received; - - int last_waypoint_number_written; - - public: - - RTE(); - ~RTE(); +class RTE : public RESPONSE { +private: + void delete_all_entries(void); + double last_message_number_received; - ROUTE_TYPE TypeOfRoute; - wxString RouteName; - wxArrayString Waypoints; - int message_number; - int total_number_of_messages; - int m_skip_checksum; - char m_complete_char; + int last_waypoint_number_written; - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); - virtual bool AddWaypoint(const wxString& name); +public: + RTE(); + ~RTE(); + ROUTE_TYPE TypeOfRoute; + wxString RouteName; + wxArrayString Waypoints; + int message_number; + int total_number_of_messages; + int m_skip_checksum; + char m_complete_char; + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); + virtual bool AddWaypoint(const wxString& name); }; -#endif // RTE_CLASS_HEADER +#endif // RTE_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/sentence.cpp b/plugins/demo_pi_sample/src/nmea0183/sentence.cpp index 4fb0307876..cebaf26247 100644 --- a/plugins/demo_pi_sample/src/nmea0183/sentence.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/sentence.cpp @@ -29,23 +29,20 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" #include #if !defined(NAN) -//static const long long lNaN = 0x7fffffffffffffff; +// static const long long lNaN = 0x7fffffffffffffff; -//#define NaN (*(double*)&lNaN) -//#else +// #define NaN (*(double*)&lNaN) +// #else static const long long lNaN = 0xfff8000000000000; #define NAN (*(double*)&lNaN) #endif - - /* ** Author: Samuel R. Blackburn ** CI$: 76300,326 @@ -54,671 +51,535 @@ static const long long lNaN = 0xfff8000000000000; ** You can use it any way you like. */ +SENTENCE::SENTENCE() { Sentence.Empty(); } -SENTENCE::SENTENCE() -{ - Sentence.Empty(); -} +SENTENCE::~SENTENCE() { Sentence.Empty(); } -SENTENCE::~SENTENCE() -{ - Sentence.Empty(); -} +NMEA0183_BOOLEAN SENTENCE::Boolean(int field_number) const { + // ASSERT_VALID( this ); -NMEA0183_BOOLEAN SENTENCE::Boolean( int field_number ) const -{ -// ASSERT_VALID( this ); - - wxString field_data; - - field_data = Field( field_number ); - - if ( field_data.StartsWith(_T("A")) ) - { - return( NTrue ); - } - else if ( field_data.StartsWith(_T("V")) ) - { - return( NFalse ); - } - else - { - return( Unknown0183 ); - } + wxString field_data; + + field_data = Field(field_number); + + if (field_data.StartsWith(_T("A"))) { + return (NTrue); + } else if (field_data.StartsWith(_T("V"))) { + return (NFalse); + } else { + return (Unknown0183); + } } -COMMUNICATIONS_MODE SENTENCE::CommunicationsMode( int field_number ) const -{ -// ASSERT_VALID( this ); - - wxString field_data; - - field_data = Field( field_number ); - - if ( field_data == _T("d") ) - { - return( F3E_G3E_SimplexTelephone ); - } - else if ( field_data == _T("e") ) - { - return( F3E_G3E_DuplexTelephone ); - } - else if ( field_data == _T("m") ) - { - return( J3E_Telephone ); - } - else if ( field_data == _T("o") ) - { - return( H3E_Telephone ); - } - else if ( field_data == _T("q") ) - { - return( F1B_J2B_FEC_NBDP_TelexTeleprinter ); - } - else if ( field_data == _T("s") ) - { - return( F1B_J2B_ARQ_NBDP_TelexTeleprinter ); - } - else if ( field_data == _T("w") ) - { - return( F1B_J2B_ReceiveOnlyTeleprinterDSC ); - } - else if ( field_data == _T("x") ) - { - return( A1A_MorseTapeRecorder ); - } - else if ( field_data == _T("{") ) - { - return( A1A_MorseKeyHeadset ); - } - else if ( field_data == _T("|") ) - { - return( F1C_F2C_F3C_FaxMachine ); - } - else - { - return( CommunicationsModeUnknown ); - } +COMMUNICATIONS_MODE SENTENCE::CommunicationsMode(int field_number) const { + // ASSERT_VALID( this ); + + wxString field_data; + + field_data = Field(field_number); + + if (field_data == _T("d")) { + return (F3E_G3E_SimplexTelephone); + } else if (field_data == _T("e")) { + return (F3E_G3E_DuplexTelephone); + } else if (field_data == _T("m")) { + return (J3E_Telephone); + } else if (field_data == _T("o")) { + return (H3E_Telephone); + } else if (field_data == _T("q")) { + return (F1B_J2B_FEC_NBDP_TelexTeleprinter); + } else if (field_data == _T("s")) { + return (F1B_J2B_ARQ_NBDP_TelexTeleprinter); + } else if (field_data == _T("w")) { + return (F1B_J2B_ReceiveOnlyTeleprinterDSC); + } else if (field_data == _T("x")) { + return (A1A_MorseTapeRecorder); + } else if (field_data == _T("{")) { + return (A1A_MorseKeyHeadset); + } else if (field_data == _T("|")) { + return (F1C_F2C_F3C_FaxMachine); + } else { + return (CommunicationsModeUnknown); + } } -unsigned char SENTENCE::ComputeChecksum( void ) const -{ - unsigned char checksum_value = 0; +unsigned char SENTENCE::ComputeChecksum(void) const { + unsigned char checksum_value = 0; - int string_length = Sentence.Length(); - int index = 1; // Skip over the $ at the begining of the sentence + int string_length = Sentence.Length(); + int index = 1; // Skip over the $ at the begining of the sentence - while( index < string_length && - Sentence[ index ] != '*' && - Sentence[ index ] != CARRIAGE_RETURN && - Sentence[ index ] != LINE_FEED ) - { - checksum_value ^= (char)Sentence[ index ]; - index++; - } + while (index < string_length && Sentence[index] != '*' && + Sentence[index] != CARRIAGE_RETURN && Sentence[index] != LINE_FEED) { + checksum_value ^= (char)Sentence[index]; + index++; + } - return( checksum_value ); + return (checksum_value); } -double SENTENCE::Double( int field_number ) const -{ - // ASSERT_VALID( this ); - if(Field( field_number ).Len() == 0) - return (NAN); - - wxCharBuffer abuf = Field( field_number).ToUTF8(); - if( !abuf.data() ) // badly formed sentence? - return (NAN); +double SENTENCE::Double(int field_number) const { + // ASSERT_VALID( this ); + if (Field(field_number).Len() == 0) return (NAN); - return( ::atof( abuf.data() )); + wxCharBuffer abuf = Field(field_number).ToUTF8(); + if (!abuf.data()) // badly formed sentence? + return (NAN); + return (::atof(abuf.data())); } +EASTWEST SENTENCE::EastOrWest(int field_number) const { + // ASSERT_VALID( this ); -EASTWEST SENTENCE::EastOrWest( int field_number ) const -{ -// ASSERT_VALID( this ); + wxString field_data; - wxString field_data; + field_data = Field(field_number); - field_data = Field( field_number ); - - if ( field_data == _T("E") ) - { - return( East ); - } - else if ( field_data == _T("W") ) - { - return( West ); - } - else - { - return( EW_Unknown ); - } + if (field_data == _T("E")) { + return (East); + } else if (field_data == _T("W")) { + return (West); + } else { + return (EW_Unknown); + } } -const wxString& SENTENCE::Field( int desired_field_number ) const -{ -// ASSERT_VALID( this ); +const wxString& SENTENCE::Field(int desired_field_number) const { + // ASSERT_VALID( this ); + + static wxString return_string; + return_string.Empty(); - static wxString return_string; - return_string.Empty(); + int index = 1; // Skip over the $ at the begining of the sentence + int current_field_number = 0; + int string_length = 0; - int index = 1; // Skip over the $ at the begining of the sentence - int current_field_number = 0; - int string_length = 0; + string_length = Sentence.Len(); - string_length = Sentence.Len(); + while (current_field_number < desired_field_number && index < string_length) { + if (Sentence[index] == ',' || Sentence[index] == '*') { + current_field_number++; + } - while( current_field_number < desired_field_number && index < string_length ) - { - if ( Sentence[ index ] == ',' || Sentence[ index ] == '*' ) - { - current_field_number++; - } + if (Sentence[index] == '*') return_string += Sentence[index]; + index++; + } - if( Sentence[ index ] == '*') - return_string += Sentence[ index ]; + if (current_field_number == desired_field_number) { + while (index < string_length && Sentence[index] != ',' && + Sentence[index] != '*' && Sentence[index] != 0x00) { + return_string += Sentence[index]; index++; - } - - if ( current_field_number == desired_field_number ) - { - while( index < string_length && - Sentence[ index ] != ',' && - Sentence[ index ] != '*' && - Sentence[ index ] != 0x00 ) - { - return_string += Sentence[ index ]; - index++; - } - } - - - return( return_string ); + } + } + + return (return_string); } -int SENTENCE::GetNumberOfDataFields( void ) const -{ -// ASSERT_VALID( this ); +int SENTENCE::GetNumberOfDataFields(void) const { + // ASSERT_VALID( this ); - int index = 1; // Skip over the $ at the begining of the sentence - int current_field_number = 0; - int string_length = 0; + int index = 1; // Skip over the $ at the begining of the sentence + int current_field_number = 0; + int string_length = 0; - string_length = Sentence.Len(); + string_length = Sentence.Len(); - while( index < string_length ) - { - if ( Sentence[ index ] == '*' ) - { - return( (int) current_field_number ); - } + while (index < string_length) { + if (Sentence[index] == '*') { + return ((int)current_field_number); + } - if ( Sentence[ index ] == ',' ) - { - current_field_number++; - } + if (Sentence[index] == ',') { + current_field_number++; + } - index++; - } + index++; + } - return( (int) current_field_number ); + return ((int)current_field_number); } -void SENTENCE::Finish( void ) -{ -// ASSERT_VALID( this ); +void SENTENCE::Finish(void) { + // ASSERT_VALID( this ); - unsigned char checksum = ComputeChecksum(); + unsigned char checksum = ComputeChecksum(); - wxString temp_string; + wxString temp_string; - temp_string.Printf(_T("*%02X%c%c"), (int) checksum, CARRIAGE_RETURN, LINE_FEED ); - Sentence += temp_string; + temp_string.Printf(_T("*%02X%c%c"), (int)checksum, CARRIAGE_RETURN, + LINE_FEED); + Sentence += temp_string; } -int SENTENCE::Integer( int field_number ) const -{ -// ASSERT_VALID( this ); - wxCharBuffer abuf = Field( field_number).ToUTF8(); - if( !abuf.data() ) // badly formed sentence? - return 0; +int SENTENCE::Integer(int field_number) const { + // ASSERT_VALID( this ); + wxCharBuffer abuf = Field(field_number).ToUTF8(); + if (!abuf.data()) // badly formed sentence? + return 0; - return( ::atoi( abuf.data() )); + return (::atoi(abuf.data())); } -NMEA0183_BOOLEAN SENTENCE::IsChecksumBad( int checksum_field_number ) const -{ -// ASSERT_VALID( this ); +NMEA0183_BOOLEAN SENTENCE::IsChecksumBad(int checksum_field_number) const { + // ASSERT_VALID( this ); - /* - ** Checksums are optional, return TRUE if an existing checksum is known to be bad - */ + /* + ** Checksums are optional, return TRUE if an existing checksum is known to be + *bad + */ - wxString checksum_in_sentence = Field( checksum_field_number ); + wxString checksum_in_sentence = Field(checksum_field_number); - if ( checksum_in_sentence == _T("") ) - { - return( Unknown0183 ); - } + if (checksum_in_sentence == _T("")) { + return (Unknown0183); + } - wxString check = checksum_in_sentence.Mid( 1 ); - if ( ComputeChecksum() != HexValue( check ) ) - { - return( NTrue ); - } + wxString check = checksum_in_sentence.Mid(1); + if (ComputeChecksum() != HexValue(check)) { + return (NTrue); + } - return( NFalse ); + return (NFalse); } -LEFTRIGHT SENTENCE::LeftOrRight( int field_number ) const -{ -// ASSERT_VALID( this ); - - wxString field_data; - - field_data = Field( field_number ); - - if ( field_data == _T("L") ) - { - return( Left ); - } - else if ( field_data == _T("R") ) - { - return( Right ); - } - else - { - return( LR_Unknown ); - } +LEFTRIGHT SENTENCE::LeftOrRight(int field_number) const { + // ASSERT_VALID( this ); + + wxString field_data; + + field_data = Field(field_number); + + if (field_data == _T("L")) { + return (Left); + } else if (field_data == _T("R")) { + return (Right); + } else { + return (LR_Unknown); + } } -NORTHSOUTH SENTENCE::NorthOrSouth( int field_number ) const -{ -// ASSERT_VALID( this ); - - wxString field_data; - - field_data = Field( field_number ); - - if ( field_data == _T("N") ) - { - return( North ); - } - else if ( field_data == _T("S") ) - { - return( South ); - } - else - { - return( NS_Unknown ); - } +NORTHSOUTH SENTENCE::NorthOrSouth(int field_number) const { + // ASSERT_VALID( this ); + + wxString field_data; + + field_data = Field(field_number); + + if (field_data == _T("N")) { + return (North); + } else if (field_data == _T("S")) { + return (South); + } else { + return (NS_Unknown); + } } -REFERENCE SENTENCE::Reference( int field_number ) const -{ -// ASSERT_VALID( this ); - - wxString field_data; - - field_data = Field( field_number ); - - if ( field_data == _T("B") ) - { - return( BottomTrackingLog ); - } - else if ( field_data == _T("M") ) - { - return( ManuallyEntered ); - } - else if ( field_data == _T("W") ) - { - return( WaterReferenced ); - } - else if ( field_data == _T("R") ) - { - return( RadarTrackingOfFixedTarget ); - } - else if ( field_data == _T("P") ) - { - return( PositioningSystemGroundReference ); - } - else - { - return( ReferenceUnknown ); - } +REFERENCE SENTENCE::Reference(int field_number) const { + // ASSERT_VALID( this ); + + wxString field_data; + + field_data = Field(field_number); + + if (field_data == _T("B")) { + return (BottomTrackingLog); + } else if (field_data == _T("M")) { + return (ManuallyEntered); + } else if (field_data == _T("W")) { + return (WaterReferenced); + } else if (field_data == _T("R")) { + return (RadarTrackingOfFixedTarget); + } else if (field_data == _T("P")) { + return (PositioningSystemGroundReference); + } else { + return (ReferenceUnknown); + } } -TRANSDUCER_TYPE SENTENCE::TransducerType( int field_number ) const -{ -// ASSERT_VALID( this ); - - wxString field_data; - - field_data = Field( field_number ); - - if ( field_data == _T("A") ) - { - return( AngularDisplacementTransducer ); - } - else if ( field_data == _T("D") ) - { - return( LinearDisplacementTransducer ); - } - else if ( field_data == _T("C") ) - { - return( TemperatureTransducer ); - } - else if ( field_data == _T("F") ) - { - return( FrequencyTransducer ); - } - else if ( field_data == _T("N") ) - { - return( ForceTransducer ); - } - else if ( field_data == _T("P") ) - { - return( PressureTransducer ); - } - else if ( field_data == _T("R") ) - { - return( FlowRateTransducer ); - } - else if ( field_data == _T("T") ) - { - return( TachometerTransducer ); - } - else if ( field_data == _T("H") ) - { - return( HumidityTransducer ); - } - else if ( field_data == _T("V") ) - { - return( VolumeTransducer ); - } - else - { - return( TransducerUnknown ); - } +TRANSDUCER_TYPE SENTENCE::TransducerType(int field_number) const { + // ASSERT_VALID( this ); + + wxString field_data; + + field_data = Field(field_number); + + if (field_data == _T("A")) { + return (AngularDisplacementTransducer); + } else if (field_data == _T("D")) { + return (LinearDisplacementTransducer); + } else if (field_data == _T("C")) { + return (TemperatureTransducer); + } else if (field_data == _T("F")) { + return (FrequencyTransducer); + } else if (field_data == _T("N")) { + return (ForceTransducer); + } else if (field_data == _T("P")) { + return (PressureTransducer); + } else if (field_data == _T("R")) { + return (FlowRateTransducer); + } else if (field_data == _T("T")) { + return (TachometerTransducer); + } else if (field_data == _T("H")) { + return (HumidityTransducer); + } else if (field_data == _T("V")) { + return (VolumeTransducer); + } else { + return (TransducerUnknown); + } } /* ** Operators */ -SENTENCE::operator wxString() const -{ -// ASSERT_VALID( this ); +SENTENCE::operator wxString() const { + // ASSERT_VALID( this ); - return( Sentence ); + return (Sentence); } -const SENTENCE& SENTENCE::operator = ( const SENTENCE& source ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator=(const SENTENCE& source) { + // ASSERT_VALID( this ); - Sentence = source.Sentence; + Sentence = source.Sentence; - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator = ( const wxString& source ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator=(const wxString& source) { + // ASSERT_VALID( this ); - Sentence = source; + Sentence = source; - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator += ( const wxString& source ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator+=(const wxString& source) { + // ASSERT_VALID( this ); - Sentence += _T(","); - Sentence += source; + Sentence += _T(","); + Sentence += source; - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator += ( double value ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator+=(double value) { + // ASSERT_VALID( this ); - wxString temp_string; + wxString temp_string; - temp_string.Printf(_T("%.3f"), value ); + temp_string.Printf(_T("%.3f"), value); - Sentence += _T(","); - Sentence += temp_string; + Sentence += _T(","); + Sentence += temp_string; - return( *this ); + return (*this); } -SENTENCE& SENTENCE::Add ( double value, int precision ) -{ -// ASSERT_VALID( this ); +SENTENCE& SENTENCE::Add(double value, int precision) { + // ASSERT_VALID( this ); - wxString temp_string; - wxString s_Precision; + wxString temp_string; + wxString s_Precision; - s_Precision.Printf(_T("%c.%if"), '%', precision ); - temp_string.Printf( s_Precision, value ); + s_Precision.Printf(_T("%c.%if"), '%', precision); + temp_string.Printf(s_Precision, value); - Sentence += _T(","); - Sentence += temp_string; + Sentence += _T(","); + Sentence += temp_string; - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator += ( COMMUNICATIONS_MODE mode ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator+=(COMMUNICATIONS_MODE mode) { + // ASSERT_VALID( this ); - Sentence += _T(","); + Sentence += _T(","); - switch( mode ) - { - case F3E_G3E_SimplexTelephone: + switch (mode) { + case F3E_G3E_SimplexTelephone: - Sentence += _T("d"); - break; + Sentence += _T("d"); + break; - case F3E_G3E_DuplexTelephone: + case F3E_G3E_DuplexTelephone: - Sentence += _T("e"); - break; + Sentence += _T("e"); + break; - case J3E_Telephone: + case J3E_Telephone: - Sentence += _T("m"); - break; + Sentence += _T("m"); + break; - case H3E_Telephone: + case H3E_Telephone: - Sentence += _T("o"); - break; + Sentence += _T("o"); + break; - case F1B_J2B_FEC_NBDP_TelexTeleprinter: + case F1B_J2B_FEC_NBDP_TelexTeleprinter: - Sentence += _T("q"); - break; + Sentence += _T("q"); + break; - case F1B_J2B_ARQ_NBDP_TelexTeleprinter: + case F1B_J2B_ARQ_NBDP_TelexTeleprinter: - Sentence += _T("s"); - break; + Sentence += _T("s"); + break; - case F1B_J2B_ReceiveOnlyTeleprinterDSC: + case F1B_J2B_ReceiveOnlyTeleprinterDSC: - Sentence += _T("w"); - break; + Sentence += _T("w"); + break; - case A1A_MorseTapeRecorder: + case A1A_MorseTapeRecorder: - Sentence += _T("x"); - break; + Sentence += _T("x"); + break; - case A1A_MorseKeyHeadset: + case A1A_MorseKeyHeadset: - Sentence += _T("{"); - break; + Sentence += _T("{"); + break; - case F1C_F2C_F3C_FaxMachine: + case F1C_F2C_F3C_FaxMachine: - Sentence += _T("|"); - break; + Sentence += _T("|"); + break; - case CommunicationsModeUnknown: + case CommunicationsModeUnknown: - break; - } + break; + } - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator += ( TRANSDUCER_TYPE transducer ) -{ -// ASSERT_VALID( this ); - - Sentence += _T(","); +const SENTENCE& SENTENCE::operator+=(TRANSDUCER_TYPE transducer) { + // ASSERT_VALID( this ); - switch( transducer ) - { - case TemperatureTransducer: + Sentence += _T(","); - Sentence += _T("C"); - break; + switch (transducer) { + case TemperatureTransducer: - case AngularDisplacementTransducer: + Sentence += _T("C"); + break; - Sentence += _T("A"); - break; + case AngularDisplacementTransducer: - case LinearDisplacementTransducer: + Sentence += _T("A"); + break; - Sentence += _T("D"); - break; + case LinearDisplacementTransducer: - case FrequencyTransducer: + Sentence += _T("D"); + break; - Sentence += _T("F"); - break; + case FrequencyTransducer: - case ForceTransducer: + Sentence += _T("F"); + break; - Sentence += _T("N"); - break; + case ForceTransducer: - case PressureTransducer: + Sentence += _T("N"); + break; - Sentence += _T("P"); - break; + case PressureTransducer: - case FlowRateTransducer: + Sentence += _T("P"); + break; - Sentence += _T("R"); - break; + case FlowRateTransducer: - case TachometerTransducer: + Sentence += _T("R"); + break; - Sentence += _T("T"); - break; + case TachometerTransducer: - case HumidityTransducer: + Sentence += _T("T"); + break; - Sentence += _T("H"); - break; + case HumidityTransducer: - case VolumeTransducer: + Sentence += _T("H"); + break; - Sentence += _T("V"); - break; + case VolumeTransducer: - case TransducerUnknown: + Sentence += _T("V"); + break; - Sentence += _T("?"); - break; + case TransducerUnknown: - } + Sentence += _T("?"); + break; + } - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator += ( NORTHSOUTH northing ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator+=(NORTHSOUTH northing) { + // ASSERT_VALID( this ); - Sentence += _T(","); + Sentence += _T(","); - if ( northing == North ) - { - Sentence += _T("N"); - } - else if ( northing == South ) - { - Sentence += _T("S"); - } + if (northing == North) { + Sentence += _T("N"); + } else if (northing == South) { + Sentence += _T("S"); + } - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator += ( int value ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator+=(int value) { + // ASSERT_VALID( this ); - wxString temp_string; + wxString temp_string; - temp_string.Printf(_T("%d"), value ); + temp_string.Printf(_T("%d"), value); - Sentence += _T(","); - Sentence += temp_string; + Sentence += _T(","); + Sentence += temp_string; - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator += ( EASTWEST easting ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator+=(EASTWEST easting) { + // ASSERT_VALID( this ); - Sentence += _T(","); + Sentence += _T(","); - if ( easting == East ) - { - Sentence += _T("E"); - } - else if ( easting == West ) - { - Sentence += _T("W"); - } + if (easting == East) { + Sentence += _T("E"); + } else if (easting == West) { + Sentence += _T("W"); + } - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator += ( NMEA0183_BOOLEAN boolean ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator+=(NMEA0183_BOOLEAN boolean) { + // ASSERT_VALID( this ); - Sentence += _T(","); + Sentence += _T(","); - if ( boolean == NTrue ) - { - Sentence += _T("A"); - } - else if ( boolean == NFalse ) - { - Sentence += _T("V"); - } + if (boolean == NTrue) { + Sentence += _T("A"); + } else if (boolean == NFalse) { + Sentence += _T("V"); + } - return( *this ); + return (*this); } -const SENTENCE& SENTENCE::operator += ( LATLONG& source ) -{ -// ASSERT_VALID( this ); +const SENTENCE& SENTENCE::operator+=(LATLONG& source) { + // ASSERT_VALID( this ); - source.Write( *this ); + source.Write(*this); - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/talkerid.cpp b/plugins/demo_pi_sample/src/nmea0183/talkerid.cpp index f5c200a657..e0a8f560db 100644 --- a/plugins/demo_pi_sample/src/nmea0183/talkerid.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/talkerid.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,19 +39,16 @@ ** You can use it any way you like. */ - wxString& talker_id( const wxString &sentence ) -{ - static wxString return_string; +wxString& talker_id(const wxString& sentence) { + static wxString return_string; - return_string.Empty(); + return_string.Empty(); - if ( sentence.Len() >= 3 ) - { - if ( sentence[ 0 ] == '$' ) - { - return_string = sentence.Mid( 1, 2 ); - } - } + if (sentence.Len() >= 3) { + if (sentence[0] == '$') { + return_string = sentence.Mid(1, 2); + } + } - return( return_string ); + return (return_string); } diff --git a/plugins/demo_pi_sample/src/nmea0183/vtg.cpp b/plugins/demo_pi_sample/src/nmea0183/vtg.cpp index fe05b068fe..3a805420c6 100644 --- a/plugins/demo_pi_sample/src/nmea0183/vtg.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/vtg.cpp @@ -29,9 +29,8 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" -//#pragma hdrstop +// #pragma hdrstop /* ** Author: Samuel R. Blackburn @@ -41,135 +40,122 @@ ** You can use it any way you like. */ -//IMPLEMENT_DYNAMIC( VTG, RESPONSE ) +// IMPLEMENT_DYNAMIC( VTG, RESPONSE ) -VTG::VTG() -{ - Mnemonic = _T("VTG"); - Empty(); +VTG::VTG() { + Mnemonic = _T("VTG"); + Empty(); } -VTG::~VTG() -{ - Mnemonic.Empty(); - Empty(); +VTG::~VTG() { + Mnemonic.Empty(); + Empty(); } -void VTG::Empty( void ) -{ -// ASSERT_VALID( this ); +void VTG::Empty(void) { + // ASSERT_VALID( this ); - TrackDegreesTrue = 0.0; - TrackDegreesMagnetic = 0.0; - SpeedKnots = 0.0; - SpeedKilometersPerHour = 0.0; + TrackDegreesTrue = 0.0; + TrackDegreesMagnetic = 0.0; + SpeedKnots = 0.0; + SpeedKilometersPerHour = 0.0; } -bool VTG::Parse( const SENTENCE& sentence ) -{ -// ASSERT_VALID( this ); - - /* - ** VTG - Track made good and Ground speed - ** - ** 1 2 3 4 5 6 7 8 9 - ** | | | | | | | | | - ** $--VTG,x.x,T,x.x,M,x.x,N,x.x,K*hh - ** - ** Field Number: - ** 1) Track Degrees - ** 2) T = True - ** 3) Track Degrees - ** 4) M = Magnetic - ** 5) Speed Knots - ** 6) N = Knots - ** 7) Speed Kilometers Per Hour - ** 8) K = Kilometers Per Hour - ** 9) Checksum - */ - - /* - ** First we check the checksum... - */ - - int target_field_count = 8; - - NMEA0183_BOOLEAN check = sentence.IsChecksumBad( 9 ); - - if ( check == NTrue ) - { +bool VTG::Parse(const SENTENCE& sentence) { + // ASSERT_VALID( this ); /* - ** This may be an NMEA Version 2.3 sentence, with "Mode" field + ** VTG - Track made good and Ground speed + ** + ** 1 2 3 4 5 6 7 8 9 + ** | | | | | | | | | + ** $--VTG,x.x,T,x.x,M,x.x,N,x.x,K*hh + ** + ** Field Number: + ** 1) Track Degrees + ** 2) T = True + ** 3) Track Degrees + ** 4) M = Magnetic + ** 5) Speed Knots + ** 6) N = Knots + ** 7) Speed Kilometers Per Hour + ** 8) K = Kilometers Per Hour + ** 9) Checksum */ - wxString checksum_in_sentence = sentence.Field( 9 ); - if(checksum_in_sentence.StartsWith(_T("*"))) // Field is a valid erroneous checksum - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - - else - { - target_field_count = 9; - check = sentence.IsChecksumBad( 10 ); - if( check == NTrue) - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - } - } - + /* + ** First we check the checksum... + */ - if ( sentence.GetNumberOfDataFields() != target_field_count ) - { - SetErrorMessage( _T("Invalid FieldCount") ); - return( FALSE ); - } + int target_field_count = 8; + + NMEA0183_BOOLEAN check = sentence.IsChecksumBad(9); + + if (check == NTrue) { + /* + ** This may be an NMEA Version 2.3 sentence, with "Mode" field + */ + wxString checksum_in_sentence = sentence.Field(9); + if (checksum_in_sentence.StartsWith( + _T("*"))) // Field is a valid erroneous checksum + { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); + } + + else { + target_field_count = 9; + check = sentence.IsChecksumBad(10); + if (check == NTrue) { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); + } + } + } + if (sentence.GetNumberOfDataFields() != target_field_count) { + SetErrorMessage(_T("Invalid FieldCount")); + return (FALSE); + } - TrackDegreesTrue = sentence.Double( 1 ); - TrackDegreesMagnetic = sentence.Double( 3 ); - SpeedKnots = sentence.Double( 5 ); - SpeedKilometersPerHour = sentence.Double( 7 ); + TrackDegreesTrue = sentence.Double(1); + TrackDegreesMagnetic = sentence.Double(3); + SpeedKnots = sentence.Double(5); + SpeedKilometersPerHour = sentence.Double(7); - return( TRUE ); + return (TRUE); } -bool VTG::Write( SENTENCE& sentence ) -{ -// ASSERT_VALID( this ); +bool VTG::Write(SENTENCE& sentence) { + // ASSERT_VALID( this ); - /* - ** Let the parent do its thing - */ + /* + ** Let the parent do its thing + */ - RESPONSE::Write( sentence ); + RESPONSE::Write(sentence); - sentence += TrackDegreesTrue; - sentence += _T("T"); - sentence += TrackDegreesMagnetic; - sentence += _T("M"); - sentence += SpeedKnots; - sentence += _T("N"); - sentence += SpeedKilometersPerHour; - sentence += _T("K"); + sentence += TrackDegreesTrue; + sentence += _T("T"); + sentence += TrackDegreesMagnetic; + sentence += _T("M"); + sentence += SpeedKnots; + sentence += _T("N"); + sentence += SpeedKilometersPerHour; + sentence += _T("K"); - sentence.Finish(); + sentence.Finish(); - return( TRUE ); + return (TRUE); } -const VTG& VTG::operator = ( const VTG& source ) -{ -// ASSERT_VALID( this ); +const VTG& VTG::operator=(const VTG& source) { + // ASSERT_VALID( this ); - TrackDegreesTrue = source.TrackDegreesTrue; - TrackDegreesMagnetic = source.TrackDegreesMagnetic; - SpeedKnots = source.SpeedKnots; - SpeedKilometersPerHour = source.SpeedKilometersPerHour; + TrackDegreesTrue = source.TrackDegreesTrue; + TrackDegreesMagnetic = source.TrackDegreesMagnetic; + SpeedKnots = source.SpeedKnots; + SpeedKilometersPerHour = source.SpeedKilometersPerHour; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/vtg.hpp b/plugins/demo_pi_sample/src/nmea0183/vtg.hpp index eb3975d3cb..2b29f6e92d 100644 --- a/plugins/demo_pi_sample/src/nmea0183/vtg.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/vtg.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( VTG_CLASS_HEADER ) +#if !defined(VTG_CLASS_HEADER) #define VTG_CLASS_HEADER /* @@ -41,36 +40,33 @@ ** You can use it any way you like. */ -class VTG : public RESPONSE -{ - - public: - - VTG(); - ~VTG(); +class VTG : public RESPONSE { +public: + VTG(); + ~VTG(); - /* - ** Data - */ + /* + ** Data + */ - double TrackDegreesTrue; - double TrackDegreesMagnetic; - double SpeedKnots; - double SpeedKilometersPerHour; + double TrackDegreesTrue; + double TrackDegreesMagnetic; + double SpeedKnots; + double SpeedKilometersPerHour; - /* - ** Methods - */ + /* + ** Methods + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); - /* - ** Operators - */ + /* + ** Operators + */ - virtual const VTG& operator = ( const VTG& source ); + virtual const VTG& operator=(const VTG& source); }; -#endif // VTG_CLASS_HEADER +#endif // VTG_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/nmea0183/wpl.cpp b/plugins/demo_pi_sample/src/nmea0183/wpl.cpp index 702f8650de..2b3b233749 100644 --- a/plugins/demo_pi_sample/src/nmea0183/wpl.cpp +++ b/plugins/demo_pi_sample/src/nmea0183/wpl.cpp @@ -29,7 +29,6 @@ * "It is BSD license, do with it what you will" * */ - #include "nmea0183.h" /* @@ -40,80 +39,70 @@ ** You can use it any way you like. */ -//IMPLEMENT_DYNAMIC( WPL, RESPONSE ) +// IMPLEMENT_DYNAMIC( WPL, RESPONSE ) -WPL::WPL() -{ - Mnemonic = _T("WPL"); - Empty(); +WPL::WPL() { + Mnemonic = _T("WPL"); + Empty(); } -WPL::~WPL() -{ - Mnemonic.Empty(); - Empty(); +WPL::~WPL() { + Mnemonic.Empty(); + Empty(); } -void WPL::Empty( void ) -{ - - Position.Empty(); - To.Empty(); +void WPL::Empty(void) { + Position.Empty(); + To.Empty(); } -bool WPL::Parse( const SENTENCE& sentence ) -{ - - /* - ** WPL - Waypoint Location - ** - ** +-------------------------------- 1) Latitude - ** | +------------------------ 2) N or S (North or South) - ** | | +---------------------- 3) Longitude - ** | | | +------------- 4) E or W (East or West) - ** | | | | +----------- 5) Waypoint name - ** | | | | | +-------6) Checksum - ** | | | | | | - ** $--WPL,llll.ll,a,yyyyy.yy,a,c--c*hh - */ - - /* - ** First we check the checksum... - */ - - if ( sentence.IsChecksumBad( 6 ) == NTrue ) - { - SetErrorMessage( _T("Invalid Checksum") ); - return( FALSE ); - } - - Position.Parse( 1, 2, 3, 4, sentence ); - To = sentence.Field( 5 ); - - return( TRUE ); +bool WPL::Parse(const SENTENCE& sentence) { + /* + ** WPL - Waypoint Location + ** + ** +-------------------------------- 1) Latitude + ** | +------------------------ 2) N or S (North or South) + ** | | +---------------------- 3) Longitude + ** | | | +------------- 4) E or W (East or West) + ** | | | | +----------- 5) Waypoint name + ** | | | | | +-------6) Checksum + ** | | | | | | + ** $--WPL,llll.ll,a,yyyyy.yy,a,c--c*hh + */ + + /* + ** First we check the checksum... + */ + + if (sentence.IsChecksumBad(6) == NTrue) { + SetErrorMessage(_T("Invalid Checksum")); + return (FALSE); + } + + Position.Parse(1, 2, 3, 4, sentence); + To = sentence.Field(5); + + return (TRUE); } -bool WPL::Write( SENTENCE& sentence ) -{ - /* - ** Let the parent do its thing - */ +bool WPL::Write(SENTENCE& sentence) { + /* + ** Let the parent do its thing + */ - RESPONSE::Write( sentence ); + RESPONSE::Write(sentence); - sentence += Position; - sentence += To; + sentence += Position; + sentence += To; - sentence.Finish(); + sentence.Finish(); - return( TRUE ); + return (TRUE); } -const WPL& WPL::operator = ( const WPL& source ) -{ - - Position = source.Position; - To = source.To; +const WPL& WPL::operator=(const WPL& source) { + Position = source.Position; + To = source.To; - return( *this ); + return (*this); } diff --git a/plugins/demo_pi_sample/src/nmea0183/wpl.hpp b/plugins/demo_pi_sample/src/nmea0183/wpl.hpp index a455deb695..3fd444f34c 100644 --- a/plugins/demo_pi_sample/src/nmea0183/wpl.hpp +++ b/plugins/demo_pi_sample/src/nmea0183/wpl.hpp @@ -29,8 +29,7 @@ * "It is BSD license, do with it what you will" * */ - -#if ! defined( WPL_CLASS_HEADER ) +#if !defined(WPL_CLASS_HEADER) #define WPL_CLASS_HEADER /* @@ -41,34 +40,31 @@ ** You can use it any way you like. */ -class WPL : public RESPONSE -{ - - public: - - WPL(); - ~WPL(); +class WPL : public RESPONSE { +public: + WPL(); + ~WPL(); - /* - ** Data - */ + /* + ** Data + */ - LATLONG Position; - wxString To; + LATLONG Position; + wxString To; - /* - ** Methods - */ + /* + ** Methods + */ - virtual void Empty( void ); - virtual bool Parse( const SENTENCE& sentence ); - virtual bool Write( SENTENCE& sentence ); + virtual void Empty(void); + virtual bool Parse(const SENTENCE& sentence); + virtual bool Write(SENTENCE& sentence); - /* - ** Operators - */ + /* + ** Operators + */ - virtual const WPL& operator = ( const WPL& source ); + virtual const WPL& operator=(const WPL& source); }; -#endif // WPL_CLASS_HEADER +#endif // WPL_CLASS_HEADER diff --git a/plugins/demo_pi_sample/src/ocpn_plugin.h b/plugins/demo_pi_sample/src/ocpn_plugin.h new file mode 100644 index 0000000000..6c4a9d1b02 --- /dev/null +++ b/plugins/demo_pi_sample/src/ocpn_plugin.h @@ -0,0 +1,1860 @@ +/************************************************************************** + * Copyright (C) 2010 - 2024 by David S. Register * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +/** + * \file + * PlugIn Object Definition/API + */ + +#ifndef _PLUGIN_H_ +#define _PLUGIN_H_ + +#ifndef DECL_EXP +#if defined(__WXMSW__) || defined(__CYGWIN__) +#define DECL_EXP __declspec(dllexport) +#elif defined __GNUC__ && __GNUC__ >= 4 +#define DECL_EXP __attribute__((visibility("default"))) +#elif defined __WXOSX__ +#define DECL_EXP __attribute__((visibility("default"))) +#else +#define DECL_EXP +#endif +#endif + +#if defined(__WXMSW__) && defined(MAKING_PLUGIN) +#define DECL_IMP __declspec(dllimport) +#else +#define DECL_IMP +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef ocpnUSE_SVG +#include +#endif // ocpnUSE_SVG + +#include +#include +#include + +class wxGLContext; + +// This is the most modern API Version number +// It is expected that the API will remain downward compatible, meaning that +// PlugIns conforming to API Version less then the most modern will also +// be correctly supported. +#define API_VERSION_MAJOR 1 +#define API_VERSION_MINOR 18 + +// Fwd Definitions +class wxFileConfig; +class wxNotebook; +class wxFont; +class wxAuiManager; +class wxScrolledWindow; +class wxGLCanvas; + +//--------------------------------------------------------------------------------------------------------- +// +// Bitfield PlugIn Capabilites flag definition +// +//--------------------------------------------------------------------------------------------------------- +#define WANTS_OVERLAY_CALLBACK 0x00000001 +#define WANTS_CURSOR_LATLON 0x00000002 +#define WANTS_TOOLBAR_CALLBACK 0x00000004 +#define INSTALLS_TOOLBAR_TOOL 0x00000008 +#define WANTS_CONFIG 0x00000010 +#define INSTALLS_TOOLBOX_PAGE 0x00000020 +#define INSTALLS_CONTEXTMENU_ITEMS 0x00000040 +#define WANTS_NMEA_SENTENCES 0x00000080 +#define WANTS_NMEA_EVENTS 0x00000100 +#define WANTS_AIS_SENTENCES 0x00000200 +#define USES_AUI_MANAGER 0x00000400 +#define WANTS_PREFERENCES 0x00000800 +#define INSTALLS_PLUGIN_CHART 0x00001000 +#define WANTS_ONPAINT_VIEWPORT 0x00002000 +#define WANTS_PLUGIN_MESSAGING 0x00004000 +#define WANTS_OPENGL_OVERLAY_CALLBACK 0x00008000 +#define WANTS_DYNAMIC_OPENGL_OVERLAY_CALLBACK 0x00010000 +#define WANTS_LATE_INIT 0x00020000 +#define INSTALLS_PLUGIN_CHART_GL 0x00040000 +#define WANTS_MOUSE_EVENTS 0x00080000 +#define WANTS_VECTOR_CHART_OBJECT_INFO 0x00100000 +#define WANTS_KEYBOARD_EVENTS 0x00200000 + +//--------------------------------------------------------------------------------------------------------- +// +// Overlay priorities +// +//--------------------------------------------------------------------------------------------------------- +#define OVERLAY_LEGACY 0 +#define OVERLAY_OVER_SHIPS 64 +#define OVERLAY_OVER_EMBOSS 96 +#define OVERLAY_OVER_UI 128 + +//---------------------------------------------------------------------------------------------------------- +// Some PlugIn API interface object class definitions +//---------------------------------------------------------------------------------------------------------- +enum PI_ColorScheme { + PI_GLOBAL_COLOR_SCHEME_RGB, + PI_GLOBAL_COLOR_SCHEME_DAY, + PI_GLOBAL_COLOR_SCHEME_DUSK, + PI_GLOBAL_COLOR_SCHEME_NIGHT, + PI_N_COLOR_SCHEMES +}; + +class PlugIn_ViewPort { +public: + double clat; // center point + double clon; + double view_scale_ppm; + double skew; + double rotation; + + float chart_scale; // conventional chart displayed scale + + int pix_width; + int pix_height; + wxRect rv_rect; + bool b_quilt; + int m_projection_type; + + double lat_min, lat_max, lon_min, lon_max; + + bool bValid; // This VP is valid +}; + +class PlugIn_Position_Fix { +public: + double Lat; + double Lon; + double Cog; + double Sog; + double Var; // Variation, typically from RMC message + time_t FixTime; + int nSats; +}; + +class PlugIn_Position_Fix_Ex { +public: + double Lat; + double Lon; + double Cog; + double Sog; + double Var; // Variation, typically from RMC message + double Hdm; + double Hdt; + time_t FixTime; + int nSats; +}; + +class Plugin_Active_Leg_Info { +public: + double Xte; // Left side of the track -> negative XTE + double Btw; + double Dtw; + wxString wp_name; // Name of destination waypoint for active leg + bool arrival; // True when within arrival circle +}; + +// Describe AIS Alarm state +enum plugin_ais_alarm_type { + PI_AIS_NO_ALARM = 0, + PI_AIS_ALARM_SET, + PI_AIS_ALARM_ACKNOWLEDGED + +}; + +class PlugIn_AIS_Target { +public: + int MMSI; + int Class; + int NavStatus; + double SOG; + double COG; + double HDG; + double Lon; + double Lat; + int ROTAIS; + char CallSign[8]; // includes terminator + char ShipName[21]; + unsigned char ShipType; + int IMO; + + double Range_NM; + double Brg; + + // Per target collision parameters + bool bCPA_Valid; + double TCPA; // Minutes + double CPA; // Nautical Miles + + plugin_ais_alarm_type alarm_state; +}; + +// ChartType constants +typedef enum ChartTypeEnumPI { + PI_CHART_TYPE_UNKNOWN = 0, + PI_CHART_TYPE_DUMMY, + PI_CHART_TYPE_DONTCARE, + PI_CHART_TYPE_KAP, + PI_CHART_TYPE_GEO, + PI_CHART_TYPE_S57, + PI_CHART_TYPE_CM93, + PI_CHART_TYPE_CM93COMP, + PI_CHART_TYPE_PLUGIN +} _ChartTypeEnumPI; + +// ChartFamily constants +typedef enum ChartFamilyEnumPI { + PI_CHART_FAMILY_UNKNOWN = 0, + PI_CHART_FAMILY_RASTER, + PI_CHART_FAMILY_VECTOR, + PI_CHART_FAMILY_DONTCARE +} _ChartFamilyEnumPI; + +// Depth unit type enum +typedef enum ChartDepthUnitTypePI { + PI_DEPTH_UNIT_UNKNOWN, + PI_DEPTH_UNIT_FEET, + PI_DEPTH_UNIT_METERS, + PI_DEPTH_UNIT_FATHOMS +} _ChartDepthUnitTypePI; + +// Projection type enum +typedef enum OcpnProjTypePI { + PI_PROJECTION_UNKNOWN, + PI_PROJECTION_MERCATOR, + PI_PROJECTION_TRANSVERSE_MERCATOR, + PI_PROJECTION_POLYCONIC, + + PI_PROJECTION_ORTHOGRAPHIC, + PI_PROJECTION_POLAR, + PI_PROJECTION_STEREOGRAPHIC, + PI_PROJECTION_GNOMONIC, + PI_PROJECTION_EQUIRECTANGULAR +} _OcpnProjTypePI; + +typedef struct _ExtentPI { + double SLAT; + double WLON; + double NLAT; + double ELON; +} ExtentPI; + +// PlugInChartBase::Init() init_flags constants +#define PI_FULL_INIT 0 +#define PI_HEADER_ONLY 1 +#define PI_THUMB_ONLY 2 + +// ---------------------------------------------------------------------------- +// PlugInChartBase +// This class is the base class for Plug-able chart types +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBase : public wxObject { +public: + // These methods Must be overriden in any derived class + PlugInChartBase(); + virtual ~PlugInChartBase(); + + virtual wxString GetFileSearchMask(void); + + virtual int Init(const wxString &full_path, int init_flags); + virtual void SetColorScheme(int cs, bool bApplyImmediate); + + virtual double GetNormalScaleMin(double canvas_scale_factor, + bool b_allow_overzoom); + virtual double GetNormalScaleMax(double canvas_scale_factor, + int canvas_width); + virtual double GetNearestPreferredScalePPM(double target_scale_ppm); + + virtual bool GetChartExtent(ExtentPI *pext); + + virtual wxBitmap &RenderRegionView(const PlugIn_ViewPort &VPoint, + const wxRegion &Region); + + virtual bool AdjustVP(PlugIn_ViewPort &vp_last, PlugIn_ViewPort &vp_proposed); + + virtual void GetValidCanvasRegion(const PlugIn_ViewPort &VPoint, + wxRegion *pValidRegion); + + virtual int GetCOVREntries() { return 0; } + virtual int GetCOVRTablePoints(int iTable) { return 0; } + virtual int GetCOVRTablenPoints(int iTable) { return 0; } + virtual float *GetCOVRTableHead(int iTable) { return (float *)NULL; } + + virtual wxBitmap *GetThumbnail(int tnx, int tny, int cs); + + // Accessors, need not be overridden in derived class if the member + // variables are maintained + virtual wxString GetFullPath() const { return m_FullPath; } + virtual ChartTypeEnumPI GetChartType() { return m_ChartType; } + virtual ChartFamilyEnumPI GetChartFamily() { return m_ChartFamily; } + virtual OcpnProjTypePI GetChartProjection() { return m_projection; } + virtual wxString GetName() { return m_Name; } + virtual wxString GetDescription() { return m_Description; } + virtual wxString GetID() { return m_ID; } + virtual wxString GetSE() { return m_SE; } + virtual wxString GetDepthUnits() { return m_DepthUnits; } + virtual wxString GetSoundingsDatum() { return m_SoundingsDatum; } + virtual wxString GetDatumString() { return m_datum_str; } + virtual wxString GetExtraInfo() { return m_ExtraInfo; } + virtual wxString GetPubDate() { return m_PubYear; } + virtual double GetChartErrorFactor() { return m_Chart_Error_Factor; } + virtual ChartDepthUnitTypePI GetDepthUnitId() { return m_depth_unit_id; } + virtual bool IsReadyToRender() { return m_bReadyToRender; } + virtual int GetNativeScale() { return m_Chart_Scale; }; + virtual double GetChartSkew() { return m_Chart_Skew; } + virtual wxDateTime GetEditionDate(void) { return m_EdDate; } + + // Methods pertaining to CHART_FAMILY_RASTER type PlugIn charts only + virtual void ComputeSourceRectangle(const PlugIn_ViewPort &vp, + wxRect *pSourceRect); + virtual double GetRasterScaleFactor(); + virtual bool GetChartBits(wxRect &source, unsigned char *pPix, int sub_samp); + virtual int GetSize_X(); + virtual int GetSize_Y(); + virtual void latlong_to_chartpix(double lat, double lon, double &pixx, + double &pixy); + virtual void chartpix_to_latlong(double pixx, double pixy, double *plat, + double *plon); + +protected: + ChartTypeEnumPI m_ChartType; + ChartFamilyEnumPI m_ChartFamily; + + wxString m_FullPath; + OcpnProjTypePI m_projection; + int m_Chart_Scale; + double m_Chart_Skew; + + wxDateTime m_EdDate; + bool m_bReadyToRender; + + wxString m_Name; + wxString m_Description; + wxString m_ID; + wxString m_SE; + wxString m_SoundingsDatum; + wxString m_datum_str; + wxString m_PubYear; + wxString m_DepthUnits; + wxString m_ExtraInfo; + + ChartDepthUnitTypePI m_depth_unit_id; + + double m_Chart_Error_Factor; +}; + +// Declare an array of PlugIn_AIS_Targets +WX_DEFINE_ARRAY_PTR(PlugIn_AIS_Target *, ArrayOfPlugIn_AIS_Targets); + +//---------------------------------------------------------------------------------------------------------- +// The Generic PlugIn Interface Class Definition +// +// This is a virtual class. +// opencpn PlugIns must derive from this class. +// There are two types of methods in this class +// a. Required...must be overridden and implemented by PlugIns +// b. Optional..may be overridden by PlugIns + +// PlugIns must implement optional method overrides consistent with their +// declared capabilities flag as returned by Init(). +//---------------------------------------------------------------------------------------------------------- +class DECL_EXP opencpn_plugin { +public: + opencpn_plugin(void *pmgr) {} + virtual ~opencpn_plugin(); + + // Public API to the PlugIn class + + // This group of methods is required, and will be called by the opencpn + // host opencpn PlugIns must implement this group + virtual int Init(void); // Return the PlugIn Capabilites flag + virtual bool DeInit(void); + + virtual int GetAPIVersionMajor(); + virtual int GetAPIVersionMinor(); + virtual int GetPlugInVersionMajor(); + virtual int GetPlugInVersionMinor(); + virtual wxBitmap *GetPlugInBitmap(); + + // These three methods should produce valid, meaningful strings always + // ---EVEN IF--- the PlugIn has not (yet) been initialized. + // They are used by the PlugInManager GUI + virtual wxString GetCommonName(); + virtual wxString GetShortDescription(); + virtual wxString GetLongDescription(); + + // This group is optional. + // PlugIns may override any of these methods as required + + virtual void SetDefaults( + void); // This will be called upon enabling a PlugIn via the user Dialog + // It gives a chance to setup any default options and behavior + + virtual int GetToolbarToolCount(void); + + virtual int GetToolboxPanelCount(void); + virtual void SetupToolboxPanel(int page_sel, wxNotebook *pnotebook); + virtual void OnCloseToolboxPanel(int page_sel, int ok_apply_cancel); + + virtual void ShowPreferencesDialog(wxWindow *parent); + + virtual bool RenderOverlay(wxMemoryDC *pmdc, PlugIn_ViewPort *vp); + virtual void SetCursorLatLon(double lat, double lon); + virtual void SetCurrentViewPort(PlugIn_ViewPort &vp); + + virtual void SetPositionFix(PlugIn_Position_Fix &pfix); + virtual void SetNMEASentence(wxString &sentence); + virtual void SetAISSentence(wxString &sentence); + + virtual void ProcessParentResize(int x, int y); + virtual void SetColorScheme(PI_ColorScheme cs); + + virtual void OnToolbarToolCallback(int id); + virtual void OnContextMenuItemCallback(int id); + + virtual void UpdateAuiStatus(void); + + virtual wxArrayString GetDynamicChartClassNameArray(void); +}; + +// the types of the class factories used to create PlugIn instances +typedef opencpn_plugin *create_t(void *); +typedef void destroy_t(opencpn_plugin *); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverloaded-virtual" +#endif + +class DECL_EXP opencpn_plugin_16 : public opencpn_plugin { +public: + opencpn_plugin_16(void *pmgr); + virtual ~opencpn_plugin_16(); + + using opencpn_plugin::RenderOverlay; + + virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); + + virtual void SetPluginMessage(wxString &message_id, wxString &message_body); +}; + +class DECL_EXP opencpn_plugin_17 : public opencpn_plugin { +public: + opencpn_plugin_17(void *pmgr); + virtual ~opencpn_plugin_17(); + + using opencpn_plugin::RenderOverlay; + + virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); + virtual bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp); + + virtual void SetPluginMessage(wxString &message_id, wxString &message_body); +}; + +class DECL_EXP opencpn_plugin_18 : public opencpn_plugin { +public: + opencpn_plugin_18(void *pmgr); + virtual ~opencpn_plugin_18(); + + using opencpn_plugin::RenderOverlay; + + virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); + virtual bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp); + virtual void SetPluginMessage(wxString &message_id, wxString &message_body); + virtual void SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix); +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +class DECL_EXP opencpn_plugin_19 : public opencpn_plugin_18 { +public: + opencpn_plugin_19(void *pmgr); + virtual ~opencpn_plugin_19(); + + virtual void OnSetupOptions(void); +}; + +class DECL_EXP opencpn_plugin_110 : public opencpn_plugin_19 { +public: + opencpn_plugin_110(void *pmgr); + virtual ~opencpn_plugin_110(); + + virtual void LateInit(void); // If WANTS_LATE_INIT is returned by Init() +}; + +class DECL_EXP opencpn_plugin_111 : public opencpn_plugin_110 { +public: + opencpn_plugin_111(void *pmgr); + virtual ~opencpn_plugin_111(); +}; + +class DECL_EXP opencpn_plugin_112 : public opencpn_plugin_111 { +public: + opencpn_plugin_112(void *pmgr); + virtual ~opencpn_plugin_112(); + + virtual bool MouseEventHook(wxMouseEvent &event); + virtual void SendVectorChartObjectInfo(wxString &chart, wxString &feature, + wxString &objname, double lat, + double lon, double scale, + int nativescale); +}; + +class DECL_EXP opencpn_plugin_113 : public opencpn_plugin_112 { +public: + opencpn_plugin_113(void *pmgr); + virtual ~opencpn_plugin_113(); + + virtual bool KeyboardEventHook(wxKeyEvent &event); + virtual void OnToolbarToolDownCallback(int id); + virtual void OnToolbarToolUpCallback(int id); +}; + +class DECL_EXP opencpn_plugin_114 : public opencpn_plugin_113 { +public: + opencpn_plugin_114(void *pmgr); + virtual ~opencpn_plugin_114(); +}; + +class DECL_EXP opencpn_plugin_115 : public opencpn_plugin_114 { +public: + opencpn_plugin_115(void *pmgr); + virtual ~opencpn_plugin_115(); +}; + +class DECL_EXP opencpn_plugin_116 : public opencpn_plugin_115 { +public: + opencpn_plugin_116(void *pmgr); + virtual ~opencpn_plugin_116(); + virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, + PlugIn_ViewPort *vp, int canvasIndex); + virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, + int canvasIndex); + virtual void PrepareContextMenu(int canvasIndex); +}; + +class DECL_EXP opencpn_plugin_117 : public opencpn_plugin_116 { +public: + opencpn_plugin_117(void *pmgr); + /* + * Forms a semantic version together with GetPlugInVersionMajor() and + * GetPlugInVersionMinor(), see https://semver.org/ + */ + virtual int GetPlugInVersionPatch(); + + /** Post-release version part, extends the semver spec. */ + virtual int GetPlugInVersionPost(); + + /** Pre-release tag version part, see GetPlugInVersionPatch() */ + virtual const char *GetPlugInVersionPre(); + + /** Build version part see GetPlugInVersionPatch(). */ + virtual const char *GetPlugInVersionBuild(); + + /*Provide active leg data to plugins*/ + virtual void SetActiveLegInfo(Plugin_Active_Leg_Info &leg_info); +}; + +class DECL_EXP opencpn_plugin_118 : public opencpn_plugin_117 { +public: + opencpn_plugin_118(void *pmgr); + + using opencpn_plugin_116::RenderGLOverlayMultiCanvas; + using opencpn_plugin_116::RenderOverlayMultiCanvas; + + /// Render plugin overlay over chart canvas in OpenGL mode + /// + /// \param pcontext Pointer to the OpenGL context + /// \param vp Pointer to the Viewport + /// \param canvasIndex Index of the chart canvas, 0 for the first canvas + /// \param priority Priority, plugins only upgrading from older API versions + /// should draw only when priority is OVERLAY_LEGACY (0) + /// \return true if overlay was rendered, false otherwise +#ifdef _MSC_VER + virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, + PlugIn_ViewPort *vp, int canvasIndex, + int priority = -1); +#else + virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, + PlugIn_ViewPort *vp, int canvasIndex, + int priority); + + bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, + int canvas_ix) override { + return RenderGLOverlayMultiCanvas(pcontext, vp, canvas_ix, -1); + } +#endif + + /// Render plugin overlay over chart canvas in non-OpenGL mode + /// + /// \param dc Reference to the "device context" + /// \param vp Pointer to the Viewport + /// \param canvasIndex Index of the chart canvas, 0 for the first canvas + /// \param priority Priority, plugins only upgrading from older API versions + /// should draw only when priority is OVERLAY_LEGACY (0) + /// \return true if overlay was rendered, false otherwise +#ifdef _MSC_VER + virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, + int canvasIndex, int priority = -1); +#else + virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, + int canvas_ix, int priority); + bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, + int canvas_ix) override { + return RenderOverlayMultiCanvas(dc, vp, canvas_ix, -1); + } +#endif +}; +//------------------------------------------------------------------ +// Route and Waypoint PlugIn support +// +//------------------------------------------------------------------ + +class DECL_EXP Plugin_Hyperlink { +public: + wxString DescrText; + wxString Link; + wxString Type; +}; + +WX_DECLARE_LIST(Plugin_Hyperlink, Plugin_HyperlinkList); + +class DECL_EXP PlugIn_Waypoint { +public: + PlugIn_Waypoint(); + PlugIn_Waypoint(double lat, double lon, const wxString &icon_ident, + const wxString &wp_name, const wxString &GUID = _T("")); + ~PlugIn_Waypoint(); + + double m_lat; + double m_lon; + + wxString m_GUID; + + wxString m_MarkName; + wxString m_MarkDescription; + wxDateTime m_CreateTime; + bool m_IsVisible; + + wxString m_IconName; + + Plugin_HyperlinkList *m_HyperlinkList; +}; + +WX_DECLARE_LIST(PlugIn_Waypoint, Plugin_WaypointList); + +class DECL_EXP PlugIn_Route { +public: + PlugIn_Route(void); + ~PlugIn_Route(void); + + wxString m_NameString; + wxString m_StartString; + wxString m_EndString; + wxString m_GUID; + + Plugin_WaypointList *pWaypointList; +}; + +class DECL_EXP PlugIn_Track { +public: + PlugIn_Track(void); + ~PlugIn_Track(void); + + wxString m_NameString; + wxString m_StartString; + wxString m_EndString; + wxString m_GUID; + + Plugin_WaypointList *pWaypointList; +}; + +//---------------------------------------------------------------------------------------------------------- +// The PlugIn CallBack API Definition +// +// The API back up to the PlugIn Manager +// PlugIns may call these static functions as necessary for system services +// +//---------------------------------------------------------------------------------------------------------- + +extern "C" DECL_EXP int InsertPlugInTool(wxString label, wxBitmap *bitmap, + wxBitmap *bmpRollover, wxItemKind kind, + wxString shortHelp, wxString longHelp, + wxObject *clientData, int position, + int tool_sel, opencpn_plugin *pplugin); +extern "C" DECL_EXP void RemovePlugInTool(int tool_id); +extern "C" DECL_EXP void SetToolbarToolViz( + int item, bool viz); // Temporarily change toolbar tool viz +extern "C" DECL_EXP void SetToolbarItemState(int item, bool toggle); +extern "C" DECL_EXP void SetToolbarToolBitmaps(int item, wxBitmap *bitmap, + wxBitmap *bmpRollover); + +extern "C" DECL_EXP int InsertPlugInToolSVG( + wxString label, wxString SVGfile, wxString SVGfileRollover, + wxString SVGfileToggled, wxItemKind kind, wxString shortHelp, + wxString longHelp, wxObject *clientData, int position, int tool_sel, + opencpn_plugin *pplugin); +extern "C" DECL_EXP void SetToolbarToolBitmapsSVG(int item, wxString SVGfile, + wxString SVGfileRollover, + wxString SVGfileToggled); + +extern "C" DECL_EXP int AddCanvasContextMenuItem(wxMenuItem *pitem, + opencpn_plugin *pplugin); +extern "C" DECL_EXP void RemoveCanvasContextMenuItem( + int item); // Fully remove this item +extern "C" DECL_EXP void SetCanvasContextMenuItemViz( + int item, bool viz); // Temporarily change context menu ptions +extern "C" DECL_EXP void SetCanvasContextMenuItemGrey(int item, bool grey); + +extern "C" DECL_EXP wxFileConfig *GetOCPNConfigObject(void); + +extern "C" DECL_EXP void RequestRefresh(wxWindow *); +extern "C" DECL_EXP bool GetGlobalColor(wxString colorName, wxColour *pcolour); + +extern "C" DECL_EXP void GetCanvasPixLL(PlugIn_ViewPort *vp, wxPoint *pp, + double lat, double lon); +extern "C" DECL_EXP void GetCanvasLLPix(PlugIn_ViewPort *vp, wxPoint p, + double *plat, double *plon); + +extern "C" DECL_EXP wxWindow *GetOCPNCanvasWindow(); + +extern "C" DECL_EXP wxFont *OCPNGetFont(wxString TextElement, int default_size); + +extern "C" DECL_EXP wxString *GetpSharedDataLocation(); + +extern "C" DECL_EXP ArrayOfPlugIn_AIS_Targets *GetAISTargetArray(void); + +extern "C" DECL_EXP wxAuiManager *GetFrameAuiManager(void); + +extern "C" DECL_EXP bool AddLocaleCatalog(wxString catalog); + +extern "C" DECL_EXP void PushNMEABuffer(wxString str); + +extern DECL_EXP wxXmlDocument GetChartDatabaseEntryXML(int dbIndex, + bool b_getGeom); + +extern DECL_EXP bool UpdateChartDBInplace(wxArrayString dir_array, + bool b_force_update, + bool b_ProgressDialog); +extern DECL_EXP wxArrayString GetChartDBDirArrayString(); + +extern "C" DECL_EXP void SendPluginMessage(wxString message_id, + wxString message_body); + +extern "C" DECL_EXP void DimeWindow(wxWindow *); + +extern "C" DECL_EXP void JumpToPosition(double lat, double lon, double scale); + +/* API 1.9 adds some common cartographic functions to avoid unnecessary code + * duplication */ +/* Study the original OpenCPN source (georef.c) for functional definitions */ + +extern "C" DECL_EXP void PositionBearingDistanceMercator_Plugin( + double lat, double lon, double brg, double dist, double *dlat, + double *dlon); +extern "C" DECL_EXP void DistanceBearingMercator_Plugin( + double lat0, double lon0, double lat1, double lon1, double *brg, + double *dist); +extern "C" DECL_EXP double DistGreatCircle_Plugin(double slat, double slon, + double dlat, double dlon); + +extern "C" DECL_EXP void toTM_Plugin(float lat, float lon, float lat0, + float lon0, double *x, double *y); +extern "C" DECL_EXP void fromTM_Plugin(double x, double y, double lat0, + double lon0, double *lat, double *lon); +extern "C" DECL_EXP void toSM_Plugin(double lat, double lon, double lat0, + double lon0, double *x, double *y); +extern "C" DECL_EXP void fromSM_Plugin(double x, double y, double lat0, + double lon0, double *lat, double *lon); +extern "C" DECL_EXP void toSM_ECC_Plugin(double lat, double lon, double lat0, + double lon0, double *x, double *y); +extern "C" DECL_EXP void fromSM_ECC_Plugin(double x, double y, double lat0, + double lon0, double *lat, + double *lon); + +extern "C" DECL_EXP bool DecodeSingleVDOMessage(const wxString &str, + PlugIn_Position_Fix_Ex *pos, + wxString *acc); +extern "C" DECL_EXP int GetChartbarHeight(void); +extern "C" DECL_EXP bool GetActiveRoutepointGPX(char *buffer, + unsigned int buffer_length); + +/* API 1.9 */ +typedef enum OptionsParentPI { + PI_OPTIONS_PARENT_DISPLAY, + PI_OPTIONS_PARENT_CONNECTIONS, + PI_OPTIONS_PARENT_CHARTS, + PI_OPTIONS_PARENT_SHIPS, + PI_OPTIONS_PARENT_UI, + PI_OPTIONS_PARENT_PLUGINS +} _OptionsParentPI; +extern DECL_EXP wxScrolledWindow *AddOptionsPage(OptionsParentPI parent, + wxString title); +extern DECL_EXP bool DeleteOptionsPage(wxScrolledWindow *page); + +/* API 1.10 */ + +/* API 1.10 adds some common functions to avoid unnecessary code duplication */ +/* Study the original OpenCPN source for functional definitions */ +extern "C" DECL_EXP double toUsrDistance_Plugin(double nm_distance, + int unit = -1); +extern "C" DECL_EXP double fromUsrDistance_Plugin(double usr_distance, + int unit = -1); +extern "C" DECL_EXP double toUsrSpeed_Plugin(double kts_speed, int unit = -1); +extern "C" DECL_EXP double fromUsrSpeed_Plugin(double usr_speed, int unit = -1); +extern "C" DECL_EXP double toUsrTemp_Plugin(double cel_temp, int unit = -1); +extern "C" DECL_EXP double fromUsrTemp_Plugin(double usr_temp, int unit = -1); +extern DECL_EXP wxString getUsrDistanceUnit_Plugin(int unit = -1); +extern DECL_EXP wxString getUsrSpeedUnit_Plugin(int unit = -1); +extern DECL_EXP wxString getUsrTempUnit_Plugin(int unit = -1); +extern DECL_EXP wxString GetNewGUID(); +extern "C" DECL_EXP bool PlugIn_GSHHS_CrossesLand(double lat1, double lon1, + double lat2, double lon2); +/** + * Start playing a sound file asynchronously. Supported formats depends + * on sound backend. + */ +extern DECL_EXP void PlugInPlaySound(wxString &sound_file); + +// API 1.10 Route and Waypoint Support +extern DECL_EXP wxBitmap *FindSystemWaypointIcon(wxString &icon_name); +extern DECL_EXP bool AddCustomWaypointIcon(wxBitmap *pimage, wxString key, + wxString description); + +extern DECL_EXP bool AddSingleWaypoint(PlugIn_Waypoint *pwaypoint, + bool b_permanent = true); +extern DECL_EXP bool DeleteSingleWaypoint(wxString &GUID); +extern DECL_EXP bool UpdateSingleWaypoint(PlugIn_Waypoint *pwaypoint); + +extern DECL_EXP bool AddPlugInRoute(PlugIn_Route *proute, + bool b_permanent = true); +extern DECL_EXP bool DeletePlugInRoute(wxString &GUID); +extern DECL_EXP bool UpdatePlugInRoute(PlugIn_Route *proute); + +extern DECL_EXP bool AddPlugInTrack(PlugIn_Track *ptrack, + bool b_permanent = true); +extern DECL_EXP bool DeletePlugInTrack(wxString &GUID); +extern DECL_EXP bool UpdatePlugInTrack(PlugIn_Track *ptrack); + +/* API 1.11 */ + +/* API 1.11 adds some more common functions to avoid unnecessary code + * duplication */ +wxColour DECL_EXP GetBaseGlobalColor(wxString colorName); +int DECL_EXP OCPNMessageBox_PlugIn(wxWindow *parent, const wxString &message, + const wxString &caption = _T("Message"), + int style = wxOK, int x = -1, int y = -1); + +extern DECL_EXP wxString toSDMM_PlugIn(int NEflag, double a, + bool hi_precision = true); + +extern "C" DECL_EXP wxString *GetpPrivateApplicationDataLocation(); +extern DECL_EXP wxString GetOCPN_ExePath(void); +extern "C" DECL_EXP wxString *GetpPlugInLocation(); +extern DECL_EXP wxString GetPlugInPath(opencpn_plugin *pplugin); + +extern "C" DECL_EXP int AddChartToDBInPlace(wxString &full_path, + bool b_RefreshCanvas); +extern "C" DECL_EXP int RemoveChartFromDBInPlace(wxString &full_path); +extern DECL_EXP wxString GetLocaleCanonicalName(); + +// API 1.11 adds access to S52 Presentation library +// Types + +// A flag field that defines the object capabilities passed by a chart to +// the S52 PLIB + +#define PLIB_CAPS_LINE_VBO 1 +#define PLIB_CAPS_LINE_BUFFER 1 << 1 +#define PLIB_CAPS_SINGLEGEO_BUFFER 1 << 2 +#define PLIB_CAPS_OBJSEGLIST 1 << 3 +#define PLIB_CAPS_OBJCATMUTATE 1 << 4 + +class PI_S57Obj; + +WX_DECLARE_LIST(PI_S57Obj, ListOfPI_S57Obj); + +// ---------------------------------------------------------------------------- +// PlugInChartBaseGL +// Derived from PlugInChartBase, add OpenGL Vector chart support +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBaseGL : public PlugInChartBase { +public: + PlugInChartBaseGL(); + virtual ~PlugInChartBaseGL(); + + virtual int RenderRegionViewOnGL(const wxGLContext &glc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region, bool b_use_stencil); + + virtual ListOfPI_S57Obj *GetObjRuleListAtLatLon(float lat, float lon, + float select_radius, + PlugIn_ViewPort *VPoint); + virtual wxString CreateObjDescriptions(ListOfPI_S57Obj *obj_list); + + virtual int GetNoCOVREntries(); + virtual int GetNoCOVRTablePoints(int iTable); + virtual int GetNoCOVRTablenPoints(int iTable); + virtual float *GetNoCOVRTableHead(int iTable); +}; + +// ---------------------------------------------------------------------------- +// PlugInChartBaseGLPlus2 +// Derived from PlugInChartBaseGL, add additional chart management methods +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBaseGLPlus2 : public PlugInChartBaseGL { +public: + PlugInChartBaseGLPlus2(); + virtual ~PlugInChartBaseGLPlus2(); + + virtual ListOfPI_S57Obj *GetLightsObjRuleListVisibleAtLatLon( + float lat, float lon, PlugIn_ViewPort *VPoint); +}; + +// ---------------------------------------------------------------------------- +// PlugInChartBaseExtended +// Derived from PlugInChartBase, add extended chart support methods +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBaseExtended : public PlugInChartBase { +public: + PlugInChartBaseExtended(); + virtual ~PlugInChartBaseExtended(); + + virtual int RenderRegionViewOnGL(const wxGLContext &glc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region, bool b_use_stencil); + + virtual wxBitmap &RenderRegionViewOnDCNoText(const PlugIn_ViewPort &VPoint, + const wxRegion &Region); + virtual bool RenderRegionViewOnDCTextOnly(wxMemoryDC &dc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region); + + virtual int RenderRegionViewOnGLNoText(const wxGLContext &glc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region, + bool b_use_stencil); + + virtual int RenderRegionViewOnGLTextOnly(const wxGLContext &glc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region, + bool b_use_stencil); + + virtual ListOfPI_S57Obj *GetObjRuleListAtLatLon(float lat, float lon, + float select_radius, + PlugIn_ViewPort *VPoint); + virtual wxString CreateObjDescriptions(ListOfPI_S57Obj *obj_list); + + virtual int GetNoCOVREntries(); + virtual int GetNoCOVRTablePoints(int iTable); + virtual int GetNoCOVRTablenPoints(int iTable); + virtual float *GetNoCOVRTableHead(int iTable); + + virtual void ClearPLIBTextList(); +}; + +// ---------------------------------------------------------------------------- +// PlugInChartBaseExtendedPlus2 +// Derived from PlugInChartBaseExtended, add additional extended chart support +// methods +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBaseExtendedPlus2 : public PlugInChartBaseExtended { +public: + PlugInChartBaseExtendedPlus2(); + virtual ~PlugInChartBaseExtendedPlus2(); + + virtual ListOfPI_S57Obj *GetLightsObjRuleListVisibleAtLatLon( + float lat, float lon, PlugIn_ViewPort *VPoint); +}; + +class wxArrayOfS57attVal; + +// name of the addressed look up table set (fifth letter) +typedef enum _PI_LUPname { + PI_SIMPLIFIED = 'L', // points + PI_PAPER_CHART = 'R', // points + PI_LINES = 'S', // lines + PI_PLAIN_BOUNDARIES = 'N', // areas + PI_SYMBOLIZED_BOUNDARIES = 'O', // areas + PI_LUPNAME_NUM = 5 +} PI_LUPname; + +// display category type +typedef enum _PI_DisCat { + PI_DISPLAYBASE = 'D', // + PI_STANDARD = 'S', // + PI_OTHER = 'O', // O for OTHER + PI_MARINERS_STANDARD = 'M', // Mariner specified + PI_MARINERS_OTHER, // value not defined + PI_DISP_CAT_NUM, // value not defined +} PI_DisCat; + +// Display Priority +typedef enum _PI_DisPrio { + PI_PRIO_NODATA = '0', // no data fill area pattern + PI_PRIO_GROUP1 = '1', // S57 group 1 filled areas + PI_PRIO_AREA_1 = '2', // superimposed areas + PI_PRIO_AREA_2 = '3', // superimposed areas also water features + PI_PRIO_SYMB_POINT = '4', // point symbol also land features + PI_PRIO_SYMB_LINE = '5', // line symbol also restricted areas + PI_PRIO_SYMB_AREA = '6', // area symbol also traffic areas + PI_PRIO_ROUTEING = '7', // routeing lines + PI_PRIO_HAZARDS = '8', // hazards + PI_PRIO_MARINERS = '9', // VRM, EBL, own ship + PI_PRIO_NUM = 10 // number of priority levels + +} PI_DisPrio; + +typedef enum PI_InitReturn { + PI_INIT_OK = 0, + PI_INIT_FAIL_RETRY, // Init failed, retry suggested + PI_INIT_FAIL_REMOVE, // Init failed, suggest remove from further use + PI_INIT_FAIL_NOERROR // Init failed, request no explicit error message +} _PI_InitReturn; + +class PI_line_segment_element { +public: + size_t vbo_offset; + size_t n_points; + int priority; + float lat_max; // segment bounding box + float lat_min; + float lon_max; + float lon_min; + int type; + void *private0; + + PI_line_segment_element *next; +}; + +class DECL_EXP PI_S57Obj { +public: + // Public Methods + PI_S57Obj(); + +public: + // Instance Data + char FeatureName[8]; + int Primitive_type; + + char *att_array; + wxArrayOfS57attVal *attVal; + int n_attr; + + int iOBJL; + int Index; + + double x; // for POINT + double y; + double z; + int npt; // number of points as needed by arrays + void *geoPt; // for LINE & AREA not described by PolyTessGeo + double *geoPtz; // an array[3] for MultiPoint, SM with Z, i.e. depth + double *geoPtMulti; // an array[2] for MultiPoint, lat/lon to make bbox + // of decomposed points + + void *pPolyTessGeo; + + double m_lat; // The lat/lon of the object's "reference" point + double m_lon; + + double chart_ref_lat; + double chart_ref_lon; + + double lat_min; + double lat_max; + double lon_min; + double lon_max; + + int Scamin; // SCAMIN attribute decoded during load + + bool bIsClone; + int nRef; // Reference counter, to signal OK for deletion + + bool bIsAton; // This object is an aid-to-navigation + bool bIsAssociable; // This object is DRGARE or DEPARE + + int m_n_lsindex; + int *m_lsindex_array; + int m_n_edge_max_points; + void *m_chart_context; + + PI_DisCat m_DisplayCat; + + void *S52_Context; + PI_S57Obj *child; // child list, used only for MultiPoint Soundings + + PI_S57Obj *next; // List linkage + + // This transform converts from object geometry + // to SM coordinates. + double x_rate; // These auxiliary transform coefficients are + double y_rate; // to be used in GetPointPix() and friends + double x_origin; // on a per-object basis if necessary + double y_origin; + + int auxParm0; // some per-object auxiliary parameters, used for OpenGL + int auxParm1; + int auxParm2; + int auxParm3; + + PI_line_segment_element *m_ls_list; + bool m_bcategory_mutable; + int m_DPRI; +}; + +wxString DECL_EXP PI_GetPLIBColorScheme(); +int DECL_EXP PI_GetPLIBDepthUnitInt(); +int DECL_EXP PI_GetPLIBSymbolStyle(); +int DECL_EXP PI_GetPLIBBoundaryStyle(); +int DECL_EXP PI_GetPLIBStateHash(); +double DECL_EXP PI_GetPLIBMarinerSafetyContour(); +bool DECL_EXP PI_GetObjectRenderBox(PI_S57Obj *pObj, double *lat_min, + double *lat_max, double *lon_min, + double *lon_max); +void DECL_EXP PI_UpdateContext(PI_S57Obj *pObj); + +bool DECL_EXP PI_PLIBObjectRenderCheck(PI_S57Obj *pObj, PlugIn_ViewPort *vp); +PI_LUPname DECL_EXP PI_GetObjectLUPName(PI_S57Obj *pObj); +PI_DisPrio DECL_EXP PI_GetObjectDisplayPriority(PI_S57Obj *pObj); +PI_DisCat DECL_EXP PI_GetObjectDisplayCategory(PI_S57Obj *pObj); +void DECL_EXP PI_PLIBSetLineFeaturePriority(PI_S57Obj *pObj, int prio); +void DECL_EXP PI_PLIBPrepareForNewRender(void); +void DECL_EXP PI_PLIBFreeContext(void *pContext); +void DECL_EXP PI_PLIBSetRenderCaps(unsigned int flags); + +bool DECL_EXP PI_PLIBSetContext(PI_S57Obj *pObj); + +int DECL_EXP PI_PLIBRenderObjectToDC(wxDC *pdc, PI_S57Obj *pObj, + PlugIn_ViewPort *vp); +int DECL_EXP PI_PLIBRenderAreaToDC(wxDC *pdc, PI_S57Obj *pObj, + PlugIn_ViewPort *vp, wxRect rect, + unsigned char *pixbuf); + +int DECL_EXP PI_PLIBRenderAreaToGL(const wxGLContext &glcc, PI_S57Obj *pObj, + PlugIn_ViewPort *vp, wxRect &render_rect); + +int DECL_EXP PI_PLIBRenderObjectToGL(const wxGLContext &glcc, PI_S57Obj *pObj, + PlugIn_ViewPort *vp, wxRect &render_rect); + +/* API 1.11 OpenGL Display List and vertex buffer object routines + + Effectively these two routines cancel each other so all + of the translation, scaling and rotation can be done by opengl. + + Display lists need only be built infrequently, but used in each frame + greatly accelerates the speed of rendering. This avoids costly calculations, + and also allows the vertexes to be stored in graphics memory. + + static int dl = 0; + glPushMatrix(); + PlugInMultMatrixViewport(current_viewport); + if(dl) + glCallList(dl); + else { + dl = glGenLists(1); + PlugInViewPort norm_viewport = current_viewport; + NormalizeViewPort(norm_viewport); + glNewList(dl, GL_COMPILE_AND_EXECUTE); + ... // use norm_viewport with GetCanvasLLPix here + glEndList(); + } + glPopMatrix(); + ... // use current_viewport with GetCanvasLLPix again +*/ + +extern DECL_EXP bool PlugInHasNormalizedViewPort(PlugIn_ViewPort *vp); +extern DECL_EXP void PlugInMultMatrixViewport(PlugIn_ViewPort *vp, + float lat = 0, float lon = 0); +extern DECL_EXP void PlugInNormalizeViewport(PlugIn_ViewPort *vp, float lat = 0, + float lon = 0); + +class wxPoint2DDouble; +extern "C" DECL_EXP void GetDoubleCanvasPixLL(PlugIn_ViewPort *vp, + wxPoint2DDouble *pp, double lat, + double lon); + +/* API 1.13 */ +/* API 1.13 adds some more common functions to avoid unnecessary code + * duplication */ + +extern DECL_EXP double fromDMM_Plugin(wxString sdms); +extern DECL_EXP void SetCanvasRotation(double rotation); +extern DECL_EXP void SetCanvasProjection(int projection); +extern DECL_EXP bool GetSingleWaypoint(wxString GUID, + PlugIn_Waypoint *pwaypoint); +extern DECL_EXP bool CheckEdgePan_PlugIn(int x, int y, bool dragging, + int margin, int delta); +extern DECL_EXP wxBitmap GetIcon_PlugIn(const wxString &name); +extern DECL_EXP void SetCursor_PlugIn(wxCursor *pPlugin_Cursor = NULL); +extern DECL_EXP wxFont *GetOCPNScaledFont_PlugIn(wxString TextElement, + int default_size = 0); +extern DECL_EXP wxFont GetOCPNGUIScaledFont_PlugIn(wxString item); +extern DECL_EXP double GetOCPNGUIToolScaleFactor_PlugIn(int GUIScaledFactor); +extern DECL_EXP double GetOCPNGUIToolScaleFactor_PlugIn(); +extern DECL_EXP float GetOCPNChartScaleFactor_Plugin(); +extern DECL_EXP wxColour GetFontColour_PlugIn(wxString TextElement); + +extern DECL_EXP double GetCanvasTilt(); +extern DECL_EXP void SetCanvasTilt(double tilt); + +/** + * Start playing a sound file asynchronously. Supported formats depends + * on sound backend. The deviceIx is only used on platforms using the + * portaudio sound backend where -1 indicates the default device. + */ +extern DECL_EXP bool PlugInPlaySoundEx(wxString &sound_file, + int deviceIndex = -1); +extern DECL_EXP void AddChartDirectory(wxString &path); +extern DECL_EXP void ForceChartDBUpdate(); +extern DECL_EXP void ForceChartDBRebuild(); + +extern DECL_EXP wxString GetWritableDocumentsDir(void); +extern DECL_EXP wxDialog *GetActiveOptionsDialog(); +extern DECL_EXP wxArrayString GetWaypointGUIDArray(void); +extern DECL_EXP wxArrayString GetIconNameArray(void); + +extern DECL_EXP bool AddPersistentFontKey(wxString TextElement); +extern DECL_EXP wxString GetActiveStyleName(); + +extern DECL_EXP wxBitmap GetBitmapFromSVGFile(wxString filename, + unsigned int width, + unsigned int height); +extern DECL_EXP bool IsTouchInterface_PlugIn(void); + +/* Platform optimized File/Dir selector dialogs */ +extern DECL_EXP int PlatformDirSelectorDialog(wxWindow *parent, + wxString *file_spec, + wxString Title, wxString initDir); + +extern DECL_EXP int PlatformFileSelectorDialog(wxWindow *parent, + wxString *file_spec, + wxString Title, wxString initDir, + wxString suggestedName, + wxString wildcard); + +/* OpenCPN HTTP File Download PlugIn Interface */ + +/* Various method Return Codes, etc */ +typedef enum _OCPN_DLStatus { + OCPN_DL_UNKNOWN = -1, + OCPN_DL_NO_ERROR = 0, + OCPN_DL_FAILED = 1, + OCPN_DL_ABORTED = 2, + OCPN_DL_USER_TIMEOUT = 4, + OCPN_DL_STARTED = 8 +} OCPN_DLStatus; + +typedef enum _OCPN_DLCondition { + OCPN_DL_EVENT_TYPE_UNKNOWN = -1, + OCPN_DL_EVENT_TYPE_START = 80, + OCPN_DL_EVENT_TYPE_PROGRESS = 81, + OCPN_DL_EVENT_TYPE_END = 82 +} OCPN_DLCondition; + +// Style definitions for Synchronous file download modal dialogs, if +// desired. Abstracted from wxCURL package +enum OCPN_DLDialogStyle { + OCPN_DLDS_ELAPSED_TIME = 0x0001, //!< The dialog shows the elapsed time. + OCPN_DLDS_ESTIMATED_TIME = + 0x0002, //!< The dialog shows the estimated total time. + OCPN_DLDS_REMAINING_TIME = 0x0004, //!< The dialog shows the remaining time. + OCPN_DLDS_SPEED = 0x0008, //!< The dialog shows the transfer speed. + OCPN_DLDS_SIZE = 0x0010, //!< The dialog shows the size of the resource to + //!< download/upload. + OCPN_DLDS_URL = + 0x0020, //!< The dialog shows the URL involved in the transfer. + + // styles related to the use of wxCurlConnectionSettingsDialog: + + OCPN_DLDS_CONN_SETTINGS_AUTH = + 0x0040, //!< The dialog allows the user to change the authentication + //!< settings. + OCPN_DLDS_CONN_SETTINGS_PORT = 0x0080, //!< The dialog allows the user to + //!< change the port for the transfer. + OCPN_DLDS_CONN_SETTINGS_PROXY = + 0x0100, //!< The dialog allows the user to change the proxy settings. + + OCPN_DLDS_CONN_SETTINGS_ALL = OCPN_DLDS_CONN_SETTINGS_AUTH | + OCPN_DLDS_CONN_SETTINGS_PORT | + OCPN_DLDS_CONN_SETTINGS_PROXY, + + OCPN_DLDS_SHOW_ALL = OCPN_DLDS_ELAPSED_TIME | OCPN_DLDS_ESTIMATED_TIME | + OCPN_DLDS_REMAINING_TIME | OCPN_DLDS_SPEED | + OCPN_DLDS_SIZE | OCPN_DLDS_URL | + OCPN_DLDS_CONN_SETTINGS_ALL, + + OCPN_DLDS_CAN_ABORT = 0x0200, //!< The transfer can be aborted by the user. + OCPN_DLDS_CAN_START = 0x0400, //!< The transfer won't start automatically. + //!< The user needs to start it. + OCPN_DLDS_CAN_PAUSE = 0x0800, //!< The transfer can be paused. + + OCPN_DLDS_AUTO_CLOSE = + 0x1000, //!< The dialog auto closes when transfer is complete. + + // by default all available features are enabled: + OCPN_DLDS_DEFAULT_STYLE = OCPN_DLDS_CAN_START | OCPN_DLDS_CAN_PAUSE | + OCPN_DLDS_CAN_ABORT | OCPN_DLDS_SHOW_ALL | + OCPN_DLDS_AUTO_CLOSE +}; + +#define ONLINE_CHECK_RETRY \ + 30 // Recheck the Internet connection availability every ONLINE_CHECK_RETRY s + +/* Synchronous (Blocking) download of a single file */ + +extern DECL_EXP _OCPN_DLStatus OCPN_downloadFile( + const wxString &url, const wxString &outputFile, const wxString &title, + const wxString &message, const wxBitmap &bitmap, wxWindow *parent, + long style, int timeout_secs); + +/* Asynchronous (Background) download of a single file */ + +extern DECL_EXP _OCPN_DLStatus +OCPN_downloadFileBackground(const wxString &url, const wxString &outputFile, + wxEvtHandler *handler, long *handle); + +extern DECL_EXP void OCPN_cancelDownloadFileBackground(long handle); + +/* Synchronous (Blocking) HTTP POST operation for small amounts of data */ + +extern DECL_EXP _OCPN_DLStatus OCPN_postDataHttp(const wxString &url, + const wxString ¶meters, + wxString &result, + int timeout_secs); + +/* Check whether connection to the Internet is working */ + +extern DECL_EXP bool OCPN_isOnline(); + +/* Supporting Event for Background downloading */ +/* OCPN_downloadEvent Definition */ + +/* PlugIn should be ready/able to handle this event after initiating a + * background file transfer + * + * The event as received should be parsed primarily by the getDLEventCondition() + * method. This will allow identification of download start, progress, and end + * states. + * + * Other accessor methods contain status, byte counts, etc. + * + * A PlugIn may safely destroy its EvtHandler after receipt of an + * OCPN_downloadEvent with getDLEventCondition == OCPN_DL_EVENT_TYPE_END + */ + +class DECL_EXP OCPN_downloadEvent : public wxEvent { +public: + OCPN_downloadEvent(wxEventType commandType = wxEVT_NULL, int id = 0); + ~OCPN_downloadEvent(); + + // accessors + _OCPN_DLStatus getDLEventStatus() { return m_stat; } + OCPN_DLCondition getDLEventCondition() { return m_condition; } + + void setDLEventStatus(_OCPN_DLStatus stat) { m_stat = stat; } + void setDLEventCondition(OCPN_DLCondition cond) { m_condition = cond; } + + void setTotal(long bytes) { m_totalBytes = bytes; } + void setTransferred(long bytes) { m_sofarBytes = bytes; } + long getTotal() { return m_totalBytes; } + long getTransferred() { return m_sofarBytes; } + + void setComplete(bool b_complete) { m_b_complete = b_complete; } + bool getComplete() { return m_b_complete; } + + // required for sending with wxPostEvent() + wxEvent *Clone() const; + +private: + OCPN_DLStatus m_stat; + OCPN_DLCondition m_condition; + + long m_totalBytes; + long m_sofarBytes; + bool m_b_complete; +}; + +// extern WXDLLIMPEXP_CORE const wxEventType wxEVT_DOWNLOAD_EVENT; + +#ifdef MAKING_PLUGIN +extern DECL_IMP wxEventType wxEVT_DOWNLOAD_EVENT; +#else +extern DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT; +#endif + +/* API 1.14 */ +/* API 1.14 adds some more common functions to avoid unnecessary code + * duplication */ + +bool LaunchDefaultBrowser_Plugin(wxString url); + +// API 1.14 Extra canvas Support + +/* Allow drawing of objects onto other OpenGL canvases */ +extern DECL_EXP void PlugInAISDrawGL(wxGLCanvas *glcanvas, + const PlugIn_ViewPort &vp); +extern DECL_EXP bool PlugInSetFontColor(const wxString TextElement, + const wxColour color); + +// API 1.15 +extern DECL_EXP double PlugInGetDisplaySizeMM(); + +// +extern DECL_EXP wxFont *FindOrCreateFont_PlugIn( + int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, + bool underline = false, const wxString &facename = wxEmptyString, + wxFontEncoding encoding = wxFONTENCODING_DEFAULT); + +extern DECL_EXP int PlugInGetMinAvailableGshhgQuality(); +extern DECL_EXP int PlugInGetMaxAvailableGshhgQuality(); + +extern DECL_EXP void PlugInHandleAutopilotRoute(bool enable); + +// API 1.16 +// +/** + * Return the plugin data directory for a given directory name. + * + * On Linux, the returned data path is an existing directory ending in + * "opencpn/plugins/" where the last part is the plugin_name + * argument. The prefix part is one of the directories listed in the + * environment variable XDG_DATA_DIRS, by default + * ~/.local/share:/usr/local/share:/usr/share. + * + * On other platforms, the returned value is GetSharedDataDir() + + * "/opencpn/plugins/" + plugin_name (with native path separators) + * if that path exists. + * + * Return "" if no existing directory is found. + */ +extern DECL_EXP wxString GetPluginDataDir(const char *plugin_name); + +extern DECL_EXP bool ShuttingDown(void); + +// Support for MUI MultiCanvas model + +extern DECL_EXP wxWindow *PluginGetFocusCanvas(); +extern DECL_EXP wxWindow *PluginGetOverlayRenderCanvas(); + +extern "C" DECL_EXP void CanvasJumpToPosition(wxWindow *canvas, double lat, + double lon, double scale); +extern "C" DECL_EXP int AddCanvasMenuItem(wxMenuItem *pitem, + opencpn_plugin *pplugin, + const char *name = ""); +extern "C" DECL_EXP void RemoveCanvasMenuItem( + int item, const char *name = ""); // Fully remove this item +extern "C" DECL_EXP void SetCanvasMenuItemViz( + int item, bool viz, + const char *name = ""); // Temporarily change context menu options +extern "C" DECL_EXP void SetCanvasMenuItemGrey(int item, bool grey, + const char *name = ""); + +// Extract waypoints, routes and tracks +extern DECL_EXP wxString GetSelectedWaypointGUID_Plugin(); +extern DECL_EXP wxString GetSelectedRouteGUID_Plugin(); +extern DECL_EXP wxString GetSelectedTrackGUID_Plugin(); + +extern DECL_EXP std::unique_ptr GetWaypoint_Plugin( + const wxString &); // doublon with GetSingleWaypoint +extern DECL_EXP std::unique_ptr GetRoute_Plugin(const wxString &); +extern DECL_EXP std::unique_ptr GetTrack_Plugin(const wxString &); + +extern DECL_EXP wxWindow *GetCanvasUnderMouse(); +extern DECL_EXP int GetCanvasIndexUnderMouse(); +// extern DECL_EXP std::vector GetCanvasArray(); +extern DECL_EXP wxWindow *GetCanvasByIndex(int canvasIndex); +extern DECL_EXP int GetCanvasCount(); +extern DECL_EXP bool CheckMUIEdgePan_PlugIn(int x, int y, bool dragging, + int margin, int delta, + int canvasIndex); +extern DECL_EXP void SetMUICursor_PlugIn(wxCursor *pCursor, int canvasIndex); + +// API 1.17 +// +extern DECL_EXP wxRect GetMasterToolbarRect(); + +enum SDDMFORMAT { + DEGREES_DECIMAL_MINUTES = 0, + DECIMAL_DEGREES, + DEGREES_MINUTES_SECONDS, + END_SDDMFORMATS +}; + +extern DECL_EXP int GetLatLonFormat(void); + +// API 1.17 +extern "C" DECL_EXP void ZeroXTE(); + +// Extended Waypoint manipulation API +class DECL_EXP PlugIn_Waypoint_Ex { +public: + PlugIn_Waypoint_Ex(); + PlugIn_Waypoint_Ex(double lat, double lon, const wxString &icon_ident, + const wxString &wp_name, const wxString &GUID = "", + const double ScaMin = 1e9, const bool bNameVisible = false, + const int nRanges = 0, const double RangeDistance = 1.0, + const wxColor RangeColor = wxColor(255, 0, 0)); + ~PlugIn_Waypoint_Ex(); + void InitDefaults(); + + bool GetFSStatus(); // return "free standing" status + // To be a "free standing waypoint"(FSWP), + // the RoutePoint will have been created by GUI dropping + // a point; by importing a waypoint in a GPX file or by + // the AddSingleWaypoint API. + + int GetRouteMembershipCount(); // Return the number of routes to which this + // WP belongs + + double m_lat; + double m_lon; + + wxString m_GUID; + + wxString m_MarkName; + wxString m_MarkDescription; + wxDateTime m_CreateTime; + bool IsVisible; + bool IsActive; + + double scamin; + bool b_useScamin; + bool IsNameVisible; + int nrange_rings; + double RangeRingSpace; + wxColour RangeRingColor; + + wxString IconName; + wxString IconDescription; + + Plugin_HyperlinkList *m_HyperlinkList; +}; + +WX_DECLARE_LIST(PlugIn_Waypoint_Ex, Plugin_WaypointExList); + +class DECL_EXP PlugIn_Route_Ex { +public: + PlugIn_Route_Ex(void); + ~PlugIn_Route_Ex(void); + + wxString m_NameString; + wxString m_StartString; + wxString m_EndString; + wxString m_GUID; + bool m_isActive; + bool m_isVisible; + wxString m_Description; + + Plugin_WaypointExList *pWaypointList; +}; + +extern DECL_EXP wxArrayString GetRouteGUIDArray(void); +extern DECL_EXP wxArrayString GetTrackGUIDArray(void); + +extern DECL_EXP bool GetSingleWaypointEx(wxString GUID, + PlugIn_Waypoint_Ex *pwaypoint); + +extern DECL_EXP bool AddSingleWaypointEx(PlugIn_Waypoint_Ex *pwaypoint, + bool b_permanent = true); +extern DECL_EXP bool UpdateSingleWaypointEx(PlugIn_Waypoint_Ex *pwaypoint); + +extern DECL_EXP bool AddPlugInRouteEx(PlugIn_Route_Ex *proute, + bool b_permanent = true); +extern DECL_EXP bool UpdatePlugInRouteEx(PlugIn_Route_Ex *proute); + +extern DECL_EXP std::unique_ptr GetWaypointEx_Plugin( + const wxString &); +extern DECL_EXP std::unique_ptr GetRouteEx_Plugin( + const wxString &); + +extern DECL_EXP wxString +GetActiveWaypointGUID(void); // if no active waypoint, returns wxEmptyString +extern DECL_EXP wxString +GetActiveRouteGUID(void); // if no active route, returns wxEmptyString + +// API 1.18 + +// Scaled display support, as on some GTK3 and Mac Retina devices +extern DECL_EXP double OCPN_GetDisplayContentScaleFactor(); + +// Scaled display support, on Windows devices +extern DECL_EXP double OCPN_GetWinDIPScaleFactor(); + +// Comm Priority query support +extern DECL_EXP std::vector GetPriorityMaps(); +extern DECL_EXP std::vector GetActivePriorityIdentifiers(); + +extern DECL_EXP int GetGlobalWatchdogTimoutSeconds(); + +typedef enum _OBJECT_LAYER_REQ { + OBJECTS_ALL = 0, + OBJECTS_NO_LAYERS, + OBJECTS_ONLY_LAYERS +} OBJECT_LAYER_REQ; + +// FIXME (dave) Implement these +extern DECL_EXP wxArrayString GetRouteGUIDArray(OBJECT_LAYER_REQ req); +extern DECL_EXP wxArrayString GetTrackGUIDArray(OBJECT_LAYER_REQ req); +extern DECL_EXP wxArrayString GetWaypointGUIDArray(OBJECT_LAYER_REQ req); + +/** listen-notify interface */ + +/* Listening to messages. */ +class ObservableListener; + +/** The event used by notify/listen. */ +class ObservedEvt; + +// This is a verbatim copy from observable_evt.h, don't define twice. +#ifndef OBSERVABLE_EVT_H +#define OBSERVABLE_EVT_H + +wxDECLARE_EVENT(obsNOTIFY, ObservedEvt); + +/** Adds a std::shared element to wxCommandEvent. */ +class ObservedEvt : public wxCommandEvent { +public: + ObservedEvt(wxEventType commandType = obsNOTIFY, int id = 0) + : wxCommandEvent(commandType, id) {} + ObservedEvt(const ObservedEvt &event) : wxCommandEvent(event) { + this->m_shared_ptr = event.m_shared_ptr; + } + + wxEvent *Clone() const { return new ObservedEvt(*this); } + + std::shared_ptr GetSharedPtr() const { return m_shared_ptr; } + + void SetSharedPtr(std::shared_ptr p) { m_shared_ptr = p; } + +private: + std::shared_ptr m_shared_ptr; +}; + +#endif // OBSERVABLE_EVT_H + +class ObservableListener; + +/** Facade for NavAddr2000. */ +struct NMEA2000Id { + const uint64_t id; + NMEA2000Id(int value) : id(static_cast(value)) {}; +}; + +extern DECL_EXP std::shared_ptr GetListener( + NMEA2000Id id, wxEventType ev, wxEvtHandler *handler); + +/** Facade for NavAddr0183. */ +struct NMEA0183Id { + const std::string id; + NMEA0183Id(const std::string &s) : id(s) {}; +}; + +extern DECL_EXP std::shared_ptr GetListener( + NMEA0183Id id, wxEventType ev, wxEvtHandler *handler); + +/** Facade for NavAddrSignalK. */ +struct SignalkId { + const std::string id; + SignalkId(const std::string &s) : id(s) {}; +}; + +extern DECL_EXP std::shared_ptr GetListener( + SignalkId id, wxEventType ev, wxEvtHandler *handler); + +/** + Return N2K payload for a received n2000 message of type id in ev. + The vector returned is described in the following example + + [147,19, // Header bytes, unused + 3, // N2K priority + 16,240,1, // example pgn 126992 encoded little endian + 255, // N2K destination address + 1, // N2K origin address + 255,255,255,255, // timestamp, unused + 8, // count of following NMEA2000 data + 13,240,207,76,208,3,94,40, // NMEA2000 data + 85 // CRC byte, unused,not included in count + ]; +*/ +extern DECL_EXP std::vector GetN2000Payload(NMEA2000Id id, + ObservedEvt ev); + +/** + * Get SignalK status payload after receiving a message. + * @return pointer to a wxJSONValue map object. Typical usage: + * + * auto ptr = GetSignalkPayload(ev); + * const auto msg = *std::static_pointer_cast(payload); + * + * The map contains the following entries: + * - "Data": the parsed json message + * - "ErrorCount": int, the number of parsing errors + * - "WarningCount": int, the number of parsing warnings + * - "Errors": list of strings, error messages. + * - "Warnings": list of strings, warning messages.. + * - "Context": string, message context + * - "ContextSelf": string, own ship context. + */ +std::shared_ptr GetSignalkPayload(ObservedEvt ev); + +/** + * Return source identifier (iface) of a received n2000 message of type id + * in ev. + */ +extern DECL_EXP std::string GetN2000Source(NMEA2000Id id, ObservedEvt ev); + +/** Return payload in a received n0183 message of type id in ev. */ +extern DECL_EXP std::string GetN0183Payload(NMEA0183Id id, ObservedEvt ev); + +/** Facade for BasicNavDataMsg. */ +struct NavDataId { + const int type; + NavDataId() : type(0) {} +}; + +extern DECL_EXP std::shared_ptr GetListener( + NavDataId id, wxEventType ev, wxEvtHandler *handler); + +/** Available decoded data for plugins. */ +struct PluginNavdata { + double lat; + double lon; + double sog; + double cog; + double var; + double hdt; + time_t time; +}; + +/** Return BasicNavDataMsg decoded data available in ev */ +extern DECL_EXP PluginNavdata GetEventNavdata(ObservedEvt ev); + +/** Plugin API supporting direct access to comm drivers for output purposes */ +/* + * Plugins may access comm ports for direct output. + * The general program flow for a plugin may look something like this + * pseudo-code: + * 1. Plugin will query OCPN core for a list of active comm drivers. + * 2. Plugin will inspect the list, and query OCPN core for driver + * attributes. + * 3. Plugin will select a comm driver with appropriate attributes for output. + * 4. Plugin will register a list of PGNs expected to be transmitted + * (N2K specific) + * 5. Plugin may then send a payload buffer to a specific comm driver for + * output as soon as possible. + * + * The mechanism for specifying a particular comm driver uses the notion of + * "handles". Each active comm driver has an associated opaque handle, managed + * by OCPN core. All references by a plugin to a driver are by means of its + * handle. Handles should be considered to be "opaque", meaning that the exact + * contents of the handle are of no specific value to the plugin, and only + * have meaning to the OCPN core management of drivers. + */ + +/** Definition of OCPN DriverHandle */ +typedef std::string DriverHandle; + +/** Error return values */ + +typedef enum CommDriverResult { + RESULT_COMM_NO_ERROR = 0, + RESULT_COMM_INVALID_HANDLE, + RESULT_COMM_INVALID_PARMS, + RESULT_COMM_TX_ERROR, + RESULT_COMM_REGISTER_GATEWAY_ERROR, + RESULT_COMM_REGISTER_PGN_ERROR +} _CommDriverResult; + +/** Query OCPN core for a list of active drivers */ +extern DECL_EXP std::vector GetActiveDrivers(); + +/** Query a specific driver for attributes */ +/* Driver attributes are available from OCPN core as a hash map of + * tag->attribute pairs. There is a defined set of common tags guaranteed + * for every driver. Both tags and attributes are defined as std::string. + * Here is the list of common tag-attribute pairs. + * + * Tag Attribute definition + * ---------- -------------------- + * "protocol" Comm bus device protocol, such as "NMEA0183", "NMEA2000" + * + * + */ + +/** Query driver attributes */ +extern DECL_EXP const std::unordered_map +GetAttributes(DriverHandle handle); + +/* Writing to a specific driver */ + +/* Comm drivers on bus protocols other than NMEA2000 may write directly to the + * port * using a simple call. The physical write operation will be queued, + * and executed in order as bandwidth allows. + * Return value is number of bytes queued for transmission. + */ +extern DECL_EXP CommDriverResult WriteCommDriver( + DriverHandle handle, const std::shared_ptr> &payload); + +/** Send a PGN message to an NMEA2000 address. */ +extern DECL_EXP CommDriverResult WriteCommDriverN2K( + DriverHandle handle, int PGN, int destinationCANAddress, int priority, + const std::shared_ptr> &payload); + +/** + * Special NMEA2000 requirements + * NMEA2000 bus protocol device management requires that devices writing on + * the bus must inform all bus listeners of the specific PGNs that may be + * transmitted by this device. Once configured, this bus management process + * will be handled transparently by the OCPN core drivers. It is only + * necessary for plugins wishing to write to the NMEA2000 bus to register the + * specific PGNs that they anticipate using, with the selected driver. + */ +extern DECL_EXP CommDriverResult RegisterTXPGNs(DriverHandle handle, + std::vector &pgn_list); + +// API 1.19 +// + +/** Facade for NavAddrPluginMsg. */ +struct PluginMsgId { + const std::string id; + PluginMsgId(const std::string &s) : id(s) {}; +}; + +extern DECL_EXP std::shared_ptr GetListener( + PluginMsgId id, wxEventType ev, wxEvtHandler *handler); + +extern DECL_EXP std::string GetPluginMsgPayload(PluginMsgId id, ObservedEvt ev); +#endif //_PLUGIN_H_ diff --git a/plugins/demo_pi_sample/src/version.h b/plugins/demo_pi_sample/src/version.h new file mode 100644 index 0000000000..85392cacc5 --- /dev/null +++ b/plugins/demo_pi_sample/src/version.h @@ -0,0 +1,10 @@ +// +// A production project should generate this file in CMakeLists.txt. +// However, this is a demo and we keep things simple + +#define PLUGIN_VERSION_MAJOR 0 +#define PLUGIN_VERSION_MINOR 1 +#define PLUGIN_VERSION_PATCH 0 +#define PLUGIN_VERSION_TWEAK 1 +#define PKG_PRERELEASE "alpha" +#define PKG_BUILD_INFO "1.deadbeef" diff --git a/plugins/wmm_pi/CMakeLists.txt b/plugins/wmm_pi/CMakeLists.txt index e13b87759c..ebfdfb3169 100644 --- a/plugins/wmm_pi/CMakeLists.txt +++ b/plugins/wmm_pi/CMakeLists.txt @@ -28,7 +28,6 @@ if (NOT CMAKE_BUILD_TYPE) ) endif (NOT CMAKE_BUILD_TYPE) -include_directories(${CMAKE_SOURCE_DIR}/include) if (APPLE) include_directories(${CMAKE_SOURCE_DIR}/buildosx/include) @@ -78,6 +77,9 @@ set(SRC_JSON add_library(${PACKAGE_NAME} SHARED ${SRC_WMM} ${SRC_JSON}) +target_include_directories( + ${PACKAGE_NAME} BEFORE PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include +) target_link_libraries(${PACKAGE_NAME} PUBLIC ocpn::opencpn) if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|AppleClang") diff --git a/plugins/wmm_pi/src/wmm_pi.cpp b/plugins/wmm_pi/src/wmm_pi.cpp index 732e3779c0..13248d9e61 100644 --- a/plugins/wmm_pi/src/wmm_pi.cpp +++ b/plugins/wmm_pi/src/wmm_pi.cpp @@ -41,6 +41,8 @@ #include "qdebug.h" #endif +#include "version.h" + float g_piGLMinSymbolLineWidth = 0.9; void WMMLogMessage1(wxString s) { wxLogMessage(_T("WMM: ") + s); } @@ -413,16 +415,16 @@ bool wmm_pi::RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp) { if (!m_bShowPlot) return true; if (!m_oDC) { - #ifdef ocpnUSE_GL - // Set the minimum line width - GLint parms[2]; - #ifndef USE_ANDROID_GLES2 - glGetIntegerv(GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0]); - #else - glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]); - #endif - g_piGLMinSymbolLineWidth = wxMax(parms[0], 1); - #endif +#ifdef ocpnUSE_GL + // Set the minimum line width + GLint parms[2]; +#ifndef USE_ANDROID_GLES2 + glGetIntegerv(GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0]); +#else + glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]); +#endif + g_piGLMinSymbolLineWidth = wxMax(parms[0], 1); +#endif m_oDC = new pi_ocpnDC(); } @@ -559,8 +561,8 @@ void wmm_pi::SetPositionFix(PlugIn_Position_Fix &pfix) { scale = wxRound(scale * 4.0) / 4.0; scale *= OCPN_GetWinDIPScaleFactor(); - //scale = - // wxMax(1.0, scale); // Let the upstream processing handle minification. + // scale = + // wxMax(1.0, scale); // Let the upstream processing handle minification. if (m_bShowIcon && m_bShowLiveIcon && ((m_LastVal != NewVal) || (scale != m_scale))) { diff --git a/test/buffer_tests.cpp b/test/buffer_tests.cpp index b324fcd179..983c32fac9 100644 --- a/test/buffer_tests.cpp +++ b/test/buffer_tests.cpp @@ -5,7 +5,8 @@ #include #include -#if (defined(OCPN_GHC_FILESYSTEM) || (defined(__clang_major__) && (__clang_major__ < 15))) +#if (defined(OCPN_GHC_FILESYSTEM) || \ + (defined(__clang_major__) && (__clang_major__ < 15))) #include namespace fs = ghc::filesystem; #else @@ -19,6 +20,7 @@ namespace fs = std::filesystem; #include #include "model/base_platform.h" +#include "model/comm_buffers.h" #include "model/comm_drv_registry.h" #include "model/comm_out_queue.h" #include "model/logger.h" @@ -36,12 +38,11 @@ static const char* const GPGGL = "$GPGGL 00"; class OverrunEvent : public wxAppConsole { public: - OverrunEvent() { bool result = false; ObsListener listener; listener.Init(CommDriverRegistry::GetInstance().evt_comm_overrun, - [&](ObservedEvt&) { result = true; }); + [&](ObservedEvt&) { result = true; }); CommOutQueue queue(3); for (int i = 0; i < 20; i++) queue.push_back(GPGGL); @@ -52,7 +53,6 @@ class OverrunEvent : public wxAppConsole { }; }; - TEST(Buffer, Single) { CommOutQueueSingle queue; for (int i = 0; i < 10; i++) queue.push_back(GPGGA); @@ -63,7 +63,7 @@ TEST(Buffer, Single) { EXPECT_FALSE(queue.push_back("foo")); } -TEST(Buffer, Size_3 ) { +TEST(Buffer, Size_3) { CommOutQueue queue(3); for (int i = 0; i < 20; i++) queue.push_back(GPGGL); @@ -87,24 +87,28 @@ TEST(Buffer, Size_3 ) { EXPECT_THROW({ queue.pop(); }, std::underflow_error); } - - TEST(Buffer, Hakefjord) { const auto path = fs::path(TESTDATA) / "Hakefjord.log"; std::ifstream stream(path.string()); MeasuredCommOutQueue queue(3); - for (std::string line; std::getline(stream, line); ) { + for (std::string line; std::getline(stream, line);) { queue.push_back(line); } RecordProperty("buffer size", std::to_string(queue.size())); for (int i = 0; i < 3; i++) queue.push_back(GPGGA); std::string line; - do { line = queue.pop(); } while (!ocpn::startswith(line, "$GPGGA")); + do { + line = queue.pop(); + } while (!ocpn::startswith(line, "$GPGGA")); + EXPECT_EQ(line, GPGGA); + do { + line = queue.pop(); + } while (!ocpn::startswith(line, "$GPGGA")); EXPECT_EQ(line, GPGGA); - do { line = queue.pop(); } while (!ocpn::startswith(line, "$GPGGA")); + do { + line = queue.pop(); + } while (!ocpn::startswith(line, "$GPGGA")); EXPECT_EQ(line, GPGGA); - do { line = queue.pop(); } while (!ocpn::startswith(line, "$GPGGA")); - EXPECT_EQ(line, GPGGA) ; RecordProperty("push_time", std::to_string(queue.push_time)); // writes to test_detail.xml if invoked with --gtest_output.xml } @@ -118,7 +122,7 @@ TEST(Buffer, RateLimit1) { EXPECT_EQ(queue.size(), 20); } -#if !defined(__APPLE__) && !defined (_WIN32) +#if !defined(__APPLE__) && !defined(_WIN32) && !defined(OCPN_DISTRO_BUILD) // The MacOS builders seems to have a lot of "too" long sleeps, // same for GA windows. Disable for now. TEST(Buffer, RateLimit2) { @@ -142,6 +146,21 @@ TEST(Buffer, RateAndSizeLimit) { // might fail due to OS gitter i. e., sleep takes "too" long } -TEST(Buffer, OverrunEvent) { - OverrunEvent event; +TEST(Buffer, OverrunEvent) { OverrunEvent event; } + +TEST(N0183Nuffer, Basic) { + static const std::string input1("$GPGGA12345*12"); + static const std::string input2("$GPGGA12\n345*12"); + + N0183Buffer n0183_buffer; + for (char c : input1) n0183_buffer.Put(c); + EXPECT_TRUE(n0183_buffer.HasSentence()); + EXPECT_EQ(n0183_buffer.GetSentence(), input1); + + for (char c : input2) n0183_buffer.Put(c); + EXPECT_FALSE(n0183_buffer.HasSentence()); + + for (char c : input1) n0183_buffer.Put(c); + EXPECT_TRUE(n0183_buffer.HasSentence()); + EXPECT_EQ(n0183_buffer.GetSentence(), input1); } diff --git a/test/ipc-srv-tests.cpp b/test/ipc-srv-tests.cpp index 0aa5282cfb..756021b6e1 100644 --- a/test/ipc-srv-tests.cpp +++ b/test/ipc-srv-tests.cpp @@ -17,6 +17,17 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ +/** + * \file + * + * Tests for the wxwidgets IPC server code. + * + * NOTE: The ` std::thread t([&] {` construct in the various Test* methods + * like TestRaise exposes some kind of bug. The wortk around for now is to + * always compile this using -O0 i. e. disable optimizations. With higher + * levels, the methods crashes on exit. + */ + #include "config.h" #include @@ -43,17 +54,6 @@ #include "model/macutils.h" #endif -/** - * \file - * - * Tests for the wxwidgets IPC server code. - * - * NOTE: The ` std::thread t([&] {` construct in the various Test* methods - * like TestRaise exposes some kind of bug. The wortk around for now is to - * always compile this using -O0 i. e. disable optimizations. With higher - * levels, the methods crashes on exit. - */ - using namespace std::literals::chrono_literals; /** Define an action to be performed when a KeyProvider is notified. */ @@ -72,7 +72,6 @@ class IpcServerTest : public wxAppConsole { private: class ObsListener : public wxEvtHandler { public: - /** Create an object which does not listen until Init(); */ ObsListener() {} @@ -107,8 +106,7 @@ class IpcServerTest : public wxAppConsole { void TestRaise(IpcServer& server) { int result0 = 5; - ObsListener listener(server.on_raise, - [&result0]() { result0 = 17; }); + ObsListener listener(server.on_raise, [&result0]() { result0 = 17; }); auto cmd = std::string(CMAKE_BINARY_DIR) + "/test/ipc-client raise"; FILE* stream = popen(cmd.c_str(), "r"); @@ -126,8 +124,7 @@ class IpcServerTest : public wxAppConsole { void TestQuit(IpcServer& server) { int result0 = 7; - ObsListener listener(server.on_quit, - [&result0]() { result0 = 13; }); + ObsListener listener(server.on_quit, [&result0]() { result0 = 13; }); auto cmd = std::string(CMAKE_BINARY_DIR) + "/test/ipc-client quit"; FILE* stream = popen(cmd.c_str(), "r"); @@ -190,7 +187,7 @@ wxIMPLEMENT_APP_NO_MAIN(IpcServerTest); #ifndef OCPN_FLATPAK -#if wxCHECK_VERSION(3,2,1) +#if wxCHECK_VERSION(3, 2, 1) TEST(IpcServer, Commands) { char arg0[32]; strcpy(arg0, "ipc-srv"); diff --git a/test/n2k_tests.cpp b/test/n2k_tests.cpp index 9c764a6fd3..e13fae934b 100644 --- a/test/n2k_tests.cpp +++ b/test/n2k_tests.cpp @@ -277,7 +277,7 @@ class LogProcessing: public N2kTest { LogProcessing(): N2kTest() { app = new N2kRunLog(); } }; - +#ifndef OCPN_DISTRO_BUILD TEST(DriverRegistry, RegisterDriver) { N2kTestDriverRegistry app; app.OnInit(); @@ -285,6 +285,7 @@ TEST(DriverRegistry, RegisterDriver) { EXPECT_EQ(int1, 0); // Driver closed. EXPECT_EQ(int2, 0); // All drivers closed. } +#endif #ifdef ENABLE_VCAN_TESTS TEST(CanEnvironment, vcan0) { diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index dea9dfed5d..84541490cd 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -497,15 +497,19 @@ TEST(RestServer, Object) { app.Run(); } +#ifndef OCPN_DISTRO_BUILD TEST(RestServer, CheckWrite) { - wxDisableAsserts(); - RestServerDlgCtx dialog_ctx; - RouteCtx route_ctx; - RestCheckWriteApp app(dialog_ctx, route_ctx, g_portable); - app.Run(); - delete g_BasePlatform; - g_BasePlatform = 0; + if (!getenv("DOCKER_BUILD")) { + wxDisableAsserts(); + RestServerDlgCtx dialog_ctx; + RouteCtx route_ctx; + RestCheckWriteApp app(dialog_ctx, route_ctx, g_portable); + app.Run(); + delete g_BasePlatform; + g_BasePlatform = 0; + } } +#endif TEST(RestServer, PluginMessage) { wxDisableAsserts(); diff --git a/test/tests.cpp b/test/tests.cpp index c072001fab..e0a3eac5e5 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -952,12 +952,12 @@ TEST(FormatTime, Basic) { span += wxTimeSpan(0, 0, 0, 10); s = formatTimeDelta(span).ToStdString(); EXPECT_EQ(s, " 2H 0M"); - s = formatTimeDelta(wxLongLong(7184.1181798492389)); + s = formatTimeDelta(wxLongLong(7184)); EXPECT_EQ(s, " 2H 0M"); - s = formatTimeDelta(wxLongLong(123.0)); + s = formatTimeDelta(wxLongLong(123)); EXPECT_EQ(s, " 2M 3S"); - s = formatTimeDelta(wxLongLong(120.0)); + s = formatTimeDelta(wxLongLong(120)); EXPECT_EQ(s, " 2M 0S"); - s = formatTimeDelta(wxLongLong(110.0)); + s = formatTimeDelta(wxLongLong(110)); EXPECT_EQ(s, " 1M 50S"); }