diff options
| author | JacobPalecki <[email protected]> | 2020-09-08 16:00:05 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2020-09-08 16:00:05 -0700 |
| commit | e5461fa84e65d78823d0022339fa2d8864f7e63c (patch) | |
| tree | b486ef524c93bfeb29a86403114b6805bf9decf1 | |
| parent | Merge pull request #19 from JacobPalecki/gainOffset (diff) | |
| parent | Save show last mouse value (diff) | |
| download | rawaccel-e5461fa84e65d78823d0022339fa2d8864f7e63c.tar.xz rawaccel-e5461fa84e65d78823d0022339fa2d8864f7e63c.zip | |
Merge pull request #20 from JacobPalecki/GUI
GUI: Add By Component & Anisotropy; Remove Logarithm and Sigmoid; Delete Console; Some Refactoring
56 files changed, 2867 insertions, 8317 deletions
diff --git a/common/accel-logarithmic.hpp b/common/accel-logarithmic.hpp deleted file mode 100644 index 1ab0e53..0000000 --- a/common/accel-logarithmic.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include <math.h> - -#include "accel-base.hpp" - -namespace rawaccel { - - /// <summary> Struct to hold logarithmic acceleration implementation. </summary> - struct logarithmic_impl { - double accel; - - logarithmic_impl(const accel_args& args) : accel(args.accel) {} - - inline double operator()(double speed) const { - //f(x) = log(m*x+1) - return log(accel * speed + 1); - } - - // incorrect but this style is slated for removal - inline double legacy_offset(double speed) const { return operator()(speed); } - }; - - using accel_logarithmic = additive_accel<logarithmic_impl>; -} diff --git a/common/accel-sigmoid.hpp b/common/accel-sigmoid.hpp deleted file mode 100644 index 239bd9d..0000000 --- a/common/accel-sigmoid.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include <math.h> - -#include "accel-base.hpp" - -namespace rawaccel { - - /// <summary> Struct to hold sigmoid (s-shaped) acceleration implementation. </summary> - struct sigmoid_impl { - double rate; - double limit; - double midpoint; - - sigmoid_impl(const accel_args& args) : - rate(args.accel), limit(args.limit - 1), midpoint(args.midpoint) - {} - - inline double operator()(double speed) const { - //f(x) = k/(1+e^(-m(x-c))) - return limit / (exp(-rate * (speed - midpoint)) + 1); - } - - inline double legacy_offset(double speed) const { return operator()(speed); } - }; - - using accel_sigmoid = additive_accel<sigmoid_impl>; - -} diff --git a/common/common.vcxitems b/common/common.vcxitems index 52d4f8a..2913080 100644 --- a/common/common.vcxitems +++ b/common/common.vcxitems @@ -17,12 +17,10 @@ <ClInclude Include="$(MSBuildThisFileDirectory)accel-base.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)accel-classic.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)accel-linear.hpp" /> - <ClInclude Include="$(MSBuildThisFileDirectory)accel-logarithmic.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)accel-natural.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)accel-naturalgain.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)accel-noaccel.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)accel-power.hpp" /> - <ClInclude Include="$(MSBuildThisFileDirectory)accel-sigmoid.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)accel-sigmoidgain.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-error.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-io.hpp" /> diff --git a/common/rawaccel-settings.h b/common/rawaccel-settings.h index b9ff946..12f136d 100644 --- a/common/rawaccel-settings.h +++ b/common/rawaccel-settings.h @@ -6,7 +6,7 @@ namespace rawaccel { enum class accel_mode { - linear, classic, natural, logarithmic, sigmoid, naturalgain, sigmoidgain, power, noaccel + linear, classic, natural, naturalgain, sigmoidgain, power, noaccel }; struct settings { diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp index d3a2a03..08ac322 100644 --- a/common/rawaccel.hpp +++ b/common/rawaccel.hpp @@ -10,10 +10,8 @@ #include "accel-classic.hpp" #include "accel-natural.hpp" #include "accel-naturalgain.hpp" -#include "accel-logarithmic.hpp" -#include "accel-sigmoid.hpp" -#include "accel-sigmoidgain.hpp" #include "accel-power.hpp" +#include "accel-sigmoidgain.hpp" #include "accel-noaccel.hpp" namespace rawaccel { @@ -83,8 +81,6 @@ namespace rawaccel { case accel_mode::linear: return vis(var.u.linear); case accel_mode::classic: return vis(var.u.classic); case accel_mode::natural: return vis(var.u.natural); - case accel_mode::logarithmic: return vis(var.u.logarithmic); - case accel_mode::sigmoid: return vis(var.u.sigmoid); case accel_mode::naturalgain: return vis(var.u.naturalgain); case accel_mode::sigmoidgain: return vis(var.u.sigmoidgain); case accel_mode::power: return vis(var.u.power); @@ -99,8 +95,6 @@ namespace rawaccel { accel_linear linear; accel_classic classic; accel_natural natural; - accel_logarithmic logarithmic; - accel_sigmoid sigmoid; accel_naturalgain naturalgain; accel_sigmoidgain sigmoidgain; accel_power power; diff --git a/console/console.cpp b/console/console.cpp deleted file mode 100644 index f86dfc2..0000000 --- a/console/console.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include <iostream> - -#include <rawaccel-io.hpp> - -#include "parse.hpp" - -namespace ra = rawaccel; - -int main(int argc, char** argv) { - try { - ra::write(ra::parse(argc, argv)); - } - catch (const std::system_error& e) { - std::cerr << e.what() << " (" << e.code() << ")\n"; - } - catch (const std::exception& e) { - std::cerr << e.what() << '\n'; - } -} diff --git a/console/console.vcxproj b/console/console.vcxproj deleted file mode 100644 index 0f87d94..0000000 --- a/console/console.vcxproj +++ /dev/null @@ -1,100 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <VCProjectVersion>16.0</VCProjectVersion> - <ProjectGuid>{AB7B3759-B85F-4067-8935-FB4539B41869}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>console</RootNamespace> - <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v142</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v142</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="Shared"> - <Import Project="..\common\common.vcxitems" Label="Shared" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <LinkIncremental>true</LinkIncremental> - <TargetName>rawaccel</TargetName> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <LinkIncremental>false</LinkIncremental> - <TargetName>rawaccel</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <LanguageStandard>stdcpplatest</LanguageStandard> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(SolutionDir)\external</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <LanguageStandard>stdcpplatest</LanguageStandard> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(SolutionDir)\external</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <GenerateDebugInformation>true</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="console.cpp" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="parse.hpp" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project>
\ No newline at end of file diff --git a/console/external/clipp.h b/console/external/clipp.h deleted file mode 100644 index cca1554..0000000 --- a/console/external/clipp.h +++ /dev/null @@ -1,7027 +0,0 @@ -/***************************************************************************** - * ___ _ _ ___ ___ - * | _|| | | | | _ \ _ \ CLIPP - command line interfaces for modern C++ - * | |_ | |_ | | | _/ _/ version 1.2.3 - * |___||___||_| |_| |_| https://github.com/muellan/clipp - * - * Licensed under the MIT License <http://opensource.org/licenses/MIT>. - * Copyright (c) 2017-2018 André Müller <[email protected]> - * - * --------------------------------------------------------------------------- - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - *****************************************************************************/ - -#ifndef AM_CLIPP_H__ -#define AM_CLIPP_H__ - -#include <cstring> -#include <string> -#include <cstdlib> -#include <cstring> -#include <cctype> -#include <memory> -#include <vector> -#include <limits> -#include <stack> -#include <algorithm> -#include <sstream> -#include <utility> -#include <iterator> -#include <functional> - - -/*************************************************************************//** - * - * @brief primary namespace - * - *****************************************************************************/ -namespace clipp { - - - -/***************************************************************************** - * - * basic constants and datatype definitions - * - *****************************************************************************/ -using arg_index = int; - -using arg_string = std::string; -using doc_string = std::string; - -using arg_list = std::vector<arg_string>; - - - -/*************************************************************************//** - * - * @brief tristate - * - *****************************************************************************/ -enum class tri : char { no, yes, either }; - -inline constexpr bool operator == (tri t, bool b) noexcept { - return b ? t != tri::no : t != tri::yes; -} -inline constexpr bool operator == (bool b, tri t) noexcept { return (t == b); } -inline constexpr bool operator != (tri t, bool b) noexcept { return !(t == b); } -inline constexpr bool operator != (bool b, tri t) noexcept { return !(t == b); } - - - -/*************************************************************************//** - * - * @brief (start,size) index range - * - *****************************************************************************/ -class subrange { -public: - using size_type = arg_string::size_type; - - /** @brief default: no match */ - explicit constexpr - subrange() noexcept : - at_{arg_string::npos}, length_{0} - {} - - /** @brief match length & position within subject string */ - explicit constexpr - subrange(size_type pos, size_type len) noexcept : - at_{pos}, length_{len} - {} - - /** @brief position of the match within the subject string */ - constexpr size_type at() const noexcept { return at_; } - /** @brief length of the matching subsequence */ - constexpr size_type length() const noexcept { return length_; } - - /** @brief returns true, if query string is a prefix of the subject string */ - constexpr bool prefix() const noexcept { - return at_ == 0; - } - - /** @brief returns true, if query is a substring of the query string */ - constexpr explicit operator bool () const noexcept { - return at_ != arg_string::npos; - } - -private: - size_type at_; - size_type length_; -}; - - - -/*************************************************************************//** - * - * @brief match predicates - * - *****************************************************************************/ -using match_predicate = std::function<bool(const arg_string&)>; -using match_function = std::function<subrange(const arg_string&)>; - - - - - - -/*************************************************************************//** - * - * @brief type traits (NOT FOR DIRECT USE IN CLIENT CODE!) - * no interface guarantees; might be changed or removed in the future - * - *****************************************************************************/ -namespace traits { - -/*************************************************************************//** - * - * @brief function (class) signature type trait - * - *****************************************************************************/ -template<class Fn, class Ret, class... Args> -constexpr auto -check_is_callable(int) -> decltype( - std::declval<Fn>()(std::declval<Args>()...), - std::integral_constant<bool, - std::is_same<Ret, std::invoke_result_t<Fn, Args...>>::value>{} ); - -template<class,class,class...> -constexpr auto -check_is_callable(long) -> std::false_type; - -template<class Fn, class Ret> -constexpr auto -check_is_callable_without_arg(int) -> decltype( - std::declval<Fn>()(), - std::integral_constant<bool, - std::is_same<Ret, std::invoke_result_t<Fn>>::value>{} ); - -template<class,class> -constexpr auto -check_is_callable_without_arg(long) -> std::false_type; - - - -template<class Fn, class... Args> -constexpr auto -check_is_void_callable(int) -> decltype( - std::declval<Fn>()(std::declval<Args>()...), std::true_type{}); - -template<class,class,class...> -constexpr auto -check_is_void_callable(long) -> std::false_type; - -template<class Fn> -constexpr auto -check_is_void_callable_without_arg(int) -> decltype( - std::declval<Fn>()(), std::true_type{}); - -template<class> -constexpr auto -check_is_void_callable_without_arg(long) -> std::false_type; - - - -template<class Fn, class Ret> -struct is_callable; - - -template<class Fn, class Ret, class... Args> -struct is_callable<Fn, Ret(Args...)> : - decltype(check_is_callable<Fn,Ret,Args...>(0)) -{}; - -template<class Fn, class Ret> -struct is_callable<Fn,Ret()> : - decltype(check_is_callable_without_arg<Fn,Ret>(0)) -{}; - - -template<class Fn, class... Args> -struct is_callable<Fn, void(Args...)> : - decltype(check_is_void_callable<Fn,Args...>(0)) -{}; - -template<class Fn> -struct is_callable<Fn,void()> : - decltype(check_is_void_callable_without_arg<Fn>(0)) -{}; - - - -/*************************************************************************//** - * - * @brief input range type trait - * - *****************************************************************************/ -template<class T> -constexpr auto -check_is_input_range(int) -> decltype( - begin(std::declval<T>()), end(std::declval<T>()), - std::true_type{}); - -template<class T> -constexpr auto -check_is_input_range(char) -> decltype( - std::begin(std::declval<T>()), std::end(std::declval<T>()), - std::true_type{}); - -template<class> -constexpr auto -check_is_input_range(long) -> std::false_type; - -template<class T> -struct is_input_range : - decltype(check_is_input_range<T>(0)) -{}; - - - -/*************************************************************************//** - * - * @brief size() member type trait - * - *****************************************************************************/ -template<class T> -constexpr auto -check_has_size_getter(int) -> - decltype(std::declval<T>().size(), std::true_type{}); - -template<class> -constexpr auto -check_has_size_getter(long) -> std::false_type; - -template<class T> -struct has_size_getter : - decltype(check_has_size_getter<T>(0)) -{}; - -} // namespace traits - - - - - - -/*************************************************************************//** - * - * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!) - * no interface guarantees; might be changed or removed in the future - * - *****************************************************************************/ -namespace detail { - - -/*************************************************************************//** - * @brief forwards string to first non-whitespace char; - * std string -> unsigned conv yields max value, but we want 0; - * also checks for nullptr - *****************************************************************************/ -inline bool -fwd_to_unsigned_int(const char*& s) -{ - if(!s) return false; - for(; std::isspace(*s); ++s); - if(!s[0] || s[0] == '-') return false; - if(s[0] == '-') return false; - return true; -} - - -/*************************************************************************//** - * - * @brief value limits clamping - * - *****************************************************************************/ -template<class T, class V, bool = (sizeof(V) > sizeof(T))> -struct limits_clamped { - static T from(const V& v) { - if(v >= V(std::numeric_limits<T>::max())) { - return std::numeric_limits<T>::max(); - } - if(v <= V(std::numeric_limits<T>::lowest())) { - return std::numeric_limits<T>::lowest(); - } - return T(v); - } -}; - -template<class T, class V> -struct limits_clamped<T,V,false> { - static T from(const V& v) { return T(v); } -}; - - -/*************************************************************************//** - * - * @brief returns value of v as a T, clamped at T's maximum - * - *****************************************************************************/ -template<class T, class V> -inline T clamped_on_limits(const V& v) { - return limits_clamped<T,V>::from(v); -} - - - - -/*************************************************************************//** - * - * @brief type conversion helpers - * - *****************************************************************************/ -template<class T> -struct make { - static inline T from(const char* s) { - if(!s) return false; - //a conversion from const char* to / must exist - return static_cast<T>(s); - } -}; - -template<> -struct make<bool> { - static inline bool from(const char* s) { - if(!s) return false; - return static_cast<bool>(s); - } -}; - -template<> -struct make<unsigned char> { - static inline unsigned char from(const char* s) { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits<unsigned char>(std::strtoull(s,nullptr,10)); - } -}; - -template<> -struct make<unsigned short int> { - static inline unsigned short int from(const char* s) { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits<unsigned short int>(std::strtoull(s,nullptr,10)); - } -}; - -template<> -struct make<unsigned int> { - static inline unsigned int from(const char* s) { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits<unsigned int>(std::strtoull(s,nullptr,10)); - } -}; - -template<> -struct make<unsigned long int> { - static inline unsigned long int from(const char* s) { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits<unsigned long int>(std::strtoull(s,nullptr,10)); - } -}; - -template<> -struct make<unsigned long long int> { - static inline unsigned long long int from(const char* s) { - if(!fwd_to_unsigned_int(s)) return (0); - return clamped_on_limits<unsigned long long int>(std::strtoull(s,nullptr,10)); - } -}; - -template<> -struct make<char> { - static inline char from(const char* s) { - //parse as single character? - const auto n = std::strlen(s); - if(n == 1) return s[0]; - //parse as integer - return clamped_on_limits<char>(std::strtoll(s,nullptr,10)); - } -}; - -template<> -struct make<short int> { - static inline short int from(const char* s) { - return clamped_on_limits<short int>(std::strtoll(s,nullptr,10)); - } -}; - -template<> -struct make<int> { - static inline int from(const char* s) { - return clamped_on_limits<int>(std::strtoll(s,nullptr,10)); - } -}; - -template<> -struct make<long int> { - static inline long int from(const char* s) { - return clamped_on_limits<long int>(std::strtoll(s,nullptr,10)); - } -}; - -template<> -struct make<long long int> { - static inline long long int from(const char* s) { - return (std::strtoll(s,nullptr,10)); - } -}; - -template<> -struct make<float> { - static inline float from(const char* s) { - return (std::strtof(s,nullptr)); - } -}; - -template<> -struct make<double> { - static inline double from(const char* s) { - return (std::strtod(s,nullptr)); - } -}; - -template<> -struct make<long double> { - static inline long double from(const char* s) { - return (std::strtold(s,nullptr)); - } -}; - -template<> -struct make<std::string> { - static inline std::string from(const char* s) { - return std::string(s); - } -}; - - - -/*************************************************************************//** - * - * @brief assigns boolean constant to one or multiple target objects - * - *****************************************************************************/ -template<class T, class V = T> -class assign_value -{ -public: - template<class X> - explicit constexpr - assign_value(T& target, X&& value) noexcept : - t_{std::addressof(target)}, v_{std::forward<X>(value)} - {} - - void operator () () const { - if(t_) *t_ = v_; - } - -private: - T* t_; - V v_; -}; - - - -/*************************************************************************//** - * - * @brief flips bools - * - *****************************************************************************/ -class flip_bool -{ -public: - explicit constexpr - flip_bool(bool& target) noexcept : - b_{&target} - {} - - void operator () () const { - if(b_) *b_ = !*b_; - } - -private: - bool* b_; -}; - - - -/*************************************************************************//** - * - * @brief increments using operator ++ - * - *****************************************************************************/ -template<class T> -class increment -{ -public: - explicit constexpr - increment(T& target) noexcept : t_{std::addressof(target)} {} - - void operator () () const { - if(t_) ++(*t_); - } - -private: - T* t_; -}; - - - -/*************************************************************************//** - * - * @brief decrements using operator -- - * - *****************************************************************************/ -template<class T> -class decrement -{ -public: - explicit constexpr - decrement(T& target) noexcept : t_{std::addressof(target)} {} - - void operator () () const { - if(t_) --(*t_); - } - -private: - T* t_; -}; - - - -/*************************************************************************//** - * - * @brief increments by a fixed amount using operator += - * - *****************************************************************************/ -template<class T> -class increment_by -{ -public: - explicit constexpr - increment_by(T& target, T by) noexcept : - t_{std::addressof(target)}, by_{std::move(by)} - {} - - void operator () () const { - if(t_) (*t_) += by_; - } - -private: - T* t_; - T by_; -}; - - - - -/*************************************************************************//** - * - * @brief makes a value from a string and assigns it to an object - * - *****************************************************************************/ -template<class T> -class map_arg_to -{ -public: - explicit constexpr - map_arg_to(T& target) noexcept : t_{std::addressof(target)} {} - - void operator () (const char* s) const { - if(t_ && s) *t_ = detail::make<T>::from(s); - } - -private: - T* t_; -}; - - -//------------------------------------------------------------------- -/** - * @brief specialization for vectors: append element - */ -template<class T> -class map_arg_to<std::vector<T>> -{ -public: - map_arg_to(std::vector<T>& target): t_{std::addressof(target)} {} - - void operator () (const char* s) const { - if(t_ && s) t_->push_back(detail::make<T>::from(s)); - } - -private: - std::vector<T>* t_; -}; - - -//------------------------------------------------------------------- -/** - * @brief specialization for bools: - * set to true regardless of string content - */ -template<> -class map_arg_to<bool> -{ -public: - map_arg_to(bool& target): t_{&target} {} - - void operator () (const char* s) const { - if(t_ && s) *t_ = true; - } - -private: - bool* t_; -}; - - -} // namespace detail - - - - - - -/*************************************************************************//** - * - * @brief string matching and processing tools - * - *****************************************************************************/ - -namespace str { - - -/*************************************************************************//** - * - * @brief converts string to value of target type 'T' - * - *****************************************************************************/ -template<class T> -T make(const arg_string& s) -{ - return detail::make<T>::from(s); -} - - - -/*************************************************************************//** - * - * @brief removes trailing whitespace from string - * - *****************************************************************************/ -template<class C, class T, class A> -inline void -trimr(std::basic_string<C,T,A>& s) -{ - if(s.empty()) return; - - s.erase( - std::find_if_not(s.rbegin(), s.rend(), - [](char c) { return std::isspace(c);} ).base(), - s.end() ); -} - - -/*************************************************************************//** - * - * @brief removes leading whitespace from string - * - *****************************************************************************/ -template<class C, class T, class A> -inline void -triml(std::basic_string<C,T,A>& s) -{ - if(s.empty()) return; - - s.erase( - s.begin(), - std::find_if_not(s.begin(), s.end(), - [](char c) { return std::isspace(c);}) - ); -} - - -/*************************************************************************//** - * - * @brief removes leading and trailing whitespace from string - * - *****************************************************************************/ -template<class C, class T, class A> -inline void -trim(std::basic_string<C,T,A>& s) -{ - triml(s); - trimr(s); -} - - -/*************************************************************************//** - * - * @brief removes all whitespaces from string - * - *****************************************************************************/ -template<class C, class T, class A> -inline void -remove_ws(std::basic_string<C,T,A>& s) -{ - if(s.empty()) return; - - s.erase(std::remove_if(s.begin(), s.end(), - [](char c) { return std::isspace(c); }), - s.end() ); -} - - -/*************************************************************************//** - * - * @brief returns true, if the 'prefix' argument - * is a prefix of the 'subject' argument - * - *****************************************************************************/ -template<class C, class T, class A> -inline bool -has_prefix(const std::basic_string<C,T,A>& subject, - const std::basic_string<C,T,A>& prefix) -{ - if(prefix.size() > subject.size()) return false; - return subject.find(prefix) == 0; -} - - -/*************************************************************************//** - * - * @brief returns true, if the 'postfix' argument - * is a postfix of the 'subject' argument - * - *****************************************************************************/ -template<class C, class T, class A> -inline bool -has_postfix(const std::basic_string<C,T,A>& subject, - const std::basic_string<C,T,A>& postfix) -{ - if(postfix.size() > subject.size()) return false; - return (subject.size() - postfix.size()) == subject.find(postfix); -} - - - -/*************************************************************************//** -* -* @brief returns longest common prefix of several -* sequential random access containers -* -* @details InputRange require begin and end (member functions or overloads) -* the elements of InputRange require a size() member -* -*****************************************************************************/ -template<class InputRange> -auto -longest_common_prefix(const InputRange& strs) - -> typename std::decay<decltype(*begin(strs))>::type -{ - static_assert(traits::is_input_range<InputRange>(), - "parameter must satisfy the InputRange concept"); - - static_assert(traits::has_size_getter< - typename std::decay<decltype(*begin(strs))>::type>(), - "elements of input range must have a ::size() member function"); - - using std::begin; - using std::end; - - using item_t = typename std::decay<decltype(*begin(strs))>::type; - using str_size_t = typename std::decay<decltype(begin(strs)->size())>::type; - - const auto n = size_t(distance(begin(strs), end(strs))); - if(n < 1) return item_t(""); - if(n == 1) return *begin(strs); - - //length of shortest string - auto m = std::min_element(begin(strs), end(strs), - [](const item_t& a, const item_t& b) { - return a.size() < b.size(); })->size(); - - //check each character until we find a mismatch - for(str_size_t i = 0; i < m; ++i) { - for(str_size_t j = 1; j < n; ++j) { - if(strs[j][i] != strs[j-1][i]) - return strs[0].substr(0, i); - } - } - return strs[0].substr(0, m); -} - - - -/*************************************************************************//** - * - * @brief returns longest substring range that could be found in 'arg' - * - * @param arg string to be searched in - * @param substrings range of candidate substrings - * - *****************************************************************************/ -template<class C, class T, class A, class InputRange> -subrange -longest_substring_match(const std::basic_string<C,T,A>& arg, - const InputRange& substrings) -{ - using string_t = std::basic_string<C,T,A>; - - static_assert(traits::is_input_range<InputRange>(), - "parameter must satisfy the InputRange concept"); - - static_assert(std::is_same<string_t, - typename std::decay<decltype(*begin(substrings))>::type>(), - "substrings must have same type as 'arg'"); - - auto i = string_t::npos; - auto n = string_t::size_type(0); - for(const auto& s : substrings) { - auto j = arg.find(s); - if(j != string_t::npos && s.size() > n) { - i = j; - n = s.size(); - } - } - return subrange{i,n}; -} - - - -/*************************************************************************//** - * - * @brief returns longest prefix range that could be found in 'arg' - * - * @param arg string to be searched in - * @param prefixes range of candidate prefix strings - * - *****************************************************************************/ -template<class C, class T, class A, class InputRange> -subrange -longest_prefix_match(const std::basic_string<C,T,A>& arg, - const InputRange& prefixes) -{ - using string_t = std::basic_string<C,T,A>; - using s_size_t = typename string_t::size_type; - - static_assert(traits::is_input_range<InputRange>(), - "parameter must satisfy the InputRange concept"); - - static_assert(std::is_same<string_t, - typename std::decay<decltype(*begin(prefixes))>::type>(), - "prefixes must have same type as 'arg'"); - - auto i = string_t::npos; - auto n = s_size_t(0); - for(const auto& s : prefixes) { - auto j = arg.find(s); - if(j == 0 && s.size() > n) { - i = 0; - n = s.size(); - } - } - return subrange{i,n}; -} - - - -/*************************************************************************//** - * - * @brief returns the first occurrence of 'query' within 'subject' - * - *****************************************************************************/ -template<class C, class T, class A> -inline subrange -substring_match(const std::basic_string<C,T,A>& subject, - const std::basic_string<C,T,A>& query) -{ - if(subject.empty() && query.empty()) return subrange(0,0); - if(subject.empty() || query.empty()) return subrange{}; - auto i = subject.find(query); - if(i == std::basic_string<C,T,A>::npos) return subrange{}; - return subrange{i,query.size()}; -} - - - -/*************************************************************************//** - * - * @brief returns first substring match (pos,len) within the input string - * that represents a number - * (with at maximum one decimal point and digit separators) - * - *****************************************************************************/ -template<class C, class T, class A> -subrange -first_number_match(std::basic_string<C,T,A> s, - C digitSeparator = C(','), - C decimalPoint = C('.'), - C exponential = C('e')) -{ - using string_t = std::basic_string<C,T,A>; - str::trim(s); - if(s.empty()) return subrange{}; - - //auto i = s.find_first_of("0123456789+-"); - //if(i == string_t::npos) { - // i = s.find(decimalPoint); - // if(i == string_t::npos) return subrange{}; - //} - //bool point = false; - - // overwritten to match numbers without leading 0, - // also commented out call to sanitize_args in parse - auto i = s.find_first_of("0123456789+-."); - if (i == string_t::npos) return subrange{}; - bool point = s[i] == decimalPoint; - - bool sep = false; - auto exp = string_t::npos; - auto j = i + 1; - for(; j < s.size(); ++j) { - if(s[j] == digitSeparator) { - if(!sep) sep = true; else break; - } - else { - sep = false; - if(s[j] == decimalPoint) { - //only one decimal point before exponent allowed - if(!point && exp == string_t::npos) point = true; else break; - } - else if(std::tolower(s[j]) == std::tolower(exponential)) { - //only one exponent separator allowed - if(exp == string_t::npos) exp = j; else break; - } - else if(exp != string_t::npos && (exp+1) == j) { - //only sign or digit after exponent separator - if(s[j] != '+' && s[j] != '-' && !std::isdigit(s[j])) break; - } - else if(!std::isdigit(s[j])) { - break; - } - } - } - - //if length == 1 then must be a digit - if(j-i == 1 && !std::isdigit(s[i])) return subrange{}; - - return subrange{i,j-i}; -} - - - -/*************************************************************************//** - * - * @brief returns first substring match (pos,len) - * that represents an integer (with optional digit separators) - * - *****************************************************************************/ -template<class C, class T, class A> -subrange -first_integer_match(std::basic_string<C,T,A> s, - C digitSeparator = C(',')) -{ - using string_t = std::basic_string<C,T,A>; - str::trim(s); - if(s.empty()) return subrange{}; - - auto i = s.find_first_of("0123456789+-"); - if(i == string_t::npos) return subrange{}; - - bool sep = false; - auto j = i + 1; - for(; j < s.size(); ++j) { - if(s[j] == digitSeparator) { - if(!sep) sep = true; else break; - } - else { - sep = false; - if(!std::isdigit(s[j])) break; - } - } - - //if length == 1 then must be a digit - if(j-i == 1 && !std::isdigit(s[i])) return subrange{}; - - return subrange{i,j-i}; -} - - - -/*************************************************************************//** - * - * @brief returns true if candidate string represents a number - * - *****************************************************************************/ -template<class C, class T, class A> -bool represents_number(const std::basic_string<C,T,A>& candidate, - C digitSeparator = C(','), - C decimalPoint = C('.'), - C exponential = C('e')) -{ - const auto match = str::first_number_match(candidate, digitSeparator, - decimalPoint, exponential); - - return (match && match.length() == candidate.size()); -} - - - -/*************************************************************************//** - * - * @brief returns true if candidate string represents an integer - * - *****************************************************************************/ -template<class C, class T, class A> -bool represents_integer(const std::basic_string<C,T,A>& candidate, - C digitSeparator = C(',')) -{ - const auto match = str::first_integer_match(candidate, digitSeparator); - return (match && match.length() == candidate.size()); -} - -} // namespace str - - - - - - -/*************************************************************************//** - * - * @brief makes function object with a const char* parameter - * that assigns a value to a ref-captured object - * - *****************************************************************************/ -template<class T, class V> -inline detail::assign_value<T,V> -set(T& target, V value) { - return detail::assign_value<T>{target, std::move(value)}; -} - - - -/*************************************************************************//** - * - * @brief makes parameter-less function object - * that assigns value(s) to a ref-captured object; - * value(s) are obtained by converting the const char* argument to - * the captured object types; - * bools are always set to true if the argument is not nullptr - * - *****************************************************************************/ -template<class T> -inline detail::map_arg_to<T> -set(T& target) { - return detail::map_arg_to<T>{target}; -} - - - -/*************************************************************************//** - * - * @brief makes function object that sets a bool to true - * - *****************************************************************************/ -inline detail::assign_value<bool> -set(bool& target) { - return detail::assign_value<bool>{target,true}; -} - -/*************************************************************************//** - * - * @brief makes function object that sets a bool to false - * - *****************************************************************************/ -inline detail::assign_value<bool> -unset(bool& target) { - return detail::assign_value<bool>{target,false}; -} - -/*************************************************************************//** - * - * @brief makes function object that flips the value of a ref-captured bool - * - *****************************************************************************/ -inline detail::flip_bool -flip(bool& b) { - return detail::flip_bool(b); -} - - - - - -/*************************************************************************//** - * - * @brief makes function object that increments using operator ++ - * - *****************************************************************************/ -template<class T> -inline detail::increment<T> -increment(T& target) { - return detail::increment<T>{target}; -} - -/*************************************************************************//** - * - * @brief makes function object that decrements using operator -- - * - *****************************************************************************/ -template<class T> -inline detail::increment_by<T> -increment(T& target, T by) { - return detail::increment_by<T>{target, std::move(by)}; -} - -/*************************************************************************//** - * - * @brief makes function object that increments by a fixed amount using operator += - * - *****************************************************************************/ -template<class T> -inline detail::decrement<T> -decrement(T& target) { - return detail::decrement<T>{target}; -} - - - - - - -/*************************************************************************//** - * - * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!) - * - *****************************************************************************/ -namespace detail { - - -/*************************************************************************//** - * - * @brief mixin that provides action definition and execution - * - *****************************************************************************/ -template<class Derived> -class action_provider -{ -private: - //--------------------------------------------------------------- - using simple_action = std::function<void()>; - using arg_action = std::function<void(const char*)>; - using index_action = std::function<void(int)>; - - //----------------------------------------------------- - class simple_action_adapter { - public: - simple_action_adapter() = default; - simple_action_adapter(const simple_action& a): action_(a) {} - simple_action_adapter(simple_action&& a): action_(std::move(a)) {} - void operator() (const char*) const { action_(); } - void operator() (int) const { action_(); } - private: - simple_action action_; - }; - - -public: - //--------------------------------------------------------------- - /** @brief adds an action that has an operator() that is callable - * with a 'const char*' argument */ - Derived& - call(arg_action a) { - argActions_.push_back(std::move(a)); - return *static_cast<Derived*>(this); - } - - /** @brief adds an action that has an operator()() */ - Derived& - call(simple_action a) { - argActions_.push_back(simple_action_adapter(std::move(a))); - return *static_cast<Derived*>(this); - } - - /** @brief adds an action that has an operator() that is callable - * with a 'const char*' argument */ - Derived& operator () (arg_action a) { return call(std::move(a)); } - - /** @brief adds an action that has an operator()() */ - Derived& operator () (simple_action a) { return call(std::move(a)); } - - - //--------------------------------------------------------------- - /** @brief adds an action that will set the value of 't' from - * a 'const char*' arg */ - template<class Target> - Derived& - set(Target& t) { - static_assert(!std::is_pointer<Target>::value, - "parameter target type must not be a pointer"); - - return call(clipp::set(t)); - } - - /** @brief adds an action that will set the value of 't' to 'v' */ - template<class Target, class Value> - Derived& - set(Target& t, Value&& v) { - return call(clipp::set(t, std::forward<Value>(v))); - } - - - //--------------------------------------------------------------- - /** @brief adds an action that will be called if a parameter - * matches an argument for the 2nd, 3rd, 4th, ... time - */ - Derived& - if_repeated(simple_action a) { - repeatActions_.push_back(simple_action_adapter{std::move(a)}); - return *static_cast<Derived*>(this); - } - /** @brief adds an action that will be called with the argument's - * index if a parameter matches an argument for - * the 2nd, 3rd, 4th, ... time - */ - Derived& - if_repeated(index_action a) { - repeatActions_.push_back(std::move(a)); - return *static_cast<Derived*>(this); - } - - - //--------------------------------------------------------------- - /** @brief adds an action that will be called if a required parameter - * is missing - */ - Derived& - if_missing(simple_action a) { - missingActions_.push_back(simple_action_adapter{std::move(a)}); - return *static_cast<Derived*>(this); - } - /** @brief adds an action that will be called if a required parameter - * is missing; the action will get called with the index of - * the command line argument where the missing event occurred first - */ - Derived& - if_missing(index_action a) { - missingActions_.push_back(std::move(a)); - return *static_cast<Derived*>(this); - } - - - //--------------------------------------------------------------- - /** @brief adds an action that will be called if a parameter - * was matched, but was unreachable in the current scope - */ - Derived& - if_blocked(simple_action a) { - blockedActions_.push_back(simple_action_adapter{std::move(a)}); - return *static_cast<Derived*>(this); - } - /** @brief adds an action that will be called if a parameter - * was matched, but was unreachable in the current scope; - * the action will be called with the index of - * the command line argument where the problem occurred - */ - Derived& - if_blocked(index_action a) { - blockedActions_.push_back(std::move(a)); - return *static_cast<Derived*>(this); - } - - - //--------------------------------------------------------------- - /** @brief adds an action that will be called if a parameter match - * was in conflict with a different alternative parameter - */ - Derived& - if_conflicted(simple_action a) { - conflictActions_.push_back(simple_action_adapter{std::move(a)}); - return *static_cast<Derived*>(this); - } - /** @brief adds an action that will be called if a parameter match - * was in conflict with a different alternative parameter; - * the action will be called with the index of - * the command line argument where the problem occurred - */ - Derived& - if_conflicted(index_action a) { - conflictActions_.push_back(std::move(a)); - return *static_cast<Derived*>(this); - } - - - //--------------------------------------------------------------- - /** @brief adds targets = either objects whose values should be - * set by command line arguments or actions that should - * be called in case of a match */ - template<class T, class... Ts> - Derived& - target(T&& t, Ts&&... ts) { - target(std::forward<T>(t)); - target(std::forward<Ts>(ts)...); - return *static_cast<Derived*>(this); - } - - /** @brief adds action that should be called in case of a match */ - template<class T, class = typename std::enable_if< - !std::is_fundamental<typename std::decay<T>::type>() && - (traits::is_callable<T,void()>() || - traits::is_callable<T,void(const char*)>() ) - >::type> - Derived& - target(T&& t) { - call(std::forward<T>(t)); - return *static_cast<Derived*>(this); - } - - /** @brief adds object whose value should be set by command line arguments - */ - template<class T, class = typename std::enable_if< - std::is_fundamental<typename std::decay<T>::type>() || - (!traits::is_callable<T,void()>() && - !traits::is_callable<T,void(const char*)>() ) - >::type> - Derived& - target(T& t) { - set(t); - return *static_cast<Derived*>(this); - } - - //TODO remove ugly empty param list overload - Derived& - target() { - return *static_cast<Derived*>(this); - } - - - //--------------------------------------------------------------- - /** @brief adds target, see member function 'target' */ - template<class Target> - inline friend Derived& - operator << (Target&& t, Derived& p) { - p.target(std::forward<Target>(t)); - return p; - } - /** @brief adds target, see member function 'target' */ - template<class Target> - inline friend Derived&& - operator << (Target&& t, Derived&& p) { - p.target(std::forward<Target>(t)); - return std::move(p); - } - - //----------------------------------------------------- - /** @brief adds target, see member function 'target' */ - template<class Target> - inline friend Derived& - operator >> (Derived& p, Target&& t) { - p.target(std::forward<Target>(t)); - return p; - } - /** @brief adds target, see member function 'target' */ - template<class Target> - inline friend Derived&& - operator >> (Derived&& p, Target&& t) { - p.target(std::forward<Target>(t)); - return std::move(p); - } - - - //--------------------------------------------------------------- - /** @brief executes all argument actions */ - void execute_actions(const arg_string& arg) const { - int i = 0; - for(const auto& a : argActions_) { - ++i; - a(arg.c_str()); - } - } - - /** @brief executes repeat actions */ - void notify_repeated(arg_index idx) const { - for(const auto& a : repeatActions_) a(idx); - } - /** @brief executes missing error actions */ - void notify_missing(arg_index idx) const { - for(const auto& a : missingActions_) a(idx); - } - /** @brief executes blocked error actions */ - void notify_blocked(arg_index idx) const { - for(const auto& a : blockedActions_) a(idx); - } - /** @brief executes conflict error actions */ - void notify_conflict(arg_index idx) const { - for(const auto& a : conflictActions_) a(idx); - } - -private: - //--------------------------------------------------------------- - std::vector<arg_action> argActions_; - std::vector<index_action> repeatActions_; - std::vector<index_action> missingActions_; - std::vector<index_action> blockedActions_; - std::vector<index_action> conflictActions_; -}; - - - - - - -/*************************************************************************//** - * - * @brief mixin that provides basic common settings of parameters and groups - * - *****************************************************************************/ -template<class Derived> -class token -{ -public: - //--------------------------------------------------------------- - using doc_string = clipp::doc_string; - - - //--------------------------------------------------------------- - /** @brief returns documentation string */ - const doc_string& doc() const noexcept { - return doc_; - } - - /** @brief sets documentations string */ - Derived& doc(const doc_string& txt) { - doc_ = txt; - return *static_cast<Derived*>(this); - } - - /** @brief sets documentations string */ - Derived& doc(doc_string&& txt) { - doc_ = std::move(txt); - return *static_cast<Derived*>(this); - } - - - //--------------------------------------------------------------- - /** @brief returns if a group/parameter is repeatable */ - bool repeatable() const noexcept { - return repeatable_; - } - - /** @brief sets repeatability of group/parameter */ - Derived& repeatable(bool yes) noexcept { - repeatable_ = yes; - return *static_cast<Derived*>(this); - } - - - //--------------------------------------------------------------- - /** @brief returns if a group/parameter is blocking/positional */ - bool blocking() const noexcept { - return blocking_; - } - - /** @brief determines, if a group/parameter is blocking/positional */ - Derived& blocking(bool yes) noexcept { - blocking_ = yes; - return *static_cast<Derived*>(this); - } - - -private: - //--------------------------------------------------------------- - doc_string doc_; - bool repeatable_ = false; - bool blocking_ = false; -}; - - - - -/*************************************************************************//** - * - * @brief sets documentation strings on a token - * - *****************************************************************************/ -template<class T> -inline T& -operator % (doc_string docstr, token<T>& p) -{ - return p.doc(std::move(docstr)); -} -//--------------------------------------------------------- -template<class T> -inline T&& -operator % (doc_string docstr, token<T>&& p) -{ - return std::move(p.doc(std::move(docstr))); -} - -//--------------------------------------------------------- -template<class T> -inline T& -operator % (token<T>& p, doc_string docstr) -{ - return p.doc(std::move(docstr)); -} -//--------------------------------------------------------- -template<class T> -inline T&& -operator % (token<T>&& p, doc_string docstr) -{ - return std::move(p.doc(std::move(docstr))); -} - - - - -/*************************************************************************//** - * - * @brief sets documentation strings on a token - * - *****************************************************************************/ -template<class T> -inline T& -doc(doc_string docstr, token<T>& p) -{ - return p.doc(std::move(docstr)); -} -//--------------------------------------------------------- -template<class T> -inline T&& -doc(doc_string docstr, token<T>&& p) -{ - return std::move(p.doc(std::move(docstr))); -} - - - -} // namespace detail - - - -/*************************************************************************//** - * - * @brief contains parameter matching functions and function classes - * - *****************************************************************************/ -namespace match { - - -/*************************************************************************//** - * - * @brief predicate that is always true - * - *****************************************************************************/ -inline bool -any(const arg_string&) { return true; } - -/*************************************************************************//** - * - * @brief predicate that is always false - * - *****************************************************************************/ -inline bool -none(const arg_string&) { return false; } - - - -/*************************************************************************//** - * - * @brief predicate that returns true if the argument string is non-empty string - * - *****************************************************************************/ -inline bool -nonempty(const arg_string& s) { - return !s.empty(); -} - - - -/*************************************************************************//** - * - * @brief predicate that returns true if the argument is a non-empty - * string that consists only of alphanumeric characters - * - *****************************************************************************/ -inline bool -alphanumeric(const arg_string& s) { - if(s.empty()) return false; - return std::all_of(s.begin(), s.end(), [](char c) {return std::isalnum(c); }); -} - - - -/*************************************************************************//** - * - * @brief predicate that returns true if the argument is a non-empty - * string that consists only of alphabetic characters - * - *****************************************************************************/ -inline bool -alphabetic(const arg_string& s) { - return std::all_of(s.begin(), s.end(), [](char c) {return std::isalpha(c); }); -} - - - -/*************************************************************************//** - * - * @brief predicate that returns false if the argument string is - * equal to any string from the exclusion list - * - *****************************************************************************/ -class none_of -{ -public: - none_of(arg_list strs): - excluded_{std::move(strs)} - {} - - template<class... Strings> - none_of(arg_string str, Strings&&... strs): - excluded_{std::move(str), std::forward<Strings>(strs)...} - {} - - template<class... Strings> - none_of(const char* str, Strings&&... strs): - excluded_{arg_string(str), std::forward<Strings>(strs)...} - {} - - bool operator () (const arg_string& arg) const { - return (std::find(begin(excluded_), end(excluded_), arg) - == end(excluded_)); - } - -private: - arg_list excluded_; -}; - - - -/*************************************************************************//** - * - * @brief predicate that returns the first substring match within the input - * string that rmeepresents a number - * (with at maximum one decimal point and digit separators) - * - *****************************************************************************/ -class numbers -{ -public: - explicit - numbers(char decimalPoint = '.', - char digitSeparator = ' ', - char exponentSeparator = 'e') - : - decpoint_{decimalPoint}, separator_{digitSeparator}, - exp_{exponentSeparator} - {} - - subrange operator () (const arg_string& s) const { - return str::first_number_match(s, separator_, decpoint_, exp_); - } - -private: - char decpoint_; - char separator_; - char exp_; -}; - - - -/*************************************************************************//** - * - * @brief predicate that returns true if the input string represents an integer - * (with optional digit separators) - * - *****************************************************************************/ -class integers { -public: - explicit - integers(char digitSeparator = ' '): separator_{digitSeparator} {} - - subrange operator () (const arg_string& s) const { - return str::first_integer_match(s, separator_); - } - -private: - char separator_; -}; - - - -/*************************************************************************//** - * - * @brief predicate that returns true if the input string represents - * a non-negative integer (with optional digit separators) - * - *****************************************************************************/ -class positive_integers { -public: - explicit - positive_integers(char digitSeparator = ' ') : separator_{ digitSeparator } {} - subrange operator () (const arg_string& s) const { - auto match = str::first_integer_match(s, separator_); - if(!match) return subrange{}; - if(s[match.at()] == '-') return subrange{}; - return match; - } - -private: - char separator_; -}; - - - -/*************************************************************************//** - * - * @brief predicate that returns true if the input string - * contains a given substring - * - *****************************************************************************/ -class substring -{ -public: - explicit - substring(arg_string str): str_{std::move(str)} {} - - subrange operator () (const arg_string& s) const { - return str::substring_match(s, str_); - } - -private: - arg_string str_; -}; - - - -/*************************************************************************//** - * - * @brief predicate that returns true if the input string starts - * with a given prefix - * - *****************************************************************************/ -class prefix { -public: - explicit - prefix(arg_string p): prefix_{std::move(p)} {} - - bool operator () (const arg_string& s) const { - return s.find(prefix_) == 0; - } - -private: - arg_string prefix_; -}; - - - -/*************************************************************************//** - * - * @brief predicate that returns true if the input string does not start - * with a given prefix - * - *****************************************************************************/ -class prefix_not { -public: - explicit - prefix_not(arg_string p): prefix_{std::move(p)} {} - - bool operator () (const arg_string& s) const { - return s.find(prefix_) != 0; - } - -private: - arg_string prefix_; -}; - - -/** @brief alias for prefix_not */ -using noprefix = prefix_not; - - - -/*************************************************************************//** - * - * @brief predicate that returns true if the length of the input string - * is wihtin a given interval - * - *****************************************************************************/ -class length { -public: - explicit - length(std::size_t exact): - min_{exact}, max_{exact} - {} - - explicit - length(std::size_t min, std::size_t max): - min_{min}, max_{max} - {} - - bool operator () (const arg_string& s) const { - return s.size() >= min_ && s.size() <= max_; - } - -private: - std::size_t min_; - std::size_t max_; -}; - - -/*************************************************************************//** - * - * @brief makes function object that returns true if the input string has a - * given minimum length - * - *****************************************************************************/ -inline length min_length(std::size_t min) -{ - return length{min, arg_string::npos-1}; -} - -/*************************************************************************//** - * - * @brief makes function object that returns true if the input string is - * not longer than a given maximum length - * - *****************************************************************************/ -inline length max_length(std::size_t max) -{ - return length{0, max}; -} - - -} // namespace match - - - - - -/*************************************************************************//** - * - * @brief command line parameter that can match one or many arguments. - * - *****************************************************************************/ -class parameter : - public detail::token<parameter>, - public detail::action_provider<parameter> -{ - /** @brief adapts a 'match_predicate' to the 'match_function' interface */ - class predicate_adapter { - public: - explicit - predicate_adapter(match_predicate pred): match_{std::move(pred)} {} - - subrange operator () (const arg_string& arg) const { - return match_(arg) ? subrange{0,arg.size()} : subrange{}; - } - - private: - match_predicate match_; - }; - -public: - //--------------------------------------------------------------- - /** @brief makes default parameter, that will match nothing */ - parameter(): - flags_{}, - matcher_{predicate_adapter{match::none}}, - label_{}, required_{false}, greedy_{false} - {} - - /** @brief makes "flag" parameter */ - template<class... Strings> - explicit - parameter(arg_string str, Strings&&... strs): - flags_{}, - matcher_{predicate_adapter{match::none}}, - label_{}, required_{false}, greedy_{false} - { - add_flags(std::move(str), std::forward<Strings>(strs)...); - } - - /** @brief makes "flag" parameter from range of strings */ - explicit - parameter(const arg_list& flaglist): - flags_{}, - matcher_{predicate_adapter{match::none}}, - label_{}, required_{false}, greedy_{false} - { - add_flags(flaglist); - } - - //----------------------------------------------------- - /** @brief makes "value" parameter with custom match predicate - * (= yes/no matcher) - */ - explicit - parameter(match_predicate filter): - flags_{}, - matcher_{predicate_adapter{std::move(filter)}}, - label_{}, required_{false}, greedy_{false} - {} - - /** @brief makes "value" parameter with custom match function - * (= partial matcher) - */ - explicit - parameter(match_function filter): - flags_{}, - matcher_{std::move(filter)}, - label_{}, required_{false}, greedy_{false} - {} - - - //--------------------------------------------------------------- - /** @brief returns if a parameter is required */ - bool - required() const noexcept { - return required_; - } - - /** @brief determines if a parameter is required */ - parameter& - required(bool yes) noexcept { - required_ = yes; - return *this; - } - - - //--------------------------------------------------------------- - /** @brief returns if a parameter should match greedily */ - bool - greedy() const noexcept { - return greedy_; - } - - /** @brief determines if a parameter should match greedily */ - parameter& - greedy(bool yes) noexcept { - greedy_ = yes; - return *this; - } - - - //--------------------------------------------------------------- - /** @brief returns parameter label; - * will be used for documentation, if flags are empty - */ - const doc_string& - label() const { - return label_; - } - - /** @brief sets parameter label; - * will be used for documentation, if flags are empty - */ - parameter& - label(const doc_string& lbl) { - label_ = lbl; - return *this; - } - - /** @brief sets parameter label; - * will be used for documentation, if flags are empty - */ - parameter& - label(doc_string&& lbl) { - label_ = lbl; - return *this; - } - - - //--------------------------------------------------------------- - /** @brief returns either longest matching prefix of 'arg' in any - * of the flags or the result of the custom match operation - */ - subrange - match(const arg_string& arg) const - { - if(flags_.empty()) { - return matcher_(arg); - } - else { - //empty flags are not allowed - if(arg.empty()) return subrange{}; - - if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) { - return subrange{0,arg.size()}; - } - return str::longest_prefix_match(arg, flags_); - } - } - - - //--------------------------------------------------------------- - /** @brief access range of flag strings */ - const arg_list& - flags() const noexcept { - return flags_; - } - - /** @brief access custom match operation */ - const match_function& - matcher() const noexcept { - return matcher_; - } - - - //--------------------------------------------------------------- - /** @brief prepend prefix to each flag */ - inline friend parameter& - with_prefix(const arg_string& prefix, parameter& p) - { - if(prefix.empty() || p.flags().empty()) return p; - - for(auto& f : p.flags_) { - if(f.find(prefix) != 0) f.insert(0, prefix); - } - return p; - } - - - /** @brief prepend prefix to each flag - */ - inline friend parameter& - with_prefixes_short_long( - const arg_string& shortpfx, const arg_string& longpfx, - parameter& p) - { - if(shortpfx.empty() && longpfx.empty()) return p; - if(p.flags().empty()) return p; - - for(auto& f : p.flags_) { - if(f.size() == 1) { - if(f.find(shortpfx) != 0) f.insert(0, shortpfx); - } else { - if(f.find(longpfx) != 0) f.insert(0, longpfx); - } - } - return p; - } - - - //--------------------------------------------------------------- - /** @brief prepend suffix to each flag */ - inline friend parameter& - with_suffix(const arg_string& suffix, parameter& p) - { - if(suffix.empty() || p.flags().empty()) return p; - - for(auto& f : p.flags_) { - if(f.find(suffix) + suffix.size() != f.size()) { - f.insert(f.end(), suffix.begin(), suffix.end()); - } - } - return p; - } - - - /** @brief prepend suffix to each flag - */ - inline friend parameter& - with_suffixes_short_long( - const arg_string& shortsfx, const arg_string& longsfx, - parameter& p) - { - if(shortsfx.empty() && longsfx.empty()) return p; - if(p.flags().empty()) return p; - - for(auto& f : p.flags_) { - if(f.size() == 1) { - if(f.find(shortsfx) + shortsfx.size() != f.size()) { - f.insert(f.end(), shortsfx.begin(), shortsfx.end()); - } - } else { - if(f.find(longsfx) + longsfx.size() != f.size()) { - f.insert(f.end(), longsfx.begin(), longsfx.end()); - } - } - } - return p; - } - -private: - //--------------------------------------------------------------- - void add_flags(arg_string str) { - //empty flags are not allowed - str::remove_ws(str); - if(!str.empty()) flags_.push_back(std::move(str)); - } - - //--------------------------------------------------------------- - void add_flags(const arg_list& strs) { - if(strs.empty()) return; - flags_.reserve(flags_.size() + strs.size()); - for(const auto& s : strs) add_flags(s); - } - - template<class String1, class String2, class... Strings> - void - add_flags(String1&& s1, String2&& s2, Strings&&... ss) { - flags_.reserve(2 + sizeof...(ss)); - add_flags(std::forward<String1>(s1)); - add_flags(std::forward<String2>(s2), std::forward<Strings>(ss)...); - } - - arg_list flags_; - match_function matcher_; - doc_string label_; - bool required_ = false; - bool greedy_ = false; -}; - - - - -/*************************************************************************//** - * - * @brief makes required non-blocking exact match parameter - * - *****************************************************************************/ -template<class String, class... Strings> -inline parameter -command(String&& flag, Strings&&... flags) -{ - return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...} - .required(true).blocking(true).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes required non-blocking exact match parameter - * - *****************************************************************************/ -template<class String, class... Strings> -inline parameter -required(String&& flag, Strings&&... flags) -{ - return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...} - .required(true).blocking(false).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes optional, non-blocking exact match parameter - * - *****************************************************************************/ -template<class String, class... Strings> -inline parameter -option(String&& flag, Strings&&... flags) -{ - return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...} - .required(false).blocking(false).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes required, blocking, repeatable value parameter; - * matches any non-empty string - * - *****************************************************************************/ -template<class... Targets> -inline parameter -value(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::nonempty} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(false); -} - -template<class Filter, class... Targets, class = typename std::enable_if< - traits::is_callable<Filter,bool(const char*)>::value || - traits::is_callable<Filter,subrange(const char*)>::value>::type> -inline parameter -value(Filter&& filter, doc_string label, Targets&&... tgts) -{ - return parameter{std::forward<Filter>(filter)} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes required, blocking, repeatable value parameter; - * matches any non-empty string - * - *****************************************************************************/ -template<class... Targets> -inline parameter -values(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::nonempty} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(true); -} - -template<class Filter, class... Targets, class = typename std::enable_if< - traits::is_callable<Filter,bool(const char*)>::value || - traits::is_callable<Filter,subrange(const char*)>::value>::type> -inline parameter -values(Filter&& filter, doc_string label, Targets&&... tgts) -{ - return parameter{std::forward<Filter>(filter)} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes optional, blocking value parameter; - * matches any non-empty string - * - *****************************************************************************/ -template<class... Targets> -inline parameter -opt_value(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::nonempty} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(false); -} - -template<class Filter, class... Targets, class = typename std::enable_if< - traits::is_callable<Filter,bool(const char*)>::value || - traits::is_callable<Filter,subrange(const char*)>::value>::type> -inline parameter -opt_value(Filter&& filter, doc_string label, Targets&&... tgts) -{ - return parameter{std::forward<Filter>(filter)} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes optional, blocking, repeatable value parameter; - * matches any non-empty string - * - *****************************************************************************/ -template<class... Targets> -inline parameter -opt_values(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::nonempty} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(true); -} - -template<class Filter, class... Targets, class = typename std::enable_if< - traits::is_callable<Filter,bool(const char*)>::value || - traits::is_callable<Filter,subrange(const char*)>::value>::type> -inline parameter -opt_values(Filter&& filter, doc_string label, Targets&&... tgts) -{ - return parameter{std::forward<Filter>(filter)} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes required, blocking value parameter; - * matches any string consisting of alphanumeric characters - * - *****************************************************************************/ -template<class... Targets> -inline parameter -word(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::alphanumeric} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes required, blocking, repeatable value parameter; - * matches any string consisting of alphanumeric characters - * - *****************************************************************************/ -template<class... Targets> -inline parameter -words(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::alphanumeric} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes optional, blocking value parameter; - * matches any string consisting of alphanumeric characters - * - *****************************************************************************/ -template<class... Targets> -inline parameter -opt_word(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::alphanumeric} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes optional, blocking, repeatable value parameter; - * matches any string consisting of alphanumeric characters - * - *****************************************************************************/ -template<class... Targets> -inline parameter -opt_words(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::alphanumeric} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes required, blocking value parameter; - * matches any string that represents a number - * - *****************************************************************************/ -template<class... Targets> -inline parameter -number(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::numbers{}} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes required, blocking, repeatable value parameter; - * matches any string that represents a number - * - *****************************************************************************/ -template<class... Targets> -inline parameter -numbers(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::numbers{}} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes optional, blocking value parameter; - * matches any string that represents a number - * - *****************************************************************************/ -template<class... Targets> -inline parameter -opt_number(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::numbers{}} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes optional, blocking, repeatable value parameter; - * matches any string that represents a number - * - *****************************************************************************/ -template<class... Targets> -inline parameter -opt_numbers(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::numbers{}} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes required, blocking value parameter; - * matches any string that represents an integer - * - *****************************************************************************/ -template<class... Targets> -inline parameter -integer(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::integers{}} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes required, blocking, repeatable value parameter; - * matches any string that represents an integer - * - *****************************************************************************/ -template<class... Targets> -inline parameter -integers(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::integers{}} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(true).blocking(true).repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes optional, blocking value parameter; - * matches any string that represents an integer - * - *****************************************************************************/ -template<class... Targets> -inline parameter -opt_integer(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::integers{}} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(false); -} - - - -/*************************************************************************//** - * - * @brief makes optional, blocking, repeatable value parameter; - * matches any string that represents an integer - * - *****************************************************************************/ -template<class... Targets> -inline parameter -opt_integers(const doc_string& label, Targets&&... tgts) -{ - return parameter{match::integers{}} - .label(label) - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes catch-all value parameter - * - *****************************************************************************/ -template<class... Targets> -inline parameter -any_other(Targets&&... tgts) -{ - return parameter{match::any} - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes catch-all value parameter with custom filter - * - *****************************************************************************/ -template<class Filter, class... Targets, class = typename std::enable_if< - traits::is_callable<Filter,bool(const char*)>::value || - traits::is_callable<Filter,subrange(const char*)>::value>::type> -inline parameter -any(Filter&& filter, Targets&&... tgts) -{ - return parameter{std::forward<Filter>(filter)} - .target(std::forward<Targets>(tgts)...) - .required(false).blocking(false).repeatable(true); -} - - - - -/*************************************************************************//** - * - * @brief group of parameters and/or other groups; - * can be configured to act as a group of alternatives (exclusive match) - * - *****************************************************************************/ -class group : - public detail::token<group> -{ - //--------------------------------------------------------------- - /** - * @brief tagged union type that either stores a parameter or a group - * and provides a common interface to them - * could be replaced by std::variant in the future - * - * Note to future self: do NOT try again to do this with - * dynamic polymorphism; there are a couple of - * nasty problems associated with it and the implementation - * becomes bloated and needlessly complicated. - */ - template<class Param, class Group> - struct child_t { - enum class type : char {param, group}; - public: - - explicit - child_t(const Param& v) : m_{v}, type_{type::param} {} - child_t( Param&& v) noexcept : m_{std::move(v)}, type_{type::param} {} - - explicit - child_t(const Group& g) : m_{g}, type_{type::group} {} - child_t( Group&& g) noexcept : m_{std::move(g)}, type_{type::group} {} - - child_t(const child_t& src): type_{src.type_} { - switch(type_) { - default: - case type::param: new(&m_)data{src.m_.param}; break; - case type::group: new(&m_)data{src.m_.group}; break; - } - } - - child_t(child_t&& src) noexcept : type_{src.type_} { - switch(type_) { - default: - case type::param: new(&m_)data{std::move(src.m_.param)}; break; - case type::group: new(&m_)data{std::move(src.m_.group)}; break; - } - } - - child_t& operator = (const child_t& src) { - destroy_content(); - type_ = src.type_; - switch(type_) { - default: - case type::param: new(&m_)data{src.m_.param}; break; - case type::group: new(&m_)data{src.m_.group}; break; - } - return *this; - } - - child_t& operator = (child_t&& src) noexcept { - destroy_content(); - type_ = src.type_; - switch(type_) { - default: - case type::param: new(&m_)data{std::move(src.m_.param)}; break; - case type::group: new(&m_)data{std::move(src.m_.group)}; break; - } - return *this; - } - - ~child_t() { - destroy_content(); - } - - const doc_string& - doc() const noexcept { - switch(type_) { - default: - case type::param: return m_.param.doc(); - case type::group: return m_.group.doc(); - } - } - - bool blocking() const noexcept { - switch(type_) { - case type::param: return m_.param.blocking(); - case type::group: return m_.group.blocking(); - default: return false; - } - } - bool repeatable() const noexcept { - switch(type_) { - case type::param: return m_.param.repeatable(); - case type::group: return m_.group.repeatable(); - default: return false; - } - } - bool required() const noexcept { - switch(type_) { - case type::param: return m_.param.required(); - case type::group: - return (m_.group.exclusive() && m_.group.all_required() ) || - (!m_.group.exclusive() && m_.group.any_required() ); - default: return false; - } - } - bool exclusive() const noexcept { - switch(type_) { - case type::group: return m_.group.exclusive(); - case type::param: - default: return false; - } - } - std::size_t param_count() const noexcept { - switch(type_) { - case type::group: return m_.group.param_count(); - case type::param: - default: return std::size_t(1); - } - } - std::size_t depth() const noexcept { - switch(type_) { - case type::group: return m_.group.depth(); - case type::param: - default: return std::size_t(0); - } - } - - void execute_actions(const arg_string& arg) const { - switch(type_) { - default: - case type::group: return; - case type::param: m_.param.execute_actions(arg); break; - } - - } - - void notify_repeated(arg_index idx) const { - switch(type_) { - default: - case type::group: return; - case type::param: m_.param.notify_repeated(idx); break; - } - } - void notify_missing(arg_index idx) const { - switch(type_) { - default: - case type::group: return; - case type::param: m_.param.notify_missing(idx); break; - } - } - void notify_blocked(arg_index idx) const { - switch(type_) { - default: - case type::group: return; - case type::param: m_.param.notify_blocked(idx); break; - } - } - void notify_conflict(arg_index idx) const { - switch(type_) { - default: - case type::group: return; - case type::param: m_.param.notify_conflict(idx); break; - } - } - - bool is_param() const noexcept { return type_ == type::param; } - bool is_group() const noexcept { return type_ == type::group; } - - Param& as_param() noexcept { return m_.param; } - Group& as_group() noexcept { return m_.group; } - - const Param& as_param() const noexcept { return m_.param; } - const Group& as_group() const noexcept { return m_.group; } - - private: - void destroy_content() { - switch(type_) { - default: - case type::param: m_.param.~Param(); break; - case type::group: m_.group.~Group(); break; - } - } - - union data { - data() {} - - data(const Param& v) : param{v} {} - data( Param&& v) noexcept : param{std::move(v)} {} - - data(const Group& g) : group{g} {} - data( Group&& g) noexcept : group{std::move(g)} {} - ~data() {} - - Param param; - Group group; - }; - - data m_; - type type_; - }; - - -public: - //--------------------------------------------------------------- - using child = child_t<parameter,group>; - using value_type = child; - -private: - using children_store = std::vector<child>; - -public: - using const_iterator = children_store::const_iterator; - using iterator = children_store::iterator; - using size_type = children_store::size_type; - - - //--------------------------------------------------------------- - /** - * @brief recursively iterates over all nodes - */ - class depth_first_traverser - { - public: - //----------------------------------------------------- - struct context { - context() = default; - context(const group& p): - parent{&p}, cur{p.begin()}, end{p.end()} - {} - const group* parent = nullptr; - const_iterator cur; - const_iterator end; - }; - using context_list = std::vector<context>; - - //----------------------------------------------------- - class memento { - friend class depth_first_traverser; - int level_; - context context_; - public: - int level() const noexcept { return level_; } - const child* param() const noexcept { return &(*context_.cur); } - }; - - depth_first_traverser() = default; - - explicit - depth_first_traverser(const group& cur): stack_{} { - if(!cur.empty()) stack_.emplace_back(cur); - } - - explicit operator bool() const noexcept { - return !stack_.empty(); - } - - int level() const noexcept { - return int(stack_.size()); - } - - bool is_first_in_parent() const noexcept { - if(stack_.empty()) return false; - return (stack_.back().cur == stack_.back().parent->begin()); - } - - bool is_last_in_parent() const noexcept { - if(stack_.empty()) return false; - return (stack_.back().cur+1 == stack_.back().end); - } - - bool is_last_in_path() const noexcept { - if(stack_.empty()) return false; - for(const auto& t : stack_) { - if(t.cur+1 != t.end) return false; - } - const auto& top = stack_.back(); - //if we have to descend into group on next ++ => not last in path - if(top.cur->is_group()) return false; - return true; - } - - /** @brief inside a group of alternatives >= minlevel */ - bool is_alternative(int minlevel = 0) const noexcept { - if(stack_.empty()) return false; - if(minlevel > 0) minlevel -= 1; - if(minlevel >= int(stack_.size())) return false; - return std::any_of(stack_.begin() + minlevel, stack_.end(), - [](const context& c) { return c.parent->exclusive(); }); - } - - /** @brief repeatable or inside a repeatable group >= minlevel */ - bool is_repeatable(int minlevel = 0) const noexcept { - if(stack_.empty()) return false; - if(stack_.back().cur->repeatable()) return true; - if(minlevel > 0) minlevel -= 1; - if(minlevel >= int(stack_.size())) return false; - return std::any_of(stack_.begin() + minlevel, stack_.end(), - [](const context& c) { return c.parent->repeatable(); }); - } - - /** @brief inside a particular group */ - bool is_inside(const group* g) const noexcept { - if(!g) return false; - return std::any_of(stack_.begin(), stack_.end(), - [g](const context& c) { return c.parent == g; }); - } - - /** @brief inside group with joinable flags */ - bool joinable() const noexcept { - if(stack_.empty()) return false; - return std::any_of(stack_.begin(), stack_.end(), - [](const context& c) { return c.parent->joinable(); }); - } - - const context_list& - stack() const { - return stack_; - } - - /** @brief innermost repeat group */ - const group* - innermost_repeat_group() const noexcept { - auto i = std::find_if(stack_.rbegin(), stack_.rend(), - [](const context& c) { return c.parent->repeatable(); }); - return i != stack_.rend() ? i->parent : nullptr; - } - - /** @brief innermost exclusive (alternatives) group */ - const group* - innermost_exclusive_group() const noexcept { - auto i = std::find_if(stack_.rbegin(), stack_.rend(), - [](const context& c) { return c.parent->exclusive(); }); - return i != stack_.rend() ? i->parent : nullptr; - } - - /** @brief innermost blocking group */ - const group* - innermost_blocking_group() const noexcept { - auto i = std::find_if(stack_.rbegin(), stack_.rend(), - [](const context& c) { return c.parent->blocking(); }); - return i != stack_.rend() ? i->parent : nullptr; - } - - /** @brief returns the outermost group that will be left on next ++*/ - const group* - outermost_blocking_group_fully_explored() const noexcept { - if(stack_.empty()) return nullptr; - - const group* g = nullptr; - for(auto i = stack_.rbegin(); i != stack_.rend(); ++i) { - if(i->cur+1 == i->end) { - if(i->parent->blocking()) g = i->parent; - } else { - return g; - } - } - return g; - } - - /** @brief outermost join group */ - const group* - outermost_join_group() const noexcept { - auto i = std::find_if(stack_.begin(), stack_.end(), - [](const context& c) { return c.parent->joinable(); }); - return i != stack_.end() ? i->parent : nullptr; - } - - const group* root() const noexcept { - return stack_.empty() ? nullptr : stack_.front().parent; - } - - /** @brief common flag prefix of all flags in current group */ - arg_string common_flag_prefix() const noexcept { - if(stack_.empty()) return ""; - auto g = outermost_join_group(); - return g ? g->common_flag_prefix() : arg_string(""); - } - - const child& - operator * () const noexcept { - return *stack_.back().cur; - } - - const child* - operator -> () const noexcept { - return &(*stack_.back().cur); - } - - const group& - parent() const noexcept { - return *(stack_.back().parent); - } - - - /** @brief go to next element of depth first search */ - depth_first_traverser& - operator ++ () { - if(stack_.empty()) return *this; - //at group -> decend into group - if(stack_.back().cur->is_group()) { - stack_.emplace_back(stack_.back().cur->as_group()); - } - else { - next_sibling(); - } - return *this; - } - - /** @brief go to next sibling of current */ - depth_first_traverser& - next_sibling() { - if(stack_.empty()) return *this; - ++stack_.back().cur; - //at the end of current group? - while(stack_.back().cur == stack_.back().end) { - //go to parent - stack_.pop_back(); - if(stack_.empty()) return *this; - //go to next sibling in parent - ++stack_.back().cur; - } - return *this; - } - - /** @brief go to next position after siblings of current */ - depth_first_traverser& - next_after_siblings() { - if(stack_.empty()) return *this; - stack_.back().cur = stack_.back().end-1; - next_sibling(); - return *this; - } - - /** - * @brief - */ - depth_first_traverser& - back_to_ancestor(const group* g) { - if(!g) return *this; - while(!stack_.empty()) { - const auto& top = stack_.back().cur; - if(top->is_group() && &(top->as_group()) == g) return *this; - stack_.pop_back(); - } - return *this; - } - - /** @brief don't visit next siblings, go back to parent on next ++ - * note: renders siblings unreachable for *this - **/ - depth_first_traverser& - skip_siblings() { - if(stack_.empty()) return *this; - //future increments won't visit subsequent siblings: - stack_.back().end = stack_.back().cur+1; - return *this; - } - - /** @brief skips all other alternatives in surrounding exclusive groups - * on next ++ - * note: renders alternatives unreachable for *this - */ - depth_first_traverser& - skip_alternatives() { - if(stack_.empty()) return *this; - - //exclude all other alternatives in surrounding groups - //by making their current position the last one - for(auto& c : stack_) { - if(c.parent && c.parent->exclusive() && c.cur < c.end) - c.end = c.cur+1; - } - - return *this; - } - - void invalidate() { - stack_.clear(); - } - - inline friend bool operator == (const depth_first_traverser& a, - const depth_first_traverser& b) - { - if(a.stack_.empty() || b.stack_.empty()) return false; - - //parents not the same -> different position - if(a.stack_.back().parent != b.stack_.back().parent) return false; - - bool aEnd = a.stack_.back().cur == a.stack_.back().end; - bool bEnd = b.stack_.back().cur == b.stack_.back().end; - //either both at the end of the same parent => same position - if(aEnd && bEnd) return true; - //or only one at the end => not at the same position - if(aEnd || bEnd) return false; - return std::addressof(*a.stack_.back().cur) == - std::addressof(*b.stack_.back().cur); - } - inline friend bool operator != (const depth_first_traverser& a, - const depth_first_traverser& b) - { - return !(a == b); - } - - memento - undo_point() const { - memento m; - m.level_ = int(stack_.size()); - if(!stack_.empty()) m.context_ = stack_.back(); - return m; - } - - void undo(const memento& m) { - if(m.level_ < 1) return; - if(m.level_ <= int(stack_.size())) { - stack_.erase(stack_.begin() + m.level_, stack_.end()); - stack_.back() = m.context_; - } - else if(stack_.empty() && m.level_ == 1) { - stack_.push_back(m.context_); - } - } - - private: - context_list stack_; - }; - - - //--------------------------------------------------------------- - group() = default; - - template<class Param, class... Params> - explicit - group(doc_string docstr, Param param, Params... params): - children_{}, exclusive_{false}, joinable_{false}, scoped_{true} - { - doc(std::move(docstr)); - push_back(std::move(param), std::move(params)...); - } - - template<class... Params> - explicit - group(parameter param, Params... params): - children_{}, exclusive_{false}, joinable_{false}, scoped_{true} - { - push_back(std::move(param), std::move(params)...); - } - - template<class P2, class... Ps> - explicit - group(group p1, P2 p2, Ps... ps): - children_{}, exclusive_{false}, joinable_{false}, scoped_{true} - { - push_back(std::move(p1), std::move(p2), std::move(ps)...); - } - - - //----------------------------------------------------- - group(const group&) = default; - group(group&&) = default; - - - //--------------------------------------------------------------- - group& operator = (const group&) = default; - group& operator = (group&&) = default; - - - //--------------------------------------------------------------- - /** @brief determines if a command line argument can be matched by a - * combination of (partial) matches through any number of children - */ - group& joinable(bool yes) { - joinable_ = yes; - return *this; - } - - /** @brief returns if a command line argument can be matched by a - * combination of (partial) matches through any number of children - */ - bool joinable() const noexcept { - return joinable_; - } - - - //--------------------------------------------------------------- - /** @brief turns explicit scoping on or off - * operators , & | and other combinating functions will - * not merge groups that are marked as scoped - */ - group& scoped(bool yes) { - scoped_ = yes; - return *this; - } - - /** @brief returns true if operators , & | and other combinating functions - * will merge groups and false otherwise - */ - bool scoped() const noexcept - { - return scoped_; - } - - - //--------------------------------------------------------------- - /** @brief determines if children are mutually exclusive alternatives */ - group& exclusive(bool yes) { - exclusive_ = yes; - return *this; - } - /** @brief returns if children are mutually exclusive alternatives */ - bool exclusive() const noexcept { - return exclusive_; - } - - - //--------------------------------------------------------------- - /** @brief returns true, if any child is required to match */ - bool any_required() const - { - return std::any_of(children_.begin(), children_.end(), - [](const child& n){ return n.required(); }); - } - /** @brief returns true, if all children are required to match */ - bool all_required() const - { - return std::all_of(children_.begin(), children_.end(), - [](const child& n){ return n.required(); }); - } - - - //--------------------------------------------------------------- - /** @brief returns true if any child is optional (=non-required) */ - bool any_optional() const { - return !all_required(); - } - /** @brief returns true if all children are optional (=non-required) */ - bool all_optional() const { - return !any_required(); - } - - - //--------------------------------------------------------------- - /** @brief returns if the entire group is blocking / positional */ - bool blocking() const noexcept { - return token<group>::blocking() || (exclusive() && all_blocking()); - } - //----------------------------------------------------- - /** @brief determines if the entire group is blocking / positional */ - group& blocking(bool yes) { - return token<group>::blocking(yes); - } - - //--------------------------------------------------------------- - /** @brief returns true if any child is blocking */ - bool any_blocking() const - { - return std::any_of(children_.begin(), children_.end(), - [](const child& n){ return n.blocking(); }); - } - //--------------------------------------------------------------- - /** @brief returns true if all children is blocking */ - bool all_blocking() const - { - return std::all_of(children_.begin(), children_.end(), - [](const child& n){ return n.blocking(); }); - } - - - //--------------------------------------------------------------- - /** @brief returns if any child is a value parameter (recursive) */ - bool any_flagless() const - { - return std::any_of(children_.begin(), children_.end(), - [](const child& p){ - return p.is_param() && p.as_param().flags().empty(); - }); - } - /** @brief returns if all children are value parameters (recursive) */ - bool all_flagless() const - { - return std::all_of(children_.begin(), children_.end(), - [](const child& p){ - return p.is_param() && p.as_param().flags().empty(); - }); - } - - - //--------------------------------------------------------------- - /** @brief adds child parameter at the end */ - group& - push_back(const parameter& v) { - children_.emplace_back(v); - return *this; - } - //----------------------------------------------------- - /** @brief adds child parameter at the end */ - group& - push_back(parameter&& v) { - children_.emplace_back(std::move(v)); - return *this; - } - //----------------------------------------------------- - /** @brief adds child group at the end */ - group& - push_back(const group& g) { - children_.emplace_back(g); - return *this; - } - //----------------------------------------------------- - /** @brief adds child group at the end */ - group& - push_back(group&& g) { - children_.emplace_back(std::move(g)); - return *this; - } - - - //----------------------------------------------------- - /** @brief adds children (groups and/or parameters) */ - template<class Param1, class Param2, class... Params> - group& - push_back(Param1&& param1, Param2&& param2, Params&&... params) - { - children_.reserve(children_.size() + 2 + sizeof...(params)); - push_back(std::forward<Param1>(param1)); - push_back(std::forward<Param2>(param2), std::forward<Params>(params)...); - return *this; - } - - - //--------------------------------------------------------------- - /** @brief adds child parameter at the beginning */ - group& - push_front(const parameter& v) { - children_.emplace(children_.begin(), v); - return *this; - } - //----------------------------------------------------- - /** @brief adds child parameter at the beginning */ - group& - push_front(parameter&& v) { - children_.emplace(children_.begin(), std::move(v)); - return *this; - } - //----------------------------------------------------- - /** @brief adds child group at the beginning */ - group& - push_front(const group& g) { - children_.emplace(children_.begin(), g); - return *this; - } - //----------------------------------------------------- - /** @brief adds child group at the beginning */ - group& - push_front(group&& g) { - children_.emplace(children_.begin(), std::move(g)); - return *this; - } - - - //--------------------------------------------------------------- - /** @brief adds all children of other group at the end */ - group& - merge(group&& g) - { - children_.insert(children_.end(), - std::make_move_iterator(g.begin()), - std::make_move_iterator(g.end())); - return *this; - } - //----------------------------------------------------- - /** @brief adds all children of several other groups at the end */ - template<class... Groups> - group& - merge(group&& g1, group&& g2, Groups&&... gs) - { - merge(std::move(g1)); - merge(std::move(g2), std::forward<Groups>(gs)...); - return *this; - } - - - //--------------------------------------------------------------- - /** @brief indexed, nutable access to child */ - child& operator [] (size_type index) noexcept { - return children_[index]; - } - /** @brief indexed, non-nutable access to child */ - const child& operator [] (size_type index) const noexcept { - return children_[index]; - } - - //--------------------------------------------------------------- - /** @brief mutable access to first child */ - child& front() noexcept { return children_.front(); } - /** @brief non-mutable access to first child */ - const child& front() const noexcept { return children_.front(); } - //----------------------------------------------------- - /** @brief mutable access to last child */ - child& back() noexcept { return children_.back(); } - /** @brief non-mutable access to last child */ - const child& back() const noexcept { return children_.back(); } - - - //--------------------------------------------------------------- - /** @brief returns true, if group has no children, false otherwise */ - bool empty() const noexcept { return children_.empty(); } - - /** @brief returns number of children */ - size_type size() const noexcept { return children_.size(); } - - /** @brief returns number of nested levels; 1 for a flat group */ - size_type depth() const { - size_type n = 0; - for(const auto& c : children_) { - auto l = 1 + c.depth(); - if(l > n) n = l; - } - return n; - } - - - //--------------------------------------------------------------- - /** @brief returns mutating iterator to position of first element */ - iterator begin() noexcept { return children_.begin(); } - /** @brief returns non-mutating iterator to position of first element */ - const_iterator begin() const noexcept { return children_.begin(); } - /** @brief returns non-mutating iterator to position of first element */ - const_iterator cbegin() const noexcept { return children_.begin(); } - - /** @brief returns mutating iterator to position one past the last element */ - iterator end() noexcept { return children_.end(); } - /** @brief returns non-mutating iterator to position one past the last element */ - const_iterator end() const noexcept { return children_.end(); } - /** @brief returns non-mutating iterator to position one past the last element */ - const_iterator cend() const noexcept { return children_.end(); } - - - //--------------------------------------------------------------- - /** @brief returns augmented iterator for depth first searches - * @details traverser knows end of iteration and can skip over children - */ - depth_first_traverser - begin_dfs() const noexcept { - return depth_first_traverser{*this}; - } - - - //--------------------------------------------------------------- - /** @brief returns recursive parameter count */ - size_type param_count() const { - size_type c = 0; - for(const auto& n : children_) { - c += n.param_count(); - } - return c; - } - - - //--------------------------------------------------------------- - /** @brief returns range of all flags (recursive) */ - arg_list all_flags() const - { - std::vector<arg_string> all; - gather_flags(children_, all); - return all; - } - - /** @brief returns true, if no flag occurs as true - * prefix of any other flag (identical flags will be ignored) */ - bool flags_are_prefix_free() const - { - const auto fs = all_flags(); - - using std::begin; using std::end; - for(auto i = begin(fs), e = end(fs); i != e; ++i) { - if(!i->empty()) { - for(auto j = i+1; j != e; ++j) { - if(!j->empty() && *i != *j) { - if(i->find(*j) == 0) return false; - if(j->find(*i) == 0) return false; - } - } - } - } - - return true; - } - - - //--------------------------------------------------------------- - /** @brief returns longest common prefix of all flags */ - arg_string common_flag_prefix() const - { - arg_list prefixes; - gather_prefixes(children_, prefixes); - return str::longest_common_prefix(prefixes); - } - - -private: - //--------------------------------------------------------------- - static void - gather_flags(const children_store& nodes, arg_list& all) - { - for(const auto& p : nodes) { - if(p.is_group()) { - gather_flags(p.as_group().children_, all); - } - else { - const auto& pf = p.as_param().flags(); - using std::begin; - using std::end; - if(!pf.empty()) all.insert(end(all), begin(pf), end(pf)); - } - } - } - //--------------------------------------------------------------- - static void - gather_prefixes(const children_store& nodes, arg_list& all) - { - for(const auto& p : nodes) { - if(p.is_group()) { - gather_prefixes(p.as_group().children_, all); - } - else if(!p.as_param().flags().empty()) { - auto pfx = str::longest_common_prefix(p.as_param().flags()); - if(!pfx.empty()) all.push_back(std::move(pfx)); - } - } - } - - //--------------------------------------------------------------- - children_store children_; - bool exclusive_ = false; - bool joinable_ = false; - bool scoped_ = false; -}; - - - -/*************************************************************************//** - * - * @brief group or parameter - * - *****************************************************************************/ -using pattern = group::child; - - - -/*************************************************************************//** - * - * @brief apply an action to all parameters in a group - * - *****************************************************************************/ -template<class Action> -void for_all_params(group& g, Action&& action) -{ - for(auto& p : g) { - if(p.is_group()) { - for_all_params(p.as_group(), action); - } - else { - action(p.as_param()); - } - } -} - -template<class Action> -void for_all_params(const group& g, Action&& action) -{ - for(auto& p : g) { - if(p.is_group()) { - for_all_params(p.as_group(), action); - } - else { - action(p.as_param()); - } - } -} - - - -/*************************************************************************//** - * - * @brief makes a group of parameters and/or groups - * - *****************************************************************************/ -inline group -operator , (parameter a, parameter b) -{ - return group{std::move(a), std::move(b)}.scoped(false); -} - -//--------------------------------------------------------- -inline group -operator , (parameter a, group b) -{ - return !b.scoped() && !b.blocking() && !b.exclusive() && !b.repeatable() - && !b.joinable() && (b.doc().empty() || b.doc() == a.doc()) - ? b.push_front(std::move(a)) - : group{std::move(a), std::move(b)}.scoped(false); -} - -//--------------------------------------------------------- -inline group -operator , (group a, parameter b) -{ - return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable() - && !a.joinable() && (a.doc().empty() || a.doc() == b.doc()) - ? a.push_back(std::move(b)) - : group{std::move(a), std::move(b)}.scoped(false); -} - -//--------------------------------------------------------- -inline group -operator , (group a, group b) -{ - return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable() - && !a.joinable() && (a.doc().empty() || a.doc() == b.doc()) - ? a.push_back(std::move(b)) - : group{std::move(a), std::move(b)}.scoped(false); -} - - - -/*************************************************************************//** - * - * @brief makes a group of alternative parameters or groups - * - *****************************************************************************/ -template<class Param, class... Params> -inline group -one_of(Param param, Params... params) -{ - return group{std::move(param), std::move(params)...}.exclusive(true); -} - - -/*************************************************************************//** - * - * @brief makes a group of alternative parameters or groups - * - *****************************************************************************/ -inline group -operator | (parameter a, parameter b) -{ - return group{std::move(a), std::move(b)}.scoped(false).exclusive(true); -} - -//------------------------------------------------------------------- -inline group -operator | (parameter a, group b) -{ - return !b.scoped() && !b.blocking() && b.exclusive() && !b.repeatable() - && !b.joinable() - && (b.doc().empty() || b.doc() == a.doc()) - ? b.push_front(std::move(a)) - : group{std::move(a), std::move(b)}.scoped(false).exclusive(true); -} - -//------------------------------------------------------------------- -inline group -operator | (group a, parameter b) -{ - return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable() - && a.blocking() == b.blocking() - && (a.doc().empty() || a.doc() == b.doc()) - ? a.push_back(std::move(b)) - : group{std::move(a), std::move(b)}.scoped(false).exclusive(true); -} - -inline group -operator | (group a, group b) -{ - return !a.scoped() && a.exclusive() &&!a.repeatable() && !a.joinable() - && a.blocking() == b.blocking() - && (a.doc().empty() || a.doc() == b.doc()) - ? a.push_back(std::move(b)) - : group{std::move(a), std::move(b)}.scoped(false).exclusive(true); -} - - - -/*************************************************************************//** - * - * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!) - * no interface guarantees; might be changed or removed in the future - * - *****************************************************************************/ -namespace detail { - -inline void set_blocking(bool) {} - -template<class P, class... Ps> -void set_blocking(bool yes, P& p, Ps&... ps) { - p.blocking(yes); - set_blocking(yes, ps...); -} - -} // namespace detail - - -/*************************************************************************//** - * - * @brief makes a parameter/group sequence by making all input objects blocking - * - *****************************************************************************/ -template<class Param, class... Params> -inline group -in_sequence(Param param, Params... params) -{ - detail::set_blocking(true, param, params...); - return group{std::move(param), std::move(params)...}.scoped(true); -} - - -/*************************************************************************//** - * - * @brief makes a parameter/group sequence by making all input objects blocking - * - *****************************************************************************/ -inline group -operator & (parameter a, parameter b) -{ - a.blocking(true); - b.blocking(true); - return group{std::move(a), std::move(b)}.scoped(true); -} - -//--------------------------------------------------------- -inline group -operator & (parameter a, group b) -{ - a.blocking(true); - return group{std::move(a), std::move(b)}.scoped(true); -} - -//--------------------------------------------------------- -inline group -operator & (group a, parameter b) -{ - b.blocking(true); - if(a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable() - && (a.doc().empty() || a.doc() == b.doc())) - { - return a.push_back(std::move(b)); - } - else { - if(!a.all_blocking()) a.blocking(true); - return group{std::move(a), std::move(b)}.scoped(true); - } -} - -inline group -operator & (group a, group b) -{ - if(!b.all_blocking()) b.blocking(true); - if(a.all_blocking() && !a.exclusive() && !a.repeatable() - && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())) - { - return a.push_back(std::move(b)); - } - else { - if(!a.all_blocking()) a.blocking(true); - return group{std::move(a), std::move(b)}.scoped(true); - } -} - - - -/*************************************************************************//** - * - * @brief makes a group of parameters and/or groups - * where all single char flag params ("-a", "b", ...) are joinable - * - *****************************************************************************/ -inline group -joinable(group g) { - return g.joinable(true); -} - -//------------------------------------------------------------------- -template<class... Params> -inline group -joinable(parameter param, Params... params) -{ - return group{std::move(param), std::move(params)...}.joinable(true); -} - -template<class P2, class... Ps> -inline group -joinable(group p1, P2 p2, Ps... ps) -{ - return group{std::move(p1), std::move(p2), std::move(ps)...}.joinable(true); -} - -template<class Param, class... Params> -inline group -joinable(doc_string docstr, Param param, Params... params) -{ - return group{std::move(param), std::move(params)...} - .joinable(true).doc(std::move(docstr)); -} - - - -/*************************************************************************//** - * - * @brief makes a repeatable copy of a parameter - * - *****************************************************************************/ -inline parameter -repeatable(parameter p) { - return p.repeatable(true); -} - -/*************************************************************************//** - * - * @brief makes a repeatable copy of a group - * - *****************************************************************************/ -inline group -repeatable(group g) { - return g.repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes a group of parameters and/or groups - * that is repeatable as a whole - * Note that a repeatable group consisting entirely of non-blocking - * children is equivalent to a non-repeatable group of - * repeatable children. - * - *****************************************************************************/ -template<class P2, class... Ps> -inline group -repeatable(parameter p1, P2 p2, Ps... ps) -{ - return group{std::move(p1), std::move(p2), - std::move(ps)...}.repeatable(true); -} - -template<class P2, class... Ps> -inline group -repeatable(group p1, P2 p2, Ps... ps) -{ - return group{std::move(p1), std::move(p2), - std::move(ps)...}.repeatable(true); -} - - - -/*************************************************************************//** - * - * @brief makes a parameter greedy (match with top priority) - * - *****************************************************************************/ -inline parameter -greedy(parameter p) { - return p.greedy(true); -} - -inline parameter -operator ! (parameter p) { - return greedy(p); -} - - - -/*************************************************************************//** - * - * @brief recursively prepends a prefix to all flags - * - *****************************************************************************/ -inline parameter&& -with_prefix(const arg_string& prefix, parameter&& p) { - return std::move(with_prefix(prefix, p)); -} - - -//------------------------------------------------------------------- -inline group& -with_prefix(const arg_string& prefix, group& g) -{ - for(auto& p : g) { - if(p.is_group()) { - with_prefix(prefix, p.as_group()); - } else { - with_prefix(prefix, p.as_param()); - } - } - return g; -} - - -inline group&& -with_prefix(const arg_string& prefix, group&& params) -{ - return std::move(with_prefix(prefix, params)); -} - - -template<class Param, class... Params> -inline group -with_prefix(arg_string prefix, Param&& param, Params&&... params) -{ - return with_prefix(prefix, group{std::forward<Param>(param), - std::forward<Params>(params)...}); -} - - - -/*************************************************************************//** - * - * @brief recursively prepends a prefix to all flags - * - * @param shortpfx : used for single-letter flags - * @param longpfx : used for flags with length > 1 - * - *****************************************************************************/ -inline parameter&& -with_prefixes_short_long(const arg_string& shortpfx, const arg_string& longpfx, - parameter&& p) -{ - return std::move(with_prefixes_short_long(shortpfx, longpfx, p)); -} - - -//------------------------------------------------------------------- -inline group& -with_prefixes_short_long(const arg_string& shortFlagPrefix, - const arg_string& longFlagPrefix, - group& g) -{ - for(auto& p : g) { - if(p.is_group()) { - with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_group()); - } else { - with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_param()); - } - } - return g; -} - - -inline group&& -with_prefixes_short_long(const arg_string& shortFlagPrefix, - const arg_string& longFlagPrefix, - group&& params) -{ - return std::move(with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, - params)); -} - - -template<class Param, class... Params> -inline group -with_prefixes_short_long(const arg_string& shortFlagPrefix, - const arg_string& longFlagPrefix, - Param&& param, Params&&... params) -{ - return with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, - group{std::forward<Param>(param), - std::forward<Params>(params)...}); -} - - - -/*************************************************************************//** - * - * @brief recursively prepends a suffix to all flags - * - *****************************************************************************/ -inline parameter&& -with_suffix(const arg_string& suffix, parameter&& p) { - return std::move(with_suffix(suffix, p)); -} - - -//------------------------------------------------------------------- -inline group& -with_suffix(const arg_string& suffix, group& g) -{ - for(auto& p : g) { - if(p.is_group()) { - with_suffix(suffix, p.as_group()); - } else { - with_suffix(suffix, p.as_param()); - } - } - return g; -} - - -inline group&& -with_suffix(const arg_string& suffix, group&& params) -{ - return std::move(with_suffix(suffix, params)); -} - - -template<class Param, class... Params> -inline group -with_suffix(arg_string suffix, Param&& param, Params&&... params) -{ - return with_suffix(suffix, group{std::forward<Param>(param), - std::forward<Params>(params)...}); -} - - - -/*************************************************************************//** - * - * @brief recursively prepends a suffix to all flags - * - * @param shortsfx : used for single-letter flags - * @param longsfx : used for flags with length > 1 - * - *****************************************************************************/ -inline parameter&& -with_suffixes_short_long(const arg_string& shortsfx, const arg_string& longsfx, - parameter&& p) -{ - return std::move(with_suffixes_short_long(shortsfx, longsfx, p)); -} - - -//------------------------------------------------------------------- -inline group& -with_suffixes_short_long(const arg_string& shortFlagSuffix, - const arg_string& longFlagSuffix, - group& g) -{ - for(auto& p : g) { - if(p.is_group()) { - with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_group()); - } else { - with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_param()); - } - } - return g; -} - - -inline group&& -with_suffixes_short_long(const arg_string& shortFlagSuffix, - const arg_string& longFlagSuffix, - group&& params) -{ - return std::move(with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, - params)); -} - - -template<class Param, class... Params> -inline group -with_suffixes_short_long(const arg_string& shortFlagSuffix, - const arg_string& longFlagSuffix, - Param&& param, Params&&... params) -{ - return with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, - group{std::forward<Param>(param), - std::forward<Params>(params)...}); -} - - - - - - - - -/*************************************************************************//** - * - * @brief parsing implementation details - * - *****************************************************************************/ - -namespace detail { - - -/*************************************************************************//** - * - * @brief DFS traverser that keeps track of 'scopes' - * scope = all parameters that are either bounded by - * two blocking parameters on the same depth level - * or the beginning/end of the outermost group - * - *****************************************************************************/ -class scoped_dfs_traverser -{ -public: - using dfs_traverser = group::depth_first_traverser; - - scoped_dfs_traverser() = default; - - explicit - scoped_dfs_traverser(const group& g): - pos_{g}, lastMatch_{}, posAfterLastMatch_{}, scopes_{}, - ignoreBlocks_{false}, - repeatGroupStarted_{false}, repeatGroupContinues_{false} - {} - - const dfs_traverser& base() const noexcept { return pos_; } - const dfs_traverser& last_match() const noexcept { return lastMatch_; } - - const group& parent() const noexcept { return pos_.parent(); } - - const group* innermost_repeat_group() const noexcept { - return pos_.innermost_repeat_group(); - } - const group* outermost_join_group() const noexcept { - return pos_.outermost_join_group(); - } - const group* innermost_blocking_group() const noexcept { - return pos_.innermost_blocking_group(); - } - const group* innermost_exclusive_group() const noexcept { - return pos_.innermost_exclusive_group(); - } - - const pattern* operator ->() const noexcept { return pos_.operator->(); } - const pattern& operator *() const noexcept { return *pos_; } - - const pattern* ptr() const noexcept { return pos_.operator->(); } - - explicit operator bool() const noexcept { return bool(pos_); } - - bool joinable() const noexcept { return pos_.joinable(); } - arg_string common_flag_prefix() const { return pos_.common_flag_prefix(); } - - void ignore_blocking(bool yes) { ignoreBlocks_ = yes; } - - void invalidate() { - pos_.invalidate(); - } - - bool matched() const noexcept { - return (pos_ == lastMatch_); - } - - bool start_of_repeat_group() const noexcept { return repeatGroupStarted_; } - - //----------------------------------------------------- - scoped_dfs_traverser& - next_sibling() { pos_.next_sibling(); return *this; } - - scoped_dfs_traverser& - next_after_siblings() { pos_.next_after_siblings(); return *this; } - - - //----------------------------------------------------- - scoped_dfs_traverser& - operator ++ () - { - if(!pos_) return *this; - - if(pos_.is_last_in_path()) { - return_to_outermost_scope(); - return *this; - } - - //current pattern can block if it didn't match already - if(ignoreBlocks_ || matched()) { - ++pos_; - } - else if(!pos_->is_group()) { - //current group can block if we didn't have any match in it - const group* g = pos_.outermost_blocking_group_fully_explored(); - //no match in 'g' before -> skip to after its siblings - if(g && !lastMatch_.is_inside(g)) { - pos_.back_to_ancestor(g).next_after_siblings(); - if(!pos_) return_to_outermost_scope(); - } - else if(pos_->blocking()) { - if(pos_.parent().exclusive()) { - pos_.next_sibling(); - } else { - //no match => skip siblings of blocking param - pos_.next_after_siblings(); - } - if(!pos_) return_to_outermost_scope(); - } else { - ++pos_; - } - } else { - ++pos_; - } - check_if_left_scope(); - return *this; - } - - //----------------------------------------------------- - void next_after_match(scoped_dfs_traverser match) - { - if(!match || ignoreBlocks_) return; - - check_repeat_group_start(match); - - lastMatch_ = match.base(); - - // if there is a blocking ancestor -> go back to it - if(!match->blocking()) { - match.pos_.back_to_ancestor(match.innermost_blocking_group()); - } - - //if match is not in current position & current position is blocking - //=> current position has to be advanced by one so that it is - //no longer reachable within current scope - //(can happen for repeatable, blocking parameters) - if(match.base() != pos_ && pos_->blocking()) pos_.next_sibling(); - - if(match->blocking()) { - if(match.pos_.is_alternative()) { - //discard other alternatives - match.pos_.skip_alternatives(); - } - - if(is_last_in_current_scope(match.pos_)) { - //if current param is not repeatable -> back to previous scope - if(!match->repeatable() && !match->is_group()) { - pos_ = std::move(match.pos_); - if(!scopes_.empty()) pos_.undo(scopes_.top()); - } - else { //stay at match position - pos_ = std::move(match.pos_); - } - } - else { //not last in current group - //if current param is not repeatable, go directly to next - if(!match->repeatable() && !match->is_group()) { - ++match.pos_; - } - - if(match.pos_.level() > pos_.level()) { - scopes_.push(pos_.undo_point()); - pos_ = std::move(match.pos_); - } - else if(match.pos_.level() < pos_.level()) { - return_to_level(match.pos_.level()); - } - else { - pos_ = std::move(match.pos_); - } - } - posAfterLastMatch_ = pos_; - } - else { - if(match.pos_.level() < pos_.level()) { - return_to_level(match.pos_.level()); - } - posAfterLastMatch_ = pos_; - } - repeatGroupContinues_ = repeat_group_continues(); - } - -private: - //----------------------------------------------------- - bool is_last_in_current_scope(const dfs_traverser& pos) const - { - if(scopes_.empty()) return pos.is_last_in_path(); - //check if we would leave the current scope on ++ - auto p = pos; - ++p; - return p.level() < scopes_.top().level(); - } - - //----------------------------------------------------- - void check_repeat_group_start(const scoped_dfs_traverser& newMatch) - { - const auto newrg = newMatch.innermost_repeat_group(); - if(!newrg) { - repeatGroupStarted_ = false; - } - else if(lastMatch_.innermost_repeat_group() != newrg) { - repeatGroupStarted_ = true; - } - else if(!repeatGroupContinues_ || !newMatch.repeatGroupContinues_) { - repeatGroupStarted_ = true; - } - else { - //special case: repeat group is outermost group - //=> we can never really 'leave' and 'reenter' it - //but if the current scope is the first element, then we are - //conceptually at a position 'before' the group - repeatGroupStarted_ = scopes_.empty() || ( - newrg == pos_.root() && - scopes_.top().param() == &(*pos_.root()->begin()) ); - } - repeatGroupContinues_ = repeatGroupStarted_; - } - - //----------------------------------------------------- - bool repeat_group_continues() const - { - if(!repeatGroupContinues_) return false; - const auto curRepGroup = pos_.innermost_repeat_group(); - if(!curRepGroup) return false; - if(curRepGroup != lastMatch_.innermost_repeat_group()) return false; - if(!posAfterLastMatch_) return false; - return true; - } - - //----------------------------------------------------- - void check_if_left_scope() - { - if(posAfterLastMatch_) { - if(pos_.level() < posAfterLastMatch_.level()) { - while(!scopes_.empty() && scopes_.top().level() >= pos_.level()) { - pos_.undo(scopes_.top()); - scopes_.pop(); - } - posAfterLastMatch_.invalidate(); - } - } - while(!scopes_.empty() && scopes_.top().level() > pos_.level()) { - pos_.undo(scopes_.top()); - scopes_.pop(); - } - repeatGroupContinues_ = repeat_group_continues(); - } - - //----------------------------------------------------- - void return_to_outermost_scope() - { - posAfterLastMatch_.invalidate(); - - if(scopes_.empty()) { - pos_.invalidate(); - repeatGroupContinues_ = false; - return; - } - - while(!scopes_.empty() && (!pos_ || pos_.level() >= 1)) { - pos_.undo(scopes_.top()); - scopes_.pop(); - } - while(!scopes_.empty()) scopes_.pop(); - - repeatGroupContinues_ = repeat_group_continues(); - } - - //----------------------------------------------------- - void return_to_level(int level) - { - if(pos_.level() <= level) return; - while(!scopes_.empty() && pos_.level() > level) { - pos_.undo(scopes_.top()); - scopes_.pop(); - } - }; - - dfs_traverser pos_; - dfs_traverser lastMatch_; - dfs_traverser posAfterLastMatch_; - std::stack<dfs_traverser::memento> scopes_; - bool ignoreBlocks_ = false; - bool repeatGroupStarted_ = false; - bool repeatGroupContinues_ = false; -}; - - - - -/***************************************************************************** - * - * some parameter property predicates - * - *****************************************************************************/ -struct select_all { - bool operator () (const parameter&) const noexcept { return true; } -}; - -struct select_flags { - bool operator () (const parameter& p) const noexcept { - return !p.flags().empty(); - } -}; - -struct select_values { - bool operator () (const parameter& p) const noexcept { - return p.flags().empty(); - } -}; - - - -/*************************************************************************//** - * - * @brief result of a matching operation - * - *****************************************************************************/ -class match_t { -public: - using size_type = arg_string::size_type; - - match_t() = default; - - match_t(arg_string s, scoped_dfs_traverser p): - str_{std::move(s)}, pos_{std::move(p)} - {} - - size_type length() const noexcept { return str_.size(); } - - const arg_string& str() const noexcept { return str_; } - const scoped_dfs_traverser& pos() const noexcept { return pos_; } - - explicit operator bool() const noexcept { return bool(pos_); } - -private: - arg_string str_; - scoped_dfs_traverser pos_; -}; - - - -/*************************************************************************//** - * - * @brief finds the first parameter that matches a given string; - * candidate parameters are traversed using a scoped DFS traverser - * - *****************************************************************************/ -template<class ParamSelector> -match_t -full_match(scoped_dfs_traverser pos, const arg_string& arg, - const ParamSelector& select) -{ - while(pos) { - if(pos->is_param()) { - const auto& param = pos->as_param(); - if(select(param)) { - const auto match = param.match(arg); - if(match && match.length() == arg.size()) { - return match_t{arg, std::move(pos)}; - } - } - } - ++pos; - } - return match_t{}; -} - - - -/*************************************************************************//** - * - * @brief finds the first parameter that matches any (non-empty) prefix - * of a given string; - * candidate parameters are traversed using a scoped DFS traverser - * - *****************************************************************************/ -template<class ParamSelector> -match_t -longest_prefix_match(scoped_dfs_traverser pos, const arg_string& arg, - const ParamSelector& select) -{ - match_t longest; - - while(pos) { - if(pos->is_param()) { - const auto& param = pos->as_param(); - if(select(param)) { - auto match = param.match(arg); - if(match.prefix()) { - if(match.length() == arg.size()) { - return match_t{arg, std::move(pos)}; - } - else if(match.length() > longest.length()) { - longest = match_t{arg.substr(match.at(), match.length()), - pos}; - } - } - } - } - ++pos; - } - return longest; -} - - - -/*************************************************************************//** - * - * @brief finds the first parameter that partially matches a given string; - * candidate parameters are traversed using a scoped DFS traverser - * - *****************************************************************************/ -template<class ParamSelector> -match_t -partial_match(scoped_dfs_traverser pos, const arg_string& arg, - const ParamSelector& select) -{ - while(pos) { - if(pos->is_param()) { - const auto& param = pos->as_param(); - if(select(param)) { - const auto match = param.match(arg); - if(match) { - return match_t{arg.substr(match.at(), match.length()), - std::move(pos)}; - } - } - } - ++pos; - } - return match_t{}; -} - -} //namespace detail - - - - - - -/***************************************************************//** - * - * @brief default command line arguments parser - * - *******************************************************************/ -class parser -{ -public: - using dfs_traverser = group::depth_first_traverser; - using scoped_dfs_traverser = detail::scoped_dfs_traverser; - - - /*****************************************************//** - * @brief arg -> parameter mapping - *********************************************************/ - class arg_mapping { - public: - friend class parser; - - explicit - arg_mapping(arg_index idx, arg_string s, - const dfs_traverser& match) - : - index_{idx}, arg_{std::move(s)}, match_{match}, - repeat_{0}, startsRepeatGroup_{false}, - blocked_{false}, conflict_{false} - {} - - explicit - arg_mapping(arg_index idx, arg_string s) : - index_{idx}, arg_{std::move(s)}, match_{}, - repeat_{0}, startsRepeatGroup_{false}, - blocked_{false}, conflict_{false} - {} - - arg_index index() const noexcept { return index_; } - const arg_string& arg() const noexcept { return arg_; } - - const parameter* param() const noexcept { - return match_ && match_->is_param() - ? &(match_->as_param()) : nullptr; - } - - std::size_t repeat() const noexcept { return repeat_; } - - bool blocked() const noexcept { return blocked_; } - bool conflict() const noexcept { return conflict_; } - - bool bad_repeat() const noexcept { - if(!param()) return false; - return repeat_ > 0 && !param()->repeatable() - && !match_.innermost_repeat_group(); - } - - bool any_error() const noexcept { - return !match_ || blocked() || conflict() || bad_repeat(); - } - - private: - arg_index index_; - arg_string arg_; - dfs_traverser match_; - std::size_t repeat_; - bool startsRepeatGroup_; - bool blocked_; - bool conflict_; - }; - - /*****************************************************//** - * @brief references a non-matched, required parameter - *********************************************************/ - class missing_event { - public: - explicit - missing_event(const parameter* p, arg_index after): - param_{p}, aftIndex_{after} - {} - - const parameter* param() const noexcept { return param_; } - - arg_index after_index() const noexcept { return aftIndex_; } - - private: - const parameter* param_; - arg_index aftIndex_; - }; - - //----------------------------------------------------- - using missing_events = std::vector<missing_event>; - using arg_mappings = std::vector<arg_mapping>; - - -private: - struct miss_candidate { - miss_candidate(dfs_traverser p, arg_index idx, - bool firstInRepeatGroup = false): - pos{std::move(p)}, index{idx}, - startsRepeatGroup{firstInRepeatGroup} - {} - - dfs_traverser pos; - arg_index index; - bool startsRepeatGroup; - }; - using miss_candidates = std::vector<miss_candidate>; - - -public: - //--------------------------------------------------------------- - /** @brief initializes parser with a command line interface - * @param offset = argument index offset used for reports - * */ - explicit - parser(const group& root, arg_index offset = 0): - root_{&root}, pos_{root}, - index_{offset-1}, eaten_{0}, - args_{}, missCand_{}, blocked_{false} - { - for_each_potential_miss(dfs_traverser{root}, - [this](const dfs_traverser& p){ - missCand_.emplace_back(p, index_); - }); - } - - - //--------------------------------------------------------------- - /** @brief processes one command line argument */ - bool operator() (const arg_string& arg) - { - ++eaten_; - ++index_; - - if(!valid()) return false; - - if(!blocked_ && try_match(arg)) return true; - - if(try_match_blocked(arg)) return false; - - //skipping of blocking & required patterns is not allowed - if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) { - blocked_ = true; - } - - add_nomatch(arg); - return false; - } - - - //--------------------------------------------------------------- - /** @brief returns range of argument -> parameter mappings */ - const arg_mappings& args() const { - return args_; - } - - /** @brief returns list of missing events */ - missing_events missed() const { - missing_events misses; - misses.reserve(missCand_.size()); - for(auto i = missCand_.begin(); i != missCand_.end(); ++i) { - misses.emplace_back(&(i->pos->as_param()), i->index); - } - return misses; - } - - /** @brief returns number of processed command line arguments */ - arg_index parse_count() const noexcept { return eaten_; } - - /** @brief returns false if previously processed command line arguments - * lead to an invalid / inconsistent parsing result - */ - bool valid() const noexcept { return bool(pos_); } - - /** @brief returns false if previously processed command line arguments - * lead to an invalid / inconsistent parsing result - */ - explicit operator bool() const noexcept { return valid(); } - - -private: - //--------------------------------------------------------------- - using match_t = detail::match_t; - - - //--------------------------------------------------------------- - /** @brief try to match argument with unreachable parameter */ - bool try_match_blocked(const arg_string& arg) - { - //try to match ahead (using temporary parser) - if(pos_) { - auto ahead = *this; - if(try_match_blocked(std::move(ahead), arg)) return true; - } - - //try to match from the beginning (using temporary parser) - if(root_) { - parser all{*root_, index_+1}; - if(try_match_blocked(std::move(all), arg)) return true; - } - - return false; - } - - //--------------------------------------------------------------- - bool try_match_blocked(parser&& parse, const arg_string& arg) - { - const auto nold = int(parse.args_.size()); - - parse.pos_.ignore_blocking(true); - - if(!parse.try_match(arg)) return false; - - for(auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) { - args_.push_back(*i); - args_.back().blocked_ = true; - } - return true; - } - - //--------------------------------------------------------------- - /** @brief try to find a parameter/pattern that matches 'arg' */ - bool try_match(const arg_string& arg) - { - //match greedy parameters before everything else - if(pos_->is_param() && pos_->blocking() && pos_->as_param().greedy()) { - const auto match = pos_->as_param().match(arg); - if(match && match.length() == arg.size()) { - add_match(detail::match_t{arg,pos_}); - return true; - } - } - - //try flags first (alone, joinable or strict sequence) - if(try_match_full(arg, detail::select_flags{})) return true; - if(try_match_joined_flags(arg)) return true; - if(try_match_joined_sequence(arg, detail::select_flags{})) return true; - //try value params (alone or strict sequence) - if(try_match_full(arg, detail::select_values{})) return true; - if(try_match_joined_sequence(arg, detail::select_all{})) return true; - //try joinable params + values in any order - if(try_match_joined_params(arg)) return true; - return false; - } - - //--------------------------------------------------------------- - /** - * @brief try to match full argument - * @param select : predicate that candidate parameters must satisfy - */ - template<class ParamSelector> - bool try_match_full(const arg_string& arg, const ParamSelector& select) - { - auto match = detail::full_match(pos_, arg, select); - if(!match) return false; - add_match(match); - return true; - } - - //--------------------------------------------------------------- - /** - * @brief try to match argument as blocking sequence of parameters - * @param select : predicate that a parameter matching the prefix of - * 'arg' must satisfy - */ - template<class ParamSelector> - bool try_match_joined_sequence(arg_string arg, - const ParamSelector& acceptFirst) - { - auto fstMatch = detail::longest_prefix_match(pos_, arg, acceptFirst); - - if(!fstMatch) return false; - - if(fstMatch.str().size() == arg.size()) { - add_match(fstMatch); - return true; - } - - if(!fstMatch.pos()->blocking()) return false; - - auto pos = fstMatch.pos(); - pos.ignore_blocking(true); - const auto parent = &pos.parent(); - if(!pos->repeatable()) ++pos; - - arg.erase(0, fstMatch.str().size()); - std::vector<match_t> matches { std::move(fstMatch) }; - - while(!arg.empty() && pos && - pos->blocking() && pos->is_param() && - (&pos.parent() == parent)) - { - auto match = pos->as_param().match(arg); - - if(match.prefix()) { - matches.emplace_back(arg.substr(0,match.length()), pos); - arg.erase(0, match.length()); - if(!pos->repeatable()) ++pos; - } - else { - if(!pos->repeatable()) return false; - ++pos; - } - - } - //if arg not fully covered => discard temporary matches - if(!arg.empty() || matches.empty()) return false; - - for(const auto& m : matches) add_match(m); - return true; - } - - //----------------------------------------------------- - /** @brief try to match 'arg' as a concatenation of joinable flags */ - bool try_match_joined_flags(const arg_string& arg) - { - return find_join_group(pos_, [&](const group& g) { - return try_match_joined(g, arg, detail::select_flags{}, - g.common_flag_prefix()); - }); - } - - //--------------------------------------------------------------- - /** @brief try to match 'arg' as a concatenation of joinable parameters */ - bool try_match_joined_params(const arg_string& arg) - { - return find_join_group(pos_, [&](const group& g) { - return try_match_joined(g, arg, detail::select_all{}); - }); - } - - //----------------------------------------------------- - /** @brief try to match 'arg' as concatenation of joinable parameters - * that are all contained within one group - */ - template<class ParamSelector> - bool try_match_joined(const group& joinGroup, arg_string arg, - const ParamSelector& select, - const arg_string& prefix = "") - { - //temporary parser with 'joinGroup' as top-level group - parser parse {joinGroup}; - //records temporary matches - std::vector<match_t> matches; - - while(!arg.empty()) { - auto match = detail::longest_prefix_match(parse.pos_, arg, select); - - if(!match) return false; - - arg.erase(0, match.str().size()); - //make sure prefix is always present after the first match - //so that, e.g., flags "-a" and "-b" will be found in "-ab" - if(!arg.empty() && !prefix.empty() && arg.find(prefix) != 0 && - prefix != match.str()) - { - arg.insert(0,prefix); - } - - parse.add_match(match); - matches.push_back(std::move(match)); - } - - if(!arg.empty() || matches.empty()) return false; - - if(!parse.missCand_.empty()) return false; - for(const auto& a : parse.args_) if(a.any_error()) return false; - - //replay matches onto *this - for(const auto& m : matches) add_match(m); - return true; - } - - //----------------------------------------------------- - template<class GroupSelector> - bool find_join_group(const scoped_dfs_traverser& start, - const GroupSelector& accept) const - { - if(start && start.parent().joinable()) { - const auto& g = start.parent(); - if(accept(g)) return true; - return false; - } - - auto pos = start; - while(pos) { - if(pos->is_group() && pos->as_group().joinable()) { - const auto& g = pos->as_group(); - if(accept(g)) return true; - pos.next_sibling(); - } - else { - ++pos; - } - } - return false; - } - - - //--------------------------------------------------------------- - void add_nomatch(const arg_string& arg) { - args_.emplace_back(index_, arg); - } - - - //--------------------------------------------------------------- - void add_match(const match_t& match) - { - const auto& pos = match.pos(); - if(!pos || !pos->is_param()) return; - - pos_.next_after_match(pos); - - arg_mapping newArg{index_, match.str(), pos.base()}; - newArg.repeat_ = occurrences_of(&pos->as_param()); - newArg.conflict_ = check_conflicts(pos.base()); - newArg.startsRepeatGroup_ = pos_.start_of_repeat_group(); - args_.push_back(std::move(newArg)); - - add_miss_candidates_after(pos); - clean_miss_candidates_for(pos.base()); - discard_alternative_miss_candidates(pos.base()); - - } - - //----------------------------------------------------- - bool check_conflicts(const dfs_traverser& match) - { - if(pos_.start_of_repeat_group()) return false; - bool conflict = false; - for(const auto& m : match.stack()) { - if(m.parent->exclusive()) { - for(auto i = args_.rbegin(); i != args_.rend(); ++i) { - if(!i->blocked()) { - for(const auto& c : i->match_.stack()) { - //sibling within same exclusive group => conflict - if(c.parent == m.parent && c.cur != m.cur) { - conflict = true; - i->conflict_ = true; - } - } - } - //check for conflicts only within current repeat cycle - if(i->startsRepeatGroup_) break; - } - } - } - return conflict; - } - - //----------------------------------------------------- - void clean_miss_candidates_for(const dfs_traverser& match) - { - auto i = std::find_if(missCand_.rbegin(), missCand_.rend(), - [&](const miss_candidate& m) { - return &(*m.pos) == &(*match); - }); - - if(i != missCand_.rend()) { - missCand_.erase(prev(i.base())); - } - } - - //----------------------------------------------------- - void discard_alternative_miss_candidates(const dfs_traverser& match) - { - if(missCand_.empty()) return; - //find out, if miss candidate is sibling of one of the same - //alternative groups that the current match is a member of - //if so, we can discard the miss - - //go through all exclusive groups of matching pattern - for(const auto& m : match.stack()) { - if(m.parent->exclusive()) { - for(auto i = int(missCand_.size())-1; i >= 0; --i) { - bool removed = false; - for(const auto& c : missCand_[i].pos.stack()) { - //sibling within same exclusive group => discard - if(c.parent == m.parent && c.cur != m.cur) { - missCand_.erase(missCand_.begin() + i); - if(missCand_.empty()) return; - removed = true; - break; - } - } - //remove miss candidates only within current repeat cycle - if(i > 0 && removed) { - if(missCand_[i-1].startsRepeatGroup) break; - } else { - if(missCand_[i].startsRepeatGroup) break; - } - } - } - } - } - - //----------------------------------------------------- - void add_miss_candidates_after(const scoped_dfs_traverser& match) - { - auto npos = match.base(); - if(npos.is_alternative()) npos.skip_alternatives(); - ++npos; - //need to add potential misses if: - //either new repeat group was started - const auto newRepGroup = match.innermost_repeat_group(); - if(newRepGroup) { - if(pos_.start_of_repeat_group()) { - for_each_potential_miss(std::move(npos), - [&,this](const dfs_traverser& pos) { - //only add candidates within repeat group - if(newRepGroup == pos.innermost_repeat_group()) { - missCand_.emplace_back(pos, index_, true); - } - }); - } - } - //... or an optional blocking param was hit - else if(match->blocking() && !match->required() && - npos.level() >= match.base().level()) - { - for_each_potential_miss(std::move(npos), - [&,this](const dfs_traverser& pos) { - //only add new candidates - if(std::find_if(missCand_.begin(), missCand_.end(), - [&](const miss_candidate& c){ - return &(*c.pos) == &(*pos); - }) == missCand_.end()) - { - missCand_.emplace_back(pos, index_); - } - }); - } - - } - - //----------------------------------------------------- - template<class Action> - static void - for_each_potential_miss(dfs_traverser pos, Action&& action) - { - const auto level = pos.level(); - while(pos && pos.level() >= level) { - if(pos->is_group() ) { - const auto& g = pos->as_group(); - if(g.all_optional() || (g.exclusive() && g.any_optional())) { - pos.next_sibling(); - } else { - ++pos; - } - } else { //param - if(pos->required()) { - action(pos); - ++pos; - } else if(pos->blocking()) { //optional + blocking - pos.next_after_siblings(); - } else { - ++pos; - } - } - } - } - - - //--------------------------------------------------------------- - std::size_t occurrences_of(const parameter* p) const - { - if(!p) return 0; - - auto i = std::find_if(args_.rbegin(), args_.rend(), - [p](const arg_mapping& a){ return a.param() == p; }); - - if(i != args_.rend()) return i->repeat() + 1; - return 0; - } - - - //--------------------------------------------------------------- - const group* root_; - scoped_dfs_traverser pos_; - arg_index index_; - arg_index eaten_; - arg_mappings args_; - miss_candidates missCand_; - bool blocked_; -}; - - - - -/*************************************************************************//** - * - * @brief contains argument -> parameter mappings - * and missing parameters - * - *****************************************************************************/ -class parsing_result -{ -public: - using arg_mapping = parser::arg_mapping; - using arg_mappings = parser::arg_mappings; - using missing_event = parser::missing_event; - using missing_events = parser::missing_events; - using iterator = arg_mappings::const_iterator; - - //----------------------------------------------------- - /** @brief default: empty result */ - parsing_result() = default; - - parsing_result(arg_mappings arg2param, missing_events misses): - arg2param_{std::move(arg2param)}, missing_{std::move(misses)} - {} - - //----------------------------------------------------- - /** @brief returns number of arguments that could not be mapped to - * a parameter - */ - arg_mappings::size_type - unmapped_args_count() const noexcept { - return std::count_if(arg2param_.begin(), arg2param_.end(), - [](const arg_mapping& a){ return !a.param(); }); - } - - /** @brief returns if any argument could only be matched by an - * unreachable parameter - */ - bool any_blocked() const noexcept { - return std::any_of(arg2param_.begin(), arg2param_.end(), - [](const arg_mapping& a){ return a.blocked(); }); - } - - /** @brief returns if any argument matched more than one parameter - * that were mutually exclusive */ - bool any_conflict() const noexcept { - return std::any_of(arg2param_.begin(), arg2param_.end(), - [](const arg_mapping& a){ return a.conflict(); }); - } - - /** @brief returns if any parameter matched repeatedly although - * it was not allowed to */ - bool any_bad_repeat() const noexcept { - return std::any_of(arg2param_.begin(), arg2param_.end(), - [](const arg_mapping& a){ return a.bad_repeat(); }); - } - - /** @brief returns true if any parsing error / violation of the - * command line interface definition occurred */ - bool any_error() const noexcept { - return unmapped_args_count() > 0 || !missing().empty() || - any_blocked() || any_conflict() || any_bad_repeat(); - } - - /** @brief returns true if no parsing error / violation of the - * command line interface definition occurred */ - explicit operator bool() const noexcept { return !any_error(); } - - /** @brief access to range of missing parameter match events */ - const missing_events& missing() const noexcept { return missing_; } - - /** @brief returns non-mutating iterator to position of - * first argument -> parameter mapping */ - iterator begin() const noexcept { return arg2param_.begin(); } - /** @brief returns non-mutating iterator to position one past the - * last argument -> parameter mapping */ - iterator end() const noexcept { return arg2param_.end(); } - -private: - //----------------------------------------------------- - arg_mappings arg2param_; - missing_events missing_; -}; - - - - -namespace detail { -namespace { - -/*************************************************************************//** - * - * @brief correct some common problems - * does not - and MUST NOT - change the number of arguments - * (no insertions or deletions allowed) - * - *****************************************************************************/ -void sanitize_args(arg_list& args) -{ - //e.g. {"-o12", ".34"} -> {"-o", "12.34"} - - if(args.empty()) return; - - for(auto i = begin(args)+1; i != end(args); ++i) { - if(i != begin(args) && i->size() > 1 && - i->find('.') == 0 && std::isdigit((*i)[1]) ) - { - //find trailing digits in previous arg - using std::prev; - auto& prv = *prev(i); - auto fstDigit = std::find_if_not(prv.rbegin(), prv.rend(), - [](arg_string::value_type c){ - return std::isdigit(c); - }).base(); - - //handle leading sign - if(fstDigit > prv.begin() && - (*prev(fstDigit) == '+' || *prev(fstDigit) == '-')) - { - --fstDigit; - } - - //prepend digits from previous arg - i->insert(begin(*i), fstDigit, end(prv)); - - //erase digits in previous arg - prv.erase(fstDigit, end(prv)); - } - } -} - - - -/*************************************************************************//** - * - * @brief executes actions based on a parsing result - * - *****************************************************************************/ -void execute_actions(const parsing_result& res) -{ - for(const auto& m : res) { - if(m.param()) { - const auto& param = *(m.param()); - - if(m.repeat() > 0) param.notify_repeated(m.index()); - if(m.blocked()) param.notify_blocked(m.index()); - if(m.conflict()) param.notify_conflict(m.index()); - //main action - if(!m.any_error()) param.execute_actions(m.arg()); - } - } - - for(auto m : res.missing()) { - if(m.param()) m.param()->notify_missing(m.after_index()); - } -} - - - -/*************************************************************************//** - * - * @brief parses input args - * - *****************************************************************************/ -static parsing_result -parse_args(const arg_list& args, const group& cli, - arg_index offset = 0) -{ - //parse args and store unrecognized arg indices - parser parse{cli, offset}; - for(const auto& arg : args) { - parse(arg); - if(!parse.valid()) break; - } - - return parsing_result{parse.args(), parse.missed()}; -} - -/*************************************************************************//** - * - * @brief parses input args & executes actions - * - *****************************************************************************/ -static parsing_result -parse_and_execute(const arg_list& args, const group& cli, - arg_index offset = 0) -{ - auto result = parse_args(args, cli, offset); - - execute_actions(result); - - return result; -} - -} //anonymous namespace -} // namespace detail - - - - -/*************************************************************************//** - * - * @brief parses vector of arg strings and executes actions - * - *****************************************************************************/ -inline parsing_result -parse(arg_list args, const group& cli) -{ - //detail::sanitize_args(args); - return detail::parse_and_execute(args, cli); -} - - -/*************************************************************************//** - * - * @brief parses initializer_list of C-style arg strings and executes actions - * - *****************************************************************************/ -inline parsing_result -parse(std::initializer_list<const char*> arglist, const group& cli) -{ - arg_list args; - args.reserve(arglist.size()); - for(auto a : arglist) { - args.push_back(a); - } - - return parse(std::move(args), cli); -} - - -/*************************************************************************//** - * - * @brief parses range of arg strings and executes actions - * - *****************************************************************************/ -template<class InputIterator> -inline parsing_result -parse(InputIterator first, InputIterator last, const group& cli) -{ - return parse(arg_list(first,last), cli); -} - - -/*************************************************************************//** - * - * @brief parses the standard array of command line arguments; omits argv[0] - * - *****************************************************************************/ -inline parsing_result -parse(const int argc, char* argv[], const group& cli, arg_index offset = 1) -{ - arg_list args; - if(offset < argc) args.assign(argv+offset, argv+argc); - //detail::sanitize_args(args); - return detail::parse_and_execute(args, cli, offset); -} - - - - - - -/*************************************************************************//** - * - * @brief filter predicate for parameters and groups; - * Can be used to limit documentation generation to parameter subsets. - * - *****************************************************************************/ -class param_filter -{ -public: - /** @brief only allow parameters with given prefix */ - param_filter& prefix(const arg_string& p) noexcept { - prefix_ = p; return *this; - } - /** @brief only allow parameters with given prefix */ - param_filter& prefix(arg_string&& p) noexcept { - prefix_ = std::move(p); return *this; - } - const arg_string& prefix() const noexcept { return prefix_; } - - /** @brief only allow parameters with given requirement status */ - param_filter& required(tri t) noexcept { required_ = t; return *this; } - tri required() const noexcept { return required_; } - - /** @brief only allow parameters with given blocking status */ - param_filter& blocking(tri t) noexcept { blocking_ = t; return *this; } - tri blocking() const noexcept { return blocking_; } - - /** @brief only allow parameters with given repeatable status */ - param_filter& repeatable(tri t) noexcept { repeatable_ = t; return *this; } - tri repeatable() const noexcept { return repeatable_; } - - /** @brief only allow parameters with given docstring status */ - param_filter& has_doc(tri t) noexcept { hasDoc_ = t; return *this; } - tri has_doc() const noexcept { return hasDoc_; } - - - /** @brief returns true, if parameter satisfies all filters */ - bool operator() (const parameter& p) const noexcept { - if(!prefix_.empty()) { - if(!std::any_of(p.flags().begin(), p.flags().end(), - [&](const arg_string& flag){ - return str::has_prefix(flag, prefix_); - })) return false; - } - if(required() != p.required()) return false; - if(blocking() != p.blocking()) return false; - if(repeatable() != p.repeatable()) return false; - if(has_doc() != !p.doc().empty()) return false; - return true; - } - -private: - arg_string prefix_; - tri required_ = tri::either; - tri blocking_ = tri::either; - tri repeatable_ = tri::either; - tri hasDoc_ = tri::yes; -}; - - - - - - -/*************************************************************************//** - * - * @brief documentation formatting options - * - *****************************************************************************/ -class doc_formatting -{ -public: - using string = doc_string; - - /** @brief same as 'first_column' */ -#if __cplusplus >= 201402L - [[deprecated]] -#endif - doc_formatting& start_column(int col) { return first_column(col); } -#if __cplusplus >= 201402L - [[deprecated]] -#endif - int start_column() const noexcept { return first_column(); } - - /** @brief determines column where documentation printing starts */ - doc_formatting& - first_column(int col) { - //limit to [0,last_column] but push doc_column to the right if necessary - if(col < 0) col = 0; - else if(col > last_column()) col = last_column(); - if(col > doc_column()) doc_column(first_column()); - firstCol_ = col; - return *this; - } - int first_column() const noexcept { - return firstCol_; - } - - /** @brief determines column where docstrings start */ - doc_formatting& - doc_column(int col) { - //limit to [first_column,last_column] - if(col < 0) col = 0; - else if(col < first_column()) col = first_column(); - else if(col > last_column()) col = last_column(); - docCol_ = col; - return *this; - } - int doc_column() const noexcept { - return docCol_; - } - - /** @brief determines column that no documentation text must exceed; - * (text should be wrapped appropriately after this column) - */ - doc_formatting& - last_column(int col) { - //limit to [first_column,oo] but push doc_column to the left if necessary - if(col < first_column()) col = first_column(); - if(col < doc_column()) doc_column(col); - lastCol_ = col; - return *this; - } - - int last_column() const noexcept { - return lastCol_; - } - - /** @brief determines indent of documentation lines - * for children of a documented group */ - doc_formatting& indent_size(int indent) { indentSize_ = indent; return *this; } - int indent_size() const noexcept { return indentSize_; } - - /** @brief determines string to be used - * if a parameter has no flags and no label */ - doc_formatting& empty_label(const string& label) { - emptyLabel_ = label; - return *this; - } - const string& empty_label() const noexcept { return emptyLabel_; } - - /** @brief determines string for separating parameters */ - doc_formatting& param_separator(const string& sep) { - paramSep_ = sep; - return *this; - } - const string& param_separator() const noexcept { return paramSep_; } - - /** @brief determines string for separating groups (in usage lines) */ - doc_formatting& group_separator(const string& sep) { - groupSep_ = sep; - return *this; - } - const string& group_separator() const noexcept { return groupSep_; } - - /** @brief determines string for separating alternative parameters */ - doc_formatting& alternative_param_separator(const string& sep) { - altParamSep_ = sep; - return *this; - } - const string& alternative_param_separator() const noexcept { return altParamSep_; } - - /** @brief determines string for separating alternative groups */ - doc_formatting& alternative_group_separator(const string& sep) { - altGroupSep_ = sep; - return *this; - } - const string& alternative_group_separator() const noexcept { return altGroupSep_; } - - /** @brief determines string for separating flags of the same parameter */ - doc_formatting& flag_separator(const string& sep) { - flagSep_ = sep; - return *this; - } - const string& flag_separator() const noexcept { return flagSep_; } - - /** @brief determines strings surrounding parameter labels */ - doc_formatting& - surround_labels(const string& prefix, const string& postfix) { - labelPre_ = prefix; - labelPst_ = postfix; - return *this; - } - const string& label_prefix() const noexcept { return labelPre_; } - const string& label_postfix() const noexcept { return labelPst_; } - - /** @brief determines strings surrounding optional parameters/groups */ - doc_formatting& - surround_optional(const string& prefix, const string& postfix) { - optionPre_ = prefix; - optionPst_ = postfix; - return *this; - } - const string& optional_prefix() const noexcept { return optionPre_; } - const string& optional_postfix() const noexcept { return optionPst_; } - - /** @brief determines strings surrounding repeatable parameters/groups */ - doc_formatting& - surround_repeat(const string& prefix, const string& postfix) { - repeatPre_ = prefix; - repeatPst_ = postfix; - return *this; - } - const string& repeat_prefix() const noexcept { return repeatPre_; } - const string& repeat_postfix() const noexcept { return repeatPst_; } - - /** @brief determines strings surrounding exclusive groups */ - doc_formatting& - surround_alternatives(const string& prefix, const string& postfix) { - alternPre_ = prefix; - alternPst_ = postfix; - return *this; - } - const string& alternatives_prefix() const noexcept { return alternPre_; } - const string& alternatives_postfix() const noexcept { return alternPst_; } - - /** @brief determines strings surrounding alternative flags */ - doc_formatting& - surround_alternative_flags(const string& prefix, const string& postfix) { - alternFlagPre_ = prefix; - alternFlagPst_ = postfix; - return *this; - } - const string& alternative_flags_prefix() const noexcept { return alternFlagPre_; } - const string& alternative_flags_postfix() const noexcept { return alternFlagPst_; } - - /** @brief determines strings surrounding non-exclusive groups */ - doc_formatting& - surround_group(const string& prefix, const string& postfix) { - groupPre_ = prefix; - groupPst_ = postfix; - return *this; - } - const string& group_prefix() const noexcept { return groupPre_; } - const string& group_postfix() const noexcept { return groupPst_; } - - /** @brief determines strings surrounding joinable groups */ - doc_formatting& - surround_joinable(const string& prefix, const string& postfix) { - joinablePre_ = prefix; - joinablePst_ = postfix; - return *this; - } - const string& joinable_prefix() const noexcept { return joinablePre_; } - const string& joinable_postfix() const noexcept { return joinablePst_; } - - /** @brief determines maximum number of flags per parameter to be printed - * in detailed parameter documentation lines */ - doc_formatting& max_flags_per_param_in_doc(int max) { - maxAltInDocs_ = max > 0 ? max : 0; - return *this; - } - int max_flags_per_param_in_doc() const noexcept { return maxAltInDocs_; } - - /** @brief determines maximum number of flags per parameter to be printed - * in usage lines */ - doc_formatting& max_flags_per_param_in_usage(int max) { - maxAltInUsage_ = max > 0 ? max : 0; - return *this; - } - int max_flags_per_param_in_usage() const noexcept { return maxAltInUsage_; } - - /** @brief determines number of empty rows after one single-line - * documentation entry */ - doc_formatting& line_spacing(int lines) { - lineSpc_ = lines > 0 ? lines : 0; - return *this; - } - int line_spacing() const noexcept { return lineSpc_; } - - /** @brief determines number of empty rows before and after a paragraph; - * a paragraph is defined by a documented group or if - * a parameter documentation entry used more than one line */ - doc_formatting& paragraph_spacing(int lines) { - paragraphSpc_ = lines > 0 ? lines : 0; - return *this; - } - int paragraph_spacing() const noexcept { return paragraphSpc_; } - - /** @brief determines if alternative flags with a common prefix should - * be printed in a merged fashion */ - doc_formatting& merge_alternative_flags_with_common_prefix(bool yes = true) { - mergeAltCommonPfx_ = yes; - return *this; - } - bool merge_alternative_flags_with_common_prefix() const noexcept { - return mergeAltCommonPfx_; - } - - /** @brief determines if joinable flags with a common prefix should - * be printed in a merged fashion */ - doc_formatting& merge_joinable_with_common_prefix(bool yes = true) { - mergeJoinableCommonPfx_ = yes; - return *this; - } - bool merge_joinable_with_common_prefix() const noexcept { - return mergeJoinableCommonPfx_; - } - - /** @brief determines if children of exclusive groups should be printed - * on individual lines if the exceed 'alternatives_min_split_size' - */ - doc_formatting& split_alternatives(bool yes = true) { - splitTopAlt_ = yes; - return *this; - } - bool split_alternatives() const noexcept { - return splitTopAlt_; - } - - /** @brief determines how many children exclusive groups can have before - * their children are printed on individual usage lines */ - doc_formatting& alternatives_min_split_size(int size) { - groupSplitSize_ = size > 0 ? size : 0; - return *this; - } - int alternatives_min_split_size() const noexcept { return groupSplitSize_; } - - /** @brief determines whether to ignore new line characters in docstrings - */ - doc_formatting& ignore_newline_chars(bool yes = true) { - ignoreNewlines_ = yes; - return *this; - } - bool ignore_newline_chars() const noexcept { - return ignoreNewlines_; - } - -private: - string paramSep_ = string(" "); - string groupSep_ = string(" "); - string altParamSep_ = string("|"); - string altGroupSep_ = string(" | "); - string flagSep_ = string(", "); - string labelPre_ = string("<"); - string labelPst_ = string(">"); - string optionPre_ = string("["); - string optionPst_ = string("]"); - string repeatPre_ = string(""); - string repeatPst_ = string("..."); - string groupPre_ = string("("); - string groupPst_ = string(")"); - string alternPre_ = string("("); - string alternPst_ = string(")"); - string alternFlagPre_ = string(""); - string alternFlagPst_ = string(""); - string joinablePre_ = string("("); - string joinablePst_ = string(")"); - string emptyLabel_ = string(""); - int firstCol_ = 8; - int docCol_ = 20; - int lastCol_ = 100; - int indentSize_ = 4; - int maxAltInUsage_ = 1; - int maxAltInDocs_ = 32; - int lineSpc_ = 0; - int paragraphSpc_ = 1; - int groupSplitSize_ = 3; - bool splitTopAlt_ = true; - bool mergeAltCommonPfx_ = false; - bool mergeJoinableCommonPfx_ = true; - bool ignoreNewlines_ = false; -}; - - - -namespace detail { - -/*************************************************************************//** - * - * @brief stream decorator - * that applies formatting like line wrapping - * - *****************************************************************************/ -template<class OStream = std::ostream, class StringT = doc_string> -class formatting_ostream -{ -public: - using string_type = StringT; - using size_type = typename string_type::size_type; - using char_type = typename string_type::value_type; - - formatting_ostream(OStream& os): - os_(os), - curCol_{0}, firstCol_{0}, lastCol_{100}, - hangingIndent_{0}, paragraphSpacing_{0}, paragraphSpacingThreshold_{2}, - curBlankLines_{0}, curParagraphLines_{1}, - totalNonBlankLines_{0}, - ignoreInputNls_{false} - {} - - - //--------------------------------------------------------------- - const OStream& base() const noexcept { return os_; } - OStream& base() noexcept { return os_; } - - bool good() const { return os_.good(); } - - - //--------------------------------------------------------------- - /** @brief determines the leftmost border of the text body */ - formatting_ostream& first_column(int c) { - firstCol_ = c < 0 ? 0 : c; - return *this; - } - int first_column() const noexcept { return firstCol_; } - - /** @brief determines the rightmost border of the text body */ - formatting_ostream& last_column(int c) { - lastCol_ = c < 0 ? 0 : c; - return *this; - } - - int last_column() const noexcept { return lastCol_; } - - int text_width() const noexcept { - return lastCol_ - firstCol_; - } - - /** @brief additional indentation for the 2nd, 3rd, ... line of - a paragraph (sequence of soft-wrapped lines) */ - formatting_ostream& hanging_indent(int amount) { - hangingIndent_ = amount; - return *this; - } - int hanging_indent() const noexcept { - return hangingIndent_; - } - - /** @brief amount of blank lines between paragraphs */ - formatting_ostream& paragraph_spacing(int lines) { - paragraphSpacing_ = lines; - return *this; - } - int paragraph_spacing() const noexcept { - return paragraphSpacing_; - } - - /** @brief insert paragraph spacing - if paragraph is at least 'lines' lines long */ - formatting_ostream& min_paragraph_lines_for_spacing(int lines) { - paragraphSpacingThreshold_ = lines; - return *this; - } - int min_paragraph_lines_for_spacing() const noexcept { - return paragraphSpacingThreshold_; - } - - /** @brief if set to true, newline characters will be ignored */ - formatting_ostream& ignore_newline_chars(bool yes) { - ignoreInputNls_ = yes; - return *this; - } - - bool ignore_newline_chars() const noexcept { - return ignoreInputNls_; - } - - - //--------------------------------------------------------------- - /* @brief insert 'n' spaces */ - void write_spaces(int n) { - if(n < 1) return; - os_ << string_type(size_type(n), ' '); - curCol_ += n; - } - - /* @brief go to new line, but continue current paragraph */ - void wrap_soft(int times = 1) { - if(times < 1) return; - if(times > 1) { - os_ << string_type(size_type(times), '\n'); - } else { - os_ << '\n'; - } - curCol_ = 0; - ++curParagraphLines_; - } - - /* @brief go to new line, and start a new paragraph */ - void wrap_hard(int times = 1) { - if(times < 1) return; - - if(paragraph_spacing() > 0 && - paragraph_lines() >= min_paragraph_lines_for_spacing()) - { - times = paragraph_spacing() + 1; - } - - if(times > 1) { - os_ << string_type(size_type(times), '\n'); - curBlankLines_ += times - 1; - } else { - os_ << '\n'; - } - if(at_begin_of_line()) { - ++curBlankLines_; - } - curCol_ = 0; - curParagraphLines_ = 1; - } - - - //--------------------------------------------------------------- - bool at_begin_of_line() const noexcept { - return curCol_ <= current_line_begin(); - } - int current_line_begin() const noexcept { - return in_hanging_part_of_paragraph() - ? firstCol_ + hangingIndent_ - : firstCol_; - } - - int current_column() const noexcept { - return curCol_; - } - - int total_non_blank_lines() const noexcept { - return totalNonBlankLines_; - } - int paragraph_lines() const noexcept { - return curParagraphLines_; - } - int blank_lines_before_paragraph() const noexcept { - return curBlankLines_; - } - - - //--------------------------------------------------------------- - template<class T> - friend formatting_ostream& - operator << (formatting_ostream& os, const T& x) { - os.write(x); - return os; - } - - void flush() { - os_.flush(); - } - - -private: - bool in_hanging_part_of_paragraph() const noexcept { - return hanging_indent() > 0 && paragraph_lines() > 1; - } - bool current_line_empty() const noexcept { - return curCol_ < 1; - } - bool left_of_text_area() const noexcept { - return curCol_ < current_line_begin(); - } - bool right_of_text_area() const noexcept { - return curCol_ > lastCol_; - } - int columns_left_in_line() const noexcept { - return lastCol_ - std::max(current_line_begin(), curCol_); - } - - void fix_indent() { - if(left_of_text_area()) { - const auto fst = current_line_begin(); - write_spaces(fst - curCol_); - curCol_ = fst; - } - } - - template<class Iter> - bool only_whitespace(Iter first, Iter last) const { - return last == std::find_if_not(first, last, - [](char_type c) { return std::isspace(c); }); - } - - /** @brief write any object */ - template<class T> - void write(const T& x) { - std::ostringstream ss; - ss << x; - write(std::move(ss).str()); - } - - /** @brief write a stringstream */ - void write(const std::ostringstream& s) { - write(s.str()); - } - - /** @brief write a string */ - void write(const string_type& s) { - write(s.begin(), s.end()); - } - - /** @brief partition output into lines */ - template<class Iter> - void write(Iter first, Iter last) - { - if(first == last) return; - if(*first == '\n') { - if(!ignore_newline_chars()) wrap_hard(); - ++first; - if(first == last) return; - } - auto i = std::find(first, last, '\n'); - if(i != last) { - if(ignore_newline_chars()) ++i; - if(i != last) { - write_line(first, i); - write(i, last); - } - } - else { - write_line(first, last); - } - } - - /** @brief handle line wrapping due to column constraints */ - template<class Iter> - void write_line(Iter first, Iter last) - { - if(first == last) return; - if(only_whitespace(first, last)) return; - - if(right_of_text_area()) wrap_soft(); - - if(at_begin_of_line()) { - //discard whitespace, it we start a new line - first = std::find_if(first, last, - [](char_type c) { return !std::isspace(c); }); - if(first == last) return; - } - - const auto n = int(std::distance(first,last)); - const auto m = columns_left_in_line(); - //if text to be printed is too long for one line -> wrap - if(n > m) { - //break before word, if break is mid-word - auto breakat = first + m; - while(breakat > first && !std::isspace(*breakat)) --breakat; - //could not find whitespace before word -> try after the word - if(!std::isspace(*breakat) && breakat == first) { - breakat = std::find_if(first+m, last, - [](char_type c) { return std::isspace(c); }); - } - if(breakat > first) { - if(curCol_ < 1) ++totalNonBlankLines_; - fix_indent(); - std::copy(first, breakat, std::ostream_iterator<char_type>(os_)); - curBlankLines_ = 0; - } - if(breakat < last) { - wrap_soft(); - write_line(breakat, last); - } - } - else { - if(curCol_ < 1) ++totalNonBlankLines_; - fix_indent(); - std::copy(first, last, std::ostream_iterator<char_type>(os_)); - curCol_ += n; - curBlankLines_ = 0; - } - } - - /** @brief write a single character */ - void write(char_type c) - { - if(c == '\n') { - if(!ignore_newline_chars()) wrap_hard(); - } - else { - if(at_begin_of_line()) ++totalNonBlankLines_; - fix_indent(); - os_ << c; - ++curCol_; - } - } - - OStream& os_; - int curCol_; - int firstCol_; - int lastCol_; - int hangingIndent_; - int paragraphSpacing_; - int paragraphSpacingThreshold_; - int curBlankLines_; - int curParagraphLines_; - int totalNonBlankLines_; - bool ignoreInputNls_; -}; - - -} - - - - -/*************************************************************************//** - * - * @brief generates usage lines - * - * @details lazily evaluated - * - *****************************************************************************/ -class usage_lines -{ -public: - using string = doc_string; - - usage_lines(const group& cli, string prefix = "", - const doc_formatting& fmt = doc_formatting{}) - : - cli_(cli), fmt_(fmt), prefix_(std::move(prefix)) - { - if(!prefix_.empty()) prefix_ += ' '; - } - - usage_lines(const group& cli, const doc_formatting& fmt): - usage_lines(cli, "", fmt) - {} - - usage_lines& ommit_outermost_group_surrounders(bool yes) { - ommitOutermostSurrounders_ = yes; - return *this; - } - bool ommit_outermost_group_surrounders() const { - return ommitOutermostSurrounders_; - } - - template<class OStream> - inline friend OStream& operator << (OStream& os, const usage_lines& p) { - p.write(os); - return os; - } - - string str() const { - std::ostringstream os; os << *this; return os.str(); - } - - -private: - using stream_t = detail::formatting_ostream<>; - const group& cli_; - doc_formatting fmt_; - string prefix_; - bool ommitOutermostSurrounders_ = false; - - - //----------------------------------------------------- - struct context { - group::depth_first_traverser pos; - std::stack<string> separators; - std::stack<string> postfixes; - int level = 0; - const group* outermost = nullptr; - bool linestart = false; - bool useOutermost = true; - int line = 0; - - bool is_singleton() const noexcept { - return linestart && pos.is_last_in_path(); - } - bool is_alternative() const noexcept { - return pos.parent().exclusive(); - } - }; - - - /***************************************************************//** - * - * @brief writes usage text for command line parameters - * - *******************************************************************/ - template<class OStream> - void write(OStream& os) const - { - detail::formatting_ostream<OStream> fos(os); - fos.first_column(fmt_.first_column()); - fos.last_column(fmt_.last_column()); - - auto hindent = int(prefix_.size()); - if(fos.first_column() + hindent >= int(0.4 * fos.text_width())) { - hindent = fmt_.indent_size(); - } - fos.hanging_indent(hindent); - - fos.paragraph_spacing(fmt_.paragraph_spacing()); - fos.min_paragraph_lines_for_spacing(2); - fos.ignore_newline_chars(fmt_.ignore_newline_chars()); - - context cur; - cur.pos = cli_.begin_dfs(); - cur.linestart = true; - cur.level = cur.pos.level(); - cur.outermost = &cli_; - - write(fos, cur, prefix_); - } - - - /***************************************************************//** - * - * @brief writes usage text for command line parameters - * - * @param prefix all that goes in front of current things to print - * - *******************************************************************/ - template<class OStream> - void write(OStream& os, context cur, string prefix) const - { - if(!cur.pos) return; - - std::ostringstream buf; - if(cur.linestart) buf << prefix; - const auto initPos = buf.tellp(); - - cur.level = cur.pos.level(); - - if(cur.useOutermost) { - //we cannot start outside of the outermost group - //so we have to treat it separately - start_group(buf, cur.pos.parent(), cur); - if(!cur.pos) { - os << buf.str(); - return; - } - } - else { - //don't visit siblings of starter node - cur.pos.skip_siblings(); - } - check_end_group(buf, cur); - - do { - if(buf.tellp() > initPos) cur.linestart = false; - if(!cur.linestart && !cur.pos.is_first_in_parent()) { - buf << cur.separators.top(); - } - if(cur.pos->is_group()) { - start_group(buf, cur.pos->as_group(), cur); - if(!cur.pos) { - os << buf.str(); - return; - } - } - else { - buf << param_label(cur.pos->as_param(), cur); - ++cur.pos; - } - check_end_group(buf, cur); - } while(cur.pos); - - os << buf.str(); - } - - - /***************************************************************//** - * - * @brief handles pattern group surrounders and separators - * and alternative splitting - * - *******************************************************************/ - void start_group(std::ostringstream& os, - const group& group, context& cur) const - { - //does cur.pos already point to a member or to group itself? - //needed for special treatment of outermost group - const bool alreadyInside = &(cur.pos.parent()) == &group; - - auto lbl = joined_label(group, cur); - if(!lbl.empty()) { - os << lbl; - cur.linestart = false; - //skip over entire group as its label has already been created - if(alreadyInside) { - cur.pos.next_after_siblings(); - } else { - cur.pos.next_sibling(); - } - } - else { - const bool splitAlternatives = group.exclusive() && - fmt_.split_alternatives() && - std::any_of(group.begin(), group.end(), - [this](const pattern& p) { - return int(p.param_count()) >= fmt_.alternatives_min_split_size(); - }); - - if(splitAlternatives) { - cur.postfixes.push(""); - cur.separators.push(""); - //recursively print alternative paths in decision-DAG - //enter group? - if(!alreadyInside) ++cur.pos; - cur.linestart = true; - cur.useOutermost = false; - auto pfx = os.str(); - os.str(""); - //print paths in DAG starting at each group member - for(std::size_t i = 0; i < group.size(); ++i) { - std::stringstream buf; - cur.outermost = cur.pos->is_group() ? &(cur.pos->as_group()) : nullptr; - write(buf, cur, pfx); - if(buf.tellp() > int(pfx.size())) { - os << buf.str(); - if(i < group.size()-1) { - if(cur.line > 0) { - os << string(fmt_.line_spacing(), '\n'); - } - ++cur.line; - os << '\n'; - } - } - cur.pos.next_sibling(); //do not descend into members - } - cur.pos.invalidate(); //signal end-of-path - return; - } - else { - //pre & postfixes, separators - auto surround = group_surrounders(group, cur); - os << surround.first; - cur.postfixes.push(std::move(surround.second)); - cur.separators.push(group_separator(group, fmt_)); - //descend into group? - if(!alreadyInside) ++cur.pos; - } - } - cur.level = cur.pos.level(); - } - - - /***************************************************************//** - * - *******************************************************************/ - void check_end_group(std::ostringstream& os, context& cur) const - { - for(; cur.level > cur.pos.level(); --cur.level) { - os << cur.postfixes.top(); - cur.postfixes.pop(); - cur.separators.pop(); - } - cur.level = cur.pos.level(); - } - - - /***************************************************************//** - * - * @brief makes usage label for one command line parameter - * - *******************************************************************/ - string param_label(const parameter& p, const context& cur) const - { - const auto& parent = cur.pos.parent(); - - const bool startsOptionalSequence = - parent.size() > 1 && p.blocking() && cur.pos.is_first_in_parent(); - - const bool outermost = - ommitOutermostSurrounders_ && cur.outermost == &parent; - - const bool showopt = !cur.is_alternative() && !p.required() - && !startsOptionalSequence && !outermost; - - const bool showrep = p.repeatable() && !outermost; - - string lbl; - - if(showrep) lbl += fmt_.repeat_prefix(); - if(showopt) lbl += fmt_.optional_prefix(); - - const auto& flags = p.flags(); - if(!flags.empty()) { - const int n = std::min(fmt_.max_flags_per_param_in_usage(), - int(flags.size())); - - const bool surrAlt = n > 1 && !showopt && !cur.is_singleton(); - - if(surrAlt) lbl += fmt_.alternative_flags_prefix(); - bool sep = false; - for(int i = 0; i < n; ++i) { - if(sep) { - if(cur.is_singleton()) - lbl += fmt_.alternative_group_separator(); - else - lbl += fmt_.flag_separator(); - } - lbl += flags[i]; - sep = true; - } - if(surrAlt) lbl += fmt_.alternative_flags_postfix(); - } - else { - if(!p.label().empty()) { - lbl += fmt_.label_prefix() - + p.label() - + fmt_.label_postfix(); - } else if(!fmt_.empty_label().empty()) { - lbl += fmt_.label_prefix() - + fmt_.empty_label() - + fmt_.label_postfix(); - } else { - return ""; - } - } - - if(showopt) lbl += fmt_.optional_postfix(); - if(showrep) lbl += fmt_.repeat_postfix(); - - return lbl; - } - - - /***************************************************************//** - * - * @brief prints flags in one group in a merged fashion - * - *******************************************************************/ - string joined_label(const group& g, const context& cur) const - { - if(!fmt_.merge_alternative_flags_with_common_prefix() && - !fmt_.merge_joinable_with_common_prefix()) return ""; - - const bool flagsonly = std::all_of(g.begin(), g.end(), - [](const pattern& p){ - return p.is_param() && !p.as_param().flags().empty(); - }); - - if(!flagsonly) return ""; - - const bool showOpt = g.all_optional() && - !(ommitOutermostSurrounders_ && cur.outermost == &g); - - auto pfx = g.common_flag_prefix(); - if(pfx.empty()) return ""; - - const auto n = pfx.size(); - if(g.exclusive() && - fmt_.merge_alternative_flags_with_common_prefix()) - { - string lbl; - if(showOpt) lbl += fmt_.optional_prefix(); - lbl += pfx + fmt_.alternatives_prefix(); - bool first = true; - for(const auto& p : g) { - if(p.is_param()) { - if(first) - first = false; - else - lbl += fmt_.alternative_param_separator(); - lbl += p.as_param().flags().front().substr(n); - } - } - lbl += fmt_.alternatives_postfix(); - if(showOpt) lbl += fmt_.optional_postfix(); - return lbl; - } - //no alternatives, but joinable flags - else if(g.joinable() && - fmt_.merge_joinable_with_common_prefix()) - { - const bool allSingleChar = std::all_of(g.begin(), g.end(), - [&](const pattern& p){ - return p.is_param() && - p.as_param().flags().front().substr(n).size() == 1; - }); - - if(allSingleChar) { - string lbl; - if(showOpt) lbl += fmt_.optional_prefix(); - lbl += pfx; - for(const auto& p : g) { - if(p.is_param()) - lbl += p.as_param().flags().front().substr(n); - } - if(showOpt) lbl += fmt_.optional_postfix(); - return lbl; - } - } - - return ""; - } - - - /***************************************************************//** - * - * @return symbols with which to surround a group - * - *******************************************************************/ - std::pair<string,string> - group_surrounders(const group& group, const context& cur) const - { - string prefix; - string postfix; - - const bool isOutermost = &group == cur.outermost; - if(isOutermost && ommitOutermostSurrounders_) - return {string{}, string{}}; - - if(group.exclusive()) { - if(group.all_optional()) { - prefix = fmt_.optional_prefix(); - postfix = fmt_.optional_postfix(); - if(group.all_flagless()) { - prefix += fmt_.label_prefix(); - postfix = fmt_.label_prefix() + postfix; - } - } else if(group.all_flagless()) { - prefix = fmt_.label_prefix(); - postfix = fmt_.label_postfix(); - } else if(!cur.is_singleton() || !isOutermost) { - prefix = fmt_.alternatives_prefix(); - postfix = fmt_.alternatives_postfix(); - } - } - else if(group.size() > 1 && - group.front().blocking() && !group.front().required()) - { - prefix = fmt_.optional_prefix(); - postfix = fmt_.optional_postfix(); - } - else if(group.size() > 1 && cur.is_alternative() && - &group != cur.outermost) - { - prefix = fmt_.group_prefix(); - postfix = fmt_.group_postfix(); - } - else if(!group.exclusive() && - group.joinable() && !cur.linestart) - { - prefix = fmt_.joinable_prefix(); - postfix = fmt_.joinable_postfix(); - } - - if(group.repeatable()) { - if(prefix.empty()) prefix = fmt_.group_prefix(); - prefix = fmt_.repeat_prefix() + prefix; - if(postfix.empty()) postfix = fmt_.group_postfix(); - postfix += fmt_.repeat_postfix(); - } - - return {std::move(prefix), std::move(postfix)}; - } - - - /***************************************************************//** - * - * @return symbol that separates members of a group - * - *******************************************************************/ - static string - group_separator(const group& group, const doc_formatting& fmt) - { - const bool only1ParamPerMember = std::all_of(group.begin(), group.end(), - [](const pattern& p) { return p.param_count() < 2; }); - - if(only1ParamPerMember) { - if(group.exclusive()) { - return fmt.alternative_param_separator(); - } else { - return fmt.param_separator(); - } - } - else { //there is at least one large group inside - if(group.exclusive()) { - return fmt.alternative_group_separator(); - } else { - return fmt.group_separator(); - } - } - } -}; - - - - -/*************************************************************************//** - * - * @brief generates parameter and group documentation from docstrings - * - * @details lazily evaluated - * - *****************************************************************************/ -class documentation -{ -public: - using string = doc_string; - using filter_function = std::function<bool(const parameter&)>; - - documentation(const group& cli, - const doc_formatting& fmt = doc_formatting{}, - filter_function filter = param_filter{}) - : - cli_(cli), fmt_{fmt}, usgFmt_{fmt}, filter_{std::move(filter)} - { - //necessary, because we re-use "usage_lines" to generate - //labels for documented groups - usgFmt_.max_flags_per_param_in_usage( - usgFmt_.max_flags_per_param_in_doc()); - } - - documentation(const group& cli, filter_function filter) : - documentation{cli, doc_formatting{}, std::move(filter)} - {} - - documentation(const group& cli, const param_filter& filter) : - documentation{cli, doc_formatting{}, - [filter](const parameter& p) { return filter(p); }} - {} - - template<class OStream> - inline friend OStream& operator << (OStream& os, const documentation& p) { - p.write(os); - return os; - } - - string str() const { - std::ostringstream os; - write(os); - return os.str(); - } - - -private: - using dfs_traverser = group::depth_first_traverser; - - const group& cli_; - doc_formatting fmt_; - doc_formatting usgFmt_; - filter_function filter_; - enum class paragraph { param, group }; - - - /***************************************************************//** - * - * @brief writes documentation to output stream - * - *******************************************************************/ - template<class OStream> - void write(OStream& os) const { - detail::formatting_ostream<OStream> fos(os); - fos.first_column(fmt_.first_column()); - fos.last_column(fmt_.last_column()); - fos.hanging_indent(0); - fos.paragraph_spacing(0); - fos.ignore_newline_chars(fmt_.ignore_newline_chars()); - print_doc(fos, cli_); - } - - - /***************************************************************//** - * - * @brief writes full documentation text for command line parameters - * - *******************************************************************/ - template<class OStream> - void print_doc(detail::formatting_ostream<OStream>& os, - const group& cli, int indentLvl = 0) const - { - if(cli.empty()) return; - - //if group itself doesn't have docstring - if(cli.doc().empty()) { - for(const auto& p : cli) { - print_doc(os, p, indentLvl); - } - } - else { //group itself does have docstring - bool anyDocInside = std::any_of(cli.begin(), cli.end(), - [](const pattern& p){ return !p.doc().empty(); }); - - if(anyDocInside) { //group docstring as title, then child entries - handle_spacing(os, paragraph::group, indentLvl); - os << cli.doc(); - for(const auto& p : cli) { - print_doc(os, p, indentLvl + 1); - } - } - else { //group label first then group docstring - auto lbl = usage_lines(cli, usgFmt_) - .ommit_outermost_group_surrounders(true).str(); - - str::trim(lbl); - handle_spacing(os, paragraph::param, indentLvl); - print_entry(os, lbl, cli.doc()); - } - } - } - - - /***************************************************************//** - * - * @brief writes documentation text for one group or parameter - * - *******************************************************************/ - template<class OStream> - void print_doc(detail::formatting_ostream<OStream>& os, - const pattern& ptrn, int indentLvl) const - { - if(ptrn.is_group()) { - print_doc(os, ptrn.as_group(), indentLvl); - } - else { - const auto& p = ptrn.as_param(); - if(!filter_(p)) return; - - handle_spacing(os, paragraph::param, indentLvl); - print_entry(os, param_label(p, fmt_), p.doc()); - } - } - - /***************************************************************//** - * - * @brief handles line and paragraph spacings - * - *******************************************************************/ - template<class OStream> - void handle_spacing(detail::formatting_ostream<OStream>& os, - paragraph p, int indentLvl) const - { - const auto oldIndent = os.first_column(); - const auto indent = fmt_.first_column() + indentLvl * fmt_.indent_size(); - - if(os.total_non_blank_lines() < 1) { - os.first_column(indent); - return; - } - - if(os.paragraph_lines() > 1 || indent < oldIndent) { - os.wrap_hard(fmt_.paragraph_spacing() + 1); - } else { - os.wrap_hard(); - } - - if(p == paragraph::group) { - if(os.blank_lines_before_paragraph() < fmt_.paragraph_spacing()) { - os.wrap_hard(fmt_.paragraph_spacing() - os.blank_lines_before_paragraph()); - } - } - else if(os.blank_lines_before_paragraph() < fmt_.line_spacing()) { - os.wrap_hard(fmt_.line_spacing() - os.blank_lines_before_paragraph()); - } - os.first_column(indent); - } - - /*********************************************************************//** - * - * @brief prints one entry = label + docstring - * - ************************************************************************/ - template<class OStream> - void print_entry(detail::formatting_ostream<OStream>& os, - const string& label, const string& docstr) const - { - if(label.empty()) return; - - os << label; - - if(!docstr.empty()) { - if(os.current_column() >= fmt_.doc_column()) os.wrap_soft(); - const auto oldcol = os.first_column(); - os.first_column(fmt_.doc_column()); - os << docstr; - os.first_column(oldcol); - } - } - - - /*********************************************************************//** - * - * @brief makes label for one parameter - * - ************************************************************************/ - static doc_string - param_label(const parameter& param, const doc_formatting& fmt) - { - doc_string lbl; - - if(param.repeatable()) lbl += fmt.repeat_prefix(); - - const auto& flags = param.flags(); - if(!flags.empty()) { - lbl += flags[0]; - const int n = std::min(fmt.max_flags_per_param_in_doc(), - int(flags.size())); - for(int i = 1; i < n; ++i) { - lbl += fmt.flag_separator() + flags[i]; - } - } - else if(!param.label().empty() || !fmt.empty_label().empty()) { - lbl += fmt.label_prefix(); - if(!param.label().empty()) { - lbl += param.label(); - } else { - lbl += fmt.empty_label(); - } - lbl += fmt.label_postfix(); - } - - if(param.repeatable()) lbl += fmt.repeat_postfix(); - - return lbl; - } - -}; - - - - -/*************************************************************************//** - * - * @brief stores strings for man page sections - * - *****************************************************************************/ -class man_page -{ -public: - //--------------------------------------------------------------- - using string = doc_string; - - //--------------------------------------------------------------- - /** @brief man page section */ - class section { - public: - using string = doc_string; - - section(string stitle, string scontent): - title_{std::move(stitle)}, content_{std::move(scontent)} - {} - - const string& title() const noexcept { return title_; } - const string& content() const noexcept { return content_; } - - private: - string title_; - string content_; - }; - -private: - using section_store = std::vector<section>; - -public: - //--------------------------------------------------------------- - using value_type = section; - using const_iterator = section_store::const_iterator; - using size_type = section_store::size_type; - - - //--------------------------------------------------------------- - man_page& - append_section(string title, string content) - { - sections_.emplace_back(std::move(title), std::move(content)); - return *this; - } - //----------------------------------------------------- - man_page& - prepend_section(string title, string content) - { - sections_.emplace(sections_.begin(), - std::move(title), std::move(content)); - return *this; - } - - - //--------------------------------------------------------------- - const section& operator [] (size_type index) const noexcept { - return sections_[index]; - } - - //--------------------------------------------------------------- - size_type size() const noexcept { return sections_.size(); } - - bool empty() const noexcept { return sections_.empty(); } - - - //--------------------------------------------------------------- - const_iterator begin() const noexcept { return sections_.begin(); } - const_iterator end() const noexcept { return sections_.end(); } - - - //--------------------------------------------------------------- - man_page& program_name(const string& n) { - progName_ = n; - return *this; - } - man_page& program_name(string&& n) { - progName_ = std::move(n); - return *this; - } - const string& program_name() const noexcept { - return progName_; - } - - - //--------------------------------------------------------------- - man_page& section_row_spacing(int rows) { - sectionSpc_ = rows > 0 ? rows : 0; - return *this; - } - int section_row_spacing() const noexcept { return sectionSpc_; } - - -private: - int sectionSpc_ = 1; - section_store sections_; - string progName_; -}; - - - -/*************************************************************************//** - * - * @brief generates man sections from command line parameters - * with sections "synopsis" and "options" - * - *****************************************************************************/ -inline man_page -make_man_page(const group& cli, - doc_string progname = "", - const doc_formatting& fmt = doc_formatting{}) -{ - man_page man; - man.append_section("SYNOPSIS", usage_lines(cli,progname,fmt).str()); - man.append_section("OPTIONS", documentation(cli,fmt).str()); - return man; -} - - - -/*************************************************************************//** - * - * @brief generates man page based on command line parameters - * - *****************************************************************************/ -template<class OStream> -OStream& -operator << (OStream& os, const man_page& man) -{ - bool first = true; - const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n'); - for(const auto& section : man) { - if(!section.content().empty()) { - if(first) first = false; else os << secSpc; - if(!section.title().empty()) os << section.title() << '\n'; - os << section.content(); - } - } - os << '\n'; - return os; -} - - - - - -/*************************************************************************//** - * - * @brief printing methods for debugging command line interfaces - * - *****************************************************************************/ -namespace debug { - - -/*************************************************************************//** - * - * @brief prints first flag or value label of a parameter - * - *****************************************************************************/ -inline doc_string doc_label(const parameter& p) -{ - if(!p.flags().empty()) return p.flags().front(); - if(!p.label().empty()) return p.label(); - return doc_string{"<?>"}; -} - -inline doc_string doc_label(const group&) -{ - return "<group>"; -} - -inline doc_string doc_label(const pattern& p) -{ - return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param()); -} - - -/*************************************************************************//** - * - * @brief prints parsing result - * - *****************************************************************************/ -template<class OStream> -void print(OStream& os, const parsing_result& result) -{ - for(const auto& m : result) { - os << "#" << m.index() << " " << m.arg() << " -> "; - auto p = m.param(); - if(p) { - os << doc_label(*p) << " \t"; - if(m.repeat() > 0) { - os << (m.bad_repeat() ? "[bad repeat " : "[repeat ") - << m.repeat() << "]"; - } - if(m.blocked()) os << " [blocked]"; - if(m.conflict()) os << " [conflict]"; - os << '\n'; - } - else { - os << " [unmapped]\n"; - } - } - - for(const auto& m : result.missing()) { - auto p = m.param(); - if(p) { - os << doc_label(*p) << " \t"; - os << " [missing after " << m.after_index() << "]\n"; - } - } -} - - -/*************************************************************************//** - * - * @brief prints parameter label and some properties - * - *****************************************************************************/ -template<class OStream> -void print(OStream& os, const parameter& p) -{ - if(p.greedy()) os << '!'; - if(p.blocking()) os << '~'; - if(!p.required()) os << '['; - os << doc_label(p); - if(p.repeatable()) os << "..."; - if(!p.required()) os << "]"; -} - - -//------------------------------------------------------------------- -template<class OStream> -void print(OStream& os, const group& g, int level = 0); - - -/*************************************************************************//** - * - * @brief prints group or parameter; uses indentation - * - *****************************************************************************/ -template<class OStream> -void print(OStream& os, const pattern& param, int level = 0) -{ - if(param.is_group()) { - print(os, param.as_group(), level); - } - else { - os << doc_string(4*level, ' '); - print(os, param.as_param()); - } -} - - -/*************************************************************************//** - * - * @brief prints group and its contents; uses indentation - * - *****************************************************************************/ -template<class OStream> -void print(OStream& os, const group& g, int level) -{ - auto indent = doc_string(4*level, ' '); - os << indent; - if(g.blocking()) os << '~'; - if(g.joinable()) os << 'J'; - os << (g.exclusive() ? "(|\n" : "(\n"); - for(const auto& p : g) { - print(os, p, level+1); - } - os << '\n' << indent << (g.exclusive() ? "|)" : ")"); - if(g.repeatable()) os << "..."; - os << '\n'; -} - - -} // namespace debug -} //namespace clipp - -#endif - diff --git a/console/parse.hpp b/console/parse.hpp deleted file mode 100644 index abdd36c..0000000 --- a/console/parse.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once - -#include <iostream> - -#include <rawaccel.hpp> -#include <rawaccel-error.hpp> - -#include "external/clipp.h" - -namespace rawaccel { - - template<typename Accel, typename StrFirst, typename... StrRest> - clipp::parameter make_accel_cmd(modifier_args& args, StrFirst&& first_flag, StrRest&&... rest) { - return clipp::command(first_flag, rest...) - .set(args.acc_fn_args.accel_mode, accel_impl_t::id<Accel>); - } - - mouse_modifier parse(int argc, char** argv) { - modifier_args args{}; - - auto make_opt_vec = [](vec2d& v, auto first_flag, auto... rest) { - return clipp::option(first_flag, rest...) & ( - clipp::number("xy", v.x, v.y) | ( - (clipp::required("x") & clipp::number("num", v.x)), - (clipp::required("y") & clipp::number("num", v.y)) - ) - ); - }; - - auto make_doc_fmt = [] { - return clipp::doc_formatting{} - .first_column(4) - .doc_column(28) - .last_column(80) - // min value to not split optional vec2 alternatives - .alternatives_min_split_size(5); - }; - - // default options - auto opt_sens = "sensitivity (default = 1)" % make_opt_vec(args.sens, "sens"); - - auto opt_rot = "counter-clockwise rotation (default = 0)" % ( - clipp::option("rotate") & - clipp::number("degrees", args.degrees) - ); - - // mode-independent accel options - auto opt_weight = "accel multiplier (default = 1)" % - make_opt_vec(args.acc_fn_args.acc_args.weight, "weight"); - - auto opt_offset = "speed (dots/ms) where accel kicks in (default = 0)" % ( - clipp::option("offset") & clipp::number("speed", args.acc_fn_args.acc_args.offset) - ); - - auto opt_cap = "accel scale cap (default = 9)" % - make_opt_vec(args.acc_fn_args.cap, "cap"); - - auto opt_tmin = "minimum time between polls (default = 0.4)" % ( - clipp::option("tmin") & - clipp::number("ms", args.acc_fn_args.time_min) - ); - - auto accel_var = (clipp::required("accel") & clipp::number("num", args.acc_fn_args.acc_args.accel)) % "ramp rate"; - auto limit_var = (clipp::required("limit") & clipp::number("scale", args.acc_fn_args.acc_args.limit)) % "limit"; - auto exp_var = (clipp::required("exponent") & clipp::number("num", args.acc_fn_args.acc_args.exponent)) % "exponent"; - - // modes - auto noaccel_mode = "no-accel mode" % make_accel_cmd<accel_noaccel>(args, "off", "noaccel"); - - auto lin_mode = "linear accel mode:" % ( - make_accel_cmd<accel_linear>(args, "linear"), - accel_var - ); - auto classic_mode = "classic accel mode:" % ( - make_accel_cmd<accel_classic>(args, "classic"), - accel_var, - exp_var - ); - auto nat_mode = "natural accel mode:" % ( - make_accel_cmd<accel_natural>(args, "natural"), - accel_var, - limit_var - ); - auto log_mode = "logarithmic accel mode:" % ( - make_accel_cmd<accel_logarithmic>(args, "logarithmic"), - accel_var - ); - auto sig_mode = "sigmoid accel mode:" % ( - make_accel_cmd<accel_sigmoid>(args, "sigmoid"), - accel_var, - limit_var, - (clipp::required("midpoint") & clipp::number("speed", args.acc_fn_args.acc_args.midpoint)) % "midpoint" - ); - auto pow_mode = "power accel mode:" % ( - make_accel_cmd<accel_power>(args, "power"), - exp_var, - (clipp::option("scale") & clipp::number("num", args.acc_fn_args.acc_args.power_scale)) % "scale factor" - ); - - auto accel_mode_exclusive = (lin_mode | classic_mode | nat_mode | log_mode | sig_mode | pow_mode); - auto accel_opts = "mode-independent accel options:" % (opt_cap, opt_weight, opt_offset, opt_tmin); - - bool help = false; - - auto cli = clipp::group(clipp::command("help").set(help)) | ( - noaccel_mode | (accel_mode_exclusive, accel_opts), - opt_sens, - opt_rot - ); - - if (!clipp::parse(argc, argv, cli)) { - constexpr int PARSE_ERROR = 1; - - std::cout << clipp::usage_lines(cli, "rawaccel", make_doc_fmt()); - std::exit(PARSE_ERROR); - } - - if (help) { - std::cout << clipp::make_man_page(cli, "rawaccel", make_doc_fmt()); - std::exit(0); - } - - return mouse_modifier(args); - } - -} // rawaccel diff --git a/grapher/Constants/Constants.cs b/grapher/Constants/Constants.cs new file mode 100644 index 0000000..aaf8bb9 --- /dev/null +++ b/grapher/Constants/Constants.cs @@ -0,0 +1,104 @@ +using System.Drawing; + +namespace grapher +{ + public static class Constants + { + #region Constants + + /// <summary> DPI by which charts are scaled if none is set by user. </summary> + public const int DefaultDPI = 1200; + + /// <summary> Poll rate by which charts are scaled if none is set by user. </summary> + public const int DefaultPollRate = 1000; + + /// <summary> Resolution of chart calulation. </summary> + public const int Resolution = 100; + + /// <summary> Multiplied by DPI over poll rate to find rough max expected velocity. </summary> + public const double MaxMultiplier = 85; + + /// <summary> Ratio of max (X, Y) used in "by component" calulations to those used in "whole vector" calculations. </summary> + public const double XYToCombinedRatio = 1.4; + + /// <summary> Possible options to display in a layout. </summary> + public const int PossibleOptionsCount = 6; + + /// <summary> Separation between X and Y active value labels, in pixels. </summary> + public const int ActiveLabelXYSeparation = 2; + + /// <summary> Vertical separation between charts, in pixels. </summary> + public const int ChartSeparationVertical = 10; + + /// <summary> Needed to show full contents in form. Unsure why. </summary> + public const int FormHeightPadding = 35; + + /// <summary> Horizontal separation between charts, in pixels. </summary> + public const int ChartSeparationHorizontal = 10; + + /// <summary> Default horizontal separation between x and y fields, in pixels. </summary> + public const int DefaultFieldSeparation = 4; + + /// <summary> Default horizontal separation between an option's label and box, in pixels. </summary> + public const int OptionLabelBoxSeperation = 10; + + /// <summary> Default horizontal separation between an option's label and box, in pixels. </summary> + public const int OptionVerticalSeperation = 4; + + /// <summary> Horizontal separation between left side of single dropdown and left side of labels beneath dropdown </summary> + public const int DropDownLeftSeparation = 10; + + /// <summary> Width of charts when widened </summary> + public const int WideChartWidth = 723; + + /// <summary> Left placement of charts when widened </summary> + public const int WideChartLeft = 333; + + /// <summary> Width of charts when narrowed </summary> + public const int NarrowChartWidth = 698; + + /// <summary> Left placement of charts when narrowed </summary> + public const int NarrowChartLeft = 482; + + public const int WriteButtonVerticalOffset = 50; + + /// <summary> Format string for shortened x and y textboxes. </summary> + public const string ShortenedFormatString = "0.###"; + + /// <summary> Format string for default active value labels. </summary> + public const string DefaultActiveValueFormatString = "0.######"; + + /// <summary> Format string for default textboxes. </summary> + public const string DefaultFieldFormatString = "0.#########"; + + /// <summary> Format string for shortened x and y fields. </summary> + public const string ShortenedFieldFormatString = "0.###"; + + /// <summary> Format string for gain cap active value label. </summary> + public const string GainCapFormatString = "0.##"; + + /// <summary> Format string for shortened x and y dropdowns. </summary> + public const string AccelDropDownDefaultFullText = "Acceleration Type"; + + /// <summary> Format string for default dropdowns. </summary> + public const string AccelDropDownDefaultShortText = "Accel Type"; + + /// <summary> Default text to be displayed on write button. </summary> + public const string WriteButtonDefaultText = "Write To Driver"; + + /// <summary> Default text to be displayed on write button. </summary> + public const string WriteButtonDelayText = "Delay"; + + /// <summary> Default name of settings file. </summary> + public const string DefaultSettingsFileName = @"settings.json"; + + #endregion Constants + + #region ReadOnly + + /// <summary> Color of font in active value labels. </summary> + public static readonly Color ActiveValueFontColor = Color.FromArgb(255, 65, 65, 65); + + #endregion ReadOnly + } +} diff --git a/grapher/Form1.Designer.cs b/grapher/Form1.Designer.cs index a7a3e16..f8a6578 100644 --- a/grapher/Form1.Designer.cs +++ b/grapher/Form1.Designer.cs @@ -55,30 +55,28 @@ namespace grapher System.Windows.Forms.DataVisualization.Charting.Series series11 = new System.Windows.Forms.DataVisualization.Charting.Series(); System.Windows.Forms.DataVisualization.Charting.Series series12 = new System.Windows.Forms.DataVisualization.Charting.Series(); this.AccelerationChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); - this.accelTypeDrop = new System.Windows.Forms.ComboBox(); + this.accelTypeDropX = new System.Windows.Forms.ComboBox(); this.sensitivityBoxX = new System.Windows.Forms.TextBox(); this.sensitivityLabel = new System.Windows.Forms.Label(); this.rotationBox = new System.Windows.Forms.TextBox(); this.rotationLabel = new System.Windows.Forms.Label(); - this.accelerationBox = new System.Windows.Forms.TextBox(); - this.constantOneLabel = new System.Windows.Forms.Label(); + this.accelerationBoxX = new System.Windows.Forms.TextBox(); + this.constantOneLabelX = new System.Windows.Forms.Label(); this.capBoxX = new System.Windows.Forms.TextBox(); - this.capLabel = new System.Windows.Forms.Label(); - this.weightBoxFirst = new System.Windows.Forms.TextBox(); - this.weightLabel = new System.Windows.Forms.Label(); - this.weightBoxSecond = new System.Windows.Forms.TextBox(); - this.limitBox = new System.Windows.Forms.TextBox(); - this.constantTwoLabel = new System.Windows.Forms.Label(); - this.midpointBox = new System.Windows.Forms.TextBox(); - this.constantThreeLabel = new System.Windows.Forms.Label(); - this.offsetBox = new System.Windows.Forms.TextBox(); - this.offsetLabel = new System.Windows.Forms.Label(); + this.capLabelX = new System.Windows.Forms.Label(); + this.weightBoxX = new System.Windows.Forms.TextBox(); + this.weightLabelX = new System.Windows.Forms.Label(); + this.weightBoxY = new System.Windows.Forms.TextBox(); + this.limitBoxX = new System.Windows.Forms.TextBox(); + this.constantTwoLabelX = new System.Windows.Forms.Label(); + this.midpointBoxX = new System.Windows.Forms.TextBox(); + this.constantThreeLabelX = new System.Windows.Forms.Label(); + this.offsetBoxX = new System.Windows.Forms.TextBox(); + this.offsetLabelX = new System.Windows.Forms.Label(); this.writeButton = new System.Windows.Forms.Button(); this.sensitivityBoxY = new System.Windows.Forms.TextBox(); this.capBoxY = new System.Windows.Forms.TextBox(); this.sensXYLock = new System.Windows.Forms.CheckBox(); - this.capXYLock = new System.Windows.Forms.CheckBox(); - this.weightXYLock = new System.Windows.Forms.CheckBox(); this.LockXYLabel = new System.Windows.Forms.Label(); this.VelocityChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.GainChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); @@ -95,6 +93,12 @@ namespace grapher this.capStyleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.gainCapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.legacyCapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.offsetStyleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.gainOffsetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.legacyOffsetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.wholeVectorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.byVectorComponentToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.startupToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.AutoWriteMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.AccelerationChartY = new System.Windows.Forms.DataVisualization.Charting.Chart(); @@ -105,18 +109,36 @@ namespace grapher this.SensitivityActiveXLabel = new System.Windows.Forms.Label(); this.SensitivityActiveYLabel = new System.Windows.Forms.Label(); this.RotationActiveLabel = new System.Windows.Forms.Label(); - this.AccelTypeActiveLabel = new System.Windows.Forms.Label(); - this.AccelerationActiveLabel = new System.Windows.Forms.Label(); + this.AccelTypeActiveLabelX = new System.Windows.Forms.Label(); + this.AccelerationActiveLabelX = new System.Windows.Forms.Label(); this.CapActiveXLabel = new System.Windows.Forms.Label(); this.WeightActiveXLabel = new System.Windows.Forms.Label(); this.WeightActiveYLabel = new System.Windows.Forms.Label(); this.CapActiveYLabel = new System.Windows.Forms.Label(); - this.OffsetActiveLabel = new System.Windows.Forms.Label(); - this.LimitExpActiveLabel = new System.Windows.Forms.Label(); - this.MidpointActiveLabel = new System.Windows.Forms.Label(); - this.offsetStyleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.gainOffsetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.legacyOffsetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.OffsetActiveXLabel = new System.Windows.Forms.Label(); + this.LimitExpActiveXLabel = new System.Windows.Forms.Label(); + this.MidpointActiveXLabel = new System.Windows.Forms.Label(); + this.accelerationBoxY = new System.Windows.Forms.TextBox(); + this.offsetBoxY = new System.Windows.Forms.TextBox(); + this.limitBoxY = new System.Windows.Forms.TextBox(); + this.midpointBoxY = new System.Windows.Forms.TextBox(); + this.accelTypeDropY = new System.Windows.Forms.ComboBox(); + this.AccelerationActiveLabelY = new System.Windows.Forms.Label(); + this.OffsetActiveYLabel = new System.Windows.Forms.Label(); + this.LimitExpActiveYLabel = new System.Windows.Forms.Label(); + this.MidpointActiveYLabel = new System.Windows.Forms.Label(); + this.ByComponentXYLock = new System.Windows.Forms.CheckBox(); + this.constantOneLabelY = new System.Windows.Forms.Label(); + this.capLabelY = new System.Windows.Forms.Label(); + this.weightLabelY = new System.Windows.Forms.Label(); + this.offsetLabelY = new System.Windows.Forms.Label(); + this.constantTwoLabelY = new System.Windows.Forms.Label(); + this.constantThreeLabelY = new System.Windows.Forms.Label(); + this.OptionSetXTitle = new System.Windows.Forms.Label(); + this.OptionSetYTitle = new System.Windows.Forms.Label(); + this.AccelTypeActiveLabelY = new System.Windows.Forms.Label(); + this.ActiveValueTitleY = new System.Windows.Forms.Label(); + this.showLastMouseMoveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); ((System.ComponentModel.ISupportInitialize)(this.AccelerationChart)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.VelocityChart)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.GainChart)).BeginInit(); @@ -134,7 +156,7 @@ namespace grapher this.AccelerationChart.ChartAreas.Add(chartArea1); legend1.Name = "Legend1"; this.AccelerationChart.Legends.Add(legend1); - this.AccelerationChart.Location = new System.Drawing.Point(333, 0); + this.AccelerationChart.Location = new System.Drawing.Point(482, 0); this.AccelerationChart.Name = "AccelerationChart"; series1.ChartArea = "ChartArea1"; series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; @@ -147,24 +169,24 @@ namespace grapher series2.Name = "LastAccelVal"; this.AccelerationChart.Series.Add(series1); this.AccelerationChart.Series.Add(series2); - this.AccelerationChart.Size = new System.Drawing.Size(723, 328); + this.AccelerationChart.Size = new System.Drawing.Size(698, 328); this.AccelerationChart.TabIndex = 0; - this.AccelerationChart.Text = "chart1"; + this.AccelerationChart.Text = "Sensitivity"; // - // accelTypeDrop + // accelTypeDropX // - this.accelTypeDrop.FormattingEnabled = true; - this.accelTypeDrop.Location = new System.Drawing.Point(24, 98); - this.accelTypeDrop.Name = "accelTypeDrop"; - this.accelTypeDrop.Size = new System.Drawing.Size(151, 21); - this.accelTypeDrop.TabIndex = 2; - this.accelTypeDrop.Text = "Acceleration Type"; + this.accelTypeDropX.FormattingEnabled = true; + this.accelTypeDropX.Location = new System.Drawing.Point(105, 110); + this.accelTypeDropX.Name = "accelTypeDropX"; + this.accelTypeDropX.Size = new System.Drawing.Size(76, 21); + this.accelTypeDropX.TabIndex = 2; + this.accelTypeDropX.Text = "Accel Type"; // // sensitivityBoxX // this.sensitivityBoxX.Location = new System.Drawing.Point(105, 46); this.sensitivityBoxX.Name = "sensitivityBoxX"; - this.sensitivityBoxX.Size = new System.Drawing.Size(32, 20); + this.sensitivityBoxX.Size = new System.Drawing.Size(34, 20); this.sensitivityBoxX.TabIndex = 3; // // sensitivityLabel @@ -180,7 +202,7 @@ namespace grapher // this.rotationBox.Location = new System.Drawing.Point(105, 72); this.rotationBox.Name = "rotationBox"; - this.rotationBox.Size = new System.Drawing.Size(70, 20); + this.rotationBox.Size = new System.Drawing.Size(76, 20); this.rotationBox.TabIndex = 5; // // rotationLabel @@ -192,137 +214,136 @@ namespace grapher this.rotationLabel.TabIndex = 6; this.rotationLabel.Text = "Rotation"; // - // accelerationBox + // accelerationBoxX // - this.accelerationBox.Location = new System.Drawing.Point(105, 125); - this.accelerationBox.Name = "accelerationBox"; - this.accelerationBox.Size = new System.Drawing.Size(69, 20); - this.accelerationBox.TabIndex = 7; + this.accelerationBoxX.Location = new System.Drawing.Point(105, 137); + this.accelerationBoxX.Name = "accelerationBoxX"; + this.accelerationBoxX.Size = new System.Drawing.Size(76, 20); + this.accelerationBoxX.TabIndex = 7; // - // constantOneLabel + // constantOneLabelX // - this.constantOneLabel.AutoSize = true; - this.constantOneLabel.Location = new System.Drawing.Point(24, 128); - this.constantOneLabel.Name = "constantOneLabel"; - this.constantOneLabel.Size = new System.Drawing.Size(66, 13); - this.constantOneLabel.TabIndex = 9; - this.constantOneLabel.Text = "Acceleration"; - this.constantOneLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.constantOneLabelX.AutoSize = true; + this.constantOneLabelX.Location = new System.Drawing.Point(24, 140); + this.constantOneLabelX.Name = "constantOneLabelX"; + this.constantOneLabelX.Size = new System.Drawing.Size(66, 13); + this.constantOneLabelX.TabIndex = 9; + this.constantOneLabelX.Text = "Acceleration"; + this.constantOneLabelX.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // capBoxX // - this.capBoxX.Location = new System.Drawing.Point(105, 151); + this.capBoxX.Location = new System.Drawing.Point(105, 163); this.capBoxX.Name = "capBoxX"; - this.capBoxX.Size = new System.Drawing.Size(32, 20); + this.capBoxX.Size = new System.Drawing.Size(76, 20); this.capBoxX.TabIndex = 10; // - // capLabel + // capLabelX // - this.capLabel.AutoSize = true; - this.capLabel.Location = new System.Drawing.Point(34, 155); - this.capLabel.Name = "capLabel"; - this.capLabel.Size = new System.Drawing.Size(26, 13); - this.capLabel.TabIndex = 11; - this.capLabel.Text = "Cap"; - this.capLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.capLabelX.AutoSize = true; + this.capLabelX.Location = new System.Drawing.Point(43, 166); + this.capLabelX.Name = "capLabelX"; + this.capLabelX.Size = new System.Drawing.Size(26, 13); + this.capLabelX.TabIndex = 11; + this.capLabelX.Text = "Cap"; + this.capLabelX.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // - // weightBoxFirst + // weightBoxX // - this.weightBoxFirst.Location = new System.Drawing.Point(105, 177); - this.weightBoxFirst.Name = "weightBoxFirst"; - this.weightBoxFirst.Size = new System.Drawing.Size(32, 20); - this.weightBoxFirst.TabIndex = 12; + this.weightBoxX.Location = new System.Drawing.Point(105, 189); + this.weightBoxX.Name = "weightBoxX"; + this.weightBoxX.Size = new System.Drawing.Size(76, 20); + this.weightBoxX.TabIndex = 12; // - // weightLabel + // weightLabelX // - this.weightLabel.AutoSize = true; - this.weightLabel.Location = new System.Drawing.Point(34, 180); - this.weightLabel.Name = "weightLabel"; - this.weightLabel.Size = new System.Drawing.Size(41, 13); - this.weightLabel.TabIndex = 13; - this.weightLabel.Text = "Weight"; - this.weightLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.weightLabelX.AutoSize = true; + this.weightLabelX.Location = new System.Drawing.Point(40, 192); + this.weightLabelX.Name = "weightLabelX"; + this.weightLabelX.Size = new System.Drawing.Size(41, 13); + this.weightLabelX.TabIndex = 13; + this.weightLabelX.Text = "Weight"; + this.weightLabelX.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // - // weightBoxSecond + // weightBoxY // - this.weightBoxSecond.Location = new System.Drawing.Point(144, 177); - this.weightBoxSecond.Name = "weightBoxSecond"; - this.weightBoxSecond.Size = new System.Drawing.Size(31, 20); - this.weightBoxSecond.TabIndex = 14; + this.weightBoxY.Location = new System.Drawing.Point(331, 189); + this.weightBoxY.Name = "weightBoxY"; + this.weightBoxY.Size = new System.Drawing.Size(76, 20); + this.weightBoxY.TabIndex = 14; // - // limitBox + // limitBoxX // - this.limitBox.Location = new System.Drawing.Point(105, 229); - this.limitBox.Name = "limitBox"; - this.limitBox.Size = new System.Drawing.Size(70, 20); - this.limitBox.TabIndex = 15; + this.limitBoxX.Location = new System.Drawing.Point(105, 241); + this.limitBoxX.Name = "limitBoxX"; + this.limitBoxX.Size = new System.Drawing.Size(76, 20); + this.limitBoxX.TabIndex = 15; // - // constantTwoLabel + // constantTwoLabelX // - this.constantTwoLabel.AutoSize = true; - this.constantTwoLabel.Location = new System.Drawing.Point(24, 232); - this.constantTwoLabel.Name = "constantTwoLabel"; - this.constantTwoLabel.Size = new System.Drawing.Size(78, 13); - this.constantTwoLabel.TabIndex = 16; - this.constantTwoLabel.Text = "Limit/Exponent"; - this.constantTwoLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.constantTwoLabelX.AutoSize = true; + this.constantTwoLabelX.Location = new System.Drawing.Point(24, 244); + this.constantTwoLabelX.Name = "constantTwoLabelX"; + this.constantTwoLabelX.Size = new System.Drawing.Size(78, 13); + this.constantTwoLabelX.TabIndex = 16; + this.constantTwoLabelX.Text = "Limit/Exponent"; + this.constantTwoLabelX.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // - // midpointBox + // midpointBoxX // - this.midpointBox.Location = new System.Drawing.Point(105, 255); - this.midpointBox.Name = "midpointBox"; - this.midpointBox.Size = new System.Drawing.Size(70, 20); - this.midpointBox.TabIndex = 17; + this.midpointBoxX.Location = new System.Drawing.Point(105, 267); + this.midpointBoxX.Name = "midpointBoxX"; + this.midpointBoxX.Size = new System.Drawing.Size(76, 20); + this.midpointBoxX.TabIndex = 17; // - // constantThreeLabel + // constantThreeLabelX // - this.constantThreeLabel.AutoSize = true; - this.constantThreeLabel.Location = new System.Drawing.Point(31, 258); - this.constantThreeLabel.Name = "constantThreeLabel"; - this.constantThreeLabel.Size = new System.Drawing.Size(47, 13); - this.constantThreeLabel.TabIndex = 18; - this.constantThreeLabel.Text = "Midpoint"; - this.constantThreeLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.constantThreeLabelX.AutoSize = true; + this.constantThreeLabelX.Location = new System.Drawing.Point(34, 270); + this.constantThreeLabelX.Name = "constantThreeLabelX"; + this.constantThreeLabelX.Size = new System.Drawing.Size(47, 13); + this.constantThreeLabelX.TabIndex = 18; + this.constantThreeLabelX.Text = "Midpoint"; + this.constantThreeLabelX.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // - // offsetBox + // offsetBoxX // - this.offsetBox.Location = new System.Drawing.Point(105, 203); - this.offsetBox.Name = "offsetBox"; - this.offsetBox.Size = new System.Drawing.Size(70, 20); - this.offsetBox.TabIndex = 19; + this.offsetBoxX.Location = new System.Drawing.Point(105, 215); + this.offsetBoxX.Name = "offsetBoxX"; + this.offsetBoxX.Size = new System.Drawing.Size(76, 20); + this.offsetBoxX.TabIndex = 19; // - // offsetLabel + // offsetLabelX // - this.offsetLabel.AutoSize = true; - this.offsetLabel.Location = new System.Drawing.Point(34, 206); - this.offsetLabel.Name = "offsetLabel"; - this.offsetLabel.Size = new System.Drawing.Size(35, 13); - this.offsetLabel.TabIndex = 20; - this.offsetLabel.Text = "Offset"; - this.offsetLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.offsetLabelX.AutoSize = true; + this.offsetLabelX.Location = new System.Drawing.Point(43, 218); + this.offsetLabelX.Name = "offsetLabelX"; + this.offsetLabelX.Size = new System.Drawing.Size(35, 13); + this.offsetLabelX.TabIndex = 20; + this.offsetLabelX.Text = "Offset"; + this.offsetLabelX.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // writeButton // - this.writeButton.Location = new System.Drawing.Point(57, 281); + this.writeButton.Location = new System.Drawing.Point(153, 293); this.writeButton.Name = "writeButton"; this.writeButton.Size = new System.Drawing.Size(102, 23); this.writeButton.TabIndex = 21; this.writeButton.Text = "Write To Driver"; this.writeButton.UseVisualStyleBackColor = true; - this.writeButton.Click += new System.EventHandler(this.writeButton_Click); // // sensitivityBoxY // - this.sensitivityBoxY.Location = new System.Drawing.Point(143, 46); + this.sensitivityBoxY.Location = new System.Drawing.Point(147, 46); this.sensitivityBoxY.Name = "sensitivityBoxY"; - this.sensitivityBoxY.Size = new System.Drawing.Size(32, 20); + this.sensitivityBoxY.Size = new System.Drawing.Size(34, 20); this.sensitivityBoxY.TabIndex = 22; // // capBoxY // - this.capBoxY.Location = new System.Drawing.Point(144, 151); + this.capBoxY.Location = new System.Drawing.Point(331, 163); this.capBoxY.Name = "capBoxY"; - this.capBoxY.Size = new System.Drawing.Size(31, 20); + this.capBoxY.Size = new System.Drawing.Size(76, 20); this.capBoxY.TabIndex = 23; // // sensXYLock @@ -330,38 +351,16 @@ namespace grapher this.sensXYLock.AutoSize = true; this.sensXYLock.Checked = true; this.sensXYLock.CheckState = System.Windows.Forms.CheckState.Checked; - this.sensXYLock.Location = new System.Drawing.Point(198, 49); + this.sensXYLock.Location = new System.Drawing.Point(282, 46); this.sensXYLock.Name = "sensXYLock"; this.sensXYLock.Size = new System.Drawing.Size(15, 14); this.sensXYLock.TabIndex = 24; this.sensXYLock.UseVisualStyleBackColor = true; // - // capXYLock - // - this.capXYLock.AutoSize = true; - this.capXYLock.Checked = true; - this.capXYLock.CheckState = System.Windows.Forms.CheckState.Checked; - this.capXYLock.Location = new System.Drawing.Point(198, 154); - this.capXYLock.Name = "capXYLock"; - this.capXYLock.Size = new System.Drawing.Size(15, 14); - this.capXYLock.TabIndex = 25; - this.capXYLock.UseVisualStyleBackColor = true; - // - // weightXYLock - // - this.weightXYLock.AutoSize = true; - this.weightXYLock.Checked = true; - this.weightXYLock.CheckState = System.Windows.Forms.CheckState.Checked; - this.weightXYLock.Location = new System.Drawing.Point(198, 180); - this.weightXYLock.Name = "weightXYLock"; - this.weightXYLock.Size = new System.Drawing.Size(15, 14); - this.weightXYLock.TabIndex = 26; - this.weightXYLock.UseVisualStyleBackColor = true; - // // LockXYLabel // this.LockXYLabel.AutoSize = true; - this.LockXYLabel.Location = new System.Drawing.Point(174, 30); + this.LockXYLabel.Location = new System.Drawing.Point(255, 30); this.LockXYLabel.Name = "LockXYLabel"; this.LockXYLabel.Size = new System.Drawing.Size(60, 13); this.LockXYLabel.TabIndex = 27; @@ -375,7 +374,7 @@ namespace grapher this.VelocityChart.ChartAreas.Add(chartArea2); legend2.Name = "Legend1"; this.VelocityChart.Legends.Add(legend2); - this.VelocityChart.Location = new System.Drawing.Point(333, 334); + this.VelocityChart.Location = new System.Drawing.Point(482, 334); this.VelocityChart.Name = "VelocityChart"; series3.ChartArea = "ChartArea1"; series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; @@ -388,7 +387,7 @@ namespace grapher series4.Name = "LastVelocityVal"; this.VelocityChart.Series.Add(series3); this.VelocityChart.Series.Add(series4); - this.VelocityChart.Size = new System.Drawing.Size(723, 307); + this.VelocityChart.Size = new System.Drawing.Size(698, 307); this.VelocityChart.TabIndex = 28; this.VelocityChart.Text = "chart1"; // @@ -400,7 +399,7 @@ namespace grapher this.GainChart.ChartAreas.Add(chartArea3); legend3.Name = "Legend1"; this.GainChart.Legends.Add(legend3); - this.GainChart.Location = new System.Drawing.Point(333, 647); + this.GainChart.Location = new System.Drawing.Point(482, 647); this.GainChart.Name = "GainChart"; series5.ChartArea = "ChartArea1"; series5.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; @@ -413,7 +412,7 @@ namespace grapher series6.Name = "LastGainVal"; this.GainChart.Series.Add(series5); this.GainChart.Series.Add(series6); - this.GainChart.Size = new System.Drawing.Size(723, 309); + this.GainChart.Size = new System.Drawing.Size(698, 309); this.GainChart.TabIndex = 29; this.GainChart.Text = "chart1"; // @@ -426,7 +425,7 @@ namespace grapher this.startupToolStripMenuItem}); this.menuStrip1.Location = new System.Drawing.Point(0, 0); this.menuStrip1.Name = "menuStrip1"; - this.menuStrip1.Size = new System.Drawing.Size(1786, 24); + this.menuStrip1.Size = new System.Drawing.Size(1884, 24); this.menuStrip1.TabIndex = 30; this.menuStrip1.Text = "menuStrip1"; // @@ -435,7 +434,8 @@ namespace grapher this.graphsToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; this.graphsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.showVelocityGainToolStripMenuItem, - this.scaleByDPIToolStripMenuItem}); + this.scaleByDPIToolStripMenuItem, + this.showLastMouseMoveToolStripMenuItem}); this.graphsToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); this.graphsToolStripMenuItem.Name = "graphsToolStripMenuItem"; this.graphsToolStripMenuItem.Size = new System.Drawing.Size(53, 20); @@ -444,7 +444,7 @@ namespace grapher // showVelocityGainToolStripMenuItem // this.showVelocityGainToolStripMenuItem.Name = "showVelocityGainToolStripMenuItem"; - this.showVelocityGainToolStripMenuItem.Size = new System.Drawing.Size(198, 22); + this.showVelocityGainToolStripMenuItem.Size = new System.Drawing.Size(199, 22); this.showVelocityGainToolStripMenuItem.Text = "Show Velocity && Gain"; // // scaleByDPIToolStripMenuItem @@ -454,7 +454,7 @@ namespace grapher this.pollRateToolStripMenuItem, this.ScaleMenuItem}); this.scaleByDPIToolStripMenuItem.Name = "scaleByDPIToolStripMenuItem"; - this.scaleByDPIToolStripMenuItem.Size = new System.Drawing.Size(198, 22); + this.scaleByDPIToolStripMenuItem.Size = new System.Drawing.Size(199, 22); this.scaleByDPIToolStripMenuItem.Text = "Scale by Mouse Settngs"; // // dPIToolStripMenuItem @@ -495,7 +495,8 @@ namespace grapher // this.advancedToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.capStyleToolStripMenuItem, - this.offsetStyleToolStripMenuItem}); + this.offsetStyleToolStripMenuItem, + this.toolStripMenuItem1}); this.advancedToolStripMenuItem.Name = "advancedToolStripMenuItem"; this.advancedToolStripMenuItem.Size = new System.Drawing.Size(72, 20); this.advancedToolStripMenuItem.Text = "Advanced"; @@ -514,15 +515,60 @@ namespace grapher this.gainCapToolStripMenuItem.Checked = true; this.gainCapToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; this.gainCapToolStripMenuItem.Name = "gainCapToolStripMenuItem"; - this.gainCapToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.gainCapToolStripMenuItem.Size = new System.Drawing.Size(147, 22); this.gainCapToolStripMenuItem.Text = "Gain (Default)"; // // legacyCapToolStripMenuItem // this.legacyCapToolStripMenuItem.Name = "legacyCapToolStripMenuItem"; - this.legacyCapToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.legacyCapToolStripMenuItem.Size = new System.Drawing.Size(147, 22); this.legacyCapToolStripMenuItem.Text = "Legacy"; // + // offsetStyleToolStripMenuItem + // + this.offsetStyleToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.gainOffsetToolStripMenuItem, + this.legacyOffsetToolStripMenuItem}); + this.offsetStyleToolStripMenuItem.Name = "offsetStyleToolStripMenuItem"; + this.offsetStyleToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.offsetStyleToolStripMenuItem.Text = "Offset Style"; + // + // gainOffsetToolStripMenuItem + // + this.gainOffsetToolStripMenuItem.Name = "gainOffsetToolStripMenuItem"; + this.gainOffsetToolStripMenuItem.Size = new System.Drawing.Size(147, 22); + this.gainOffsetToolStripMenuItem.Text = "Gain (Default)"; + // + // legacyOffsetToolStripMenuItem + // + this.legacyOffsetToolStripMenuItem.Name = "legacyOffsetToolStripMenuItem"; + this.legacyOffsetToolStripMenuItem.Size = new System.Drawing.Size(147, 22); + this.legacyOffsetToolStripMenuItem.Text = "Legacy"; + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.wholeVectorToolStripMenuItem, + this.byVectorComponentToolStripMenuItem}); + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(180, 22); + this.toolStripMenuItem1.Text = "Application Style"; + // + // wholeVectorToolStripMenuItem + // + this.wholeVectorToolStripMenuItem.Checked = true; + this.wholeVectorToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; + this.wholeVectorToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.wholeVectorToolStripMenuItem.Name = "wholeVectorToolStripMenuItem"; + this.wholeVectorToolStripMenuItem.Size = new System.Drawing.Size(154, 22); + this.wholeVectorToolStripMenuItem.Text = "Whole"; + // + // byVectorComponentToolStripMenuItem + // + this.byVectorComponentToolStripMenuItem.Name = "byVectorComponentToolStripMenuItem"; + this.byVectorComponentToolStripMenuItem.Size = new System.Drawing.Size(154, 22); + this.byVectorComponentToolStripMenuItem.Text = "By Component"; + // // startupToolStripMenuItem // this.startupToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -548,7 +594,7 @@ namespace grapher this.AccelerationChartY.ChartAreas.Add(chartArea4); legend4.Name = "Legend1"; this.AccelerationChartY.Legends.Add(legend4); - this.AccelerationChartY.Location = new System.Drawing.Point(1062, 0); + this.AccelerationChartY.Location = new System.Drawing.Point(1186, 0); this.AccelerationChartY.Name = "AccelerationChartY"; series7.ChartArea = "ChartArea1"; series7.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; @@ -561,7 +607,7 @@ namespace grapher series8.Name = "LastAccelVal"; this.AccelerationChartY.Series.Add(series7); this.AccelerationChartY.Series.Add(series8); - this.AccelerationChartY.Size = new System.Drawing.Size(723, 328); + this.AccelerationChartY.Size = new System.Drawing.Size(698, 328); this.AccelerationChartY.TabIndex = 31; this.AccelerationChartY.Text = "chart1"; // @@ -573,7 +619,7 @@ namespace grapher this.VelocityChartY.ChartAreas.Add(chartArea5); legend5.Name = "Legend1"; this.VelocityChartY.Legends.Add(legend5); - this.VelocityChartY.Location = new System.Drawing.Point(1062, 334); + this.VelocityChartY.Location = new System.Drawing.Point(1186, 334); this.VelocityChartY.Name = "VelocityChartY"; series9.ChartArea = "ChartArea1"; series9.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; @@ -586,7 +632,7 @@ namespace grapher series10.Name = "LastVelocityVal"; this.VelocityChartY.Series.Add(series9); this.VelocityChartY.Series.Add(series10); - this.VelocityChartY.Size = new System.Drawing.Size(723, 307); + this.VelocityChartY.Size = new System.Drawing.Size(698, 307); this.VelocityChartY.TabIndex = 32; this.VelocityChartY.Text = "chart1"; // @@ -598,7 +644,7 @@ namespace grapher this.GainChartY.ChartAreas.Add(chartArea6); legend6.Name = "Legend1"; this.GainChartY.Legends.Add(legend6); - this.GainChartY.Location = new System.Drawing.Point(1062, 647); + this.GainChartY.Location = new System.Drawing.Point(1186, 647); this.GainChartY.Name = "GainChartY"; series11.ChartArea = "ChartArea1"; series11.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; @@ -611,7 +657,7 @@ namespace grapher series12.Name = "LastGainVal"; this.GainChartY.Series.Add(series11); this.GainChartY.Series.Add(series12); - this.GainChartY.Size = new System.Drawing.Size(723, 309); + this.GainChartY.Size = new System.Drawing.Size(698, 309); this.GainChartY.TabIndex = 33; this.GainChartY.Text = "chart1"; // @@ -627,16 +673,16 @@ namespace grapher // ActiveValueTitle // this.ActiveValueTitle.AutoSize = true; - this.ActiveValueTitle.Location = new System.Drawing.Point(248, 30); + this.ActiveValueTitle.Location = new System.Drawing.Point(187, 30); this.ActiveValueTitle.Name = "ActiveValueTitle"; - this.ActiveValueTitle.Size = new System.Drawing.Size(67, 13); + this.ActiveValueTitle.Size = new System.Drawing.Size(37, 13); this.ActiveValueTitle.TabIndex = 35; - this.ActiveValueTitle.Text = "Active Value"; + this.ActiveValueTitle.Text = "Active"; // // SensitivityActiveXLabel // this.SensitivityActiveXLabel.AutoSize = true; - this.SensitivityActiveXLabel.Location = new System.Drawing.Point(258, 49); + this.SensitivityActiveXLabel.Location = new System.Drawing.Point(187, 49); this.SensitivityActiveXLabel.Name = "SensitivityActiveXLabel"; this.SensitivityActiveXLabel.Size = new System.Drawing.Size(14, 13); this.SensitivityActiveXLabel.TabIndex = 36; @@ -645,7 +691,7 @@ namespace grapher // SensitivityActiveYLabel // this.SensitivityActiveYLabel.AutoSize = true; - this.SensitivityActiveYLabel.Location = new System.Drawing.Point(286, 50); + this.SensitivityActiveYLabel.Location = new System.Drawing.Point(216, 49); this.SensitivityActiveYLabel.Name = "SensitivityActiveYLabel"; this.SensitivityActiveYLabel.Size = new System.Drawing.Size(14, 13); this.SensitivityActiveYLabel.TabIndex = 37; @@ -654,34 +700,34 @@ namespace grapher // RotationActiveLabel // this.RotationActiveLabel.AutoSize = true; - this.RotationActiveLabel.Location = new System.Drawing.Point(268, 75); + this.RotationActiveLabel.Location = new System.Drawing.Point(196, 75); this.RotationActiveLabel.Name = "RotationActiveLabel"; this.RotationActiveLabel.Size = new System.Drawing.Size(13, 13); this.RotationActiveLabel.TabIndex = 38; this.RotationActiveLabel.Text = "0"; // - // AccelTypeActiveLabel + // AccelTypeActiveLabelX // - this.AccelTypeActiveLabel.AutoSize = true; - this.AccelTypeActiveLabel.Location = new System.Drawing.Point(258, 98); - this.AccelTypeActiveLabel.Name = "AccelTypeActiveLabel"; - this.AccelTypeActiveLabel.Size = new System.Drawing.Size(41, 13); - this.AccelTypeActiveLabel.TabIndex = 39; - this.AccelTypeActiveLabel.Text = "Default"; + this.AccelTypeActiveLabelX.AutoSize = true; + this.AccelTypeActiveLabelX.Location = new System.Drawing.Point(196, 113); + this.AccelTypeActiveLabelX.Name = "AccelTypeActiveLabelX"; + this.AccelTypeActiveLabelX.Size = new System.Drawing.Size(66, 13); + this.AccelTypeActiveLabelX.TabIndex = 39; + this.AccelTypeActiveLabelX.Text = "SigmoidGain"; // - // AccelerationActiveLabel + // AccelerationActiveLabelX // - this.AccelerationActiveLabel.AutoSize = true; - this.AccelerationActiveLabel.Location = new System.Drawing.Point(268, 128); - this.AccelerationActiveLabel.Name = "AccelerationActiveLabel"; - this.AccelerationActiveLabel.Size = new System.Drawing.Size(13, 13); - this.AccelerationActiveLabel.TabIndex = 40; - this.AccelerationActiveLabel.Text = "0"; + this.AccelerationActiveLabelX.AutoSize = true; + this.AccelerationActiveLabelX.Location = new System.Drawing.Point(196, 140); + this.AccelerationActiveLabelX.Name = "AccelerationActiveLabelX"; + this.AccelerationActiveLabelX.Size = new System.Drawing.Size(13, 13); + this.AccelerationActiveLabelX.TabIndex = 40; + this.AccelerationActiveLabelX.Text = "0"; // // CapActiveXLabel // this.CapActiveXLabel.AutoSize = true; - this.CapActiveXLabel.Location = new System.Drawing.Point(259, 151); + this.CapActiveXLabel.Location = new System.Drawing.Point(196, 166); this.CapActiveXLabel.Name = "CapActiveXLabel"; this.CapActiveXLabel.Size = new System.Drawing.Size(13, 13); this.CapActiveXLabel.TabIndex = 41; @@ -690,7 +736,7 @@ namespace grapher // WeightActiveXLabel // this.WeightActiveXLabel.AutoSize = true; - this.WeightActiveXLabel.Location = new System.Drawing.Point(259, 180); + this.WeightActiveXLabel.Location = new System.Drawing.Point(196, 192); this.WeightActiveXLabel.Name = "WeightActiveXLabel"; this.WeightActiveXLabel.Size = new System.Drawing.Size(13, 13); this.WeightActiveXLabel.TabIndex = 42; @@ -699,7 +745,7 @@ namespace grapher // WeightActiveYLabel // this.WeightActiveYLabel.AutoSize = true; - this.WeightActiveYLabel.Location = new System.Drawing.Point(286, 180); + this.WeightActiveYLabel.Location = new System.Drawing.Point(413, 192); this.WeightActiveYLabel.Name = "WeightActiveYLabel"; this.WeightActiveYLabel.Size = new System.Drawing.Size(13, 13); this.WeightActiveYLabel.TabIndex = 43; @@ -708,74 +754,256 @@ namespace grapher // CapActiveYLabel // this.CapActiveYLabel.AutoSize = true; - this.CapActiveYLabel.Location = new System.Drawing.Point(286, 151); + this.CapActiveYLabel.Location = new System.Drawing.Point(413, 166); this.CapActiveYLabel.Name = "CapActiveYLabel"; this.CapActiveYLabel.Size = new System.Drawing.Size(13, 13); this.CapActiveYLabel.TabIndex = 44; this.CapActiveYLabel.Text = "0"; // - // OffsetActiveLabel - // - this.OffsetActiveLabel.AutoSize = true; - this.OffsetActiveLabel.Location = new System.Drawing.Point(268, 206); - this.OffsetActiveLabel.Name = "OffsetActiveLabel"; - this.OffsetActiveLabel.Size = new System.Drawing.Size(13, 13); - this.OffsetActiveLabel.TabIndex = 45; - this.OffsetActiveLabel.Text = "0"; - // - // LimitExpActiveLabel + // OffsetActiveXLabel // - this.LimitExpActiveLabel.AutoSize = true; - this.LimitExpActiveLabel.Location = new System.Drawing.Point(268, 232); - this.LimitExpActiveLabel.Name = "LimitExpActiveLabel"; - this.LimitExpActiveLabel.Size = new System.Drawing.Size(13, 13); - this.LimitExpActiveLabel.TabIndex = 46; - this.LimitExpActiveLabel.Text = "0"; - // - // MidpointActiveLabel - // - this.MidpointActiveLabel.AutoSize = true; - this.MidpointActiveLabel.Location = new System.Drawing.Point(268, 255); - this.MidpointActiveLabel.Name = "MidpointActiveLabel"; - this.MidpointActiveLabel.Size = new System.Drawing.Size(13, 13); - this.MidpointActiveLabel.TabIndex = 47; - this.MidpointActiveLabel.Text = "0"; - // - // offsetStyleToolStripMenuItem - // - this.offsetStyleToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.gainOffsetToolStripMenuItem, - this.legacyOffsetToolStripMenuItem}); - this.offsetStyleToolStripMenuItem.Name = "offsetStyleToolStripMenuItem"; - this.offsetStyleToolStripMenuItem.Size = new System.Drawing.Size(180, 22); - this.offsetStyleToolStripMenuItem.Text = "Offset Style"; - // - // gainOffsetToolStripMenuItem - // - this.gainOffsetToolStripMenuItem.Name = "gainOffsetToolStripMenuItem"; - this.gainOffsetToolStripMenuItem.Size = new System.Drawing.Size(180, 22); - this.gainOffsetToolStripMenuItem.Text = "Gain (Default)"; - // - // legacyOffsetToolStripMenuItem - // - this.legacyOffsetToolStripMenuItem.Name = "legacyOffsetToolStripMenuItem"; - this.legacyOffsetToolStripMenuItem.Size = new System.Drawing.Size(180, 22); - this.legacyOffsetToolStripMenuItem.Text = "Legacy"; + this.OffsetActiveXLabel.AutoSize = true; + this.OffsetActiveXLabel.Location = new System.Drawing.Point(196, 218); + this.OffsetActiveXLabel.Name = "OffsetActiveXLabel"; + this.OffsetActiveXLabel.Size = new System.Drawing.Size(13, 13); + this.OffsetActiveXLabel.TabIndex = 45; + this.OffsetActiveXLabel.Text = "0"; + // + // LimitExpActiveXLabel + // + this.LimitExpActiveXLabel.AutoSize = true; + this.LimitExpActiveXLabel.Location = new System.Drawing.Point(196, 244); + this.LimitExpActiveXLabel.Name = "LimitExpActiveXLabel"; + this.LimitExpActiveXLabel.Size = new System.Drawing.Size(13, 13); + this.LimitExpActiveXLabel.TabIndex = 46; + this.LimitExpActiveXLabel.Text = "0"; + // + // MidpointActiveXLabel + // + this.MidpointActiveXLabel.AutoSize = true; + this.MidpointActiveXLabel.Location = new System.Drawing.Point(196, 270); + this.MidpointActiveXLabel.Name = "MidpointActiveXLabel"; + this.MidpointActiveXLabel.Size = new System.Drawing.Size(13, 13); + this.MidpointActiveXLabel.TabIndex = 47; + this.MidpointActiveXLabel.Text = "0"; + // + // accelerationBoxY + // + this.accelerationBoxY.Location = new System.Drawing.Point(331, 137); + this.accelerationBoxY.Name = "accelerationBoxY"; + this.accelerationBoxY.Size = new System.Drawing.Size(76, 20); + this.accelerationBoxY.TabIndex = 48; + // + // offsetBoxY + // + this.offsetBoxY.Location = new System.Drawing.Point(331, 215); + this.offsetBoxY.Name = "offsetBoxY"; + this.offsetBoxY.Size = new System.Drawing.Size(76, 20); + this.offsetBoxY.TabIndex = 49; + // + // limitBoxY + // + this.limitBoxY.Location = new System.Drawing.Point(331, 241); + this.limitBoxY.Name = "limitBoxY"; + this.limitBoxY.Size = new System.Drawing.Size(76, 20); + this.limitBoxY.TabIndex = 50; + // + // midpointBoxY + // + this.midpointBoxY.Location = new System.Drawing.Point(331, 267); + this.midpointBoxY.Name = "midpointBoxY"; + this.midpointBoxY.Size = new System.Drawing.Size(76, 20); + this.midpointBoxY.TabIndex = 51; + // + // accelTypeDropY + // + this.accelTypeDropY.FormattingEnabled = true; + this.accelTypeDropY.Location = new System.Drawing.Point(331, 110); + this.accelTypeDropY.Name = "accelTypeDropY"; + this.accelTypeDropY.Size = new System.Drawing.Size(76, 21); + this.accelTypeDropY.TabIndex = 52; + this.accelTypeDropY.Text = "Accel Type"; + // + // AccelerationActiveLabelY + // + this.AccelerationActiveLabelY.AutoSize = true; + this.AccelerationActiveLabelY.Location = new System.Drawing.Point(413, 140); + this.AccelerationActiveLabelY.Name = "AccelerationActiveLabelY"; + this.AccelerationActiveLabelY.Size = new System.Drawing.Size(13, 13); + this.AccelerationActiveLabelY.TabIndex = 53; + this.AccelerationActiveLabelY.Text = "0"; + // + // OffsetActiveYLabel + // + this.OffsetActiveYLabel.AutoSize = true; + this.OffsetActiveYLabel.Location = new System.Drawing.Point(413, 218); + this.OffsetActiveYLabel.Name = "OffsetActiveYLabel"; + this.OffsetActiveYLabel.Size = new System.Drawing.Size(13, 13); + this.OffsetActiveYLabel.TabIndex = 54; + this.OffsetActiveYLabel.Text = "0"; + // + // LimitExpActiveYLabel + // + this.LimitExpActiveYLabel.AutoSize = true; + this.LimitExpActiveYLabel.Location = new System.Drawing.Point(413, 244); + this.LimitExpActiveYLabel.Name = "LimitExpActiveYLabel"; + this.LimitExpActiveYLabel.Size = new System.Drawing.Size(13, 13); + this.LimitExpActiveYLabel.TabIndex = 55; + this.LimitExpActiveYLabel.Text = "0"; + // + // MidpointActiveYLabel + // + this.MidpointActiveYLabel.AutoSize = true; + this.MidpointActiveYLabel.Location = new System.Drawing.Point(413, 270); + this.MidpointActiveYLabel.Name = "MidpointActiveYLabel"; + this.MidpointActiveYLabel.Size = new System.Drawing.Size(13, 13); + this.MidpointActiveYLabel.TabIndex = 56; + this.MidpointActiveYLabel.Text = "0"; + // + // ByComponentXYLock + // + this.ByComponentXYLock.AutoSize = true; + this.ByComponentXYLock.Checked = true; + this.ByComponentXYLock.CheckState = System.Windows.Forms.CheckState.Checked; + this.ByComponentXYLock.Location = new System.Drawing.Point(282, 93); + this.ByComponentXYLock.Name = "ByComponentXYLock"; + this.ByComponentXYLock.Size = new System.Drawing.Size(15, 14); + this.ByComponentXYLock.TabIndex = 57; + this.ByComponentXYLock.UseVisualStyleBackColor = true; + // + // constantOneLabelY + // + this.constantOneLabelY.AutoSize = true; + this.constantOneLabelY.Location = new System.Drawing.Point(258, 140); + this.constantOneLabelY.Name = "constantOneLabelY"; + this.constantOneLabelY.Size = new System.Drawing.Size(66, 13); + this.constantOneLabelY.TabIndex = 58; + this.constantOneLabelY.Text = "Acceleration"; + // + // capLabelY + // + this.capLabelY.AutoSize = true; + this.capLabelY.Location = new System.Drawing.Point(279, 166); + this.capLabelY.Name = "capLabelY"; + this.capLabelY.Size = new System.Drawing.Size(26, 13); + this.capLabelY.TabIndex = 59; + this.capLabelY.Text = "Cap"; + // + // weightLabelY + // + this.weightLabelY.AutoSize = true; + this.weightLabelY.Location = new System.Drawing.Point(264, 192); + this.weightLabelY.Name = "weightLabelY"; + this.weightLabelY.Size = new System.Drawing.Size(41, 13); + this.weightLabelY.TabIndex = 60; + this.weightLabelY.Text = "Weight"; + // + // offsetLabelY + // + this.offsetLabelY.AutoSize = true; + this.offsetLabelY.Location = new System.Drawing.Point(270, 218); + this.offsetLabelY.Name = "offsetLabelY"; + this.offsetLabelY.Size = new System.Drawing.Size(35, 13); + this.offsetLabelY.TabIndex = 61; + this.offsetLabelY.Text = "Offset"; + // + // constantTwoLabelY + // + this.constantTwoLabelY.AutoSize = true; + this.constantTwoLabelY.Location = new System.Drawing.Point(264, 244); + this.constantTwoLabelY.Name = "constantTwoLabelY"; + this.constantTwoLabelY.Size = new System.Drawing.Size(51, 13); + this.constantTwoLabelY.TabIndex = 62; + this.constantTwoLabelY.Text = "Limit/Exp"; + // + // constantThreeLabelY + // + this.constantThreeLabelY.AutoSize = true; + this.constantThreeLabelY.Location = new System.Drawing.Point(264, 270); + this.constantThreeLabelY.Name = "constantThreeLabelY"; + this.constantThreeLabelY.Size = new System.Drawing.Size(47, 13); + this.constantThreeLabelY.TabIndex = 63; + this.constantThreeLabelY.Text = "Midpoint"; + // + // OptionSetXTitle + // + this.OptionSetXTitle.AutoSize = true; + this.OptionSetXTitle.Location = new System.Drawing.Point(142, 94); + this.OptionSetXTitle.Name = "OptionSetXTitle"; + this.OptionSetXTitle.Size = new System.Drawing.Size(14, 13); + this.OptionSetXTitle.TabIndex = 64; + this.OptionSetXTitle.Text = "X"; + // + // OptionSetYTitle + // + this.OptionSetYTitle.AutoSize = true; + this.OptionSetYTitle.Location = new System.Drawing.Point(359, 94); + this.OptionSetYTitle.Name = "OptionSetYTitle"; + this.OptionSetYTitle.Size = new System.Drawing.Size(14, 13); + this.OptionSetYTitle.TabIndex = 65; + this.OptionSetYTitle.Text = "Y"; + // + // AccelTypeActiveLabelY + // + this.AccelTypeActiveLabelY.AutoSize = true; + this.AccelTypeActiveLabelY.Location = new System.Drawing.Point(413, 113); + this.AccelTypeActiveLabelY.Name = "AccelTypeActiveLabelY"; + this.AccelTypeActiveLabelY.Size = new System.Drawing.Size(66, 13); + this.AccelTypeActiveLabelY.TabIndex = 66; + this.AccelTypeActiveLabelY.Text = "SigmoidGain"; + // + // ActiveValueTitleY + // + this.ActiveValueTitleY.AutoSize = true; + this.ActiveValueTitleY.Location = new System.Drawing.Point(428, 30); + this.ActiveValueTitleY.Name = "ActiveValueTitleY"; + this.ActiveValueTitleY.Size = new System.Drawing.Size(37, 13); + this.ActiveValueTitleY.TabIndex = 67; + this.ActiveValueTitleY.Text = "Active"; + // + // showLastMouseMoveToolStripMenuItem + // + this.showLastMouseMoveToolStripMenuItem.Checked = true; + this.showLastMouseMoveToolStripMenuItem.CheckOnClick = true; + this.showLastMouseMoveToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; + this.showLastMouseMoveToolStripMenuItem.Name = "showLastMouseMoveToolStripMenuItem"; + this.showLastMouseMoveToolStripMenuItem.Size = new System.Drawing.Size(199, 22); + this.showLastMouseMoveToolStripMenuItem.Text = "Show Last Mouse Move"; // // RawAcceleration // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1786, 958); - this.Controls.Add(this.MidpointActiveLabel); - this.Controls.Add(this.LimitExpActiveLabel); - this.Controls.Add(this.OffsetActiveLabel); + this.ClientSize = new System.Drawing.Size(1884, 956); + this.Controls.Add(this.ActiveValueTitleY); + this.Controls.Add(this.AccelTypeActiveLabelY); + this.Controls.Add(this.OptionSetYTitle); + this.Controls.Add(this.OptionSetXTitle); + this.Controls.Add(this.constantThreeLabelY); + this.Controls.Add(this.constantTwoLabelY); + this.Controls.Add(this.offsetLabelY); + this.Controls.Add(this.weightLabelY); + this.Controls.Add(this.capLabelY); + this.Controls.Add(this.constantOneLabelY); + this.Controls.Add(this.ByComponentXYLock); + this.Controls.Add(this.MidpointActiveYLabel); + this.Controls.Add(this.LimitExpActiveYLabel); + this.Controls.Add(this.OffsetActiveYLabel); + this.Controls.Add(this.AccelerationActiveLabelY); + this.Controls.Add(this.accelTypeDropY); + this.Controls.Add(this.midpointBoxY); + this.Controls.Add(this.limitBoxY); + this.Controls.Add(this.offsetBoxY); + this.Controls.Add(this.accelerationBoxY); + this.Controls.Add(this.MidpointActiveXLabel); + this.Controls.Add(this.LimitExpActiveXLabel); + this.Controls.Add(this.OffsetActiveXLabel); this.Controls.Add(this.CapActiveYLabel); this.Controls.Add(this.WeightActiveYLabel); this.Controls.Add(this.WeightActiveXLabel); this.Controls.Add(this.CapActiveXLabel); - this.Controls.Add(this.AccelerationActiveLabel); - this.Controls.Add(this.AccelTypeActiveLabel); + this.Controls.Add(this.AccelerationActiveLabelX); + this.Controls.Add(this.AccelTypeActiveLabelX); this.Controls.Add(this.RotationActiveLabel); this.Controls.Add(this.SensitivityActiveYLabel); this.Controls.Add(this.SensitivityActiveXLabel); @@ -787,30 +1015,28 @@ namespace grapher this.Controls.Add(this.GainChart); this.Controls.Add(this.VelocityChart); this.Controls.Add(this.LockXYLabel); - this.Controls.Add(this.weightXYLock); - this.Controls.Add(this.capXYLock); this.Controls.Add(this.sensXYLock); this.Controls.Add(this.capBoxY); this.Controls.Add(this.sensitivityBoxY); this.Controls.Add(this.writeButton); - this.Controls.Add(this.offsetLabel); - this.Controls.Add(this.offsetBox); - this.Controls.Add(this.constantThreeLabel); - this.Controls.Add(this.midpointBox); - this.Controls.Add(this.constantTwoLabel); - this.Controls.Add(this.limitBox); - this.Controls.Add(this.weightBoxSecond); - this.Controls.Add(this.weightLabel); - this.Controls.Add(this.weightBoxFirst); - this.Controls.Add(this.capLabel); + this.Controls.Add(this.offsetLabelX); + this.Controls.Add(this.offsetBoxX); + this.Controls.Add(this.constantThreeLabelX); + this.Controls.Add(this.midpointBoxX); + this.Controls.Add(this.constantTwoLabelX); + this.Controls.Add(this.limitBoxX); + this.Controls.Add(this.weightBoxY); + this.Controls.Add(this.weightLabelX); + this.Controls.Add(this.weightBoxX); + this.Controls.Add(this.capLabelX); this.Controls.Add(this.capBoxX); - this.Controls.Add(this.constantOneLabel); - this.Controls.Add(this.accelerationBox); + this.Controls.Add(this.constantOneLabelX); + this.Controls.Add(this.accelerationBoxX); this.Controls.Add(this.rotationLabel); this.Controls.Add(this.rotationBox); this.Controls.Add(this.sensitivityLabel); this.Controls.Add(this.sensitivityBoxX); - this.Controls.Add(this.accelTypeDrop); + this.Controls.Add(this.accelTypeDropX); this.Controls.Add(this.AccelerationChart); this.Controls.Add(this.menuStrip1); this.Name = "RawAcceleration"; @@ -833,30 +1059,28 @@ namespace grapher #endregion private System.Windows.Forms.DataVisualization.Charting.Chart AccelerationChart; - private System.Windows.Forms.ComboBox accelTypeDrop; + private System.Windows.Forms.ComboBox accelTypeDropX; private System.Windows.Forms.TextBox sensitivityBoxX; private System.Windows.Forms.Label sensitivityLabel; private System.Windows.Forms.TextBox rotationBox; private System.Windows.Forms.Label rotationLabel; - private System.Windows.Forms.TextBox accelerationBox; - private System.Windows.Forms.Label constantOneLabel; + private System.Windows.Forms.TextBox accelerationBoxX; + private System.Windows.Forms.Label constantOneLabelX; private System.Windows.Forms.TextBox capBoxX; - private System.Windows.Forms.Label capLabel; - private System.Windows.Forms.TextBox weightBoxFirst; - private System.Windows.Forms.Label weightLabel; - private System.Windows.Forms.TextBox weightBoxSecond; - private System.Windows.Forms.TextBox limitBox; - private System.Windows.Forms.Label constantTwoLabel; - private System.Windows.Forms.TextBox midpointBox; - private System.Windows.Forms.Label constantThreeLabel; - private System.Windows.Forms.TextBox offsetBox; - private System.Windows.Forms.Label offsetLabel; + private System.Windows.Forms.Label capLabelX; + private System.Windows.Forms.TextBox weightBoxX; + private System.Windows.Forms.Label weightLabelX; + private System.Windows.Forms.TextBox weightBoxY; + private System.Windows.Forms.TextBox limitBoxX; + private System.Windows.Forms.Label constantTwoLabelX; + private System.Windows.Forms.TextBox midpointBoxX; + private System.Windows.Forms.Label constantThreeLabelX; + private System.Windows.Forms.TextBox offsetBoxX; + private System.Windows.Forms.Label offsetLabelX; private System.Windows.Forms.Button writeButton; private System.Windows.Forms.TextBox sensitivityBoxY; private System.Windows.Forms.TextBox capBoxY; private System.Windows.Forms.CheckBox sensXYLock; - private System.Windows.Forms.CheckBox capXYLock; - private System.Windows.Forms.CheckBox weightXYLock; private System.Windows.Forms.Label LockXYLabel; private System.Windows.Forms.DataVisualization.Charting.Chart VelocityChart; private System.Windows.Forms.DataVisualization.Charting.Chart GainChart; @@ -881,20 +1105,44 @@ namespace grapher private System.Windows.Forms.Label SensitivityActiveXLabel; private System.Windows.Forms.Label SensitivityActiveYLabel; private System.Windows.Forms.Label RotationActiveLabel; - private System.Windows.Forms.Label AccelTypeActiveLabel; - private System.Windows.Forms.Label AccelerationActiveLabel; + private System.Windows.Forms.Label AccelTypeActiveLabelX; + private System.Windows.Forms.Label AccelerationActiveLabelX; private System.Windows.Forms.Label CapActiveXLabel; private System.Windows.Forms.Label WeightActiveXLabel; private System.Windows.Forms.Label WeightActiveYLabel; private System.Windows.Forms.Label CapActiveYLabel; - private System.Windows.Forms.Label OffsetActiveLabel; - private System.Windows.Forms.Label LimitExpActiveLabel; - private System.Windows.Forms.Label MidpointActiveLabel; + private System.Windows.Forms.Label OffsetActiveXLabel; + private System.Windows.Forms.Label LimitExpActiveXLabel; + private System.Windows.Forms.Label MidpointActiveXLabel; private System.Windows.Forms.ToolStripMenuItem startupToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem AutoWriteMenuItem; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem wholeVectorToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem byVectorComponentToolStripMenuItem; + private System.Windows.Forms.TextBox accelerationBoxY; + private System.Windows.Forms.TextBox offsetBoxY; + private System.Windows.Forms.TextBox limitBoxY; + private System.Windows.Forms.TextBox midpointBoxY; + private System.Windows.Forms.ComboBox accelTypeDropY; + private System.Windows.Forms.Label AccelerationActiveLabelY; + private System.Windows.Forms.Label OffsetActiveYLabel; + private System.Windows.Forms.Label LimitExpActiveYLabel; + private System.Windows.Forms.Label MidpointActiveYLabel; + private System.Windows.Forms.CheckBox ByComponentXYLock; + private System.Windows.Forms.Label constantOneLabelY; + private System.Windows.Forms.Label capLabelY; + private System.Windows.Forms.Label weightLabelY; + private System.Windows.Forms.Label offsetLabelY; + private System.Windows.Forms.Label constantTwoLabelY; + private System.Windows.Forms.Label constantThreeLabelY; + private System.Windows.Forms.Label OptionSetXTitle; + private System.Windows.Forms.Label OptionSetYTitle; + private System.Windows.Forms.Label AccelTypeActiveLabelY; private System.Windows.Forms.ToolStripMenuItem offsetStyleToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem gainOffsetToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem legacyOffsetToolStripMenuItem; + private System.Windows.Forms.Label ActiveValueTitleY; + private System.Windows.Forms.ToolStripMenuItem showLastMouseMoveToolStripMenuItem; } } diff --git a/grapher/Form1.cs b/grapher/Form1.cs index ba0730c..103f239 100644 --- a/grapher/Form1.cs +++ b/grapher/Form1.cs @@ -12,6 +12,7 @@ using System.Runtime.InteropServices; using grapher.Models.Calculations; using grapher.Models.Options; using grapher.Models.Serialized; +using grapher.Models; namespace grapher { @@ -36,146 +37,84 @@ namespace grapher throw; } - var accelCharts = new AccelCharts( - this, - new ChartXY(AccelerationChart, AccelerationChartY), - new ChartXY(VelocityChart, VelocityChartY), - new ChartXY(GainChart, GainChartY), - showVelocityGainToolStripMenuItem, - new CheckBox[] { sensXYLock, weightXYLock, capXYLock }); - - ActiveValueTitle.AutoSize = false; - ActiveValueTitle.Left = LockXYLabel.Left + LockXYLabel.Width; - ActiveValueTitle.Width = AccelerationChart.Left - ActiveValueTitle.Left; - ActiveValueTitle.TextAlign = ContentAlignment.MiddleCenter; - - var sensitivity = new OptionXY( + AccelGUI = AccelGUIFactory.Construct( + this, + activeAccel, + AccelerationChart, + AccelerationChartY, + VelocityChart, + VelocityChartY, + GainChart, + GainChartY, + accelTypeDropX, + accelTypeDropY, + writeButton, + showVelocityGainToolStripMenuItem, + showLastMouseMoveToolStripMenuItem, + wholeVectorToolStripMenuItem, + byVectorComponentToolStripMenuItem, + gainCapToolStripMenuItem, + legacyCapToolStripMenuItem, + gainOffsetToolStripMenuItem, + legacyOffsetToolStripMenuItem, + AutoWriteMenuItem, + scaleByDPIToolStripMenuItem, + DPITextBox, + PollRateTextBox, sensitivityBoxX, sensitivityBoxY, - sensXYLock, - this, - 1, - sensitivityLabel, - new ActiveValueLabelXY( - new ActiveValueLabel(SensitivityActiveXLabel, ActiveValueTitle), - new ActiveValueLabel(SensitivityActiveYLabel, ActiveValueTitle)), - "Sensitivity", - accelCharts); - - var rotation = new Option( rotationBox, - this, - 0, - rotationLabel, - new ActiveValueLabel(RotationActiveLabel, ActiveValueTitle), - "Rotation"); - - var weight = new OptionXY( - weightBoxFirst, - weightBoxSecond, - weightXYLock, - this, - 1, - weightLabel, - new ActiveValueLabelXY( - new ActiveValueLabel(WeightActiveXLabel, ActiveValueTitle), - new ActiveValueLabel(WeightActiveYLabel, ActiveValueTitle)), - "Weight", - accelCharts); - - var cap = new OptionXY( + weightBoxX, + weightBoxY, capBoxX, capBoxY, - capXYLock, - this, - 0, - capLabel, - new ActiveValueLabelXY( - new ActiveValueLabel(CapActiveXLabel, ActiveValueTitle), - new ActiveValueLabel(CapActiveYLabel, ActiveValueTitle)), - "Cap", - accelCharts); - - var offset = new Option( - offsetBox, - this, - 0, - offsetLabel, - new ActiveValueLabel(OffsetActiveLabel, ActiveValueTitle), - "Offset"); - - var offsetOptions = new OffsetOptions( - gainOffsetToolStripMenuItem, - legacyOffsetToolStripMenuItem, - offset); - - // The name and layout of these options is handled by AccelerationOptions object. - var acceleration = new Option( - new Field(accelerationBox, this, 0), - constantOneLabel, - new ActiveValueLabel(AccelerationActiveLabel, ActiveValueTitle)); - - var limitOrExponent = new Option( - new Field(limitBox, this, 2), - constantTwoLabel, - new ActiveValueLabel(LimitExpActiveLabel, ActiveValueTitle)); - - var midpoint = new Option( - new Field(midpointBox, this, 0), - constantThreeLabel, - new ActiveValueLabel(MidpointActiveLabel, ActiveValueTitle)); - - var accelerationOptions = new AccelOptions( - accelTypeDrop, - new Option[] - { - offset, - acceleration, - limitOrExponent, - midpoint, - }, - new OptionXY[] - { - weight, - cap, - }, - writeButton, - new ActiveValueLabel(AccelTypeActiveLabel, ActiveValueTitle)); - - var capOptions = new CapOptions( - gainCapToolStripMenuItem, - legacyCapToolStripMenuItem, - cap, - weight); - - var accelCalculator = new AccelCalculator( - new Field(DPITextBox.TextBox, this, AccelCalculator.DefaultDPI), - new Field(PollRateTextBox.TextBox, this, AccelCalculator.DefaultPollRate)); - - var settings = new SettingsManager( - activeAccel, - accelCalculator.DPI, - accelCalculator.PollRate, - AutoWriteMenuItem); - - AccelGUI = new AccelGUI( - this, - accelCalculator, - accelCharts, - settings, - accelerationOptions, - sensitivity, - rotation, - weight, - capOptions, - offsetOptions, - acceleration, - limitOrExponent, - midpoint, - writeButton, - MouseLabel, - ScaleMenuItem, - AutoWriteMenuItem); + offsetBoxX, + offsetBoxY, + accelerationBoxX, + accelerationBoxY, + limitBoxX, + limitBoxY, + midpointBoxX, + midpointBoxY, + sensXYLock, + ByComponentXYLock, + LockXYLabel, + sensitivityLabel, + rotationLabel, + weightLabelX, + weightLabelY, + capLabelX, + capLabelY, + offsetLabelX, + offsetLabelY, + constantOneLabelX, + constantOneLabelY, + constantTwoLabelX, + constantTwoLabelY, + constantThreeLabelX, + constantThreeLabelY, + ActiveValueTitle, + ActiveValueTitleY, + SensitivityActiveXLabel, + SensitivityActiveYLabel, + RotationActiveLabel, + WeightActiveXLabel, + WeightActiveYLabel, + CapActiveXLabel, + CapActiveYLabel, + OffsetActiveXLabel, + OffsetActiveYLabel, + AccelerationActiveLabelX, + AccelerationActiveLabelY, + LimitExpActiveXLabel, + LimitExpActiveYLabel, + MidpointActiveXLabel, + MidpointActiveYLabel, + AccelTypeActiveLabelX, + AccelTypeActiveLabelY, + OptionSetXTitle, + OptionSetYTitle, + MouseLabel); } #endregion Constructor @@ -203,16 +142,11 @@ namespace grapher } - private void writeButton_Click(object sender, EventArgs e) - { - AccelGUI.UpdateActiveSettingsFromFields(); - } - - #endregion Methods - private void RawAcceleration_Paint(object sender, PaintEventArgs e) { - AccelGUI.AccelCharts.DrawPoints(); + AccelGUI.AccelCharts.DrawLastMovement(); } + + #endregion Method } } diff --git a/grapher/Layouts/ClassicLayout.cs b/grapher/Layouts/ClassicLayout.cs index 05f6c82..572566f 100644 --- a/grapher/Layouts/ClassicLayout.cs +++ b/grapher/Layouts/ClassicLayout.cs @@ -1,9 +1,4 @@ using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Layouts { @@ -14,8 +9,13 @@ namespace grapher.Layouts { Name = "Classic"; Index = (int)AccelMode.classic; - ShowOptions = new bool[] { true, true, true, false }; - OptionNames = new string[] { Offset, Acceleration, Exponent, string.Empty }; + + AccelLayout = new OptionLayout(true, Acceleration); + CapLayout = new OptionLayout(true, Cap); + WeightLayout = new OptionLayout(true, Weight); + OffsetLayout = new OptionLayout(true, Offset); + LimExpLayout = new OptionLayout(true, Exponent); + MidpointLayout = new OptionLayout(false, string.Empty); } } } diff --git a/grapher/Layouts/DefaultLayout.cs b/grapher/Layouts/DefaultLayout.cs index 42841d5..83535c2 100644 --- a/grapher/Layouts/DefaultLayout.cs +++ b/grapher/Layouts/DefaultLayout.cs @@ -1,11 +1,4 @@ using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - namespace grapher.Layouts { @@ -16,9 +9,14 @@ namespace grapher.Layouts { Name = "Default"; Index = (int)AccelMode.noaccel; - ShowOptions = new bool[] { true, true, true, true }; - OptionNames = new string[] { Offset, Acceleration, $"{Limit}\\{Exponent}", Midpoint }; ButtonEnabled = false; + + AccelLayout = new OptionLayout(true, Acceleration); + CapLayout = new OptionLayout(true, Cap); + WeightLayout = new OptionLayout(true, Weight); + OffsetLayout = new OptionLayout(true, Offset); + LimExpLayout = new OptionLayout(true, $"{Limit}\\{Exponent}"); + MidpointLayout = new OptionLayout(true, Midpoint); } } } diff --git a/grapher/Layouts/LayoutBase.cs b/grapher/Layouts/LayoutBase.cs index eed1716..6ed8fee 100644 --- a/grapher/Layouts/LayoutBase.cs +++ b/grapher/Layouts/LayoutBase.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using grapher.Models.Options; using System.Windows.Forms; namespace grapher.Layouts @@ -15,12 +11,18 @@ namespace grapher.Layouts public const string Limit = "Limit"; public const string Midpoint = "Midpoint"; public const string Offset = "Offset"; + public const string Cap = "Cap"; + public const string Weight = "Weight"; public LayoutBase() { - ShowOptions = new bool[] { false, false, false, false }; - ShowOptionsXY = new bool[] { true, true }; - OptionNames = new string[] { string.Empty, string.Empty, string.Empty, string.Empty }; + AccelLayout = new OptionLayout(false, string.Empty); + CapLayout = new OptionLayout(false, string.Empty); + WeightLayout = new OptionLayout(false, string.Empty); + OffsetLayout = new OptionLayout(false, string.Empty); + LimExpLayout = new OptionLayout(false, string.Empty); + MidpointLayout = new OptionLayout(false, string.Empty); + ButtonEnabled = true; } @@ -32,43 +34,75 @@ namespace grapher.Layouts public string Name { get; internal set; } - internal bool[] ShowOptions { get; set; } + protected bool ButtonEnabled { get; set; } + + protected OptionLayout AccelLayout { get; set; } + + protected OptionLayout CapLayout { get; set; } - internal bool[] ShowOptionsXY { get; set; } + protected OptionLayout WeightLayout { get; set; } - internal string[] OptionNames { get; set; } + protected OptionLayout OffsetLayout { get; set; } - internal bool ButtonEnabled { get; set; } + protected OptionLayout LimExpLayout { get; set; } - public void Layout(Option[] options, OptionXY[] optionsXY, Button button) + protected OptionLayout MidpointLayout { get; set; } + + public void Layout( + IOption accelOption, + IOption capOption, + IOption weightOption, + IOption offsetOption, + IOption limExpOption, + IOption midpointOption, + Button button, + int top) { - // Relies on AccelOptions to keep lengths correct. - for (int i = 0; i < options.Length; i++) - { - if (ShowOptions[i]) - { - options[i].Show(OptionNames[i]); - } - else - { - options[i].Hide(); - } - } + AccelLayout.Layout(accelOption); + CapLayout.Layout(capOption); + WeightLayout.Layout(weightOption); + OffsetLayout.Layout(offsetOption); + LimExpLayout.Layout(limExpOption); + MidpointLayout.Layout(midpointOption); + + button.Enabled = ButtonEnabled; - // Relies on AccelOptions to keep lengths correct. - for (int i = 0; i< optionsXY.Length; i++) + IOption previous = null; + foreach (var option in new IOption[] { accelOption, capOption, weightOption, offsetOption, limExpOption, midpointOption}) { - if (ShowOptionsXY[i]) - { - optionsXY[i].Show(); - } - else + if (option.Visible) { - optionsXY[i].Hide(); + if (previous != null) + { + option.SnapTo(previous); + } + else + { + option.Top = top; + } + + previous = option; } } + } - button.Enabled = ButtonEnabled; + public void Layout( + IOption accelOption, + IOption capOption, + IOption weightOption, + IOption offsetOption, + IOption limExpOption, + IOption midpointOption, + Button button) + { + Layout(accelOption, + capOption, + weightOption, + offsetOption, + limExpOption, + midpointOption, + button, + accelOption.Top); } } } diff --git a/grapher/Layouts/LinearLayout.cs b/grapher/Layouts/LinearLayout.cs index 0a79a64..fded8c7 100644 --- a/grapher/Layouts/LinearLayout.cs +++ b/grapher/Layouts/LinearLayout.cs @@ -1,9 +1,4 @@ using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Layouts { @@ -14,8 +9,13 @@ namespace grapher.Layouts { Name = "Linear"; Index = (int)AccelMode.linear; - ShowOptions = new bool[] { true, true, false, false }; - OptionNames = new string[] { Offset, Acceleration, string.Empty, string.Empty }; + + AccelLayout = new OptionLayout(true, Acceleration); + CapLayout = new OptionLayout(true, Cap); + WeightLayout = new OptionLayout(false, string.Empty); + OffsetLayout = new OptionLayout(true, Offset); + LimExpLayout = new OptionLayout(false, string.Empty); + MidpointLayout = new OptionLayout(false, string.Empty); } } } diff --git a/grapher/Layouts/LogLayout.cs b/grapher/Layouts/LogLayout.cs deleted file mode 100644 index 1206fb3..0000000 --- a/grapher/Layouts/LogLayout.cs +++ /dev/null @@ -1,21 +0,0 @@ -using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace grapher.Layouts -{ - public class LogLayout : LayoutBase - { - public LogLayout() - : base() - { - Name = "Logarithmic"; - Index = (int)AccelMode.logarithmic; - ShowOptions = new bool[] { true, true, false, false }; - OptionNames = new string[] { Offset, Acceleration, string.Empty, string.Empty }; - } - } -} diff --git a/grapher/Layouts/NaturalGainLayout.cs b/grapher/Layouts/NaturalGainLayout.cs index 9bb1ec8..b982b91 100644 --- a/grapher/Layouts/NaturalGainLayout.cs +++ b/grapher/Layouts/NaturalGainLayout.cs @@ -1,9 +1,4 @@ using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Layouts { @@ -14,8 +9,13 @@ namespace grapher.Layouts { Name = "NaturalGain"; Index = (int)AccelMode.naturalgain; - ShowOptions = new bool[] { true, true, true, false }; - OptionNames = new string[] { Offset, Acceleration, Limit, string.Empty }; + + AccelLayout = new OptionLayout(true, Acceleration); + CapLayout = new OptionLayout(false, string.Empty); + WeightLayout = new OptionLayout(false, string.Empty); + OffsetLayout = new OptionLayout(true, Offset); + LimExpLayout = new OptionLayout(true, Limit); + MidpointLayout = new OptionLayout(false, string.Empty); } } } diff --git a/grapher/Layouts/NaturalLayout.cs b/grapher/Layouts/NaturalLayout.cs index 44c6c18..aa5c22c 100644 --- a/grapher/Layouts/NaturalLayout.cs +++ b/grapher/Layouts/NaturalLayout.cs @@ -1,9 +1,4 @@ using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Layouts { @@ -14,8 +9,13 @@ namespace grapher.Layouts { Name = "Natural"; Index = (int)AccelMode.natural; - ShowOptions = new bool[] { true, true, true, false }; - OptionNames = new string[] { Offset, Acceleration, Limit, string.Empty }; + + AccelLayout = new OptionLayout(true, Acceleration); + CapLayout = new OptionLayout(false, string.Empty); + WeightLayout = new OptionLayout(false, string.Empty); + OffsetLayout = new OptionLayout(true, Offset); + LimExpLayout = new OptionLayout(true, Limit); + MidpointLayout = new OptionLayout(false, string.Empty); } } } diff --git a/grapher/Layouts/OffLayout.cs b/grapher/Layouts/OffLayout.cs index ce7c149..85c8d3f 100644 --- a/grapher/Layouts/OffLayout.cs +++ b/grapher/Layouts/OffLayout.cs @@ -1,9 +1,4 @@ using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Layouts { @@ -14,10 +9,14 @@ namespace grapher.Layouts { Name = "Off"; Index = (int)AccelMode.noaccel; - ShowOptions = new bool[] { false, false, false, false }; - OptionNames = new string[] { string.Empty, string.Empty, string.Empty, string.Empty }; - ShowOptionsXY = new bool[] { false, false }; ButtonEnabled = true; + + AccelLayout = new OptionLayout(false, string.Empty); + CapLayout = new OptionLayout(false, string.Empty); + WeightLayout = new OptionLayout(false, string.Empty); + OffsetLayout = new OptionLayout(false, string.Empty); + LimExpLayout = new OptionLayout(false, string.Empty); + MidpointLayout = new OptionLayout(false, string.Empty); } } } diff --git a/grapher/Layouts/OptionLayout.cs b/grapher/Layouts/OptionLayout.cs new file mode 100644 index 0000000..2f29706 --- /dev/null +++ b/grapher/Layouts/OptionLayout.cs @@ -0,0 +1,41 @@ +using grapher.Models.Options; + +namespace grapher.Layouts +{ + public class OptionLayout + { + #region Constructors + + public OptionLayout(bool show, string name) + { + Show = show; + Name = name; + } + + #endregion Constructors + + #region Properties + + private bool Show { get; } + + private string Name { get; } + + #endregion Properties + + #region Methods + + public void Layout(IOption option) + { + if (Show) + { + option.Show(Name); + } + else + { + option.Hide(); + } + } + + #endregion Methods + } +} diff --git a/grapher/Layouts/PowerLayout.cs b/grapher/Layouts/PowerLayout.cs index c14083a..e0dcaf8 100644 --- a/grapher/Layouts/PowerLayout.cs +++ b/grapher/Layouts/PowerLayout.cs @@ -1,9 +1,4 @@ using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Layouts { @@ -14,8 +9,13 @@ namespace grapher.Layouts { Name = "Power"; Index = (int)AccelMode.power; - ShowOptions = new bool[] { true, true, true, false }; - OptionNames = new string[] { Offset, Scale, Exponent, string.Empty }; + + AccelLayout = new OptionLayout(true, Acceleration); + CapLayout = new OptionLayout(true, Cap); + WeightLayout = new OptionLayout(true, Weight); + OffsetLayout = new OptionLayout(true, Offset); + LimExpLayout = new OptionLayout(true, Limit); + MidpointLayout = new OptionLayout(false, string.Empty); } } } diff --git a/grapher/Layouts/SigmoidGainLayout.cs b/grapher/Layouts/SigmoidGainLayout.cs index 93214ec..183e31f 100644 --- a/grapher/Layouts/SigmoidGainLayout.cs +++ b/grapher/Layouts/SigmoidGainLayout.cs @@ -1,9 +1,4 @@ using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Layouts { @@ -14,8 +9,13 @@ namespace grapher.Layouts { Name = "SigmoidGain"; Index = (int)AccelMode.sigmoidgain; - ShowOptions = new bool[] { true, true, true, true }; - OptionNames = new string[] { Offset, Acceleration, Limit, Midpoint }; + + AccelLayout = new OptionLayout(true, Acceleration); + CapLayout = new OptionLayout(false, string.Empty); + WeightLayout = new OptionLayout(false, string.Empty); + OffsetLayout = new OptionLayout(true, Offset); + LimExpLayout = new OptionLayout(true, Limit); + MidpointLayout = new OptionLayout(true, Midpoint); } } } diff --git a/grapher/Layouts/SigmoidLayout.cs b/grapher/Layouts/SigmoidLayout.cs deleted file mode 100644 index 1c7f0b9..0000000 --- a/grapher/Layouts/SigmoidLayout.cs +++ /dev/null @@ -1,21 +0,0 @@ -using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace grapher.Layouts -{ - public class SigmoidLayout : LayoutBase - { - public SigmoidLayout() - : base() - { - Name = "Sigmoid"; - Index = (int)AccelMode.sigmoid; - ShowOptions = new bool[] { true, true, true, true }; - OptionNames = new string[] { Offset, Acceleration, Limit, Midpoint }; - } - } -} diff --git a/grapher/Models/AccelGUI.cs b/grapher/Models/AccelGUI.cs index a7d5d49..3acb943 100644 --- a/grapher/Models/AccelGUI.cs +++ b/grapher/Models/AccelGUI.cs @@ -3,67 +3,47 @@ using grapher.Models.Mouse; using grapher.Models.Options; using grapher.Models.Serialized; using System; -using System.CodeDom; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; -using System.Windows.Forms.DataVisualization.Charting; namespace grapher { public class AccelGUI { - #region constructors + #region Constructors public AccelGUI( RawAcceleration accelForm, AccelCalculator accelCalculator, AccelCharts accelCharts, SettingsManager settings, - AccelOptions accelOptions, - OptionXY sensitivity, - Option rotation, - OptionXY weight, - CapOptions cap, - OffsetOptions offset, - Option acceleration, - Option limtOrExp, - Option midpoint, + ApplyOptions applyOptions, Button writeButton, Label mouseMoveLabel, - ToolStripMenuItem scaleMenuItem, - ToolStripMenuItem autoWriteMenuItem) + ToolStripMenuItem scaleMenuItem) { AccelForm = accelForm; AccelCalculator = accelCalculator; AccelCharts = accelCharts; - AccelerationOptions = accelOptions; - Sensitivity = sensitivity; - Rotation = rotation; - Weight = weight; - Cap = cap; - Offset = offset; - Acceleration = acceleration; - LimitOrExponent = limtOrExp; - Midpoint = midpoint; + ApplyOptions = applyOptions; WriteButton = writeButton; ScaleMenuItem = scaleMenuItem; Settings = settings; Settings.Startup(); - UpdateGraph(); + RefreshOnRead(); MouseWatcher = new MouseWatcher(AccelForm, mouseMoveLabel, AccelCharts); ScaleMenuItem.Click += new System.EventHandler(OnScaleMenuItemClick); + WriteButton.Click += new System.EventHandler(OnWriteButtonClick); + + ButtonTimer = SetupButtonTimer(); + SetupWriteButton(); } - #endregion constructors + #endregion Constructors - #region properties + #region Properties public RawAcceleration AccelForm { get; } @@ -73,67 +53,33 @@ namespace grapher public SettingsManager Settings { get; } - public AccelOptions AccelerationOptions { get; } - - public OptionXY Sensitivity { get; } - - public Option Rotation { get; } - - public OptionXY Weight { get; } - - public CapOptions Cap { get; } - - public OffsetOptions Offset { get; } - - public Option Acceleration { get; } - - public Option LimitOrExponent { get; } - - public Option Midpoint { get; } + public ApplyOptions ApplyOptions { get; } public Button WriteButton { get; } + public Timer ButtonTimer { get; } + public MouseWatcher MouseWatcher { get; } public ToolStripMenuItem ScaleMenuItem { get; } - #endregion properties + #endregion Properties - #region methods + #region Methods public void UpdateActiveSettingsFromFields() { var settings = new DriverSettings { - rotation = Rotation.Field.Data, + rotation = ApplyOptions.Rotation.Field.Data, sensitivity = new Vec2<double> { - x = Sensitivity.Fields.X, - y = Sensitivity.Fields.Y - }, - combineMagnitudes = true, - modes = new Vec2<AccelMode> - { - x = (AccelMode)AccelerationOptions.AccelerationIndex - }, - args = new Vec2<AccelArgs> - { - x = new AccelArgs - { - offset = Offset.Offset, - legacy_offset = Offset.LegacyOffset, - weight = Weight.Fields.X, - gainCap = Cap.VelocityGainCap, - scaleCap = Cap.SensitivityCapX, - accel = Acceleration.Field.Data, - rate = Acceleration.Field.Data, - powerScale = Acceleration.Field.Data, - limit = LimitOrExponent.Field.Data, - exponent = LimitOrExponent.Field.Data, - powerExponent = LimitOrExponent.Field.Data, - midpoint = Midpoint.Field.Data - } + x = ApplyOptions.Sensitivity.Fields.X, + y = ApplyOptions.Sensitivity.Fields.Y }, + combineMagnitudes = ApplyOptions.IsWhole, + modes = ApplyOptions.GetModes(), + args = ApplyOptions.GetArgs(), minimumTime = .4 }; @@ -141,10 +87,17 @@ namespace grapher { AccelForm.Invoke((MethodInvoker)delegate { + WriteButtonDelay(); UpdateGraph(); }); }); - + RefreshOnRead(); + } + + public void RefreshOnRead() + { + UpdateGraph(); + UpdateShownActiveValues(); } public void UpdateGraph() @@ -154,29 +107,66 @@ namespace grapher Settings.ActiveAccel, Settings.RawAccelSettings.AccelerationSettings); AccelCharts.Bind(); - UpdateActiveValueLabels(); } - public void UpdateActiveValueLabels() + public void UpdateShownActiveValues() { var settings = Settings.RawAccelSettings.AccelerationSettings; - - Sensitivity.SetActiveValues(settings.sensitivity.x, settings.sensitivity.y); - Rotation.SetActiveValue(settings.rotation); - AccelerationOptions.SetActiveValue((int)settings.modes.x); - Offset.SetActiveValue(settings.args.x.offset, settings.args.y.offset); - Weight.SetActiveValues(settings.args.x.weight, settings.args.x.weight); - Acceleration.SetActiveValue(settings.args.x.accel); // rate, powerscale - LimitOrExponent.SetActiveValue(settings.args.x.limit); //exp, powerexp - Midpoint.SetActiveValue(settings.args.x.midpoint); - //Cap.SetActiveValues(Settings.ActiveAccel.GainCap, Settings.ActiveAccel.CapX, Settings.ActiveAccel.CapY, Settings.ActiveAccel.GainCapEnabled); + + AccelCharts.ShowActive(settings); + ApplyOptions.SetActiveValues(settings); + } + + private Timer SetupButtonTimer() + { + Timer buttonTimer = new Timer(); + buttonTimer.Enabled = true; + buttonTimer.Interval = Convert.ToInt32(ManagedAccel.WriteDelay); + buttonTimer.Tick += new System.EventHandler(OnButtonTimerTick); + return buttonTimer; + } + + private void SetupWriteButton() + { + WriteButton.Top = AccelCharts.SensitivityChart.Top + AccelCharts.SensitivityChart.Height - Constants.WriteButtonVerticalOffset; + SetWriteButtonDefault(); + } + + private void SetWriteButtonDefault() + { + WriteButton.Text = Constants.WriteButtonDefaultText; + WriteButton.Enabled = true; + } + + private void SetWriteButtonDelay() + { + WriteButton.Enabled = false; + WriteButton.Text = $"{Constants.WriteButtonDelayText} : {ButtonTimer.Interval} ms"; } private void OnScaleMenuItemClick(object sender, EventArgs e) { UpdateGraph(); } - #endregion methods + + private void OnWriteButtonClick(object sender, EventArgs e) + { + UpdateActiveSettingsFromFields(); + } + + private void OnButtonTimerTick(object sender, EventArgs e) + { + ButtonTimer.Stop(); + SetWriteButtonDefault(); + } + + private void WriteButtonDelay() + { + SetWriteButtonDelay(); + ButtonTimer.Start(); + } + + #endregion Methods } } diff --git a/grapher/Models/AccelGUIFactory.cs b/grapher/Models/AccelGUIFactory.cs new file mode 100644 index 0000000..42a7b83 --- /dev/null +++ b/grapher/Models/AccelGUIFactory.cs @@ -0,0 +1,303 @@ +using grapher.Models.Calculations; +using grapher.Models.Options; +using grapher.Models.Serialized; +using System.Windows.Forms; +using System.Windows.Forms.DataVisualization.Charting; + +namespace grapher.Models +{ + public static class AccelGUIFactory + { + #region Methods + + public static AccelGUI Construct( + RawAcceleration form, + ManagedAccel activeAccel, + Chart accelerationChart, + Chart accelerationChartY, + Chart velocityChart, + Chart velocityChartY, + Chart gainChart, + Chart gainChartY, + ComboBox accelTypeDropX, + ComboBox accelTypeDropY, + Button writeButton, + ToolStripMenuItem showVelocityGainToolStripMenuItem, + ToolStripMenuItem showLastMouseMoveMenuItem, + ToolStripMenuItem wholeVectorToolStripMenuItem, + ToolStripMenuItem byVectorComponentToolStripMenuItem, + ToolStripMenuItem velocityGainCapToolStripMenuItem, + ToolStripMenuItem legacyCapToolStripMenuItem, + ToolStripMenuItem gainOffsetToolStripMenuItem, + ToolStripMenuItem legacyOffsetToolStripMenuItem, + ToolStripMenuItem autoWriteMenuItem, + ToolStripMenuItem scaleMenuItem, + ToolStripTextBox dpiTextBox, + ToolStripTextBox pollRateTextBox, + TextBox sensitivityBoxX, + TextBox sensitivityBoxY, + TextBox rotationBox, + TextBox weightBoxX, + TextBox weightBoxY, + TextBox capBoxX, + TextBox capBoxY, + TextBox offsetBoxX, + TextBox offsetBoxY, + TextBox accelerationBoxX, + TextBox accelerationBoxY, + TextBox limitBoxX, + TextBox limitBoxY, + TextBox midpointBoxX, + TextBox midpointBoxY, + CheckBox sensXYLock, + CheckBox byComponentXYLock, + Label lockXYLabel, + Label sensitivityLabel, + Label rotationLabel, + Label weightLabelX, + Label weightLabelY, + Label capLabelX, + Label capLabelY, + Label offsetLabelX, + Label offsetLabelY, + Label constantOneLabelX, + Label constantOneLabelY, + Label constantTwoLabelX, + Label constantTwoLabelY, + Label constantThreeLabelX, + Label constantThreeLabelY, + Label activeValueTitleX, + Label activeValueTitleY, + Label sensitivityActiveXLabel, + Label sensitivityActiveYLabel, + Label rotationActiveLabel, + Label weightActiveXLabel, + Label weightActiveYLabel, + Label capActiveXLabel, + Label capActiveYLabel, + Label offsetActiveLabelX, + Label offsetActiveLabelY, + Label accelerationActiveLabelX, + Label accelerationActiveLabelY, + Label limitExpActiveLabelX, + Label limitExpActiveLabelY, + Label midpointActiveLabelX, + Label midpointActiveLabelY, + Label accelTypeActiveLabelX, + Label accelTypeActiveLabelY, + Label optionSetXTitle, + Label optionSetYTitle, + Label mouseLabel) + { + var accelCharts = new AccelCharts( + form, + new ChartXY(accelerationChart, accelerationChartY), + new ChartXY(velocityChart, velocityChartY), + new ChartXY(gainChart, gainChartY), + showVelocityGainToolStripMenuItem, + showLastMouseMoveMenuItem, + writeButton); + + var sensitivity = new OptionXY( + sensitivityBoxX, + sensitivityBoxY, + sensXYLock, + form, + 1, + sensitivityLabel, + new ActiveValueLabelXY( + new ActiveValueLabel(sensitivityActiveXLabel, activeValueTitleX), + new ActiveValueLabel(sensitivityActiveYLabel, activeValueTitleX)), + "Sensitivity"); + + var rotation = new Option( + rotationBox, + form, + 0, + rotationLabel, + 0, + new ActiveValueLabel(rotationActiveLabel, activeValueTitleX), + "Rotation"); + + var optionSetYLeft = rotation.Left + rotation.Width; + + var weightX = new Option( + weightBoxX, + form, + 1, + weightLabelX, + 0, + new ActiveValueLabel(weightActiveXLabel, activeValueTitleX), + "Weight"); + + var weightY = new Option( + weightBoxY, + form, + 1, + weightLabelY, + optionSetYLeft, + new ActiveValueLabel(weightActiveYLabel, activeValueTitleY), + "Weight"); + + var capX = new Option( + capBoxX, + form, + 0, + capLabelX, + 0, + new ActiveValueLabel(capActiveXLabel, activeValueTitleX), + "Cap"); + + var capY = new Option( + capBoxY, + form, + 0, + capLabelY, + optionSetYLeft, + new ActiveValueLabel(capActiveYLabel, activeValueTitleY), + "Cap"); + + var offsetX = new Option( + offsetBoxX, + form, + 0, + offsetLabelX, + 0, + new ActiveValueLabel(offsetActiveLabelX, activeValueTitleX), + "Offset"); + + var offsetY = new Option( + offsetBoxY, + form, + 0, + offsetLabelY, + optionSetYLeft, + new ActiveValueLabel(offsetActiveLabelY, activeValueTitleY), + "Offset"); + + var offsetOptionsX = new OffsetOptions( + gainOffsetToolStripMenuItem, + legacyOffsetToolStripMenuItem, + offsetX); + + var offsetOptionsY = new OffsetOptions( + gainOffsetToolStripMenuItem, + legacyOffsetToolStripMenuItem, + offsetY); + + var accelerationX = new Option( + new Field(accelerationBoxX, form, 0), + constantOneLabelX, + new ActiveValueLabel(accelerationActiveLabelX, activeValueTitleX), + 0); + + var accelerationY = new Option( + new Field(accelerationBoxY, form, 0), + constantOneLabelY, + new ActiveValueLabel(accelerationActiveLabelY, activeValueTitleY), + optionSetYLeft); + + var limitOrExponentX = new Option( + new Field(limitBoxX, form, 2), + constantTwoLabelX, + new ActiveValueLabel(limitExpActiveLabelX, activeValueTitleX), + 0); + + var limitOrExponentY = new Option( + new Field(limitBoxY, form, 2), + constantTwoLabelY, + new ActiveValueLabel(limitExpActiveLabelY, activeValueTitleY), + optionSetYLeft); + + var midpointX = new Option( + new Field(midpointBoxX, form, 0), + constantThreeLabelX, + new ActiveValueLabel(midpointActiveLabelX, activeValueTitleY), + 0); + + var midpointY = new Option( + new Field(midpointBoxY, form, 0), + constantThreeLabelY, + new ActiveValueLabel(midpointActiveLabelY, activeValueTitleY), + optionSetYLeft); + + var capOptionsX = new CapOptions( + velocityGainCapToolStripMenuItem, + legacyCapToolStripMenuItem, + capX); + + var capOptionsY = new CapOptions( + velocityGainCapToolStripMenuItem, + legacyCapToolStripMenuItem, + capY); + + var accelerationOptionsX = new AccelTypeOptions( + accelTypeDropX, + accelerationX, + capOptionsX, + weightX, + offsetOptionsX, + limitOrExponentX, + midpointX, + writeButton, + new ActiveValueLabel(accelTypeActiveLabelX, activeValueTitleX)); + + var accelerationOptionsY = new AccelTypeOptions( + accelTypeDropY, + accelerationY, + capOptionsY, + weightY, + offsetOptionsY, + limitOrExponentY, + midpointY, + writeButton, + new ActiveValueLabel(accelTypeActiveLabelY, activeValueTitleY)); + + var optionsSetX = new AccelOptionSet( + optionSetXTitle, + activeValueTitleX, + rotationBox.Top + rotationBox.Height + Constants.OptionVerticalSeperation, + accelerationOptionsX); + + var optionsSetY = new AccelOptionSet( + optionSetYTitle, + activeValueTitleY, + rotationBox.Top + rotationBox.Height + Constants.OptionVerticalSeperation, + accelerationOptionsY); + + var applyOptions = new ApplyOptions( + wholeVectorToolStripMenuItem, + byVectorComponentToolStripMenuItem, + byComponentXYLock, + optionsSetX, + optionsSetY, + sensitivity, + rotation, + lockXYLabel, + accelCharts); + + var accelCalculator = new AccelCalculator( + new Field(dpiTextBox.TextBox, form, Constants.DefaultDPI), + new Field(pollRateTextBox.TextBox, form, Constants.DefaultPollRate)); + + var settings = new SettingsManager( + activeAccel, + accelCalculator.DPI, + accelCalculator.PollRate, + autoWriteMenuItem, + showLastMouseMoveMenuItem); + + return new AccelGUI( + form, + accelCalculator, + accelCharts, + settings, + applyOptions, + writeButton, + mouseLabel, + scaleMenuItem); + } + + #endregion Methods + } +} diff --git a/grapher/Models/Calculations/AccelCalculator.cs b/grapher/Models/Calculations/AccelCalculator.cs index 102de8d..092a7aa 100644 --- a/grapher/Models/Calculations/AccelCalculator.cs +++ b/grapher/Models/Calculations/AccelCalculator.cs @@ -3,19 +3,12 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; namespace grapher.Models.Calculations { public class AccelCalculator { - public const int DefaultDPI = 1200; - public const int DefaultPollRate = 1000; - public const int Resolution = 100; - public const double MaxMultiplier = 85; - public const double XYToCombinedRatio = 1.3; + #region Structs public struct MagnitudeData { @@ -24,6 +17,9 @@ namespace grapher.Models.Calculations public int y; } + #endregion Structs + + #region Constructors public AccelCalculator(Field dpi, Field pollRate) { @@ -31,6 +27,10 @@ namespace grapher.Models.Calculations PollRate = pollRate; } + #endregion Constructors + + #region Properties + public ReadOnlyCollection<MagnitudeData> MagnitudesCombined { get; private set; } public ReadOnlyCollection<MagnitudeData> MagnitudesX { get; private set; } @@ -47,6 +47,10 @@ namespace grapher.Models.Calculations private int Increment { get; set; } + #endregion Fields + + #region Methods + public void Calculate(AccelData data, ManagedAccel accel, DriverSettings settings) { ScaleByMouseSettings(); @@ -171,12 +175,14 @@ namespace grapher.Models.Calculations public void ScaleByMouseSettings() { var dpiPollFactor = DPI.Data / PollRate.Data; - CombinedMaxVelocity = dpiPollFactor * MaxMultiplier; - Increment = (int) Math.Floor(CombinedMaxVelocity / Resolution); - XYMaxVelocity = CombinedMaxVelocity * 1.5; + CombinedMaxVelocity = dpiPollFactor * Constants.MaxMultiplier; + Increment = (int)Math.Floor(CombinedMaxVelocity / Constants.Resolution); + XYMaxVelocity = CombinedMaxVelocity * Constants.XYToCombinedRatio; MagnitudesCombined = GetMagnitudes(); MagnitudesX = GetMagnitudesX(); MagnitudesY = GetMagnitudesY(); } + + #endregion Methods } } diff --git a/grapher/Models/Calculations/AccelChartData.cs b/grapher/Models/Calculations/AccelChartData.cs index 20142a7..8c0c8ea 100644 --- a/grapher/Models/Calculations/AccelChartData.cs +++ b/grapher/Models/Calculations/AccelChartData.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Models.Calculations { public class AccelChartData { + #region Constructors + public AccelChartData() { AccelPoints = new SortedDictionary<double, double>(); @@ -17,6 +17,10 @@ namespace grapher.Models.Calculations OutVelocityToPoints = new Dictionary<double, (double, double, double)>(); } + #endregion Constructors + + #region Properties + public SortedDictionary<double, double> AccelPoints { get; } public SortedDictionary<double, double> VelocityPoints { get; } @@ -27,6 +31,10 @@ namespace grapher.Models.Calculations public Dictionary<double, (double, double, double)> OutVelocityToPoints { get; } + #endregion Properties + + #region Methods + public void Clear() { AccelPoints.Clear(); @@ -57,5 +65,7 @@ namespace grapher.Models.Calculations return values; } } + + #endregion Methods } } diff --git a/grapher/Models/Calculations/AccelData.cs b/grapher/Models/Calculations/AccelData.cs index 683c67e..eef4d01 100644 --- a/grapher/Models/Calculations/AccelData.cs +++ b/grapher/Models/Calculations/AccelData.cs @@ -1,15 +1,11 @@ using grapher.Models.Charts; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using static grapher.AccelCharts; namespace grapher.Models.Calculations { public class AccelData { + #region Constructors public AccelData( EstimatedPoints combined, @@ -25,6 +21,10 @@ namespace grapher.Models.Calculations EstimatedY = y; } + #endregion Constructors + + #region Properties + public AccelChartData Combined { get; } public AccelChartData X { get; } @@ -37,6 +37,10 @@ namespace grapher.Models.Calculations private EstimatedPoints EstimatedY { get; } + #endregion Properties + + #region Methods + public void Clear() { Combined.Clear(); @@ -70,5 +74,6 @@ namespace grapher.Models.Calculations EstimatedY.Gain.Set(inYVelocity, yGain); } + #endregion Methods } } diff --git a/grapher/Models/Charts/AccelCharts.cs b/grapher/Models/Charts/AccelCharts.cs index 1aa3909..3f228c3 100644 --- a/grapher/Models/Charts/AccelCharts.cs +++ b/grapher/Models/Charts/AccelCharts.cs @@ -1,23 +1,15 @@ using grapher.Models.Calculations; using grapher.Models.Charts; +using grapher.Models.Serialized; using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Security.Permissions; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; -using System.Windows.Forms.DataVisualization.Charting; namespace grapher { public class AccelCharts { - public const int ChartSeparationVertical = 10; - - /// <summary> Needed to show full contents in form. Unsure why. </summary> - public const int FormHeightPadding = 35; + #region Constructors public AccelCharts( Form form, @@ -25,7 +17,8 @@ namespace grapher ChartXY velocityChart, ChartXY gainChart, ToolStripMenuItem enableVelocityAndGain, - ICollection<CheckBox> checkBoxesXY) + ToolStripMenuItem enableLastMouseMove, + Button writeButton) { Estimated = new EstimatedPoints(); EstimatedX = new EstimatedPoints(); @@ -37,7 +30,8 @@ namespace grapher VelocityChart = velocityChart; GainChart = gainChart; EnableVelocityAndGain = enableVelocityAndGain; - CheckBoxesXY = checkBoxesXY; + EnableLastValue = enableLastMouseMove; + WriteButton = writeButton; SensitivityChart.SetPointBinds(Estimated.Sensitivity, EstimatedX.Sensitivity, EstimatedY.Sensitivity); VelocityChart.SetPointBinds(Estimated.Velocity, EstimatedX.Velocity, EstimatedY.Velocity); @@ -45,21 +39,28 @@ namespace grapher SensitivityChart.SetTop(0); VelocityChart.SetHeight(SensitivityChart.Height); - VelocityChart.SetTop(SensitivityChart.Height + ChartSeparationVertical); + VelocityChart.SetTop(SensitivityChart.Height + Constants.ChartSeparationVertical); GainChart.SetHeight(SensitivityChart.Height); - GainChart.SetTop(VelocityChart.Top + VelocityChart.Height + ChartSeparationVertical); + GainChart.SetTop(VelocityChart.Top + VelocityChart.Height + Constants.ChartSeparationVertical); Rectangle screenRectangle = ContaingForm.RectangleToScreen(ContaingForm.ClientRectangle); FormBorderHeight = screenRectangle.Top - ContaingForm.Top; EnableVelocityAndGain.Click += new System.EventHandler(OnEnableClick); - EnableVelocityAndGain.CheckedChanged += new System.EventHandler(OnEnableCheckStateChange); + EnableVelocityAndGain.CheckedChanged += new System.EventHandler(OnEnableVelocityGainCheckStateChange); + + EnableLastValue.CheckedChanged += new System.EventHandler(OnEnableLastMouseMoveCheckStateChange); HideVelocityAndGain(); + SensitivityChart.Show(); Combined = false; ShowCombined(); } + #endregion Constructors + + #region Properties + public Form ContaingForm { get; } public ChartXY SensitivityChart { get; } @@ -70,6 +71,10 @@ namespace grapher public ToolStripMenuItem EnableVelocityAndGain { get; } + private ToolStripMenuItem EnableLastValue { get; } + + private Button WriteButton { get; } + public AccelData AccelData { get; } private EstimatedPoints Estimated { get; } @@ -78,12 +83,14 @@ namespace grapher private EstimatedPoints EstimatedY { get; } - private ICollection<CheckBox> CheckBoxesXY { get; } - private bool Combined { get; set; } private int FormBorderHeight { get; } + #endregion Properties + + #region Methods + public void MakeDots(int x, int y, double timeInMs) { if (Combined) @@ -96,11 +103,14 @@ namespace grapher } } - public void DrawPoints() + public void DrawLastMovement() { - SensitivityChart.DrawPoints(); - VelocityChart.DrawPoints(); - GainChart.DrawPoints(); + if (EnableLastValue.Checked) + { + SensitivityChart.DrawLastMovementValue(); + VelocityChart.DrawLastMovementValue(); + GainChart.DrawLastMovementValue(); + } } public void Bind() @@ -119,9 +129,9 @@ namespace grapher } } - public void RefreshXY() + public void ShowActive(DriverSettings driverSettings) { - if (CheckBoxesXY.All(box => box.Checked)) + if (driverSettings.combineMagnitudes) { ShowCombined(); } @@ -131,12 +141,30 @@ namespace grapher } } + public void SetWidened() + { + SensitivityChart.SetWidened(); + VelocityChart.SetWidened(); + GainChart.SetWidened(); + UpdateFormWidth(); + AlignWriteButton(); + } + + public void SetNarrowed() + { + SensitivityChart.SetNarrowed(); + VelocityChart.SetNarrowed(); + GainChart.SetNarrowed(); + UpdateFormWidth(); + AlignWriteButton(); + } + private void OnEnableClick(object sender, EventArgs e) { EnableVelocityAndGain.Checked = !EnableVelocityAndGain.Checked; } - private void OnEnableCheckStateChange(object sender, EventArgs e) + private void OnEnableVelocityGainCheckStateChange(object sender, EventArgs e) { if (EnableVelocityAndGain.Checked) { @@ -148,14 +176,24 @@ namespace grapher } } + private void OnEnableLastMouseMoveCheckStateChange(object sender, EventArgs e) + { + if (!EnableLastValue.Checked) + { + SensitivityChart.ClearLastValue(); + VelocityChart.ClearLastValue(); + GainChart.ClearLastValue(); + } + } + private void ShowVelocityAndGain() { VelocityChart.Show(); GainChart.Show(); - ContaingForm.Height = SensitivityChart.Height + - ChartSeparationVertical + + ContaingForm.Height = SensitivityChart.Height + + Constants.ChartSeparationVertical + VelocityChart.Height + - ChartSeparationVertical + + Constants.ChartSeparationVertical + GainChart.Height + FormBorderHeight; } @@ -199,5 +237,12 @@ namespace grapher { ContaingForm.Width = SensitivityChart.Left + SensitivityChart.Width; } + + private void AlignWriteButton() + { + WriteButton.Left = SensitivityChart.Left / 2 - WriteButton.Width / 2; + } + + #endregion Methods } } diff --git a/grapher/Models/Charts/ChartXY.cs b/grapher/Models/Charts/ChartXY.cs index 81874a2..30be229 100644 --- a/grapher/Models/Charts/ChartXY.cs +++ b/grapher/Models/Charts/ChartXY.cs @@ -1,26 +1,11 @@ -using grapher.Models.Charts; -using grapher.Models.Mouse; -using System; +using grapher.Models.Mouse; using System.Collections; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; -using static grapher.AccelCharts; namespace grapher { public class ChartXY { - #region Consts - - public const int ChartSeparationHorizontal = 10; - - #endregion Consts - #region Constructors public ChartXY(Chart chartX, Chart chartY) @@ -31,10 +16,16 @@ namespace grapher ChartY.Top = ChartX.Top; ChartY.Height = ChartX.Height; ChartY.Width = ChartX.Width; - ChartY.Left = ChartX.Left + ChartX.Width + ChartSeparationHorizontal; + ChartY.Left = ChartX.Left + ChartX.Width + Constants.ChartSeparationHorizontal; SetupChart(ChartX); SetupChart(ChartY); + + Combined = false; + SetCombined(); + + Widened = false; + SetWidened(); } #endregion Constructors @@ -82,6 +73,10 @@ namespace grapher public bool Combined { get; private set; } + public bool Widened { get; private set; } + + public bool Visible { get; private set; } + private PointData CombinedPointData { get; set; } private PointData XPointData { get; set; } @@ -134,7 +129,7 @@ namespace grapher YPointData = y; } - public void DrawPoints() + public void DrawLastMovementValue() { if(Combined) { @@ -147,6 +142,12 @@ namespace grapher } } + public void ClearLastValue() + { + ChartX.Series[1].Points.Clear(); + ChartY.Series[1].Points.Clear(); + } + public void Bind(IDictionary data) { ChartX.Series[0].Points.DataBindXY(data.Keys, data.Values); @@ -171,7 +172,7 @@ namespace grapher { if (Combined) { - if (ChartX.Visible) + if (Visible) { ChartY.Show(); } @@ -180,19 +181,56 @@ namespace grapher } } + public void SetWidened() + { + if (!Widened) + { + ChartX.Width = Constants.WideChartWidth; + ChartY.Width = Constants.WideChartWidth; + + ChartX.Left = Constants.WideChartLeft; + ChartY.Left = ChartX.Left + ChartX.Width + Constants.ChartSeparationHorizontal; + + Widened = true; + } + } + + public void SetNarrowed() + { + if (Widened) + { + ChartX.Width = Constants.NarrowChartWidth; + ChartY.Width = Constants.NarrowChartWidth; + + ChartX.Left = Constants.NarrowChartLeft; + ChartY.Left = ChartX.Left + ChartX.Width + Constants.ChartSeparationHorizontal; + + Widened = false; + } + } + public void Hide() { - ChartX.Hide(); - ChartY.Hide(); + if (Visible) + { + ChartX.Hide(); + ChartY.Hide(); + Visible = false; + } } public void Show() { - ChartX.Show(); - - if (!Combined) + if (!Visible) { - ChartY.Show(); + ChartX.Show(); + + if (!Combined) + { + ChartY.Show(); + } + + Visible = true; } } diff --git a/grapher/Models/Charts/EstimatedPoints.cs b/grapher/Models/Charts/EstimatedPoints.cs index fa0718b..f7ba3ce 100644 --- a/grapher/Models/Charts/EstimatedPoints.cs +++ b/grapher/Models/Charts/EstimatedPoints.cs @@ -1,14 +1,11 @@ using grapher.Models.Mouse; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Models.Charts { public class EstimatedPoints { + #region Constructors + public EstimatedPoints() { Sensitivity = new PointData(); @@ -16,10 +13,16 @@ namespace grapher.Models.Charts Gain = new PointData(); } + #endregion Constructors + + #region Properties + public PointData Sensitivity { get; } public PointData Velocity { get; } public PointData Gain { get; } + + #endregion Properties } } diff --git a/grapher/Models/Fields/Field.cs b/grapher/Models/Fields/Field.cs index 1810081..0d1813e 100644 --- a/grapher/Models/Fields/Field.cs +++ b/grapher/Models/Fields/Field.cs @@ -1,22 +1,12 @@ using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace grapher { public class Field { - #region Constants - - public const string DefaultFormatString = "0.#########"; - - #endregion Constants - - #region Enums + #region Enumerations public enum FieldState { @@ -27,8 +17,13 @@ namespace grapher Unavailable, } - #endregion Enums + #endregion Enumerations + + #region Fields + + private double _data; + #endregion Fields #region Constructors @@ -36,11 +31,11 @@ namespace grapher { DefaultText = DecimalString(defaultData); Box = box; - Data = defaultData; + _data = defaultData; DefaultData = defaultData; State = FieldState.Undefined; ContainingForm = containingForm; - FormatString = DefaultFormatString; + FormatString = Constants.DefaultFieldFormatString; box.KeyDown += new System.Windows.Forms.KeyEventHandler(KeyDown); box.Leave += new System.EventHandler(FocusLeave); @@ -55,8 +50,6 @@ namespace grapher private Form ContainingForm { get; } - public double Data { get; private set; } - public string FormatString { get; set; } public string DefaultText { get; } @@ -65,6 +58,68 @@ namespace grapher public FieldState PreviousState { get; private set; } + public double Data { + get + { + if (Box.Visible) + { + return _data; + } + else + { + return DefaultData; + } + } + } + + public int Top + { + get + { + return Box.Top; + } + set + { + Box.Top = value; + } + } + + public int Height + { + get + { + return Box.Height; + } + set + { + Box.Height = value; + } + } + + public int Left + { + get + { + return Box.Left; + } + set + { + Box.Left = value; + } + } + + public int Width + { + get + { + return Box.Width; + } + set + { + Box.Width = value; + } + } + private double DefaultData { get; } #endregion Properties @@ -81,7 +136,7 @@ namespace grapher PreviousState = FieldState.Default; } - Data = DefaultData; + _data = DefaultData; Box.Text = DefaultText; ContainingForm.ActiveControl = null; } @@ -118,7 +173,7 @@ namespace grapher { SetToEntered(); - Data = value; + _data = value; Box.Text = DecimalString(Data); } @@ -197,7 +252,7 @@ namespace grapher { try { - Data = Convert.ToDouble(Box.Text); + _data = Convert.ToDouble(Box.Text); } catch { diff --git a/grapher/Models/Fields/FieldXY.cs b/grapher/Models/Fields/FieldXY.cs index 609af9d..15e6800 100644 --- a/grapher/Models/Fields/FieldXY.cs +++ b/grapher/Models/Fields/FieldXY.cs @@ -1,38 +1,36 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace grapher { public class FieldXY { - public const int DefaultSeparation = 4; + #region Constructors - public const string ShortenedFormatString = "0.###"; - - public FieldXY(TextBox xBox, TextBox yBox, CheckBox lockCheckBox, Form containingForm, double defaultData, AccelCharts accelCharts) + public FieldXY(TextBox xBox, TextBox yBox, CheckBox lockCheckBox, Form containingForm, double defaultData) { XField = new Field(xBox, containingForm, defaultData); YField = new Field(yBox, containingForm, defaultData); - YField.FormatString = ShortenedFormatString; + YField.FormatString = Constants.ShortenedFormatString; LockCheckBox = lockCheckBox; LockCheckBox.CheckedChanged += new System.EventHandler(CheckChanged); - AccelCharts = accelCharts; - XField.Box.Width = (YField.Box.Left + YField.Box.Width - XField.Box.Left - DefaultSeparation) / 2; + XField.Box.Width = (YField.Box.Left + YField.Box.Width - XField.Box.Left - Constants.DefaultFieldSeparation) / 2; YField.Box.Width = XField.Box.Width; DefaultWidthX = XField.Box.Width; DefaultWidthY = YField.Box.Width; - YField.Box.Left = XField.Box.Left + XField.Box.Width + DefaultSeparation; + YField.Box.Left = XField.Box.Left + XField.Box.Width + Constants.DefaultFieldSeparation; CombinedWidth = DefaultWidthX + DefaultWidthY + YField.Box.Left - (XField.Box.Left + DefaultWidthX); SetCombined(); } + + #endregion Constructors + + #region Properties + public double X { get => XField.Data; @@ -59,7 +57,58 @@ namespace grapher public Field YField { get; } - private AccelCharts AccelCharts { get; } + public int CombinedWidth { get; } + + public int Left { + get + { + return XField.Left; + } + set + { + } + } + + public int Width + { + get + { + return CombinedWidth; + } + set + { + } + } + + public int Top + { + get + { + return XField.Top; + } + set + { + } + } + + public int Height + { + get + { + return XField.Height; + } + set + { + } + } + + public bool Visible + { + get + { + return XField.Box.Visible; + } + } private bool Combined { get; set; } @@ -67,7 +116,10 @@ namespace grapher private int DefaultWidthY { get; } - private int CombinedWidth { get; } + + #endregion Properties + + #region Methods private void CheckChanged(object sender, EventArgs e) { @@ -79,8 +131,6 @@ namespace grapher { SetSeparate(); } - - AccelCharts.RefreshXY(); } public void SetCombined() @@ -89,7 +139,7 @@ namespace grapher YField.SetToUnavailable(); YField.Box.Hide(); XField.Box.Width = CombinedWidth; - XField.FormatString = Field.DefaultFormatString; + XField.FormatString = Constants.DefaultFieldFormatString; } public void SetSeparate() @@ -99,7 +149,7 @@ namespace grapher XField.Box.Width = DefaultWidthX; YField.Box.Width = DefaultWidthY; - XField.FormatString = ShortenedFormatString; + XField.FormatString = Constants.ShortenedFormatString; if (XField.State == Field.FieldState.Default) { @@ -131,5 +181,7 @@ namespace grapher XField.Box.Hide(); YField.Box.Hide(); } + + #endregion Methods } } diff --git a/grapher/Models/Mouse/MouseWatcher.cs b/grapher/Models/Mouse/MouseWatcher.cs index fea4e2d..405110e 100644 --- a/grapher/Models/Mouse/MouseWatcher.cs +++ b/grapher/Models/Mouse/MouseWatcher.cs @@ -1,16 +1,12 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace grapher.Models.Mouse { public class MouseWatcher { + #region External /// <summary> /// Enumeration containing HID usage page flags. /// </summary> @@ -677,6 +673,10 @@ namespace grapher.Models.Mouse [DllImport("user32.dll")] public static extern int GetRawInputData(IntPtr hRawInput, RawInputCommand uiCommand, out RawInput pData, ref int pcbSize, int cbSizeHeader); + #endregion External + + #region Constructors + public MouseWatcher(Form containingForm, Label display, AccelCharts accelCharts) { ContainingForm = containingForm; @@ -695,6 +695,10 @@ namespace grapher.Models.Mouse PollTime = 1; } + #endregion Constructors + + #region Properties + private Form ContainingForm { get; } private Label Display { get; } @@ -703,6 +707,10 @@ namespace grapher.Models.Mouse private double PollTime { get; } + #endregion Properties + + #region Methods + public void OnMouseMove(int x, int y, double timeInMs) { Display.Text = $"Last (x, y): ({x}, {y})"; @@ -723,5 +731,7 @@ namespace grapher.Models.Mouse } } + + #endregion Methods } } diff --git a/grapher/Models/Mouse/PointData.cs b/grapher/Models/Mouse/PointData.cs index 12a6e73..374c52e 100644 --- a/grapher/Models/Mouse/PointData.cs +++ b/grapher/Models/Mouse/PointData.cs @@ -1,13 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace grapher.Models.Mouse { public class PointData { + #region Constructors + public PointData() { Lock = new Object(); @@ -15,6 +13,10 @@ namespace grapher.Models.Mouse Y = new double[] { 0 }; } + #endregion Constructors + + #region Properties + public Object Lock { get; } private double[] X { get; set; } @@ -29,6 +31,10 @@ namespace grapher.Models.Mouse } } + #endregion Properties + + #region Methods + public void Get(out double[] x, out double[] y) { lock(Lock) @@ -37,5 +43,7 @@ namespace grapher.Models.Mouse y = Y; } } + + #endregion Methods } } diff --git a/grapher/Models/Options/AccelOptionSet.cs b/grapher/Models/Options/AccelOptionSet.cs new file mode 100644 index 0000000..bc0d368 --- /dev/null +++ b/grapher/Models/Options/AccelOptionSet.cs @@ -0,0 +1,117 @@ +using grapher.Models.Serialized; +using System.Drawing; +using System.Windows.Forms; + +namespace grapher.Models.Options +{ + public class AccelOptionSet + { + public AccelOptionSet( + Label title, + Label activeValuesTitle, + int topAnchor, + AccelTypeOptions accelTypeOptions) + { + OptionsTitle = title; + ActiveValuesTitle = activeValuesTitle; + TopAnchor = topAnchor; + Options = accelTypeOptions; + + ActiveValuesTitle.AutoSize = false; + ActiveValuesTitle.TextAlign = ContentAlignment.MiddleCenter; + + Options.ShowFull(); + + OptionsTitle.Top = TopAnchor; + IsTitleMode = true; + SetRegularMode(); + } + + public int TopAnchor { get; } + + public Label OptionsTitle { get; } + + public Label ActiveValuesTitle { get; } + + public AccelTypeOptions Options { get; } + + + public bool IsTitleMode { get; private set; } + + public void SetRegularMode() + { + if (IsTitleMode) + { + IsTitleMode = false; + + HideTitle(); + Options.ShowFull(); + } + } + + public void SetTitleMode(string title) + { + OptionsTitle.Text = title; + + if (!IsTitleMode) + { + IsTitleMode = true; + + Options.ShowShortened(); + DisplayTitle(); + } + } + + public void Hide() + { + OptionsTitle.Hide(); + ActiveValuesTitle.Hide(); + Options.Hide(); + } + + public void Show() + { + if (IsTitleMode) + { + OptionsTitle.Show(); + } + + ActiveValuesTitle.Show(); + Options.Show(); + } + + public void DisplayTitle() + { + OptionsTitle.Show(); + + Options.Top = OptionsTitle.Top + OptionsTitle.Height + Constants.OptionVerticalSeperation; + } + + public void HideTitle() + { + OptionsTitle.Hide(); + + Options.Top = TopAnchor; + } + + public void SetArgs(ref AccelArgs args) + { + Options.SetArgs(ref args); + } + + public AccelArgs GenerateArgs() + { + return Options.GenerateArgs(); + } + + public void SetActiveValues(int mode, AccelArgs args) + { + Options.SetActiveValues(mode, args); + } + + public void AlignActiveValues() + { + Options.AlignActiveValues(); + } + } +} diff --git a/grapher/Models/Options/AccelOptions.cs b/grapher/Models/Options/AccelOptions.cs deleted file mode 100644 index 6b98274..0000000 --- a/grapher/Models/Options/AccelOptions.cs +++ /dev/null @@ -1,91 +0,0 @@ -using grapher.Layouts; -using grapher.Models.Options; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace grapher -{ - public class AccelOptions - { - public const int PossibleOptionsCount = 4; - public const int PossibleOptionsXYCount = 2; - - public static readonly Dictionary<string, LayoutBase> AccelerationTypes = new List<LayoutBase> - { - new LinearLayout(), - new ClassicLayout(), - new NaturalLayout(), - new LogLayout(), - new SigmoidLayout(), - new PowerLayout(), - new NaturalGainLayout(), - new SigmoidGainLayout(), - new OffLayout() - }.ToDictionary(k => k.Name); - - public AccelOptions( - ComboBox accelDropdown, - Option[] options, - OptionXY[] optionsXY, - Button writeButton, - ActiveValueLabel activeValueLabel) - { - AccelDropdown = accelDropdown; - AccelDropdown.Items.Clear(); - AccelDropdown.Items.AddRange(AccelerationTypes.Keys.ToArray()); - AccelDropdown.SelectedIndexChanged += new System.EventHandler(OnIndexChanged); - - if (options.Length > PossibleOptionsCount) - { - throw new Exception("Layout given too many options."); - } - - if (optionsXY.Length > PossibleOptionsXYCount) - { - throw new Exception("Layout given too many options."); - } - - Options = options; - OptionsXY = optionsXY; - WriteButton = writeButton; - ActiveValueLabel = activeValueLabel; - - Layout("Off"); - } - - public Button WriteButton { get; } - - public ComboBox AccelDropdown { get; } - - public int AccelerationIndex { get; private set; } - - public ActiveValueLabel ActiveValueLabel { get; } - - public Option[] Options { get; } - - public OptionXY[] OptionsXY { get; } - - public void SetActiveValue(int index) - { - var name = AccelerationTypes.Where(t => t.Value.Index == index).FirstOrDefault().Value.Name; - ActiveValueLabel.SetValue(name); - } - - private void OnIndexChanged(object sender, EventArgs e) - { - var accelerationTypeString = AccelDropdown.SelectedItem.ToString(); - Layout(accelerationTypeString); - } - - private void Layout(string type) - { - var accelerationType = AccelerationTypes[type]; - AccelerationIndex = accelerationType.Index; - accelerationType.Layout(Options, OptionsXY, WriteButton); - } - } -} diff --git a/grapher/Models/Options/AccelTypeOptions.cs b/grapher/Models/Options/AccelTypeOptions.cs new file mode 100644 index 0000000..14c2019 --- /dev/null +++ b/grapher/Models/Options/AccelTypeOptions.cs @@ -0,0 +1,281 @@ +using grapher.Layouts; +using grapher.Models.Options; +using grapher.Models.Serialized; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace grapher +{ + public class AccelTypeOptions : OptionBase + { + #region Fields + + public static readonly Dictionary<string, LayoutBase> AccelerationTypes = new List<LayoutBase> + { + new LinearLayout(), + new ClassicLayout(), + new NaturalLayout(), + new PowerLayout(), + new NaturalGainLayout(), + new SigmoidGainLayout(), + new OffLayout() + }.ToDictionary(k => k.Name); + + #endregion Fields + + #region Constructors + + public AccelTypeOptions( + ComboBox accelDropdown, + Option acceleration, + CapOptions cap, + Option weight, + OffsetOptions offset, + Option limitOrExponent, + Option midpoint, + Button writeButton, + ActiveValueLabel accelTypeActiveValue) + { + AccelDropdown = accelDropdown; + AccelDropdown.Items.Clear(); + AccelDropdown.Items.AddRange(AccelerationTypes.Keys.ToArray()); + AccelDropdown.SelectedIndexChanged += new System.EventHandler(OnIndexChanged); + + Acceleration = acceleration; + Cap = cap; + Weight = weight; + Offset = offset; + LimitOrExponent = limitOrExponent; + Midpoint = midpoint; + WriteButton = writeButton; + AccelTypeActiveValue = accelTypeActiveValue; + + AccelTypeActiveValue.Left = AccelDropdown.Left + AccelDropdown.Width; + AccelTypeActiveValue.Height = AccelDropdown.Height; + + Layout("Off"); + ShowingDefault = true; + } + + #endregion Constructors + + #region Properties + + public Button WriteButton { get; } + + public ComboBox AccelDropdown { get; } + + public int AccelerationIndex + { + get + { + return AccelerationType.Index; + } + } + + public LayoutBase AccelerationType { get; private set; } + + public ActiveValueLabel AccelTypeActiveValue { get; } + + public Option Acceleration { get; } + + public CapOptions Cap { get; } + + public Option Weight { get; } + + public OffsetOptions Offset { get; } + + public Option LimitOrExponent { get; } + + public Option Midpoint { get; } + + public override int Top + { + get + { + return AccelDropdown.Top; + } + set + { + AccelDropdown.Top = value; + AccelTypeActiveValue.Top = value; + Layout(value + AccelDropdown.Height + Constants.OptionVerticalSeperation); + } + } + + public override int Height + { + get + { + return AccelDropdown.Height; + } + } + + public override int Left + { + get + { + return AccelDropdown.Left; + } + set + { + AccelDropdown.Left = value; + } + } + + public override int Width + { + get + { + return AccelDropdown.Width; + } + set + { + AccelDropdown.Width = value; + } + } + + public override bool Visible + { + get + { + return AccelDropdown.Visible; + } + } + + private bool ShowingDefault { get; set; } + + #endregion Properties + + #region Methods + + public override void Hide() + { + AccelDropdown.Hide(); + AccelTypeActiveValue.Hide(); + + Acceleration.Hide(); + Cap.Hide(); + Weight.Hide(); + Offset.Hide(); + LimitOrExponent.Hide(); + Midpoint.Hide(); + } + + public void Show() + { + AccelDropdown.Show(); + AccelTypeActiveValue.Show(); + Layout(); + } + + public override void Show(string name) + { + Show(); + } + + public void SetActiveValues(int index, AccelArgs args) + { + var name = AccelerationTypes.Where(t => t.Value.Index == index).FirstOrDefault().Value.Name; + AccelTypeActiveValue.SetValue(name); + + Weight.SetActiveValue(args.weight); + Cap.SetActiveValues(args.gainCap, args.scaleCap, args.gainCap > 0); + Offset.SetActiveValue(args.offset, args.legacy_offset); + Acceleration.SetActiveValue(args.accel); + LimitOrExponent.SetActiveValue(args.exponent); + Midpoint.SetActiveValue(args.midpoint); + } + + public void ShowFull() + { + if (ShowingDefault) + { + AccelDropdown.Text = Constants.AccelDropDownDefaultFullText; + } + + Left = Acceleration.Left + Constants.DropDownLeftSeparation; + Width = Acceleration.Width - Constants.DropDownLeftSeparation; + } + + public void ShowShortened() + { + if (ShowingDefault) + { + AccelDropdown.Text = Constants.AccelDropDownDefaultShortText; + } + + Left = Acceleration.Field.Left; + Width = Acceleration.Field.Width; + } + + public void SetArgs(ref AccelArgs args) + { + args.accel = Acceleration.Field.Data; + args.rate = Acceleration.Field.Data; + args.powerScale = Acceleration.Field.Data; + args.gainCap = Cap.VelocityGainCap; + args.scaleCap = Cap.SensitivityCap; + args.limit = LimitOrExponent.Field.Data; + args.exponent = LimitOrExponent.Field.Data; + args.powerExponent = LimitOrExponent.Field.Data; + args.offset = Offset.Offset; + args.legacy_offset = Offset.LegacyOffset; + args.midpoint = Midpoint.Field.Data; + args.weight = Weight.Field.Data; + } + + public AccelArgs GenerateArgs() + { + AccelArgs args = new AccelArgs(); + SetArgs(ref args); + return args; + } + + public override void AlignActiveValues() + { + AccelTypeActiveValue.Align(); + Acceleration.AlignActiveValues(); + Cap.AlignActiveValues(); + Offset.AlignActiveValues(); + Weight.AlignActiveValues(); + LimitOrExponent.AlignActiveValues(); + Midpoint.AlignActiveValues(); + } + + private void OnIndexChanged(object sender, EventArgs e) + { + var accelerationTypeString = AccelDropdown.SelectedItem.ToString(); + Layout(accelerationTypeString, Beneath); + ShowingDefault = false; + } + + private void Layout(string type, int top = -1) + { + AccelerationType = AccelerationTypes[type]; + Layout(top); + } + + private void Layout(int top = -1) + { + if (top < 0) + { + top = Acceleration.Top; + } + + AccelerationType.Layout( + Acceleration, + Cap, + Weight, + Offset, + LimitOrExponent, + Midpoint, + WriteButton, + top); + } + + #endregion Methods + } +} diff --git a/grapher/Models/Options/ActiveValueLabel.cs b/grapher/Models/Options/ActiveValueLabel.cs index 138775a..18a4400 100644 --- a/grapher/Models/Options/ActiveValueLabel.cs +++ b/grapher/Models/Options/ActiveValueLabel.cs @@ -1,34 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Drawing; using System.Windows.Forms; namespace grapher.Models.Options { public class ActiveValueLabel { - public const string DefaultFormatString = "0.######"; - public static readonly Color ActiveValueFontColor = Color.FromArgb(255, 65, 65, 65); + #region Constants + + + #endregion Constants + + #region Fields private string _prefix; private string _value; + #endregion Fields + + #region Constructors + public ActiveValueLabel(Label valueLabel, Label centeringLabel) { ValueLabel = valueLabel; - ValueLabel.ForeColor = ActiveValueFontColor; - Left = centeringLabel.Left; - Width = centeringLabel.Width; ValueLabel.AutoSize = false; ValueLabel.TextAlign = ContentAlignment.MiddleCenter; + ValueLabel.ForeColor = Constants.ActiveValueFontColor; - FormatString = DefaultFormatString; + CenteringLabel = centeringLabel; + Align(); + + FormatString = Constants.DefaultActiveValueFormatString; Prefix = string.Empty; } + #endregion Constructors + + #region Properties + public Label ValueLabel { get; } public string FormatString { get; set; } @@ -79,6 +87,37 @@ namespace grapher.Models.Options } } + public int Top + { + get + { + return ValueLabel.Top; + } + set + { + ValueLabel.Top = value; + } + } + + public int Height + { + get + { + return ValueLabel.Height; + } + + set + { + ValueLabel.Height = value; + } + } + + public Label CenteringLabel { get; } + + #endregion Properties + + #region Methods + public void Hide() { ValueLabel.Hide(); @@ -103,5 +142,13 @@ namespace grapher.Models.Options { ValueLabel.Text = string.IsNullOrWhiteSpace(Prefix) ? Value: $"{Prefix}: {Value}"; } + + public void Align() + { + Left = CenteringLabel.Left; + Width = CenteringLabel.Width; + } + + #endregion Methods } } diff --git a/grapher/Models/Options/ActiveValueLabelXY.cs b/grapher/Models/Options/ActiveValueLabelXY.cs index 12506e9..9498c66 100644 --- a/grapher/Models/Options/ActiveValueLabelXY.cs +++ b/grapher/Models/Options/ActiveValueLabelXY.cs @@ -1,15 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace grapher.Models.Options +namespace grapher.Models.Options { public class ActiveValueLabelXY { - public const int ActiveLabelXYSeparation = 2; - public const string ShortenedFormatString = "0.###"; + #region Constants + + + #endregion Constants + + #region Constructors public ActiveValueLabelXY( ActiveValueLabel x, @@ -18,26 +16,57 @@ namespace grapher.Models.Options X = x; Y = y; - FullWidth = x.Width; - ShortenedWidth = (FullWidth - ActiveLabelXYSeparation) / 2; - - Y.Left = X.Left + ShortenedWidth + ActiveLabelXYSeparation; + Align(x.Width); Y.Width = ShortenedWidth; - Y.FormatString = ShortenedFormatString; + Y.FormatString = Constants.ShortenedFormatString; Combined = false; SetCombined(); } + #endregion Constructors + + #region Properties + public ActiveValueLabel X { get; } public ActiveValueLabel Y { get; } public bool Combined { get; private set; } - private int FullWidth { get; } + public int Left + { + get + { + return X.Left; + } + set + { + X.Left = value; + SetYLeft(); + } + } + + public int Height + { + get + { + return X.Height; + } + set + { + X.Height = value; + Y.Height = value; + } + } + + private int FullWidth { get; set; } + + private int ShortenedWidth { get; set; } - private int ShortenedWidth { get; } + #endregion Properties + + #region Methods public void SetValues(double x, double y) { @@ -58,7 +87,7 @@ namespace grapher.Models.Options { if (!Combined) { - X.FormatString = ActiveValueLabel.DefaultFormatString; + X.FormatString = Constants.DefaultActiveValueFormatString; X.Width = FullWidth; X.Prefix = string.Empty; Y.Hide(); @@ -71,7 +100,7 @@ namespace grapher.Models.Options { if (Combined) { - X.FormatString = ShortenedFormatString; + X.FormatString = Constants.ShortenedFormatString; X.Width = ShortenedWidth; X.Prefix = "X"; Y.Prefix = "Y"; @@ -80,5 +109,35 @@ namespace grapher.Models.Options Combined = false; } + + public void AlignActiveValues() + { + Align(X.CenteringLabel.Width); + + if (Combined) + { + X.Width = FullWidth; + } + else + { + X.Width = ShortenedWidth; + } + } + + private void Align (int width) + { + FullWidth = width; + ShortenedWidth = (FullWidth - Constants.ActiveLabelXYSeparation) / 2; + + SetYLeft(); + Y.Width = ShortenedWidth; + } + + private void SetYLeft() + { + Y.Left = X.Left + ShortenedWidth + Constants.ActiveLabelXYSeparation; + } + + #endregion Methods } } diff --git a/grapher/Models/Options/ApplyOptions.cs b/grapher/Models/Options/ApplyOptions.cs new file mode 100644 index 0000000..241fe50 --- /dev/null +++ b/grapher/Models/Options/ApplyOptions.cs @@ -0,0 +1,255 @@ +using grapher.Models.Serialized; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace grapher.Models.Options +{ + public class ApplyOptions + { + #region Constructors + + public ApplyOptions( + ToolStripMenuItem wholeVectorMenuItem, + ToolStripMenuItem byComponentMenuItem, + CheckBox byComponentVectorXYLock, + AccelOptionSet optionSetX, + AccelOptionSet optionSetY, + OptionXY sensitivity, + Option rotation, + Label lockXYLabel, + AccelCharts accelCharts) + { + WholeVectorMenuItem = wholeVectorMenuItem; + ByComponentVectorMenuItem = byComponentMenuItem; + + WholeVectorMenuItem.Click += new System.EventHandler(OnWholeClicked); + ByComponentVectorMenuItem.Click += new System.EventHandler(OnByComponentClicked); + + WholeVectorMenuItem.CheckedChanged += new System.EventHandler(OnWholeCheckedChange); + ByComponentVectorMenuItem.CheckedChanged += new System.EventHandler(OnByComponentCheckedChange); + + ByComponentVectorXYLock = byComponentVectorXYLock; + OptionSetX = optionSetX; + OptionSetY = optionSetY; + Sensitivity = sensitivity; + Rotation = rotation; + LockXYLabel = lockXYLabel; + AccelCharts = accelCharts; + + LockXYLabel.AutoSize = false; + LockXYLabel.TextAlign = ContentAlignment.MiddleCenter; + + ByComponentVectorXYLock.CheckedChanged += new System.EventHandler(OnByComponentXYLockChecked); + ByComponentVectorXYLock.Checked = true; + + Rotation.SnapTo(Sensitivity); + + EnableWholeApplication(); + } + + #endregion Constructors + + #region Properties + + public ToolStripMenuItem WholeVectorMenuItem { get; } + + public ToolStripMenuItem ByComponentVectorMenuItem { get; } + + public CheckBox ByComponentVectorXYLock { get; } + + public AccelOptionSet OptionSetX { get; } + + public AccelOptionSet OptionSetY { get; } + + public OptionXY Sensitivity { get; } + + public Option Rotation { get; } + + public AccelCharts AccelCharts { get; } + + public Label ActiveValueTitleY { get; } + + public Label LockXYLabel { get; } + + public bool IsWhole { get; private set; } + + #endregion Properties + + #region Methods + + public Vec2<AccelMode> GetModes() + { + var xMode = (AccelMode)OptionSetX.Options.AccelerationIndex; + + return new Vec2<AccelMode> + { + x = xMode, + y = ByComponentVectorXYLock.Checked ? xMode : (AccelMode)OptionSetY.Options.AccelerationIndex + }; + } + + public Vec2<AccelArgs> GetArgs() + { + var xArgs = OptionSetX.GenerateArgs(); + + return new Vec2<AccelArgs> + { + x = xArgs, + y = ByComponentVectorXYLock.Checked ? xArgs : OptionSetY.GenerateArgs() + }; + + } + + public void SetActiveValues( + double xSens, + double ySens, + double rotation, + int xMode, + int yMode, + AccelArgs xArgs, + AccelArgs yArgs, + bool isWhole) + { + Sensitivity.SetActiveValues(xSens, ySens); + Rotation.SetActiveValue(rotation); + OptionSetX.SetActiveValues(xMode, xArgs); + OptionSetY.SetActiveValues(yMode, yArgs); + WholeVectorMenuItem.Checked = isWhole; + ByComponentVectorMenuItem.Checked = !isWhole; + } + + public void SetActiveValues(DriverSettings settings) + { + SetActiveValues( + settings.sensitivity.x, + settings.sensitivity.x, + settings.rotation, + (int)settings.modes.x, + (int)settings.modes.y, + settings.args.x, + settings.args.y, + settings.combineMagnitudes); + } + + public void OnWholeClicked(object sender, EventArgs e) + { + if (!WholeVectorMenuItem.Checked) + { + WholeVectorMenuItem.Checked = true; + ByComponentVectorMenuItem.Checked = false; + } + } + + public void OnByComponentClicked(object sender, EventArgs e) + { + if (!ByComponentVectorMenuItem.Checked) + { + WholeVectorMenuItem.Checked = false; + ByComponentVectorMenuItem.Checked = true; + } + } + + public void OnWholeCheckedChange(object sender, EventArgs e) + { + if (WholeVectorMenuItem.Checked) + { + EnableWholeApplication(); + } + } + + public void OnByComponentCheckedChange(object sender, EventArgs e) + { + if (ByComponentVectorMenuItem.Checked) + { + EnableByComponentApplication(); + } + } + + public void ShowWholeSet() + { + OptionSetX.SetRegularMode(); + OptionSetY.Hide(); + AccelCharts.SetWidened(); + SetActiveTitlesWhole(); + } + + public void ShowByComponentAsOneSet() + { + OptionSetX.SetTitleMode("X = Y"); + OptionSetY.Hide(); + AccelCharts.SetWidened(); + SetActiveTitlesByComponents(); + } + + public void ShowByComponentAsTwoSets() + { + OptionSetX.SetTitleMode("X"); + OptionSetY.SetTitleMode("Y"); + OptionSetY.Show(); + AccelCharts.SetNarrowed(); + } + + public void ShowByComponentSet() + { + if (ByComponentVectorXYLock.Checked) + { + ShowByComponentAsOneSet(); + } + else + { + ShowByComponentAsTwoSets(); + } + } + + private void OnByComponentXYLockChecked(object sender, EventArgs e) + { + if (!IsWhole) + { + ShowByComponentSet(); + } + } + + public void EnableWholeApplication() + { + IsWhole = true; + ByComponentVectorXYLock.Hide(); + ShowWholeSet(); + } + + public void EnableByComponentApplication() + { + IsWhole = false; + ByComponentVectorXYLock.Show(); + ShowByComponentSet(); + } + + private void SetActiveTitlesWhole() + { + OptionSetX.ActiveValuesTitle.Left = OptionSetX.Options.Left + OptionSetX.Options.Width; + LockXYLabel.Width = (AccelCharts.SensitivityChart.Left - OptionSetX.ActiveValuesTitle.Left) / 2; + OptionSetX.ActiveValuesTitle.Width = LockXYLabel.Width; + LockXYLabel.Left = OptionSetX.ActiveValuesTitle.Left + OptionSetX.ActiveValuesTitle.Width; + Sensitivity.Fields.LockCheckBox.Left = LockXYLabel.Left + LockXYLabel.Width / 2 - Sensitivity.Fields.LockCheckBox.Width / 2; + ByComponentVectorXYLock.Left = Sensitivity.Fields.LockCheckBox.Left; + AlignActiveValues(); + } + + private void SetActiveTitlesByComponents() + { + OptionSetY.ActiveValuesTitle.Left = OptionSetY.Options.Left + OptionSetY.Options.Width; + OptionSetY.ActiveValuesTitle.Width = Constants.NarrowChartLeft - OptionSetY.ActiveValuesTitle.Left; + AlignActiveValues(); + } + + private void AlignActiveValues() + { + OptionSetX.AlignActiveValues(); + OptionSetY.AlignActiveValues(); + Sensitivity.AlignActiveValues(); + Rotation.AlignActiveValues(); + } + + #endregion Methods + } +} diff --git a/grapher/Models/Options/CapOptions.cs b/grapher/Models/Options/CapOptions.cs index 87bac88..5e47d7b 100644 --- a/grapher/Models/Options/CapOptions.cs +++ b/grapher/Models/Options/CapOptions.cs @@ -1,29 +1,27 @@ using grapher.Models.Options; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace grapher { - public class CapOptions + public class CapOptions : OptionBase { + #region Constants - public const string GainCapFormatString = "0.##"; + + #endregion Constants + + #region Constructors public CapOptions( ToolStripMenuItem velocityGainCapCheck, ToolStripMenuItem legacyCapCheck, - OptionXY capOption, - OptionXY weightOption) + Option capOption) { VelocityGainCapCheck = velocityGainCapCheck; LegacyCapCheck = legacyCapCheck; CapOption = capOption; - WeightOption = weightOption; LegacyCapCheck.Click += new System.EventHandler(OnSensitivityCapCheckClick); VelocityGainCapCheck.Click += new System.EventHandler(OnVelocityGainCapCheckClick); @@ -34,20 +32,24 @@ namespace grapher EnableVelocityGainCap(); } + #endregion Constructors + + #region Properties + public ToolStripMenuItem LegacyCapCheck { get; } public ToolStripMenuItem VelocityGainCapCheck { get; } - public OptionXY CapOption { get; } + public Option CapOption { get; } - public OptionXY WeightOption { get; } + public bool IsSensitivityGain { get; private set; } - public double SensitivityCapX { + public double SensitivityCap { get { if (IsSensitivityGain) { - return CapOption.Fields.X; + return CapOption.Field.Data; } else { @@ -56,52 +58,122 @@ namespace grapher } } - public double SensitivityCapY { + public double VelocityGainCap { get { if (IsSensitivityGain) { - return CapOption.Fields.Y; + return 0; } else { - return 0; + return CapOption.Field.Data; } } } - public double VelocityGainCap { + public override int Top + { + get + { + return CapOption.Top; + } + set + { + CapOption.Top = value; + } + } + + public override int Height + { get { - if (IsSensitivityGain) - { - return 0; - } - else - { - return CapOption.Fields.X; - } + return CapOption.Height; } } - public bool IsSensitivityGain { get; private set; } + public override int Left + { + get + { + return CapOption.Left; + } + set + { + CapOption.Left = value; + } + } + + public override int Width + { + get + { + return CapOption.Width; + } + set + { + CapOption.Width = value; + } + } - public void SetActiveValues(double gainCap, double sensCapX, double sensCapY, bool capGainEnabled) + public override bool Visible + { + get + { + return CapOption.Visible; + } + } + + #endregion Properties + + #region Methods + + public override void Hide() + { + CapOption.Hide(); + } + + public void Show() + { + CapOption.Show(); + } + + public override void Show(string name) + { + CapOption.Show(name); + } + + public void SnapTo(Option option) + { + Top = option.Top + option.Height + Constants.OptionVerticalSeperation; + } + + + public void SetActiveValues(double gainCap, double sensCap, bool capGainEnabled) { if (capGainEnabled) { - CapOption.ActiveValueLabels.X.FormatString = GainCapFormatString; - CapOption.ActiveValueLabels.X.Prefix = "Gain"; - CapOption.SetActiveValues(gainCap, gainCap); + CapOption.ActiveValueLabel.FormatString = Constants.GainCapFormatString; + CapOption.ActiveValueLabel.Prefix = "Gain"; + CapOption.SetActiveValue(gainCap); + LegacyCapCheck.Checked = true; + VelocityGainCapCheck.Checked = false; } else { - CapOption.ActiveValueLabels.X.FormatString = ActiveValueLabel.DefaultFormatString; - CapOption.ActiveValueLabels.X.Prefix = string.Empty; - CapOption.SetActiveValues(sensCapX, sensCapY); + CapOption.ActiveValueLabel.FormatString = Constants.DefaultActiveValueFormatString; + CapOption.ActiveValueLabel.Prefix = string.Empty; + CapOption.SetActiveValue(sensCap); + LegacyCapCheck.Checked = false; + VelocityGainCapCheck.Checked = true; } } + public override void AlignActiveValues() + { + CapOption.AlignActiveValues(); + } + void OnSensitivityCapCheckClick(object sender, EventArgs e) { if (!LegacyCapCheck.Checked) @@ -139,19 +211,15 @@ namespace grapher void EnableSensitivityCap() { IsSensitivityGain = true; - CapOption.Fields.LockCheckBox.Enabled = true; - WeightOption.Fields.LockCheckBox.Enabled = true; CapOption.SetName("Sensitivity Cap"); } void EnableVelocityGainCap() { IsSensitivityGain = false; - CapOption.Fields.LockCheckBox.Checked = true; - CapOption.Fields.LockCheckBox.Enabled = false; - WeightOption.Fields.LockCheckBox.Checked = true; - WeightOption.Fields.LockCheckBox.Enabled = false; CapOption.SetName("Velocity Gain Cap"); } + + #endregion Methods } } diff --git a/grapher/Models/Options/IOption.cs b/grapher/Models/Options/IOption.cs new file mode 100644 index 0000000..fff1623 --- /dev/null +++ b/grapher/Models/Options/IOption.cs @@ -0,0 +1,23 @@ +namespace grapher.Models.Options +{ + public interface IOption + { + int Top { get; set; } + + int Height { get; } + + int Left { get; } + + int Width { get; } + + int Beneath { get; } + + bool Visible { get; } + + void Show(string name); + + void Hide(); + + void SnapTo(IOption option); + } +} diff --git a/grapher/Models/Options/OffsetOptions.cs b/grapher/Models/Options/OffsetOptions.cs index 0b01ab9..b351ab5 100644 --- a/grapher/Models/Options/OffsetOptions.cs +++ b/grapher/Models/Options/OffsetOptions.cs @@ -1,13 +1,9 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace grapher.Models.Options { - public class OffsetOptions + public class OffsetOptions : OptionBase { public OffsetOptions( ToolStripMenuItem velocityGainOffsetCheck, @@ -65,6 +61,73 @@ namespace grapher.Models.Options } } + public override int Top + { + get + { + return OffsetOption.Top; + } + set + { + OffsetOption.Top = value; + } + } + + public override int Height + { + get + { + return OffsetOption.Height; + } + } + + public override int Left + { + get + { + return OffsetOption.Left; + } + set + { + OffsetOption.Left = value; + } + } + + public override int Width + { + get + { + return OffsetOption.Width; + } + set + { + OffsetOption.Width = value; + } + } + + public override bool Visible + { + get + { + return OffsetOption.Visible; + } + } + + public override void Hide() + { + OffsetOption.Hide(); + } + + public void Show() + { + OffsetOption.Show(); + } + + public override void Show(string name) + { + OffsetOption.Show(name); + } + public void SetActiveValue(double offset, double legacyOffset) { if (offset > 0) @@ -77,6 +140,11 @@ namespace grapher.Models.Options } } + public override void AlignActiveValues() + { + OffsetOption.AlignActiveValues(); + } + public void OnVelocityGainOffsetClick(object sender, EventArgs e) { if (!VelocityGainOffsetCheck.Checked) diff --git a/grapher/Models/Options/Option.cs b/grapher/Models/Options/Option.cs index b0ef374..5dc022b 100644 --- a/grapher/Models/Options/Option.cs +++ b/grapher/Models/Options/Option.cs @@ -1,23 +1,30 @@ using grapher.Models.Options; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace grapher { - public class Option + public class Option : OptionBase { + #region Constructors + public Option( Field field, Label label, - ActiveValueLabel activeValueLabel) + ActiveValueLabel activeValueLabel, + int left) { Field = field; Label = label; ActiveValueLabel = activeValueLabel; + Left = left; + + label.AutoSize = false; + label.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + label.Width = Field.Left - left - Constants.OptionLabelBoxSeperation; + label.Height = Field.Height; + + ActiveValueLabel.Left = Field.Left + Field.Width; + ActiveValueLabel.Height = Field.Height; } public Option( @@ -25,11 +32,13 @@ namespace grapher Form containingForm, double defaultData, Label label, + int left, ActiveValueLabel activeValueLabel) : this( new Field(box, containingForm, defaultData), label, - activeValueLabel) + activeValueLabel, + left) { } @@ -38,6 +47,7 @@ namespace grapher Form containingForm, double defaultData, Label label, + int left, ActiveValueLabel activeValueLabel, string startingName) : this( @@ -45,22 +55,82 @@ namespace grapher containingForm, defaultData, label, + left, activeValueLabel) { SetName(startingName); } + #endregion Constructors + + #region Properties + public Field Field { get; } public Label Label { get; } public ActiveValueLabel ActiveValueLabel { get; } + public override int Top + { + get + { + return Field.Top; + } + set + { + Field.Top = value; + Label.Top = value; + ActiveValueLabel.Top = value; + } + } + + public override int Height + { + get + { + return Field.Height; + } + } + + public override int Left + { + get + { + return Label.Left; + } + set + { + Label.Left = value; + } + } + public override int Width + { + get + { + return Field.Left + Field.Width - Label.Left; + } + set + { + } + } + + public override bool Visible + { + get + { + return Field.Box.Visible; + } + } + + #endregion Properties + + #region Methods + public void SetName(string name) { Label.Text = name; //Label.Left = Convert.ToInt32((Field.Box.Left / 2.0) - (Label.Width / 2.0)); //Centered - Label.Left = Convert.ToInt32(Field.Box.Left - Label.Width - 10); //Right-aligned } public void SetActiveValue(double value) @@ -68,7 +138,7 @@ namespace grapher ActiveValueLabel.SetValue(value); } - public void Hide() + public override void Hide() { Field.Box.Hide(); Label.Hide(); @@ -87,11 +157,18 @@ namespace grapher ActiveValueLabel.SetValue(value); } - public void Show(string name) + public override void Show(string name) { SetName(name); Show(); } + + public override void AlignActiveValues() + { + ActiveValueLabel.Align(); + } + + #endregion Methods } } diff --git a/grapher/Models/Options/OptionBase.cs b/grapher/Models/Options/OptionBase.cs new file mode 100644 index 0000000..5f6dca1 --- /dev/null +++ b/grapher/Models/Options/OptionBase.cs @@ -0,0 +1,33 @@ +namespace grapher.Models.Options +{ + public abstract class OptionBase : IOption + { + public abstract int Top { get; set; } + + public abstract int Height { get; } + + public abstract int Left { get; set; } + + public abstract int Width { get; set; } + + public int Beneath { + get + { + return Top + Height + Constants.OptionVerticalSeperation; + } + } + + public abstract bool Visible { get; } + + public abstract void Show(string Name); + + public abstract void Hide(); + + public abstract void AlignActiveValues(); + + public virtual void SnapTo(IOption option) + { + Top = option.Beneath; + } + } +} diff --git a/grapher/Models/Options/OptionXY.cs b/grapher/Models/Options/OptionXY.cs index 8e22617..6d6129a 100644 --- a/grapher/Models/Options/OptionXY.cs +++ b/grapher/Models/Options/OptionXY.cs @@ -1,20 +1,18 @@ using grapher.Models.Options; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace grapher { - public class OptionXY + public class OptionXY : OptionBase { + #region Constructors public OptionXY(FieldXY fields, Label label, ActiveValueLabelXY activeValueLabels) { Fields = fields; Label = label; ActiveValueLabels = activeValueLabels; + ActiveValueLabels.Left = fields.CombinedWidth + fields.Left; } public OptionXY( @@ -24,9 +22,8 @@ namespace grapher Form containingForm, double defaultData, Label label, - AccelCharts accelCharts, ActiveValueLabelXY activeValueLabels) - : this(new FieldXY(xBox, yBox, lockCheckBox, containingForm, defaultData, accelCharts), label, activeValueLabels) + : this(new FieldXY(xBox, yBox, lockCheckBox, containingForm, defaultData), label, activeValueLabels) { } @@ -38,8 +35,7 @@ namespace grapher double defaultData, Label label, ActiveValueLabelXY activeValueLabels, - string startingName, - AccelCharts accelCharts): + string startingName): this( xBox, yBox, @@ -47,18 +43,76 @@ namespace grapher containingForm, defaultData, label, - accelCharts, activeValueLabels) { SetName(startingName); } + #endregion Constructors + + #region Properties + public FieldXY Fields { get; } public Label Label { get; } public ActiveValueLabelXY ActiveValueLabels { get; } + public override int Top + { + get + { + return Fields.Top; + } + set + { + Fields.Top = value; + } + } + + public override int Height + { + get + { + return Fields.Height; + } + } + + public override int Left + { + get + { + return Fields.Left; + } + set + { + Fields.Left = value; + } + } + + public override int Width + { + get + { + return Fields.Width; + } + set + { + Fields.Width = value; + } + } + + public override bool Visible + { + get + { + return Fields.Visible; + } + } + #endregion Properties + + #region Methods + public void SetName(string name) { Label.Text = name; @@ -71,7 +125,12 @@ namespace grapher ActiveValueLabels.SetValues(x, y); } - public void Hide() + public override void AlignActiveValues() + { + ActiveValueLabels.AlignActiveValues(); + } + + public override void Hide() { Fields.Hide(); Fields.LockCheckBox.Hide(); @@ -85,12 +144,13 @@ namespace grapher Label.Show(); } - public void Show(string name) + public override void Show(string name) { SetName(name); Show(); } + #endregion Methods } } diff --git a/grapher/Models/Serialized/DriverSettings.cs b/grapher/Models/Serialized/DriverSettings.cs index cdccf88..d7c9444 100644 --- a/grapher/Models/Serialized/DriverSettings.cs +++ b/grapher/Models/Serialized/DriverSettings.cs @@ -4,11 +4,17 @@ using System.Threading; namespace grapher.Models.Serialized { + #region Enumerations + public enum AccelMode { - linear, classic, natural, logarithmic, sigmoid, naturalgain, sigmoidgain, power, noaccel + linear, classic, natural, naturalgain, sigmoidgain, power, noaccel } + #endregion Enumerations + + #region Structs + [StructLayout(LayoutKind.Sequential)] public struct AccelArgs { @@ -33,10 +39,14 @@ namespace grapher.Models.Serialized public T y; } + #endregion Structs + [StructLayout(LayoutKind.Sequential)] [Serializable] public class DriverSettings { + #region Fields + private static readonly IntPtr UnmanagedSettingsHandle = Marshal.AllocHGlobal(Marshal.SizeOf<DriverSettings>()); private static object UnmanagedSettingsLock = new object(); @@ -48,6 +58,10 @@ namespace grapher.Models.Serialized public Vec2<double> sensitivity; public double minimumTime; + #endregion Fields + + #region Methods + public static DriverSettings GetActive() { DriverInterop.GetActiveSettings(UnmanagedSettingsHandle); @@ -97,5 +111,7 @@ namespace grapher.Models.Serialized */ return true; } + + #endregion Methods } } diff --git a/grapher/Models/Serialized/GUISettings.cs b/grapher/Models/Serialized/GUISettings.cs index 7c8e9a4..2543104 100644 --- a/grapher/Models/Serialized/GUISettings.cs +++ b/grapher/Models/Serialized/GUISettings.cs @@ -1,17 +1,13 @@ using Newtonsoft.Json; using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; namespace grapher.Models.Serialized { [Serializable] public class GUISettings { + #region Constructors + public GUISettings() {} public GUISettings(bool autoWrite, int dpi, int pollRate) @@ -21,6 +17,10 @@ namespace grapher.Models.Serialized PollRate = pollRate; } + #endregion Constructors + + #region Properties + [JsonProperty(Order = 1)] public bool AutoWriteToDriverOnStartup { get; set; } @@ -29,5 +29,10 @@ namespace grapher.Models.Serialized [JsonProperty(Order = 3)] public int PollRate { get; set; } + + [JsonProperty(Order = 4)] + public bool ShowLastMouseMove { get; set; } + + #endregion Properties } } diff --git a/grapher/Models/Serialized/RawAccelSettings.cs b/grapher/Models/Serialized/RawAccelSettings.cs index 7aed917..3f5aebc 100644 --- a/grapher/Models/Serialized/RawAccelSettings.cs +++ b/grapher/Models/Serialized/RawAccelSettings.cs @@ -1,26 +1,25 @@ using Newtonsoft.Json; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; namespace grapher.Models.Serialized { [Serializable] public class RawAccelSettings { - public const string DefaultSettingsFileName = @"settings.json"; + #region Fields + public static readonly string ExecutingDirectory = AppDomain.CurrentDomain.BaseDirectory; - public static readonly string DefaultSettingsFile = Path.Combine(ExecutingDirectory, DefaultSettingsFileName); + public static readonly string DefaultSettingsFile = Path.Combine(ExecutingDirectory, Constants.DefaultSettingsFileName); public static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error, }; + #endregion Fields + + #region Constructors + public RawAccelSettings() { } public RawAccelSettings( @@ -31,10 +30,18 @@ namespace grapher.Models.Serialized GUISettings = guiSettings; } + #endregion Constructors + + #region Properties + public GUISettings GUISettings { get; set; } public DriverSettings AccelerationSettings { get; set; } + #endregion Properties + + #region Methods + public static RawAccelSettings Load() { return Load(DefaultSettingsFile); @@ -75,5 +82,7 @@ namespace grapher.Models.Serialized { File.WriteAllText(file, JsonConvert.SerializeObject(this, Formatting.Indented)); } + + #endregion Methods } } diff --git a/grapher/Models/Serialized/SettingsManager.cs b/grapher/Models/Serialized/SettingsManager.cs index c300bde..ccffc3f 100644 --- a/grapher/Models/Serialized/SettingsManager.cs +++ b/grapher/Models/Serialized/SettingsManager.cs @@ -6,18 +6,26 @@ namespace grapher.Models.Serialized { public class SettingsManager { + #region Constructors + public SettingsManager( ManagedAccel activeAccel, Field dpiField, Field pollRateField, - ToolStripMenuItem autoWrite) + ToolStripMenuItem autoWrite, + ToolStripMenuItem showLastMouseMove) { ActiveAccel = activeAccel; DpiField = dpiField; PollRateField = pollRateField; AutoWriteMenuItem = autoWrite; + ShowLastMouseMoveMenuItem = showLastMouseMove; } + #endregion Constructors + + #region Properties + public ManagedAccel ActiveAccel { get; } public RawAccelSettings RawAccelSettings { get; private set; } @@ -28,6 +36,12 @@ namespace grapher.Models.Serialized private ToolStripMenuItem AutoWriteMenuItem { get; set; } + private ToolStripMenuItem ShowLastMouseMoveMenuItem { get; set; } + + #endregion Properties + + #region Methods + public void UpdateActiveSettings(DriverSettings settings, Action afterAccelSettingsUpdate = null) { settings.SendToDriverAndUpdate(ActiveAccel, () => @@ -37,7 +51,8 @@ namespace grapher.Models.Serialized { AutoWriteToDriverOnStartup = AutoWriteMenuItem.Checked, DPI = (int)DpiField.Data, - PollRate = (int)PollRateField.Data + PollRate = (int)PollRateField.Data, + ShowLastMouseMove = ShowLastMouseMoveMenuItem.Checked, }; RawAccelSettings.Save(); @@ -53,6 +68,7 @@ namespace grapher.Models.Serialized DpiField.SetToEntered(RawAccelSettings.GUISettings.DPI); PollRateField.SetToEntered(RawAccelSettings.GUISettings.PollRate); AutoWriteMenuItem.Checked = RawAccelSettings.GUISettings.AutoWriteToDriverOnStartup; + ShowLastMouseMoveMenuItem.Checked = RawAccelSettings.GUISettings.ShowLastMouseMove; } public void Startup() @@ -80,9 +96,12 @@ namespace grapher.Models.Serialized { AutoWriteToDriverOnStartup = AutoWriteMenuItem.Checked, DPI = (int)DpiField.Data, - PollRate = (int)PollRateField.Data + PollRate = (int)PollRateField.Data, + ShowLastMouseMove = ShowLastMouseMoveMenuItem.Checked, }); RawAccelSettings.Save(); } + + #endregion Methods } } diff --git a/grapher/Program.cs b/grapher/Program.cs index b36ece3..85fd040 100644 --- a/grapher/Program.cs +++ b/grapher/Program.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using System.Windows.Forms; namespace grapher diff --git a/grapher/grapher.csproj b/grapher/grapher.csproj index 28322bb..74e2959 100644 --- a/grapher/grapher.csproj +++ b/grapher/grapher.csproj @@ -53,8 +53,10 @@ <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> + <Compile Include="Constants\Constants.cs" /> <Compile Include="Layouts\NaturalGainLayout.cs" /> <Compile Include="Layouts\SigmoidGainLayout.cs" /> + <Compile Include="Models\AccelGUIFactory.cs" /> <Compile Include="Models\Calculations\AccelCalculator.cs" /> <Compile Include="Models\Calculations\AccelChartData.cs" /> <Compile Include="Models\Calculations\AccelData.cs" /> @@ -63,9 +65,11 @@ <Compile Include="Models\Charts\EstimatedPoints.cs" /> <Compile Include="Models\Mouse\MouseWatcher.cs" /> <Compile Include="Models\Mouse\PointData.cs" /> - <Compile Include="Models\Options\AccelOptions.cs" /> + <Compile Include="Models\Options\AccelTypeOptions.cs" /> + <Compile Include="Models\Options\AccelOptionSet.cs" /> <Compile Include="Models\Options\ActiveValueLabel.cs" /> <Compile Include="Models\Options\ActiveValueLabelXY.cs" /> + <Compile Include="Models\Options\ApplyOptions.cs" /> <Compile Include="Models\Options\CapOptions.cs" /> <Compile Include="Models\Charts\ChartXY.cs" /> <Compile Include="Models\Fields\Field.cs" /> @@ -80,13 +84,14 @@ <Compile Include="Layouts\DefaultLayout.cs" /> <Compile Include="Layouts\LayoutBase.cs" /> <Compile Include="Layouts\LinearLayout.cs" /> - <Compile Include="Layouts\LogLayout.cs" /> <Compile Include="Layouts\NaturalLayout.cs" /> <Compile Include="Layouts\OffLayout.cs" /> <Compile Include="Layouts\PowerLayout.cs" /> - <Compile Include="Layouts\SigmoidLayout.cs" /> + <Compile Include="Models\Options\IOption.cs" /> <Compile Include="Models\Options\OffsetOptions.cs" /> <Compile Include="Models\Options\Option.cs" /> + <Compile Include="Layouts\OptionLayout.cs" /> + <Compile Include="Models\Options\OptionBase.cs" /> <Compile Include="Models\Options\OptionXY.cs" /> <Compile Include="Models\Serialized\GUISettings.cs" /> <Compile Include="Models\Serialized\DriverSettings.cs" /> diff --git a/rawaccel.sln b/rawaccel.sln index 9eb1101..f2eab80 100644 --- a/rawaccel.sln +++ b/rawaccel.sln @@ -9,8 +9,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcx EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "installer", "installer\installer.vcxproj", "{896950D1-520A-420A-B6B1-73014B92A68C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console", "console\console.vcxproj", "{AB7B3759-B85F-4067-8935-FB4539B41869}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common-install", "common-install\common-install.vcxitems", "{058D66C6-D88B-4FDB-B0E4-0A6FE7483B95}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uninstaller", "uninstaller\uninstaller.vcxproj", "{A4097FF6-A6F0-44E8-B8D0-538D0FB75936}" @@ -27,7 +25,6 @@ Global common\common.vcxitems*{60d6c942-ac20-4c05-a2be-54b5c966534d}*SharedItemsImports = 4 common-install\common-install.vcxitems*{896950d1-520a-420a-b6b1-73014b92a68c}*SharedItemsImports = 4 common-install\common-install.vcxitems*{a4097ff6-a6f0-44e8-b8d0-538d0fb75936}*SharedItemsImports = 4 - common\common.vcxitems*{ab7b3759-b85f-4067-8935-fb4539b41869}*SharedItemsImports = 4 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -43,10 +40,6 @@ Global {896950D1-520A-420A-B6B1-73014B92A68C}.Debug|x64.Build.0 = Debug|x64 {896950D1-520A-420A-B6B1-73014B92A68C}.Release|x64.ActiveCfg = Release|x64 {896950D1-520A-420A-B6B1-73014B92A68C}.Release|x64.Build.0 = Release|x64 - {AB7B3759-B85F-4067-8935-FB4539B41869}.Debug|x64.ActiveCfg = Debug|x64 - {AB7B3759-B85F-4067-8935-FB4539B41869}.Debug|x64.Build.0 = Debug|x64 - {AB7B3759-B85F-4067-8935-FB4539B41869}.Release|x64.ActiveCfg = Release|x64 - {AB7B3759-B85F-4067-8935-FB4539B41869}.Release|x64.Build.0 = Release|x64 {A4097FF6-A6F0-44E8-B8D0-538D0FB75936}.Debug|x64.ActiveCfg = Debug|x64 {A4097FF6-A6F0-44E8-B8D0-538D0FB75936}.Debug|x64.Build.0 = Debug|x64 {A4097FF6-A6F0-44E8-B8D0-538D0FB75936}.Release|x64.ActiveCfg = Release|x64 diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index 757d3f1..3108bda 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -24,6 +24,8 @@ public ref class ManagedAccel mouse_modifier* const modifier_instance = new mouse_modifier(); public: + static initonly double WriteDelay = -10000000 / -10000.0; + virtual ~ManagedAccel() { delete modifier_instance; |