diff --git a/CommonWPF/CommonWPF.csproj b/CommonWPF/CommonWPF.csproj new file mode 100644 index 0000000..a820073 --- /dev/null +++ b/CommonWPF/CommonWPF.csproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + {1299AA2E-606D-4F3E-B3A9-3F9421E44667} + library + CommonWPF + CommonWPF + v4.6.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + 4.0 + + + + + + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + \ No newline at end of file diff --git a/CommonWPF/InverseBooleanConverter.cs b/CommonWPF/InverseBooleanConverter.cs new file mode 100644 index 0000000..89f32a6 --- /dev/null +++ b/CommonWPF/InverseBooleanConverter.cs @@ -0,0 +1,40 @@ +/* + * Copyright 2019 faddenSoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using System; +using System.Windows.Data; + +namespace CommonWPF { + /// + /// A value inverter for negating a boolean value (value --> !value). + /// + /// See https://stackoverflow.com/questions/1039636/how-to-bind-inverse-boolean-properties-in-wpf + /// + [ValueConversion(typeof(bool), typeof(bool))] + public class InverseBooleanConverter : IValueConverter { + public object Convert(object value, Type targetType, object parameter, + System.Globalization.CultureInfo culture) { + if (targetType != typeof(bool)) + throw new InvalidOperationException("The target must be a boolean"); + + return !(bool)value; + } + + public object ConvertBack(object value, Type targetType, object parameter, + System.Globalization.CultureInfo culture) { + throw new NotSupportedException(); + } + } +} diff --git a/CommonWPF/MultiKeyInputGesture.cs b/CommonWPF/MultiKeyInputGesture.cs new file mode 100644 index 0000000..8c2bed0 --- /dev/null +++ b/CommonWPF/MultiKeyInputGesture.cs @@ -0,0 +1,105 @@ +/* + * Copyright 2018 faddenSoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +/// +/// Handle a multi-key input sequence for WPF windows. +/// +/// +/// Also posted as https://stackoverflow.com/a/56452142/294248 +/// +/// Example: +/// {RoutedUICommand}.InputGestures.Add( +/// new MultiKeyInputGesture(new KeyGesture[] { +/// new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl+H"), +/// new KeyGesture(Key.C, ModifierKeys.Control, "Ctrl+C") +/// }) ); +/// +/// TODO: if you have more than one handler, the handler that completes a sequence will +/// "eat" the final key, and the other handlers won't reset. Might need to define an event +/// that all gesture objects subscribe to, so they all reset at once. In the mean time, the +/// reset-after-time handler solves the problem if the user is moving slowly enough. +/// +namespace CommonWPF { + public class MultiKeyInputGesture : InputGesture { + private const int MAX_PAUSE_MILLIS = 1500; + + private InputGestureCollection mGestures = new InputGestureCollection(); + + private DateTime mLastWhen = DateTime.Now; + private int mCheckIdx; + private string mIdStr; + + + public MultiKeyInputGesture(KeyGesture[] keys) { + Debug.Assert(keys.Length > 0); + + StringBuilder idSb = new StringBuilder(); + + // Grab a copy of the array contents. + foreach (KeyGesture kg in keys) { + mGestures.Add(kg); + idSb.Append(kg.DisplayString[kg.DisplayString.Length - 1]); + } + mIdStr = idSb.ToString(); + } + + public override bool Matches(object targetElement, InputEventArgs inputEventArgs) { + if (!(inputEventArgs is KeyEventArgs)) { + // does this actually happen? + return false; + } + + DateTime now = DateTime.Now; + if ((now - mLastWhen).TotalMilliseconds > MAX_PAUSE_MILLIS) { + //Debug.WriteLine("MKIG " + mIdStr + ": too long since last key (" + + // (now - mLastWhen).TotalMilliseconds + " ms"); + mCheckIdx = 0; + } + mLastWhen = now; + + if (((KeyEventArgs)inputEventArgs).IsRepeat) { + // ignore key-repeat noise (especially from modifiers) + return false; + } + + if (!mGestures[mCheckIdx].Matches(null, inputEventArgs)) { + if (mCheckIdx > 0) { + //Debug.WriteLine("MKIG " + mIdStr + ": no match, resetting"); + mCheckIdx = 0; + } + return false; + } + + //Debug.WriteLine("MKIG " + mIdStr + ": matched gesture #" + mCheckIdx); + mCheckIdx++; + if (mCheckIdx == mGestures.Count) { + //Debug.WriteLine("MKIG " + mIdStr + ": match"); + mCheckIdx = 0; + inputEventArgs.Handled = true; + return true; + } + + return false; + } + } +} diff --git a/CommonWPF/Properties/AssemblyInfo.cs b/CommonWPF/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e089079 --- /dev/null +++ b/CommonWPF/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CommonWPF")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CommonWPF")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly:ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CommonWPF/Properties/Resources.Designer.cs b/CommonWPF/Properties/Resources.Designer.cs new file mode 100644 index 0000000..4720be0 --- /dev/null +++ b/CommonWPF/Properties/Resources.Designer.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CommonWPF.Properties { + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if ((resourceMan == null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CommonWPF.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/CommonWPF/Properties/Resources.resx b/CommonWPF/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/CommonWPF/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CommonWPF/Properties/Settings.Designer.cs b/CommonWPF/Properties/Settings.Designer.cs new file mode 100644 index 0000000..926afbe --- /dev/null +++ b/CommonWPF/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CommonWPF.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/CommonWPF/Properties/Settings.settings b/CommonWPF/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/CommonWPF/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/SourceGenWPF/MainController.cs b/SourceGenWPF/MainController.cs index 5f12fec..7b714d0 100644 --- a/SourceGenWPF/MainController.cs +++ b/SourceGenWPF/MainController.cs @@ -54,10 +54,10 @@ namespace SourceGenWPF { /// /// Current code list view selection. The length will match the DisplayList Count. - /// - /// A simple foreach through codeListView.SelectedIndices on a 500K-line data set - /// takes about 2.5 seconds on a fast Win10 x64 machine. Fortunately the control - /// notifies us of changes to the selection, so we can track it ourselves. + /// + /// WinForms was bad -- a simple foreach through SelectedIndices on a 500K-line data set + /// took about 2.5 seconds on a fast Win10 x64 machine. In WPF you get a list of + /// selected objects, which is fine unless what you really want is the line number. /// private VirtualListViewSelection mCodeViewSelection = new VirtualListViewSelection(); #endif diff --git a/SourceGenWPF/ProjWin/MainWindow.xaml b/SourceGenWPF/ProjWin/MainWindow.xaml index 3bec1cf..fc7f655 100644 --- a/SourceGenWPF/ProjWin/MainWindow.xaml +++ b/SourceGenWPF/ProjWin/MainWindow.xaml @@ -45,6 +45,10 @@ limitations under the License. Ctrl+Shift+A + + + + @@ -56,6 +60,10 @@ limitations under the License. + + + + @@ -106,10 +114,10 @@ limitations under the License. - - - - + + + + diff --git a/SourceGenWPF/ProjWin/MainWindow.xaml.cs b/SourceGenWPF/ProjWin/MainWindow.xaml.cs index 418fb42..bb90350 100644 --- a/SourceGenWPF/ProjWin/MainWindow.xaml.cs +++ b/SourceGenWPF/ProjWin/MainWindow.xaml.cs @@ -31,6 +31,8 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; +using CommonWPF; + namespace SourceGenWPF.ProjWin { /// /// Interaction logic for MainWindow.xaml @@ -61,10 +63,40 @@ namespace SourceGenWPF.ProjWin { mMainCtrl = new MainController(this); + AddMultiKeyGestures(); + //GridView gv = (GridView)codeListView.View; //gv.Columns[0].Width = 50; } + private void AddMultiKeyGestures() { + RoutedUICommand ruic; + + ruic = (RoutedUICommand)FindResource("HintAsCodeEntryPoint"); + ruic.InputGestures.Add( + new MultiKeyInputGesture(new KeyGesture[] { + new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl+H"), + new KeyGesture(Key.C, ModifierKeys.Control, "Ctrl+C") + })); + ruic = (RoutedUICommand)FindResource("HintAsDataStart"); + ruic.InputGestures.Add( + new MultiKeyInputGesture(new KeyGesture[] { + new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl+H"), + new KeyGesture(Key.D, ModifierKeys.Control, "Ctrl+D") + })); + ruic = (RoutedUICommand)FindResource("HintAsInlineData"); + ruic.InputGestures.Add( + new MultiKeyInputGesture(new KeyGesture[] { + new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl+H"), + new KeyGesture(Key.I, ModifierKeys.Control, "Ctrl+I") + })); + ruic = (RoutedUICommand)FindResource("RemoveHints"); + ruic.InputGestures.Add( + new MultiKeyInputGesture(new KeyGesture[] { + new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl+H"), + new KeyGesture(Key.R, ModifierKeys.Control, "Ctrl+R") + })); + } private void Window_Loaded(object sender, RoutedEventArgs e) { mMainCtrl.WindowLoaded(); @@ -135,6 +167,22 @@ namespace SourceGenWPF.ProjWin { Debug.WriteLine("assembling"); } + private void HintAsCodeEntryPoint_Executed(object sender, ExecutedRoutedEventArgs e) { + Debug.WriteLine("hint as code entry point"); + } + + private void HintAsDataStart_Executed(object sender, ExecutedRoutedEventArgs e) { + Debug.WriteLine("hint as data start"); + } + + private void HintAsInlineData_Executed(object sender, ExecutedRoutedEventArgs e) { + Debug.WriteLine("hint as inline data"); + } + + private void RemoveHints_Executed(object sender, ExecutedRoutedEventArgs e) { + Debug.WriteLine("remove hints"); + } + private void SelectAllCmd_Executed(object sender, ExecutedRoutedEventArgs e) { DateTime start = DateTime.Now; diff --git a/SourceGenWPF/SourceGenWPF.csproj b/SourceGenWPF/SourceGenWPF.csproj index 1bb2e95..d67e810 100644 --- a/SourceGenWPF/SourceGenWPF.csproj +++ b/SourceGenWPF/SourceGenWPF.csproj @@ -183,6 +183,10 @@ {a2993eac-35d8-4768-8c54-152b4e14d69c} CommonUtil + + {1299aa2e-606d-4f3e-b3a9-3f9421e44667} + CommonWPF + {70f04543-9e46-4ad3-875a-160fd198c0ff} PluginCommon diff --git a/WorkBench.sln b/WorkBench.sln index 4c632ec..f6b05fa 100644 --- a/WorkBench.sln +++ b/WorkBench.sln @@ -26,6 +26,11 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonWinForms", "CommonWinForms\CommonWinForms.csproj", "{08EC328D-078E-4236-B574-BE6B3FD85915}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGenWPF", "SourceGenWPF\SourceGenWPF.csproj", "{30C35BBC-B9ED-4723-8F9D-597D51CCB13A}" + ProjectSection(ProjectDependencies) = postProject + {1299AA2E-606D-4F3E-B3A9-3F9421E44667} = {1299AA2E-606D-4F3E-B3A9-3F9421E44667} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonWPF", "CommonWPF\CommonWPF.csproj", "{1299AA2E-606D-4F3E-B3A9-3F9421E44667}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -69,6 +74,10 @@ Global {30C35BBC-B9ED-4723-8F9D-597D51CCB13A}.Debug|Any CPU.Build.0 = Debug|Any CPU {30C35BBC-B9ED-4723-8F9D-597D51CCB13A}.Release|Any CPU.ActiveCfg = Release|Any CPU {30C35BBC-B9ED-4723-8F9D-597D51CCB13A}.Release|Any CPU.Build.0 = Release|Any CPU + {1299AA2E-606D-4F3E-B3A9-3F9421E44667}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1299AA2E-606D-4F3E-B3A9-3F9421E44667}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1299AA2E-606D-4F3E-B3A9-3F9421E44667}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1299AA2E-606D-4F3E-B3A9-3F9421E44667}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE