diff --git a/Plugins/Wox.Plugin.Calculator/DecimalSeparator.cs b/Plugins/Wox.Plugin.Calculator/DecimalSeparator.cs new file mode 100644 index 000000000..5aad9b39a --- /dev/null +++ b/Plugins/Wox.Plugin.Calculator/DecimalSeparator.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Wox.Core; + +namespace Wox.Plugin.Caculator +{ + [TypeConverter(typeof(LocalizationConverter))] + public enum DecimalSeparator + { + [LocalizedDescription("wox_plugin_calculator_decimal_seperator_use_system_locale")] + UseSystemLocale, + + [LocalizedDescription("wox_plugin_calculator_decimal_seperator_dot")] + Dot, + + [LocalizedDescription("wox_plugin_calculator_decimal_seperator_comma")] + Comma + } +} \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Calculator/Languages/de.xaml b/Plugins/Wox.Plugin.Calculator/Languages/de.xaml index 286e4bc9b..eb6847af0 100644 --- a/Plugins/Wox.Plugin.Calculator/Languages/de.xaml +++ b/Plugins/Wox.Plugin.Calculator/Languages/de.xaml @@ -7,4 +7,10 @@ Keine Zahl (NaN) Ausdruck falsch oder nicht vollständig (Klammern vergessen?) Diese Zahl in die Zwischenablage kopieren + Dezimaltrennzeichen + Das Dezimaltrennzeichen, welches bei der Ausgabe verwendet werden soll. + Systemeinstellung nutzen + Komma (,) + Punkt (.) + Max. Nachkommastellen \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Calculator/Languages/en.xaml b/Plugins/Wox.Plugin.Calculator/Languages/en.xaml index 3b5fa32b3..0df0d3342 100644 --- a/Plugins/Wox.Plugin.Calculator/Languages/en.xaml +++ b/Plugins/Wox.Plugin.Calculator/Languages/en.xaml @@ -7,4 +7,10 @@ Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) Copy this number to the clipboard + Decimal separator + The decimal separator to be used in the output. + Use system locale + Comma (,) + Dot (.) + Max. decimal places \ No newline at end of file diff --git a/Plugins/Wox.Plugin.Calculator/Main.cs b/Plugins/Wox.Plugin.Calculator/Main.cs index fab0b8e03..f2f7f7e18 100644 --- a/Plugins/Wox.Plugin.Calculator/Main.cs +++ b/Plugins/Wox.Plugin.Calculator/Main.cs @@ -1,12 +1,18 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Globalization; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Windows; +using System.Windows.Controls; using Mages.Core; +using Wox.Infrastructure.Storage; +using Wox.Plugin.Caculator.ViewModels; +using Wox.Plugin.Caculator.Views; namespace Wox.Plugin.Caculator { - public class Main : IPlugin, IPluginI18n + public class Main : IPlugin, IPluginI18n, ISavable, ISettingProvider { private static readonly Regex RegValidExpressChar = new Regex( @"^(" + @@ -21,20 +27,33 @@ namespace Wox.Plugin.Caculator private static readonly Engine MagesEngine; private PluginInitContext Context { get; set; } + private static Settings _settings; + private static SettingsViewModel _viewModel; + static Main() { - MagesEngine = new Engine(); + MagesEngine = new Engine(); + } + + public void Init(PluginInitContext context) + { + Context = context; + + _viewModel = new SettingsViewModel(); + _settings = _viewModel.Settings; } public List Query(Query query) { - if (query.Search.Length <= 2 // don't affect when user only input "e" or "i" keyword - || !RegValidExpressChar.IsMatch(query.Search) - || !IsBracketComplete(query.Search)) return new List(); + if (!CanCalculate(query)) + { + return new List(); + } try { - var result = MagesEngine.Interpret(query.Search); + var expression = query.Search.Replace(",", "."); + var result = MagesEngine.Interpret(expression); if (result.ToString() == "NaN") result = Context.API.GetTranslation("wox_plugin_calculator_not_a_number"); @@ -42,14 +61,16 @@ namespace Wox.Plugin.Caculator if (result is Function) result = Context.API.GetTranslation("wox_plugin_calculator_expression_not_complete"); - if (!string.IsNullOrEmpty(result?.ToString())) { + decimal roundedResult = Math.Round(Convert.ToDecimal(result), _settings.MaxDecimalPlaces, MidpointRounding.AwayFromZero); + string newResult = ChangeDecimalSeparator(roundedResult, GetDecimalSeparator()); + return new List { new Result { - Title = result.ToString(), + Title = newResult, IcoPath = "Images/calculator.png", Score = 300, SubTitle = Context.API.GetTranslation("wox_plugin_calculator_copy_number_to_clipboard"), @@ -57,7 +78,7 @@ namespace Wox.Plugin.Caculator { try { - Clipboard.SetText(result.ToString()); + Clipboard.SetText(newResult); return true; } catch (ExternalException) @@ -78,6 +99,53 @@ namespace Wox.Plugin.Caculator return new List(); } + private bool CanCalculate(Query query) + { + // Don't execute when user only input "e" or "i" keyword + if (query.Search.Length < 2) + { + return false; + } + + if (!RegValidExpressChar.IsMatch(query.Search)) + { + return false; + } + + if (!IsBracketComplete(query.Search)) + { + return false; + } + + return true; + } + + private string ChangeDecimalSeparator(decimal value, string newDecimalSeparator) + { + if (String.IsNullOrEmpty(newDecimalSeparator)) + { + return value.ToString(); + } + + var numberFormatInfo = new NumberFormatInfo + { + NumberDecimalSeparator = newDecimalSeparator + }; + return value.ToString(numberFormatInfo); + } + + private string GetDecimalSeparator() + { + string systemDecimalSeperator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + switch (_settings.DecimalSeparator) + { + case DecimalSeparator.UseSystemLocale: return systemDecimalSeperator; + case DecimalSeparator.Dot: return "."; + case DecimalSeparator.Comma: return ","; + default: return systemDecimalSeperator; + } + } + private bool IsBracketComplete(string query) { var matchs = RegBrackets.Matches(query); @@ -96,12 +164,7 @@ namespace Wox.Plugin.Caculator return leftBracketCount == 0; } - - public void Init(PluginInitContext context) - { - Context = context; - } - + public string GetTranslatedPluginTitle() { return Context.API.GetTranslation("wox_plugin_caculator_plugin_name"); @@ -111,5 +174,15 @@ namespace Wox.Plugin.Caculator { return Context.API.GetTranslation("wox_plugin_caculator_plugin_description"); } + + public Control CreateSettingPanel() + { + return new CalculatorSettings(_viewModel); + } + + public void Save() + { + _viewModel.Save(); + } } } diff --git a/Plugins/Wox.Plugin.Calculator/Settings.cs b/Plugins/Wox.Plugin.Calculator/Settings.cs new file mode 100644 index 000000000..ce01f2da0 --- /dev/null +++ b/Plugins/Wox.Plugin.Calculator/Settings.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wox.Plugin.Caculator +{ + public class Settings + { + public DecimalSeparator DecimalSeparator { get; set; } = DecimalSeparator.UseSystemLocale; + public int MaxDecimalPlaces { get; set; } = 10; + } +} diff --git a/Plugins/Wox.Plugin.Calculator/ViewModels/SettingsViewModel.cs b/Plugins/Wox.Plugin.Calculator/ViewModels/SettingsViewModel.cs new file mode 100644 index 000000000..8550f47a5 --- /dev/null +++ b/Plugins/Wox.Plugin.Calculator/ViewModels/SettingsViewModel.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Wox.Infrastructure.Storage; +using Wox.Infrastructure.UserSettings; + +namespace Wox.Plugin.Caculator.ViewModels +{ + public class SettingsViewModel : BaseModel, ISavable + { + private readonly PluginJsonStorage _storage; + + public SettingsViewModel() + { + _storage = new PluginJsonStorage(); + Settings = _storage.Load(); + } + + public Settings Settings { get; set; } + + public IEnumerable MaxDecimalPlacesRange => Enumerable.Range(1, 20); + + public void Save() + { + _storage.Save(); + } + } +} diff --git a/Plugins/Wox.Plugin.Calculator/Views/CalculatorSettings.xaml b/Plugins/Wox.Plugin.Calculator/Views/CalculatorSettings.xaml new file mode 100644 index 000000000..183f10e5f --- /dev/null +++ b/Plugins/Wox.Plugin.Calculator/Views/CalculatorSettings.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/Wox.Plugin.Calculator/Views/CalculatorSettings.xaml.cs b/Plugins/Wox.Plugin.Calculator/Views/CalculatorSettings.xaml.cs new file mode 100644 index 000000000..c15bdda77 --- /dev/null +++ b/Plugins/Wox.Plugin.Calculator/Views/CalculatorSettings.xaml.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Wox.Plugin.Caculator.ViewModels; + +namespace Wox.Plugin.Caculator.Views +{ + /// + /// Interaction logic for CalculatorSettings.xaml + /// + public partial class CalculatorSettings : UserControl + { + private readonly SettingsViewModel _viewModel; + private readonly Settings _settings; + + public CalculatorSettings(SettingsViewModel viewModel) + { + _viewModel = viewModel; + _settings = viewModel.Settings; + DataContext = viewModel; + InitializeComponent(); + } + + private void CalculatorSettings_Loaded(object sender, RoutedEventArgs e) + { + DecimalSeparatorComboBox.SelectedItem = _settings.DecimalSeparator; + MaxDecimalPlaces.SelectedItem = _settings.MaxDecimalPlaces; + } + } + + +} diff --git a/Plugins/Wox.Plugin.Calculator/Wox.Plugin.Calculator.csproj b/Plugins/Wox.Plugin.Calculator/Wox.Plugin.Calculator.csproj index b0a091934..f24ad118e 100644 --- a/Plugins/Wox.Plugin.Calculator/Wox.Plugin.Calculator.csproj +++ b/Plugins/Wox.Plugin.Calculator/Wox.Plugin.Calculator.csproj @@ -46,14 +46,22 @@ + + Properties\SolutionAssemblyInfo.cs + + + + + CalculatorSettings.xaml + @@ -61,6 +69,10 @@ + + {B749F0DB-8E75-47DB-9E5E-265D16D0C0D2} + Wox.Core + {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3} Wox.Infrastructure @@ -122,12 +134,18 @@ 10.3.0 - 1.5.0 + 1.6.0 4.0.0 + + + Designer + MSBuild:Compile + + diff --git a/Wox.Core/Resource/LocalizationConverter.cs b/Wox.Core/Resource/LocalizationConverter.cs new file mode 100644 index 000000000..d86bf5f7e --- /dev/null +++ b/Wox.Core/Resource/LocalizationConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using System.Windows.Data; + +namespace Wox.Core +{ + public class LocalizationConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (targetType == typeof(string) && value != null) + { + FieldInfo fi = value.GetType().GetField(value.ToString()); + if (fi != null) + { + string localizedDescription = string.Empty; + var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); + if ((attributes.Length > 0) && (!String.IsNullOrEmpty(attributes[0].Description))) + { + localizedDescription = attributes[0].Description; + } + + return (!String.IsNullOrEmpty(localizedDescription)) ? localizedDescription : value.ToString(); + } + } + + return string.Empty; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Wox.Core/Resource/LocalizedDescriptionAttribute.cs b/Wox.Core/Resource/LocalizedDescriptionAttribute.cs new file mode 100644 index 000000000..ef1257220 --- /dev/null +++ b/Wox.Core/Resource/LocalizedDescriptionAttribute.cs @@ -0,0 +1,27 @@ +using System.ComponentModel; +using Wox.Core.Resource; + +namespace Wox.Core +{ + public class LocalizedDescriptionAttribute : DescriptionAttribute + { + private readonly Internationalization _translator; + private readonly string _resourceKey; + + public LocalizedDescriptionAttribute(string resourceKey) + { + _translator = InternationalizationManager.Instance; + _resourceKey = resourceKey; + } + + public override string Description + { + get + { + string description = _translator.GetTranslation(_resourceKey); + return string.IsNullOrWhiteSpace(description) ? + string.Format("[[{0}]]", _resourceKey) : description; + } + } + } +} diff --git a/Wox.Core/Wox.Core.csproj b/Wox.Core/Wox.Core.csproj index bb5fbdc3c..0cdc22b52 100644 --- a/Wox.Core/Wox.Core.csproj +++ b/Wox.Core/Wox.Core.csproj @@ -53,6 +53,8 @@ + + diff --git a/Wox.Infrastructure/UI/EnumBindingSource.cs b/Wox.Infrastructure/UI/EnumBindingSource.cs new file mode 100644 index 000000000..c23aacd81 --- /dev/null +++ b/Wox.Infrastructure/UI/EnumBindingSource.cs @@ -0,0 +1,57 @@ +using System; +using System.Windows.Markup; + +namespace Wox.Infrastructure.UI +{ + public class EnumBindingSourceExtension : MarkupExtension + { + private Type _enumType; + public Type EnumType + { + get { return _enumType; } + set + { + if (value != _enumType) + { + if (value != null) + { + Type enumType = Nullable.GetUnderlyingType(value) ?? value; + if (!enumType.IsEnum) + { + throw new ArgumentException("Type must represent an enum."); + } + } + + _enumType = value; + } + } + } + + public EnumBindingSourceExtension() { } + + public EnumBindingSourceExtension(Type enumType) + { + EnumType = enumType; + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + if (_enumType == null) + { + throw new InvalidOperationException("The EnumType must be specified."); + } + + Type actualEnumType = Nullable.GetUnderlyingType(_enumType) ?? _enumType; + Array enumValues = Enum.GetValues(actualEnumType); + + if (actualEnumType == _enumType) + { + return enumValues; + } + + Array tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1); + enumValues.CopyTo(tempArray, 1); + return tempArray; + } + } +} diff --git a/Wox.Infrastructure/Wox.Infrastructure.csproj b/Wox.Infrastructure/Wox.Infrastructure.csproj index 16194a35a..445454093 100644 --- a/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -72,6 +72,7 @@ +