diff --git a/ApplicationLibCode/Application/Tools/CMakeLists_files.cmake b/ApplicationLibCode/Application/Tools/CMakeLists_files.cmake index 5c122e3be8..8370d004ef 100644 --- a/ApplicationLibCode/Application/Tools/CMakeLists_files.cmake +++ b/ApplicationLibCode/Application/Tools/CMakeLists_files.cmake @@ -49,6 +49,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RiaVec3Tools.h ${CMAKE_CURRENT_LIST_DIR}/RiaEnsembleNameTools.h ${CMAKE_CURRENT_LIST_DIR}/RiaSummaryStringTools.h + ${CMAKE_CURRENT_LIST_DIR}/RiaNetworkTools.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -95,6 +96,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RiaEnsembleNameTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaVec3Tools.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaSummaryStringTools.cpp + ${CMAKE_CURRENT_LIST_DIR}/RiaNetworkTools.cpp ) list(APPEND CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES}) diff --git a/ApplicationLibCode/Application/Tools/RiaNetworkTools.cpp b/ApplicationLibCode/Application/Tools/RiaNetworkTools.cpp new file mode 100644 index 0000000000..5935f8db8f --- /dev/null +++ b/ApplicationLibCode/Application/Tools/RiaNetworkTools.cpp @@ -0,0 +1,102 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2022 Equinor ASA +// +// ResInsight 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 3 of the License, or +// (at your option) any later version. +// +// ResInsight 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 at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RiaNetworkTools.h" + +#include +#include +#include +#include +#include +#include +#include + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiaNetworkTools::openUrl( const QString& urlString ) +{ + QDesktopServices::openUrl( QUrl( urlString ) ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiaNetworkTools::createAndOpenUrlWithFallback( const QString& urlSubString ) +{ + QStringList urls; + + QString urlStringWithPrefix = urlSubString.trimmed(); + if ( urlStringWithPrefix.isEmpty() ) return; + if ( urlStringWithPrefix[0] != '/' ) urlStringWithPrefix = '/' + urlStringWithPrefix; + + urls += "https://resinsight.org" + urlStringWithPrefix; + urls += "https://opm.github.io/ResInsight-UserDocumentation" + urlStringWithPrefix; + + openUrlWithFallback( urls ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiaNetworkTools::openUrlWithFallback( const QStringList& urlList ) +{ + for ( const auto& url : urlList ) + { + if ( doesResourceExist( url ) ) + { + QDesktopServices::openUrl( QUrl( url ) ); + return; + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RiaNetworkTools::doesResourceExist( const QString& urlString ) +{ + // Based on https://karanbalkar.com/posts/sending-http-request-using-qt5/ + + // create custom temporary event loop on stack + QEventLoop eventLoop; + + // "quit()" the event-loop, when the network request "finished()" + QNetworkAccessManager mgr; + QObject::connect( &mgr, SIGNAL( finished( QNetworkReply* ) ), &eventLoop, SLOT( quit() ) ); + + // the HTTP request + const QUrl qurl( urlString ); + QNetworkRequest request( qurl ); + QNetworkReply* reply = mgr.get( request ); + eventLoop.exec(); // blocks stack until "finished()" has been called + + auto statusCode = request.attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); + + auto error = reply->error(); + if ( error == QNetworkReply::NoError && statusCode == 200 ) + { + delete reply; + + return true; + } + + delete reply; + + return false; +} diff --git a/ApplicationLibCode/Application/Tools/RiaNetworkTools.h b/ApplicationLibCode/Application/Tools/RiaNetworkTools.h new file mode 100644 index 0000000000..4f1984e083 --- /dev/null +++ b/ApplicationLibCode/Application/Tools/RiaNetworkTools.h @@ -0,0 +1,37 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2022 Equinor ASA +// +// ResInsight 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 3 of the License, or +// (at your option) any later version. +// +// ResInsight 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 at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +//================================================================================================== +// +// +// +//================================================================================================== +class RiaNetworkTools +{ +public: + static void openUrl( const QString& urlString ); + static void createAndOpenUrlWithFallback( const QString& urlSubString ); + static void openUrlWithFallback( const QStringList& urlList ); + +private: + static bool doesResourceExist( const QString& urlString ); +}; diff --git a/ApplicationLibCode/ProjectDataModel/RimUserDefinedCalculation.cpp b/ApplicationLibCode/ProjectDataModel/RimUserDefinedCalculation.cpp index 170d5d7ed3..6ba3610ad3 100644 --- a/ApplicationLibCode/ProjectDataModel/RimUserDefinedCalculation.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimUserDefinedCalculation.cpp @@ -21,13 +21,16 @@ #include "expressionparser/ExpressionParser.h" #include "RiaLogging.h" +#include "RiaNetworkTools.h" #include "RimProject.h" #include "RimUserDefinedCalculationVariable.h" #include "RiuExpressionContextMenuManager.h" +#include "cafPdmUiLabelEditor.h" #include "cafPdmUiLineEditor.h" +#include "cafPdmUiPushButtonEditor.h" #include "cafPdmUiTableViewEditor.h" #include "cafPdmUiTextEditor.h" @@ -45,9 +48,21 @@ RimUserDefinedCalculation::RimUserDefinedCalculation() CAF_PDM_InitFieldNoDefault( &m_description, "Description", "Description" ); m_description.uiCapability()->setUiReadOnly( true ); - CAF_PDM_InitField( &m_expression, "Expression", QString( "" ), "Expression" ); + CAF_PDM_InitField( &m_expression, "Expression", QString( "" ), "" ); m_expression.uiCapability()->setUiEditorTypeName( caf::PdmUiTextEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_helpButton, "HelpButton", "" ); + caf::PdmUiPushButtonEditor::configureEditorForField( &m_helpButton ); + m_helpButton.xmlCapability()->disableIO(); + + CAF_PDM_InitFieldNoDefault( &m_helpText, + "Label", + "Use the right-click menu inside the text area for quick access to operators and " + "functions." ); + m_helpText.uiCapability()->setUiEditorTypeName( caf::PdmUiLabelEditor::uiEditorTypeName() ); + m_helpText.xmlCapability()->disableIO(); + m_helpText = "Use the right-click menu inside the text area for quick access to operators and functions."; + CAF_PDM_InitField( &m_unit, "Unit", QString( "" ), "Unit" ); m_unit.uiCapability()->setUiEditorTypeName( caf::PdmUiLineEditor::uiEditorTypeName() ); @@ -293,6 +308,16 @@ void RimUserDefinedCalculation::fieldChangedByUi( const caf::PdmFieldHandle* cha const QVariant& oldValue, const QVariant& newValue ) { + if ( changedField == &m_helpButton ) + { + m_helpButton = false; + + QString urlString = "https://resinsight.org/calculated-data/calculatorexpressions/"; + RiaNetworkTools::openUrl( urlString ); + + return; + } + m_isDirty = true; PdmObject::fieldChangedByUi( changedField, oldValue, newValue ); @@ -303,8 +328,10 @@ void RimUserDefinedCalculation::fieldChangedByUi( const caf::PdmFieldHandle* cha //-------------------------------------------------------------------------------------------------- void RimUserDefinedCalculation::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { + uiOrdering.add( &m_helpButton ); uiOrdering.add( &m_description ); uiOrdering.add( &m_expression ); + uiOrdering.add( &m_helpText ); uiOrdering.add( &m_unit ); } @@ -377,6 +404,14 @@ void RimUserDefinedCalculation::defineEditorAttribute( const caf::PdmFieldHandle myAttr->enableDropTarget = true; } } + else if ( field == &m_helpButton ) + { + auto* attrib = dynamic_cast( attribute ); + if ( attrib ) + { + attrib->m_buttonText = "Open Help Page"; + } + } } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RimUserDefinedCalculation.h b/ApplicationLibCode/ProjectDataModel/RimUserDefinedCalculation.h index eefc27e9ee..50e6093191 100644 --- a/ApplicationLibCode/ProjectDataModel/RimUserDefinedCalculation.h +++ b/ApplicationLibCode/ProjectDataModel/RimUserDefinedCalculation.h @@ -86,6 +86,8 @@ protected: protected: caf::PdmField m_description; caf::PdmField m_expression; + caf::PdmField m_helpButton; + caf::PdmField m_helpText; caf::PdmField m_unit; caf::PdmChildArrayField m_variables; diff --git a/ApplicationLibCode/UserInterface/RiuExpressionContextMenuManager.cpp b/ApplicationLibCode/UserInterface/RiuExpressionContextMenuManager.cpp index 8af449b4df..24060ed745 100644 --- a/ApplicationLibCode/UserInterface/RiuExpressionContextMenuManager.cpp +++ b/ApplicationLibCode/UserInterface/RiuExpressionContextMenuManager.cpp @@ -23,6 +23,10 @@ const std::map> RiuExpressionContextMenuManager::MENU_MAP = { { "Basic Operators", { "+", "-", "*", "/", "x^n" } }, { "Assignment Operators", { ":=" } }, + { "If Statements", + { "VAR_1 := if((X < 0.01), 0.01, X)", + "VAR_1 := if((X < 0.01 AND Y > 2.5), 0.01, X)", + "VAR_1 := if((X < 0.01 OR Y > 2.5), 0.01, X)" } }, { "Scalar Functions", { "avg(x)", "max(x)", "min(x)", "sum(x)" } }, { "Vector Functions", { "abs(x)", "ceil(x)", "floor(x)", "frac(x)", "log(x)", "log10(x)", "pow(x, n)", "round(x)", "sgn(x)", "sqrt(x)", "trunc(x)" } },