From 8f0d8886387dcf308bb6c6a6f0002f01072b8421 Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Mon, 18 Mar 2024 01:18:54 +0000 Subject: [PATCH] Removed unused classes and fixed a few other things --- SharpPad.sln | 6 - SharpPad/AdvancedMenuService/MenuService.cs | 2 +- .../RegularMenuService/AdvancedMenuItem.cs | 2 +- .../RegularMenuService/AdvancedRegularMenu.cs | 2 +- SharpPad/App.config | 15 + SharpPad/App.xaml | 7 +- SharpPad/App.xaml.cs | 14 +- SharpPad/ApplicationCore.cs | 21 +- SharpPad/AsyncDemo.cs | 2 +- .../AttachedInteractivity.cs | 96 -- .../AttachedProperties/MultiSelectorHelper.cs | 220 ----- .../ObservableSelectionHelper.cs | 504 ----------- SharpPad/Behaviours/Behaviour.cs | 2 +- SharpPad/Behaviours/BehaviourBase.cs | 2 +- SharpPad/Behaviours/BehaviourCollection.cs | 2 +- SharpPad/Behaviours/IBehaviour.cs | 2 +- .../Standard/KeyStrokeCommandBehaviour.cs | 2 +- .../Behaviours/TestBlueBackgroundBehaviour.cs | 2 +- SharpPad/CommandSystem/AsyncCommand.cs | 2 +- SharpPad/CommandSystem/CommandGroup.cs | 2 +- SharpPad/CommandSystem/Executability.cs | 2 +- SharpPad/CommandSystem/SimpleCommandGroup.cs | 2 +- .../Usages/BaseToggleButtonCommandUsage.cs | 2 +- .../Usages/BasicButtonCommandUsage.cs | 2 +- .../Usages/CommandSourceCommandUsage.cs | 2 +- SharpPad/CommandSystem/Usages/CommandUsage.cs | 2 +- .../Usages/CommandUsageManager.cs | 2 +- .../Usages/CommandUsageModelHelper.cs | 2 +- SharpPad/Controls/Bindings/Binders.cs | 2 +- .../Dragger/EditCompletedEventHandler.cs | 22 - .../Controls/Dragger/EditStartEventHandler.cs | 22 - .../Controls/Dragger/HorizontalIncrement.cs | 32 - SharpPad/Controls/Dragger/IChangeMapper.cs | 24 - SharpPad/Controls/Dragger/IValueFormatter.cs | 24 - .../Controls/Dragger/IValuePreProcessor.cs | 27 - SharpPad/Controls/Dragger/NumberDragger.cs | 846 ------------------ .../Controls/Dragger/NumberDraggerStyles.xaml | 101 --- .../Dragger/UnitToPercentFormatter.cs | 29 - .../Controls/Dragger/ValueStringFormatter.cs | 28 - .../Controls/Dragger/VerticalIncrement.cs | 32 - .../MultiSelectTreeViewAutomationPeer.cs | 137 --- .../MultiSelectTreeViewItemAutomationPeer.cs | 344 ------- ...ltiSelectTreeViewItemDataAutomationPeer.cs | 202 ----- .../Controls/BorderSelectionLogic.cs | 277 ------ .../TreeViews/Controls/EditTextBox.cs | 81 -- .../TreeViews/Controls/FocusHelper.cs | 83 -- .../TreeViews/Controls/ISelectionStrategy.cs | 78 -- .../Controls/TreeViews/Controls/ListUtils.cs | 79 -- .../TreeViews/Controls/MultiSelectTreeView.cs | 565 ------------ .../Controls/MultiSelectTreeViewItem.cs | 500 ----------- .../TreeViews/Controls/SelectionMultiple.cs | 387 -------- SharpPad/Controls/TreeViews/SOURCECODE.txt | 8 - .../TreeViews/Themes/EditTextBox.xaml | 39 - .../Themes/MultiSelectTreeView.Aero2.xaml | 67 -- .../Themes/MultiSelectTreeViewItem.Aero2.xaml | 240 ----- SharpPad/Converters/BoolConverterAND.cs | 66 -- SharpPad/Converters/DbToVolumeConverter.cs | 62 -- SharpPad/Converters/VolumeToDbConverter.cs | 62 -- SharpPad/DispatcherDelegate.cs | 2 +- SharpPad/History/ChildManagerHistoryAction.cs | 2 +- SharpPad/History/HistoryAction.cs | 2 +- SharpPad/History/HistoryManager.cs | 2 +- SharpPad/History/IHistoryAction.cs | 2 +- SharpPad/History/MergedHistoryAction.cs | 2 +- SharpPad/IDispatcher.cs | 2 +- .../Contexts/ContextDataHelper.cs | 2 +- SharpPad/Keymap.xml | 8 + .../Notepads/Commands/CloseDocumentCommand.cs | 13 +- SharpPad/Notepads/Commands/DocumentCommand.cs | 2 +- SharpPad/Notepads/Commands/EditorCommand.cs | 2 +- .../Notepads/Commands/FindModelCommand.cs | 2 +- .../Commands/FindModelCommandUsage.cs | 2 +- .../Commands/NewFileCommand.cs} | 16 +- SharpPad/Notepads/Commands/NotepadCommand.cs | 101 +-- .../Notepads/Commands/OpenFilesCommand.cs | 96 ++ .../Commands/SaveAllDocumentsCommand.cs} | 17 +- .../Contexts/EditMenuContextGenerator.cs | 2 +- .../Contexts/NotepadTabContextGenerator.cs | 2 +- .../Controls/FindAndReplaceControl.cs | 2 +- .../Notepads/Controls/INotepadEditorUI.cs | 2 +- SharpPad/Notepads/Controls/INotepadTabUI.cs | 2 +- .../Notepads/Controls/NotepadControls.xaml | 6 +- .../Notepads/Controls/NotepadEditorControl.cs | 112 +-- .../Notepads/Controls/NotepadTabControl.cs | 2 +- SharpPad/Notepads/Controls/NotepadTabItem.cs | 2 +- .../SearchResultBackgroundRenderer.cs | 87 ++ .../SearchResultColorizingTransformer.cs | 65 ++ SharpPad/Notepads/FindAndReplaceModel.cs | 2 +- SharpPad/Notepads/Notepad.cs | 58 +- SharpPad/Notepads/NotepadDocument.cs | 2 +- SharpPad/Notepads/NotepadDropRegistry.cs | 2 +- SharpPad/Notepads/NotepadEditor.cs | 14 +- .../Serialisation/SerialisationRegistry.cs | 292 ------ SharpPad/Notepads/Views/NotepadWindow.xaml.cs | 20 +- SharpPad/Properties/Settings.Designer.cs | 34 +- SharpPad/Properties/Settings.settings | 15 +- SharpPad/RBC/BinaryUtils.cs | 213 ----- .../RBC/Events/ReadFromRBEEventHandler.cs | 22 - SharpPad/RBC/Events/WriteToRBEEventHandler.cs | 22 - SharpPad/RBC/RBEArray.cs | 425 --------- SharpPad/RBC/RBEBase.cs | 210 ----- SharpPad/RBC/RBEDictionary.cs | 361 -------- SharpPad/RBC/RBEGuid.cs | 48 - SharpPad/RBC/RBEList.cs | 313 ------- SharpPad/RBC/RBEPrimitive.cs | 256 ------ SharpPad/RBC/RBEStruct.cs | 110 --- SharpPad/RBC/RBEType.cs | 48 - SharpPad/RBC/RBEUtils.cs | 156 ---- SharpPad/SharpPad.csproj | 116 +-- .../Managing/ShortcutInputManager.cs | 5 +- SharpPad/Shortcuts/WPF/ShortcutStyles.xaml | 36 + .../WPF/ShortcutToolTip.cs} | 9 +- .../Shortcuts/WPF/ShortcutTooltipService.cs | 61 ++ SharpPad/Tasks/ActivityDialog.xaml.cs | 2 +- SharpPad/Tasks/ActivityTask.cs | 2 +- SharpPad/Tasks/CompletionRange.cs | 2 +- SharpPad/Tasks/DefaultProgressTracker.cs | 2 +- SharpPad/Tasks/EmptyActivityProgress.cs | 2 +- SharpPad/Tasks/IActivityProgress.cs | 2 +- SharpPad/Tasks/StandardActivityControl.cs | 2 +- SharpPad/Tasks/TaskManager.cs | 2 +- SharpPad/Themes/ControlColours.xaml | 4 +- SharpPad/Themes/Controls.xaml | 31 +- SharpPad/Utils/Accessing/ValueAccessors.cs | 2 +- SharpPad/Utils/AsyncLock.cs | 114 --- SharpPad/Utils/AudioUtils.cs | 30 - SharpPad/Utils/BinarySearch.cs | 2 +- SharpPad/Utils/CASLock.cs | 91 -- SharpPad/Utils/CollectionUtils.cs | 22 + .../AdvancedReadOnlyObservableCollection.cs | 92 -- SharpPad/Utils/Colour.cs | 39 - SharpPad/Utils/CursorUtils.cs | 105 --- SharpPad/Utils/DispatcherCallback.cs | 58 -- SharpPad/Utils/Disposable/DisposableRef.cs | 156 ---- SharpPad/Utils/DoubleUtils.cs | 6 +- SharpPad/Utils/DynamicCodeConverter.cs | 111 --- SharpPad/Utils/EventArgsCache.cs | 30 - SharpPad/Utils/ExceptionEventHandler.cs | 2 +- .../Expressions/BasicNumericExpression.cs | 109 --- .../Expressions/ComplexNumericExpression.cs | 416 --------- SharpPad/Utils/FFmpegError.cs | 43 - SharpPad/Utils/FileUtils.cs | 78 -- SharpPad/Utils/HandlerList.cs | 68 -- SharpPad/Utils/Helper.cs | 57 -- SharpPad/Utils/IDisplayName.cs | 34 - SharpPad/Utils/IRenameTarget.cs | 33 - SharpPad/Utils/InheritedProperty.cs | 79 -- SharpPad/Utils/IntRect.cs | 42 - SharpPad/Utils/ItemCacheStack.cs | 67 -- SharpPad/Utils/Lang.cs | 34 - SharpPad/Utils/NumberAverager.cs | 49 - SharpPad/Utils/Numerics/SizeI.cs | 57 -- SharpPad/Utils/Numerics/Vector2i.cs | 57 -- SharpPad/Utils/Periodic.cs | 32 - SharpPad/Utils/PrecisionTimer.cs | 156 ---- SharpPad/Utils/RDA/IDispatchAction.cs | 2 +- SharpPad/Utils/RandomUtils.cs | 123 --- SharpPad/Utils/Rect2d.cs | 123 --- SharpPad/Utils/Rect2i.cs | 119 --- SharpPad/Utils/Reference.cs | 39 - SharpPad/Utils/ReferenceEqualityComparer.cs | 34 - .../ResourceDictionaryDataTemplateSelector.cs | 39 - SharpPad/Utils/ScrollerUtils.cs | 55 -- SharpPad/Utils/SelectionType.cs | 29 - SharpPad/Utils/ShellIconSize.cs | 26 - SharpPad/Utils/ShellUtils.cs | 105 --- SharpPad/Utils/TemplateUtils.cs | 2 +- SharpPad/Utils/TextIncrement.cs | 208 ----- SharpPad/Utils/TextPointerUtils.cs | 40 - SharpPad/Utils/TextUtils.cs | 50 -- SharpPad/Utils/UIUtils.cs | 98 -- SharpPad/Utils/Validate.cs | 2 +- SharpPad/Utils/Vec2i.cs | 57 -- .../VisualAncestorChangedEventInterface.cs | 2 +- SharpPad/Views/WindowEx.cs | 3 +- SharpPad/packages.config | 1 - 176 files changed, 668 insertions(+), 11609 deletions(-) delete mode 100644 SharpPad/AttachedProperties/AttachedInteractivity.cs delete mode 100644 SharpPad/AttachedProperties/MultiSelectorHelper.cs delete mode 100644 SharpPad/AttachedProperties/ObservableSelectionHelper.cs delete mode 100644 SharpPad/Controls/Dragger/EditCompletedEventHandler.cs delete mode 100644 SharpPad/Controls/Dragger/EditStartEventHandler.cs delete mode 100644 SharpPad/Controls/Dragger/HorizontalIncrement.cs delete mode 100644 SharpPad/Controls/Dragger/IChangeMapper.cs delete mode 100644 SharpPad/Controls/Dragger/IValueFormatter.cs delete mode 100644 SharpPad/Controls/Dragger/IValuePreProcessor.cs delete mode 100644 SharpPad/Controls/Dragger/NumberDragger.cs delete mode 100644 SharpPad/Controls/Dragger/NumberDraggerStyles.xaml delete mode 100644 SharpPad/Controls/Dragger/UnitToPercentFormatter.cs delete mode 100644 SharpPad/Controls/Dragger/ValueStringFormatter.cs delete mode 100644 SharpPad/Controls/Dragger/VerticalIncrement.cs delete mode 100644 SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewAutomationPeer.cs delete mode 100644 SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewItemAutomationPeer.cs delete mode 100644 SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewItemDataAutomationPeer.cs delete mode 100644 SharpPad/Controls/TreeViews/Controls/BorderSelectionLogic.cs delete mode 100644 SharpPad/Controls/TreeViews/Controls/EditTextBox.cs delete mode 100644 SharpPad/Controls/TreeViews/Controls/FocusHelper.cs delete mode 100644 SharpPad/Controls/TreeViews/Controls/ISelectionStrategy.cs delete mode 100644 SharpPad/Controls/TreeViews/Controls/ListUtils.cs delete mode 100644 SharpPad/Controls/TreeViews/Controls/MultiSelectTreeView.cs delete mode 100644 SharpPad/Controls/TreeViews/Controls/MultiSelectTreeViewItem.cs delete mode 100644 SharpPad/Controls/TreeViews/Controls/SelectionMultiple.cs delete mode 100644 SharpPad/Controls/TreeViews/SOURCECODE.txt delete mode 100644 SharpPad/Controls/TreeViews/Themes/EditTextBox.xaml delete mode 100644 SharpPad/Controls/TreeViews/Themes/MultiSelectTreeView.Aero2.xaml delete mode 100644 SharpPad/Controls/TreeViews/Themes/MultiSelectTreeViewItem.Aero2.xaml delete mode 100644 SharpPad/Converters/BoolConverterAND.cs delete mode 100644 SharpPad/Converters/DbToVolumeConverter.cs delete mode 100644 SharpPad/Converters/VolumeToDbConverter.cs rename SharpPad/{Controls/Dragger/EditCompletedEventArgs.cs => Notepads/Commands/NewFileCommand.cs} (60%) create mode 100644 SharpPad/Notepads/Commands/OpenFilesCommand.cs rename SharpPad/{Utils/KBUtils.cs => Notepads/Commands/SaveAllDocumentsCommand.cs} (55%) create mode 100644 SharpPad/Notepads/Controls/SearchResultBackgroundRenderer.cs create mode 100644 SharpPad/Notepads/Controls/SearchResultColorizingTransformer.cs delete mode 100644 SharpPad/Notepads/Serialisation/SerialisationRegistry.cs delete mode 100644 SharpPad/RBC/BinaryUtils.cs delete mode 100644 SharpPad/RBC/Events/ReadFromRBEEventHandler.cs delete mode 100644 SharpPad/RBC/Events/WriteToRBEEventHandler.cs delete mode 100644 SharpPad/RBC/RBEArray.cs delete mode 100644 SharpPad/RBC/RBEBase.cs delete mode 100644 SharpPad/RBC/RBEDictionary.cs delete mode 100644 SharpPad/RBC/RBEGuid.cs delete mode 100644 SharpPad/RBC/RBEList.cs delete mode 100644 SharpPad/RBC/RBEPrimitive.cs delete mode 100644 SharpPad/RBC/RBEStruct.cs delete mode 100644 SharpPad/RBC/RBEType.cs delete mode 100644 SharpPad/RBC/RBEUtils.cs create mode 100644 SharpPad/Shortcuts/WPF/ShortcutStyles.xaml rename SharpPad/{Controls/Dragger/EditStartEventArgs.cs => Shortcuts/WPF/ShortcutToolTip.cs} (72%) create mode 100644 SharpPad/Shortcuts/WPF/ShortcutTooltipService.cs delete mode 100644 SharpPad/Utils/AsyncLock.cs delete mode 100644 SharpPad/Utils/AudioUtils.cs delete mode 100644 SharpPad/Utils/CASLock.cs delete mode 100644 SharpPad/Utils/Collections/AdvancedReadOnlyObservableCollection.cs delete mode 100644 SharpPad/Utils/Colour.cs delete mode 100644 SharpPad/Utils/CursorUtils.cs delete mode 100644 SharpPad/Utils/DispatcherCallback.cs delete mode 100644 SharpPad/Utils/Disposable/DisposableRef.cs delete mode 100644 SharpPad/Utils/DynamicCodeConverter.cs delete mode 100644 SharpPad/Utils/EventArgsCache.cs delete mode 100644 SharpPad/Utils/Expressions/BasicNumericExpression.cs delete mode 100644 SharpPad/Utils/Expressions/ComplexNumericExpression.cs delete mode 100644 SharpPad/Utils/FFmpegError.cs delete mode 100644 SharpPad/Utils/FileUtils.cs delete mode 100644 SharpPad/Utils/HandlerList.cs delete mode 100644 SharpPad/Utils/Helper.cs delete mode 100644 SharpPad/Utils/IDisplayName.cs delete mode 100644 SharpPad/Utils/IRenameTarget.cs delete mode 100644 SharpPad/Utils/InheritedProperty.cs delete mode 100644 SharpPad/Utils/IntRect.cs delete mode 100644 SharpPad/Utils/ItemCacheStack.cs delete mode 100644 SharpPad/Utils/Lang.cs delete mode 100644 SharpPad/Utils/NumberAverager.cs delete mode 100644 SharpPad/Utils/Numerics/SizeI.cs delete mode 100644 SharpPad/Utils/Numerics/Vector2i.cs delete mode 100644 SharpPad/Utils/Periodic.cs delete mode 100644 SharpPad/Utils/PrecisionTimer.cs delete mode 100644 SharpPad/Utils/RandomUtils.cs delete mode 100644 SharpPad/Utils/Rect2d.cs delete mode 100644 SharpPad/Utils/Rect2i.cs delete mode 100644 SharpPad/Utils/Reference.cs delete mode 100644 SharpPad/Utils/ReferenceEqualityComparer.cs delete mode 100644 SharpPad/Utils/ResourceDictionaryDataTemplateSelector.cs delete mode 100644 SharpPad/Utils/ScrollerUtils.cs delete mode 100644 SharpPad/Utils/SelectionType.cs delete mode 100644 SharpPad/Utils/ShellIconSize.cs delete mode 100644 SharpPad/Utils/ShellUtils.cs delete mode 100644 SharpPad/Utils/TextIncrement.cs delete mode 100644 SharpPad/Utils/TextPointerUtils.cs delete mode 100644 SharpPad/Utils/TextUtils.cs delete mode 100644 SharpPad/Utils/UIUtils.cs delete mode 100644 SharpPad/Utils/Vec2i.cs diff --git a/SharpPad.sln b/SharpPad.sln index 12ac9ae..2bc2a4d 100644 --- a/SharpPad.sln +++ b/SharpPad.sln @@ -7,18 +7,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpPad", "SharpPad\SharpP EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 - Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DCFF60C3-0984-4579-B730-217FB8AAFF78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DCFF60C3-0984-4579-B730-217FB8AAFF78}.Debug|Any CPU.Build.0 = Debug|Any CPU {DCFF60C3-0984-4579-B730-217FB8AAFF78}.Debug|x64.ActiveCfg = Debug|x64 {DCFF60C3-0984-4579-B730-217FB8AAFF78}.Debug|x64.Build.0 = Debug|x64 - {DCFF60C3-0984-4579-B730-217FB8AAFF78}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DCFF60C3-0984-4579-B730-217FB8AAFF78}.Release|Any CPU.Build.0 = Release|Any CPU {DCFF60C3-0984-4579-B730-217FB8AAFF78}.Release|x64.ActiveCfg = Release|x64 {DCFF60C3-0984-4579-B730-217FB8AAFF78}.Release|x64.Build.0 = Release|x64 EndGlobalSection diff --git a/SharpPad/AdvancedMenuService/MenuService.cs b/SharpPad/AdvancedMenuService/MenuService.cs index e29beb0..7276a2f 100644 --- a/SharpPad/AdvancedMenuService/MenuService.cs +++ b/SharpPad/AdvancedMenuService/MenuService.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/AdvancedMenuService/RegularMenuService/AdvancedMenuItem.cs b/SharpPad/AdvancedMenuService/RegularMenuService/AdvancedMenuItem.cs index 1252017..1c9139e 100644 --- a/SharpPad/AdvancedMenuService/RegularMenuService/AdvancedMenuItem.cs +++ b/SharpPad/AdvancedMenuService/RegularMenuService/AdvancedMenuItem.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/AdvancedMenuService/RegularMenuService/AdvancedRegularMenu.cs b/SharpPad/AdvancedMenuService/RegularMenuService/AdvancedRegularMenu.cs index b907368..d075f06 100644 --- a/SharpPad/AdvancedMenuService/RegularMenuService/AdvancedRegularMenu.cs +++ b/SharpPad/AdvancedMenuService/RegularMenuService/AdvancedRegularMenu.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/App.config b/SharpPad/App.config index 193aecc..dbb0630 100644 --- a/SharpPad/App.config +++ b/SharpPad/App.config @@ -1,6 +1,21 @@  + + +
+ + + + + + 600 + + + 600 + + + \ No newline at end of file diff --git a/SharpPad/App.xaml b/SharpPad/App.xaml index 44136ff..ff75e61 100644 --- a/SharpPad/App.xaml +++ b/SharpPad/App.xaml @@ -12,16 +12,11 @@ + - - - - - - diff --git a/SharpPad/App.xaml.cs b/SharpPad/App.xaml.cs index 644c7cc..df9afa9 100644 --- a/SharpPad/App.xaml.cs +++ b/SharpPad/App.xaml.cs @@ -8,6 +8,7 @@ using SharpPad.Logger; using SharpPad.Notepads; using SharpPad.Notepads.Views; +using SharpPad.Properties; using SharpPad.Services.Messages; using SharpPad.Shortcuts.Managing; using SharpPad.Shortcuts.WPF; @@ -61,7 +62,18 @@ private void App_OnStartup(object sender, StartupEventArgs args) { // Notepad init Notepad notepad = new Notepad(); - NotepadWindow window = new NotepadWindow(); + int prefWidth = Settings.Default.NotepadWindowWidth; + if (prefWidth < 20) + prefWidth = 600; + + int prefHeight = Settings.Default.NotepadWindowHeight; + if (prefHeight < 20) + prefHeight = 600; + + NotepadWindow window = new NotepadWindow() { + Width = prefWidth, Height = prefHeight + }; + window.Show(); window.Notepad = notepad; this.MainWindow = window; diff --git a/SharpPad/ApplicationCore.cs b/SharpPad/ApplicationCore.cs index 38ee29a..152bb81 100644 --- a/SharpPad/ApplicationCore.cs +++ b/SharpPad/ApplicationCore.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.Diagnostics; using System.IO; using System.Threading.Tasks; @@ -27,6 +28,7 @@ using SharpPad.Logger; using SharpPad.Notepads; using SharpPad.Notepads.Commands; +using SharpPad.Properties; using SharpPad.Services.Files; using SharpPad.Services.Messages; using SharpPad.Services.WPF.Files; @@ -79,9 +81,11 @@ public void OnApplicationLoaded(Notepad notepad, string[] args) { if (args.Length > 0 && File.Exists(args[0])) { OpenFilesCommand.OpenFile(notepad, args[0]); } - else if (Debugger.IsAttached) { - this.LoadDefaultNotepad(); - + else { +#if DEBUG + this.Nodepad.AddNewEditorForDocument(new NotepadDocument() {DocumentName = "New Document 1", Document = {Text = ""}, IsModified = false}); + this.Nodepad.AddNewEditorForDocument(new NotepadDocument() {DocumentName = "New Document 2", Document = {Text = "some text here"}, IsModified = false}); + this.Nodepad.AddNewEditorForDocument(new NotepadDocument() {DocumentName = "New Document 3", Document = {Text = "some more text 111"}, IsModified = false}); TaskManager.Instance.RunTask(async () => { IActivityProgress prog = TaskManager.Instance.CurrentTask.Progress; prog.Text = "Dummy task"; @@ -96,15 +100,14 @@ public void OnApplicationLoaded(Notepad notepad, string[] args) { prog.OnProgress(progressPerUpdate); } }); +#else + this.Nodepad.AddNewEditorForDocument(new NotepadDocument() {DocumentName = "Document 1", Document = {Text = ""}, IsModified = false}); +#endif } } - public void OnApplicationExiting() { } - - private void LoadDefaultNotepad() { - this.Nodepad.AddNewEditor(new NotepadDocument() {DocumentName = "New Document 1", Document = {Text = ""}, IsModified = false}); - this.Nodepad.AddNewEditor(new NotepadDocument() {DocumentName = "New Document 2", Document = {Text = "some text here"}, IsModified = false}); - this.Nodepad.AddNewEditor(new NotepadDocument() {DocumentName = "New Document 3", Document = {Text = "some more text 111"}, IsModified = false}); + public void OnApplicationExiting() { + Settings.Default.Save(); } public void RegisterActions(CommandManager manager) { diff --git a/SharpPad/AsyncDemo.cs b/SharpPad/AsyncDemo.cs index 48eb4d0..cb0e33d 100644 --- a/SharpPad/AsyncDemo.cs +++ b/SharpPad/AsyncDemo.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/AttachedProperties/AttachedInteractivity.cs b/SharpPad/AttachedProperties/AttachedInteractivity.cs deleted file mode 100644 index 14d78c9..0000000 --- a/SharpPad/AttachedProperties/AttachedInteractivity.cs +++ /dev/null @@ -1,96 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using SharpPad.Utils; - -namespace SharpPad.AttachedProperties { - public static class AttachedInteractivity { - public static readonly DependencyProperty DoubleClickCommandProperty = DependencyProperty.RegisterAttached("DoubleClickCommand", typeof(ICommand), typeof(AttachedInteractivity), new PropertyMetadata(null, OnDoubleClickCommandChanged)); - public static readonly DependencyProperty UseICGForParameterProperty = DependencyProperty.RegisterAttached("UseICGForParameter", typeof(bool), typeof(AttachedInteractivity), new PropertyMetadata(BoolBox.False)); - public static readonly DependencyProperty UseDataContextAsParameterProperty = DependencyProperty.RegisterAttached("UseDataContextAsParameter", typeof(bool), typeof(AttachedInteractivity), new PropertyMetadata(BoolBox.True)); - - public static void SetDoubleClickCommand(DependencyObject element, ICommand value) => element.SetValue(DoubleClickCommandProperty, value); - public static ICommand GetDoubleClickCommand(DependencyObject element) => (ICommand) element.GetValue(DoubleClickCommandProperty); - - public static void SetUseICGForParameter(DependencyObject element, bool value) => element.SetValue(UseICGForParameterProperty, value.Box()); - public static bool GetUseICGForParameter(DependencyObject element) => (bool) element.GetValue(UseICGForParameterProperty); - - public static void SetUseDataContextAsParameter(DependencyObject element, bool value) => element.SetValue(UseDataContextAsParameterProperty, value.Box()); - public static bool GetUseDataContextAsParameter(DependencyObject element) => (bool) element.GetValue(UseDataContextAsParameterProperty); - - private static readonly MouseButtonEventHandler Handler = ControlOnMouseDown; - - private static void OnDoubleClickCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - if (d is UIElement control) { - // if (e.OldValue != null) { - // int index = 0; - // foreach (InputBinding item in control.InputBindings) { - // if (item.Command == e.OldValue) { - // control.InputBindings.RemoveAt(index); - // break; - // } - // index++; - // } - // } - // control.InputBindings.Add(new MouseBinding((ICommand) e.NewValue, new MouseGesture(MouseAction.LeftDoubleClick))); - control.PreviewMouseDown -= Handler; - if (e.NewValue != null) { - control.PreviewMouseDown += Handler; - } - } - } - - private static void ControlOnMouseDown(object sender, MouseButtonEventArgs e) { - if (e.ClickCount == 2 && sender is UIElement control) { - object parameter; - ICommand command = GetDoubleClickCommand(control); - if (command != null && command.CanExecute(parameter = GetParamForCommand(control))) { - command.Execute(parameter); - e.Handled = true; - } - } - } - - private static object GetParamForCommand(DependencyObject control) { - FrameworkElement element = control as FrameworkElement; - object result = null; - if (element != null && GetUseICGForParameter(control)) { - if (element.Parent is ItemsControl x1) { - result = x1.ItemContainerGenerator.ItemFromContainer(control); - } - else if (element.TemplatedParent is ItemsControl x2) { - result = x2.ItemContainerGenerator.ItemFromContainer(control); - } - } - - if (result == DependencyProperty.UnsetValue) { - result = null; - } - - if (result != null) { - return result; - } - - return GetUseDataContextAsParameter(control) ? element?.DataContext : null; - } - } -} \ No newline at end of file diff --git a/SharpPad/AttachedProperties/MultiSelectorHelper.cs b/SharpPad/AttachedProperties/MultiSelectorHelper.cs deleted file mode 100644 index 2db0244..0000000 --- a/SharpPad/AttachedProperties/MultiSelectorHelper.cs +++ /dev/null @@ -1,220 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System.Collections; -using System.Collections.Specialized; -using System.Diagnostics; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using SharpPad.Utils; - -namespace SharpPad.AttachedProperties { - /// - /// A helper class for binding selected item collections. - /// - /// When using observable collections, you just bind it directly and then handle the collection changed events in your code - /// - /// - /// When using a normal list (that does not implement ), ensure the list is non-null at - /// all times. This class will set the list property (when the selected items change) to the same instance, - /// allowing you to handle the change in your property's setter - /// - /// - public static class MultiSelectorHelper { - public static readonly DependencyProperty SelectedItemsProperty = - DependencyProperty.RegisterAttached( - "SelectedItems", - typeof(IList), - typeof(MultiSelectorHelper), - new FrameworkPropertyMetadata(null, OnSelectedItemsChanged)); - - public static readonly DependencyProperty UpdatePropertyOnSelectionChangedProperty = - DependencyProperty.RegisterAttached( - "UpdatePropertyOnSelectionChanged", - typeof(bool), - typeof(MultiSelectorHelper), - new PropertyMetadata(BoolBox.True, (d, e) => AutoRegisterSelectionChangedHandler(d))); - - private static readonly DependencyPropertyKey UpdatingSelectionProperty = - DependencyProperty.RegisterAttachedReadOnly( - "UpdatingSelection", - typeof(bool), - typeof(MultiSelectorHelper), - new PropertyMetadata(BoolBox.False)); - - private static readonly DependencyProperty IsSelectionChangedRegisteredProperty = - DependencyProperty.RegisterAttached( - "IsSelectionChangedRegistered", - typeof(bool), - typeof(MultiSelectorHelper), - new PropertyMetadata(BoolBox.False)); - - public static IList GetSelectedItems(DependencyObject obj) => (IList) obj.GetValue(SelectedItemsProperty); - - public static void SetSelectedItems(DependencyObject obj, IList value) => obj.SetValue(SelectedItemsProperty, value); - - public static bool GetUpdatePropertyOnSelectionChanged(DependencyObject obj) => (bool) obj.GetValue(UpdatePropertyOnSelectionChangedProperty); - - public static void SetUpdatePropertyOnSelectionChanged(DependencyObject obj, bool value) => obj.SetValue(UpdatePropertyOnSelectionChangedProperty, value.Box()); - - private static void SetUpdatingSelection(DependencyObject element, bool value) => element.SetValue(UpdatingSelectionProperty, value.Box()); - private static bool IsUpdatingSelection(DependencyObject element) => (bool) element.GetValue(UpdatingSelectionProperty.DependencyProperty); - - private static void SetIsSelectionChangedRegistered(DependencyObject element, bool value) => element.SetValue(IsSelectionChangedRegisteredProperty, value); - - private static bool GetIsSelectionChangedRegistered(DependencyObject element) => (bool) element.GetValue(IsSelectionChangedRegisteredProperty); - - private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - if (IsUpdatingSelection(d)) { - return; - } - - if (d is Selector) { - IList newList = (IList) e.NewValue; - SetUpdatingSelection(d, true); - try { - IList list = null; - if (d is ListBox box) { - if (box.SelectionMode != SelectionMode.Single) - list = box.SelectedItems; - } - else if (d is MultiSelector ms) { - list = ms.SelectedItems; - } - - if (list != null) { - list.Clear(); - if (newList != null && newList.Count > 0) { - foreach (object item in newList) { - list.Add(item); - } - } - } - } - finally { - SetUpdatingSelection(d, false); - } - } - - AutoRegisterSelectionChangedHandler(d); - } - - private static void AutoRegisterSelectionChangedHandler(DependencyObject d) { - if (d is Selector s) { - if (GetIsSelectionChangedRegistered(d)) { - if (!GetUpdatePropertyOnSelectionChanged(d)) { - SetIsSelectionChangedRegistered(d, false); - s.SelectionChanged -= OnUISelectionChanged; - } - } - else if (GetUpdatePropertyOnSelectionChanged(d)) { - SetIsSelectionChangedRegistered(d, true); - s.SelectionChanged += OnUISelectionChanged; - } - } - } - - private static bool ListEquals(IList a, IList b) { - int cA = a.Count, cB = b.Count; - if (cA != cB) { - return false; - } - - for (int i = 0; i < cA; i++) { - if (!ReferenceEquals(a[i], b[i])) { - return false; - } - } - - return true; - } - - private static void OnUISelectionChanged(object sender, SelectionChangedEventArgs e) { - if (sender is Selector selector) { - if (IsUpdatingSelection(selector)) - return; - - IList dstList = GetSelectedItems(selector); - if (dstList == null) - return; - - bool update = false; - try { - IList srcList; - switch (selector) { - case ListBox lb when lb.SelectionMode != SelectionMode.Single: - srcList = lb.SelectedItems; - break; - case MultiSelector ms: - srcList = ms.SelectedItems; - break; - default: - srcList = null; - break; - } - - if (srcList != null) { - // Can massively improve performance for property pages - if (ListEquals(srcList, dstList)) { - return; - } - - SetUpdatingSelection(selector, update = true); - if (srcList.Count < 2) { - // most likely more efficient to clear and add a possible single selection - dstList.Clear(); - foreach (object item in srcList) - dstList.Add(item); - } - else { - int expected = dstList.Count - e.RemovedItems.Count + e.AddedItems.Count; - if (expected == srcList.Count) { - foreach (object o in e.RemovedItems) - dstList.Remove(o); - foreach (object o in e.AddedItems) - dstList.Add(o); - } - else { - Debug.WriteLine($"Selection discrepancy: Expected {expected} selected items in the source list, but got {srcList.Count}"); - dstList.Clear(); - foreach (object item in srcList) - dstList.Add(item); - } - } - } - else { - SetUpdatingSelection(selector, update = true); - foreach (object o in e.RemovedItems) - dstList.Remove(o); - foreach (object o in e.AddedItems) - dstList.Add(o); - } - - if (!(dstList is INotifyCollectionChanged)) - SetSelectedItems(selector, dstList); - } - finally { - if (update) - SetUpdatingSelection(selector, false); - } - } - } - } -} \ No newline at end of file diff --git a/SharpPad/AttachedProperties/ObservableSelectionHelper.cs b/SharpPad/AttachedProperties/ObservableSelectionHelper.cs deleted file mode 100644 index 4ce2cd7..0000000 --- a/SharpPad/AttachedProperties/ObservableSelectionHelper.cs +++ /dev/null @@ -1,504 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using SharpPad.Utils; - -namespace SharpPad.AttachedProperties { - /// - /// A helper class for binding a source collection (typically an observable collection) to a target's selected items, - /// with support for two-way selection change. The source list must implement - /// - public static class ObservableSelectionHelper { - private static readonly Dictionary> SourceToTargetMap; - private static readonly object SourceToTargetLock; - private static readonly List ActiveUpdateList; - private static readonly NotifyCollectionChangedEventHandler CachedSourceCollectionChanged = OnSourceCollectionChanged; - private static readonly SelectionChangedEventHandler CachedTargetCollectionChanged = OnTargetCollectionChanged; - - public static readonly DependencyProperty SelectedItemsProperty = - DependencyProperty.RegisterAttached( - "SelectedItems", - typeof(IList), - typeof(ObservableSelectionHelper), - new FrameworkPropertyMetadata(null, OnSelectedItemsChanged)); - - // I'm using this instead of removing the CollectionChanged handler of the source list, - // because that operations is more intensive as it has to scan the multimap of weak references - - private static readonly DependencyPropertyKey IsProcessingSourceSelectionChangedPropertyKey = - DependencyProperty.RegisterAttachedReadOnly( - "IsProcessingSourceSelectionChanged", - typeof(bool), - typeof(ObservableSelectionHelper), - new FrameworkPropertyMetadata(BoolBox.False, FrameworkPropertyMetadataOptions.NotDataBindable)); - - private static readonly DependencyProperty IsProcessingSourceSelectionChangedProperty; - - static ObservableSelectionHelper() { - SourceToTargetMap = new Dictionary>(); - SourceToTargetLock = new object(); - IsProcessingSourceSelectionChangedProperty = IsProcessingSourceSelectionChangedPropertyKey.DependencyProperty; - ActiveUpdateList = new List(); - } - - private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - if (ReferenceEquals(e.OldValue, e.NewValue)) { - return; - } - - Selector target; - IList targetList; - if (d is ListBox lb) { - if (lb.SelectionMode == SelectionMode.Single) - throw new Exception("List box's SelectionMode is not set to multiple or extended"); - targetList = lb.SelectedItems; - target = lb; - } - else if (d is MultiSelector ms) { - targetList = ms.SelectedItems; - target = ms; - } - else { - throw new Exception("Unsupported target type: " + d.GetType()); - } - - IList sourceList = (IList) e.NewValue; - if (sourceList != null && !(sourceList is INotifyCollectionChanged)) { - throw new Exception($"Source collection must implement " + nameof(INotifyCollectionChanged)); - } - - // Fun fact: Selector uses an observable collection for the internal SelectedItems property - - if (e.OldValue != null) { - UpdateSourceEventHandler(target, e.OldValue, false); - UpdateTargetEventHandler(target, false); - } - - if (sourceList != null) { - if (!ListEquals(targetList, sourceList)) { - targetList.Clear(); - foreach (object item in sourceList) { - targetList.Add(item); - } - } - - UpdateSourceEventHandler(target, sourceList, true); - UpdateTargetEventHandler(target, true); - } - } - - private static void UpdateTargetEventHandler(DependencyObject target, bool connect) { - if (connect) { - ((Selector) target).SelectionChanged += CachedTargetCollectionChanged; - } - else { - ((Selector) target).SelectionChanged -= CachedTargetCollectionChanged; - } - } - - private static void UpdateSourceEventHandler(DependencyObject target, object sourceList, bool connect) { - if (target == null) - throw new ArgumentNullException(nameof(target)); - - lock (SourceToTargetLock) { - if (connect) { - ((INotifyCollectionChanged) sourceList).CollectionChanged += CachedSourceCollectionChanged; - AddTargetHandler(target, sourceList); - } - else { - ((INotifyCollectionChanged) sourceList).CollectionChanged -= CachedSourceCollectionChanged; - RemoveTargetHandler(target, sourceList); - } - } - } - - private static void RemoveTargetHandler(DependencyObject target, object sourceList) { - if (SourceToTargetMap.TryGetValue(sourceList, out List references)) { - for (int i = references.Count - 1; i >= 0; i--) { - object referenceTarget = references[i].Target; - if (ReferenceEquals(referenceTarget, target)) { - references.RemoveAt(i); - } - } - } - } - - private static void AddTargetHandler(DependencyObject target, object sourceList) { - if (!SourceToTargetMap.TryGetValue(sourceList, out List references)) { - SourceToTargetMap[sourceList] = references = new List(); - } - else if (references.Count > 0) { - for (int i = references.Count - 1; i >= 0; i--) { - object referenceTarget = references[i].Target; - if (ReferenceEquals(referenceTarget, target)) { - return; - } - } - } - - references.Add(new WeakReference(target)); - } - - // SOURCE: A data object that typically raises property and data changed notifications - // TARGET: A dependency object (typically), e.g. ListBox - - // A view model's observable collection changed - private static void OnSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - // Here we need to get the binding target(s) from the source - if (!DispatcherUtils.IsOnMainThread()) { - // log just in case the exception gets eaten up by a black hole - throw new Exception("Cannot process observable collection changes while off the main thread"); - } - - IList sourceList = (IList) sender; - for (int i = ActiveUpdateList.Count - 1; i >= 0; i--) { - if (ReferenceEquals(ActiveUpdateList[i], sourceList)) { - return; - } - } - - List targets; - lock (SourceToTargetLock) { - if (!SourceToTargetMap.TryGetValue(sender, out List references) || references.Count < 1) { - return; - } - - targets = null; - for (int i = 0; i < references.Count; i++) { - DependencyObject target = (DependencyObject) references[i].Target; - if (target != null) { - if (!GetIsProcessingSourceSelectionChanged(target)) { - (targets ?? (targets = new List(1))).Add(target); - } - else { - // ooops - } - } - else { - references.RemoveAt(i--); - } - } - } - - if (targets == null) { - return; - } - - // Here, we update the target's selection from the source list - // This would require disconnecting selection changed events - using (ErrorList list = new ErrorList("Failed to handle observable collection changed event", false)) { - foreach (DependencyObject target in targets) { - UpdateTargetEventHandler(target, false); - try { - IList targetList; - if (target is ListBox listBox) { - if (listBox.SelectionMode == SelectionMode.Single) { - if (sourceList.Count < 1) { - listBox.ClearValue(Selector.SelectedItemProperty); - } - else { - object firstNonNull = null; - for (int i = 0; i < sourceList.Count; i++) { - if ((firstNonNull = sourceList[i]) != null) - break; - } - - listBox.SelectedItem = firstNonNull; - } - - continue; - } - else { - targetList = listBox.SelectedItems; - } - } - else if (target is MultiSelector ms) { - targetList = ms.SelectedItems; - } - else { - continue; - } - - // The code below has to handle the cases when the source selected items are not actually - // contained in the target object's items collection, and therefore, adding them to the - // target's selection list wouldn't really do anything anyway. - - // There isn't really an efficient way to do this. WPF internally has helper 'assumption' - // flags which can, but apart from that, you have to lookup each item new selected item - // in the target's items collection to see if they can actually be selected - - // So this code will just do some possible preconditional checks, and if they succeed, - // it lets WPF handle those cases where the items cannot be selected - int targetListCount = targetList.Count; - switch (e.Action) { - case NotifyCollectionChangedAction.Add: { - int index = e.NewStartingIndex; - if (index == -1) { - index = targetListCount; - } - - if (index > targetListCount) { - // weird selection setup; possibly bound a MultiSelectTreeView to a ListBox? - foreach (object item in e.NewItems) { - targetList.Add(item); - } - } - else { - foreach (object item in e.NewItems) { - targetList.Insert(index++, item); - } - } - - break; - } - case NotifyCollectionChangedAction.Remove: { - if (targetListCount < 1) { - break; - } - - int index = e.OldStartingIndex; - foreach (object item in e.OldItems) { - if (index != -1 && index < targetList.Count && ReferenceEquals(targetList[index], item)) { - targetList.RemoveAt(index); - } - else { - targetList.Remove(item); - } - } - - break; - } - case NotifyCollectionChangedAction.Replace: { - if (targetListCount < 1) { - break; - } - - int rmvidx = e.OldStartingIndex; - foreach (object item in e.OldItems) { - if (rmvidx != -1 && rmvidx < targetList.Count && ReferenceEquals(targetList[rmvidx], item)) { - targetList.RemoveAt(rmvidx); - } - else { - targetList.Remove(item); - } - } - - int addidx = e.NewStartingIndex; - foreach (object item in e.NewItems) { - if (addidx != -1 && addidx < targetList.Count) { - targetList.Insert(addidx++, item); - } - else { - targetList.Add(item); - } - } - - break; - } - case NotifyCollectionChangedAction.Move: { - int i = e.OldStartingIndex, j = e.NewStartingIndex; - if (i == -1 || j == -1 || i > targetListCount || j > targetListCount) { - break; - } - - foreach (object item in e.NewItems) { - if (!ReferenceEquals(targetList[i], item)) - Debug.WriteLine("Possibly corrupt selected items for move operation: indices do not match"); - CollectionUtils.MoveItem(targetList, i++, j++); - } - - break; - } - case NotifyCollectionChangedAction.Reset: - if (targetListCount > 0) - targetList.Clear(); - break; - default: throw new ArgumentOutOfRangeException(); - } - } - catch (Exception exception) { - list.Add(new Exception($"Failed to update target selection for '{target}' ({target.GetType()})", exception)); - } - finally { - UpdateTargetEventHandler(target, true); - } - } - - if (list.TryGetException(out Exception error)) { - IoC.MessageService.ShowMessage("Error", "An exception occurred while processing selection change. " + - "This may have corrupted the application in some way, so please restart.\n\n" + - "See the app logs for more info", error.GetToString()); - } - } - } - - // A list box's selection changed - private static void OnTargetCollectionChanged(object sender, SelectionChangedEventArgs e) { - if (!(sender is Selector selector)) { - throw new Exception("Unsupported sender object for SelectionChanged event"); - } - - IList dataSourceList = GetSelectedItems(selector); - if (dataSourceList == null) { - return; - } - - IList selectorList; - if (selector is ListBox list) { - if (list.SelectionMode == SelectionMode.Single) { - object selection = list.SelectedItem; - if (selection != null) { - if (dataSourceList.Count == 1 && Equals(dataSourceList[0], selection)) { - return; - } - } - else if (dataSourceList.Count < 1) { - return; - } - - SetIsProcessingSourceSelectionChanged(selector, dataSourceList, true); - // UpdateSourceEventHandler(selector, dataSourceList, false); - if (dataSourceList.Count > 0) - dataSourceList.Clear(); - if (selection != null) - dataSourceList.Add(selection); - // UpdateSourceEventHandler(selector, dataSourceList, true); - SetIsProcessingSourceSelectionChanged(selector, dataSourceList, false); - return; - } - else { - selectorList = list.SelectedItems; - } - } - else if (selector is MultiSelector ms) { - selectorList = ms.SelectedItems; - } - else { - selectorList = null; - } - - if (selectorList == null) { - SetIsProcessingSourceSelectionChanged(selector, dataSourceList, true); - // UpdateSourceEventHandler(selector, dataSourceList, false); - try { - foreach (object o in e.RemovedItems) - dataSourceList.Remove(o); - foreach (object o in e.AddedItems) - dataSourceList.Add(o); - } - finally { - SetIsProcessingSourceSelectionChanged(selector, dataSourceList, false); - // UpdateSourceEventHandler(selector, dataSourceList, true); - } - - return; - } - - // Can massively improve performance for property pages - if (ListEquals(dataSourceList, selectorList)) { - return; - } - - SetIsProcessingSourceSelectionChanged(selector, dataSourceList, true); - // UpdateSourceEventHandler(selector, dataSourceList, false); - try { - if (selectorList.Count < 2) { - // most likely more efficient to clear and add a possible single selection - if (dataSourceList.Count > 0) - dataSourceList.Clear(); - foreach (object item in selectorList) - dataSourceList.Add(item); - } - else { - int expected = dataSourceList.Count - e.RemovedItems.Count + e.AddedItems.Count; - if (expected == selectorList.Count) { - foreach (object o in e.RemovedItems) - dataSourceList.Remove(o); - foreach (object o in e.AddedItems) - dataSourceList.Add(o); - } - else { - Debug.WriteLine($"Selection discrepancy: Expected {expected} selected items in the source list, but got {selectorList.Count}"); - dataSourceList.Clear(); - foreach (object item in selectorList) - dataSourceList.Add(item); - } - } - } - finally { - SetIsProcessingSourceSelectionChanged(selector, dataSourceList, false); - // UpdateSourceEventHandler(selector, dataSourceList, true); - } - } - - private static bool ListEquals(IList a, IList b) { - int cA = a.Count, cB = b.Count; - if (cA != cB) { - return false; - } - - for (int i = 0; i < cA; i++) { - if (!ReferenceEquals(a[i], b[i])) { - return false; - } - } - - return true; - } - - public static IList GetSelectedItems(DependencyObject obj) { - return (IList) obj.GetValue(SelectedItemsProperty); - } - - public static void SetSelectedItems(DependencyObject obj, IList value) { - obj.SetValue(SelectedItemsProperty, value); - } - - private static void SetIsProcessingSourceSelectionChanged(DependencyObject element, IList sourceList, bool value) { - if (value) { - element.SetValue(IsProcessingSourceSelectionChangedPropertyKey, BoolBox.True); - foreach (IList list in ActiveUpdateList) - if (ReferenceEquals(sourceList, list)) - return; - ActiveUpdateList.Add(sourceList); - } - else { - element.SetValue(IsProcessingSourceSelectionChangedPropertyKey, BoolBox.False); - for (int i = ActiveUpdateList.Count - 1; i >= 0; i--) { - if (ReferenceEquals(sourceList, ActiveUpdateList[i])) { - ActiveUpdateList.RemoveAt(i); - return; - } - } - } - } - - private static bool GetIsProcessingSourceSelectionChanged(DependencyObject element) { - return (bool) element.GetValue(IsProcessingSourceSelectionChangedProperty); - } - } -} \ No newline at end of file diff --git a/SharpPad/Behaviours/Behaviour.cs b/SharpPad/Behaviours/Behaviour.cs index 9be3034..4d4e2ed 100644 --- a/SharpPad/Behaviours/Behaviour.cs +++ b/SharpPad/Behaviours/Behaviour.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Behaviours/BehaviourBase.cs b/SharpPad/Behaviours/BehaviourBase.cs index a339b30..f31ebfa 100644 --- a/SharpPad/Behaviours/BehaviourBase.cs +++ b/SharpPad/Behaviours/BehaviourBase.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Behaviours/BehaviourCollection.cs b/SharpPad/Behaviours/BehaviourCollection.cs index 142b841..a7cb7bc 100644 --- a/SharpPad/Behaviours/BehaviourCollection.cs +++ b/SharpPad/Behaviours/BehaviourCollection.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Behaviours/IBehaviour.cs b/SharpPad/Behaviours/IBehaviour.cs index 87def5f..d48414e 100644 --- a/SharpPad/Behaviours/IBehaviour.cs +++ b/SharpPad/Behaviours/IBehaviour.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Behaviours/Standard/KeyStrokeCommandBehaviour.cs b/SharpPad/Behaviours/Standard/KeyStrokeCommandBehaviour.cs index 4a6774e..403294c 100644 --- a/SharpPad/Behaviours/Standard/KeyStrokeCommandBehaviour.cs +++ b/SharpPad/Behaviours/Standard/KeyStrokeCommandBehaviour.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Behaviours/TestBlueBackgroundBehaviour.cs b/SharpPad/Behaviours/TestBlueBackgroundBehaviour.cs index 7fe4ee5..33020ce 100644 --- a/SharpPad/Behaviours/TestBlueBackgroundBehaviour.cs +++ b/SharpPad/Behaviours/TestBlueBackgroundBehaviour.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/AsyncCommand.cs b/SharpPad/CommandSystem/AsyncCommand.cs index 00a25ca..1e0125e 100644 --- a/SharpPad/CommandSystem/AsyncCommand.cs +++ b/SharpPad/CommandSystem/AsyncCommand.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/CommandGroup.cs b/SharpPad/CommandSystem/CommandGroup.cs index 7171bad..4bb5c49 100644 --- a/SharpPad/CommandSystem/CommandGroup.cs +++ b/SharpPad/CommandSystem/CommandGroup.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/Executability.cs b/SharpPad/CommandSystem/Executability.cs index 8f55d64..f3e8a47 100644 --- a/SharpPad/CommandSystem/Executability.cs +++ b/SharpPad/CommandSystem/Executability.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/SimpleCommandGroup.cs b/SharpPad/CommandSystem/SimpleCommandGroup.cs index 9953d38..1547ce5 100644 --- a/SharpPad/CommandSystem/SimpleCommandGroup.cs +++ b/SharpPad/CommandSystem/SimpleCommandGroup.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/Usages/BaseToggleButtonCommandUsage.cs b/SharpPad/CommandSystem/Usages/BaseToggleButtonCommandUsage.cs index 9f7e129..2dd4ec0 100644 --- a/SharpPad/CommandSystem/Usages/BaseToggleButtonCommandUsage.cs +++ b/SharpPad/CommandSystem/Usages/BaseToggleButtonCommandUsage.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/Usages/BasicButtonCommandUsage.cs b/SharpPad/CommandSystem/Usages/BasicButtonCommandUsage.cs index 11357d4..f2e3ff7 100644 --- a/SharpPad/CommandSystem/Usages/BasicButtonCommandUsage.cs +++ b/SharpPad/CommandSystem/Usages/BasicButtonCommandUsage.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/Usages/CommandSourceCommandUsage.cs b/SharpPad/CommandSystem/Usages/CommandSourceCommandUsage.cs index 5612cc6..24286f2 100644 --- a/SharpPad/CommandSystem/Usages/CommandSourceCommandUsage.cs +++ b/SharpPad/CommandSystem/Usages/CommandSourceCommandUsage.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/Usages/CommandUsage.cs b/SharpPad/CommandSystem/Usages/CommandUsage.cs index 037539d..5960399 100644 --- a/SharpPad/CommandSystem/Usages/CommandUsage.cs +++ b/SharpPad/CommandSystem/Usages/CommandUsage.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/Usages/CommandUsageManager.cs b/SharpPad/CommandSystem/Usages/CommandUsageManager.cs index 71226b0..90c662a 100644 --- a/SharpPad/CommandSystem/Usages/CommandUsageManager.cs +++ b/SharpPad/CommandSystem/Usages/CommandUsageManager.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/CommandSystem/Usages/CommandUsageModelHelper.cs b/SharpPad/CommandSystem/Usages/CommandUsageModelHelper.cs index 27da1a6..c440b71 100644 --- a/SharpPad/CommandSystem/Usages/CommandUsageModelHelper.cs +++ b/SharpPad/CommandSystem/Usages/CommandUsageModelHelper.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Controls/Bindings/Binders.cs b/SharpPad/Controls/Bindings/Binders.cs index d22d3c8..ed10432 100644 --- a/SharpPad/Controls/Bindings/Binders.cs +++ b/SharpPad/Controls/Bindings/Binders.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Controls/Dragger/EditCompletedEventHandler.cs b/SharpPad/Controls/Dragger/EditCompletedEventHandler.cs deleted file mode 100644 index cac290b..0000000 --- a/SharpPad/Controls/Dragger/EditCompletedEventHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.Controls.Dragger { - public delegate void EditCompletedEventHandler(object sender, EditCompletedEventArgs e); -} \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/EditStartEventHandler.cs b/SharpPad/Controls/Dragger/EditStartEventHandler.cs deleted file mode 100644 index 4fa60ee..0000000 --- a/SharpPad/Controls/Dragger/EditStartEventHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.Controls.Dragger { - public delegate void EditStartEventHandler(object sender, EditStartEventArgs e); -} \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/HorizontalIncrement.cs b/SharpPad/Controls/Dragger/HorizontalIncrement.cs deleted file mode 100644 index 2172371..0000000 --- a/SharpPad/Controls/Dragger/HorizontalIncrement.cs +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.Controls.Dragger { - public enum HorizontalIncrement { - /// - /// Decrement the value when dragged left, increment when dragged right (default) - /// - LeftDecrRightIncr, - - /// - /// Increment the value when dragged left, decrement when dragged right - /// - LeftIncrRightDecr - } -} \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/IChangeMapper.cs b/SharpPad/Controls/Dragger/IChangeMapper.cs deleted file mode 100644 index 5f93d12..0000000 --- a/SharpPad/Controls/Dragger/IChangeMapper.cs +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.Controls.Dragger { - public interface IChangeMapper { - void OnValueChanged(double value, out double tiny, out double small, out double normal, out double large); - } -} \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/IValueFormatter.cs b/SharpPad/Controls/Dragger/IValueFormatter.cs deleted file mode 100644 index 3c31213..0000000 --- a/SharpPad/Controls/Dragger/IValueFormatter.cs +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.Controls.Dragger { - public interface IValueFormatter { - string ToString(double value, int? places); - } -} \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/IValuePreProcessor.cs b/SharpPad/Controls/Dragger/IValuePreProcessor.cs deleted file mode 100644 index a213e41..0000000 --- a/SharpPad/Controls/Dragger/IValuePreProcessor.cs +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.Controls.Dragger { - /// - /// An interface for pre-processing a 's value before it is updated - /// - public interface IValuePreProcessor { - double Process(double value, double min, double max); - } -} \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/NumberDragger.cs b/SharpPad/Controls/Dragger/NumberDragger.cs deleted file mode 100644 index c668025..0000000 --- a/SharpPad/Controls/Dragger/NumberDragger.cs +++ /dev/null @@ -1,846 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Data; -using System.Windows.Input; -using SharpPad.Utils; -using SharpPad.Utils.Expressions; - -namespace SharpPad.Controls.Dragger { - [TemplatePart(Name = nameof(PART_HintTextBlock), Type = typeof(TextBlock))] - [TemplatePart(Name = nameof(PART_TextBlock), Type = typeof(TextBlock))] - [TemplatePart(Name = nameof(PART_TextBox), Type = typeof(TextBox))] - public class NumberDragger : RangeBase { - public static readonly DependencyProperty TinyChangeProperty = DependencyProperty.Register("TinyChange", typeof(double), typeof(NumberDragger), new PropertyMetadata(0.001d)); - public static readonly DependencyProperty MassiveChangeProperty = DependencyProperty.Register("MassiveChange", typeof(double), typeof(NumberDragger), new PropertyMetadata(5d)); - protected static readonly DependencyPropertyKey IsDraggingPropertyKey = DependencyProperty.RegisterReadOnly("IsDragging", typeof(bool), typeof(NumberDragger), new PropertyMetadata(BoolBox.False, (d, e) => ((NumberDragger) d).OnIsDraggingChanged((bool) e.OldValue, (bool) e.NewValue))); - public static readonly DependencyProperty IsDraggingProperty = IsDraggingPropertyKey.DependencyProperty; - public static readonly DependencyProperty CompleteEditOnTextBoxLostFocusProperty = DependencyProperty.Register("CompleteEditOnTextBoxLostFocus", typeof(bool?), typeof(NumberDragger), new PropertyMetadata(BoolBox.True)); - public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(NumberDragger), new PropertyMetadata(Orientation.Horizontal, (d, e) => ((NumberDragger) d).OnOrientationChanged((Orientation) e.OldValue, (Orientation) e.NewValue))); - public static readonly DependencyProperty HorizontalIncrementProperty = DependencyProperty.Register("HorizontalIncrement", typeof(HorizontalIncrement), typeof(NumberDragger), new PropertyMetadata(HorizontalIncrement.LeftDecrRightIncr)); - public static readonly DependencyProperty VerticalIncrementProperty = DependencyProperty.Register("VerticalIncrement", typeof(VerticalIncrement), typeof(NumberDragger), new PropertyMetadata(VerticalIncrement.UpDecrDownIncr)); - public static readonly DependencyPropertyKey IsEditingTextBoxPropertyKey = DependencyProperty.RegisterReadOnly("IsEditingTextBox", typeof(bool), typeof(NumberDragger), new PropertyMetadata(BoolBox.False, (d, e) => ((NumberDragger) d).OnIsEditingTextBoxChanged((bool) e.OldValue, (bool) e.NewValue), (d, v) => ((NumberDragger) d).OnCoerceIsEditingTextBox(v))); - public static readonly DependencyProperty IsEditingTextBoxProperty = IsEditingTextBoxPropertyKey.DependencyProperty; - public static readonly DependencyProperty RoundedPlacesProperty = DependencyProperty.Register("RoundedPlaces", typeof(int?), typeof(NumberDragger), new PropertyMetadata(null, (d, e) => ((NumberDragger) d).OnRoundedPlacesChanged((int?) e.OldValue, (int?) e.NewValue))); - public static readonly DependencyProperty PreviewRoundedPlacesProperty = DependencyProperty.Register("PreviewRoundedPlaces", typeof(int?), typeof(NumberDragger), new PropertyMetadata((int?) 4, (d, e) => ((NumberDragger) d).OnPreviewRoundedPlacesChanged((int?) e.OldValue, (int?) e.NewValue))); - public static readonly DependencyProperty PreviewDisplayTextOverrideProperty = DependencyProperty.Register("PreviewDisplayTextOverride", typeof(string), typeof(NumberDragger), new PropertyMetadata(null, (o, args) => ((NumberDragger) o).UpdateText())); - public static readonly DependencyProperty EditingHintProperty = DependencyProperty.Register("EditingHint", typeof(string), typeof(NumberDragger), new PropertyMetadata(null)); - public static readonly DependencyProperty RestoreValueOnCancelProperty = DependencyProperty.Register("RestoreValueOnCancel", typeof(bool), typeof(NumberDragger), new PropertyMetadata(BoolBox.True)); - public static readonly DependencyProperty ChangeMapperProperty = DependencyProperty.Register("ChangeMapper", typeof(IChangeMapper), typeof(NumberDragger), new PropertyMetadata(null)); - public static readonly DependencyProperty ValuePreProcessorProperty = DependencyProperty.Register("ValuePreProcessor", typeof(IValuePreProcessor), typeof(NumberDragger), new PropertyMetadata(null)); - public static readonly DependencyProperty IsDoubleClickToEditProperty = DependencyProperty.Register("IsDoubleClickToEdit", typeof(bool), typeof(NumberDragger), new PropertyMetadata(BoolBox.False)); - public static readonly DependencyProperty ForcedReadOnlyStateProperty = DependencyProperty.Register("ForcedReadOnlyState", typeof(bool?), typeof(NumberDragger), new PropertyMetadata(null)); - public static readonly DependencyProperty PreviewValueFormatterProperty = DependencyProperty.Register("PreviewValueFormatter", typeof(IValueFormatter), typeof(NumberDragger), new PropertyMetadata(null)); - - #region Properties - - /// - /// Gets or sets the tiny-change value. This is added or subtracted when CTRL + SHIFT is pressed - /// - public double TinyChange { - get => (double) this.GetValue(TinyChangeProperty); - set => this.SetValue(TinyChangeProperty, value); - } - - /// - /// Gets or sets the massive change value. This is added or subtracted when CTRL is pressed - /// - public double MassiveChange { - get => (double) this.GetValue(MassiveChangeProperty); - set => this.SetValue(MassiveChangeProperty, value); - } - - public bool IsDragging { - get => (bool) this.GetValue(IsDraggingProperty); - protected set => this.SetValue(IsDraggingPropertyKey, value.Box()); - } - - public bool? CompleteEditOnTextBoxLostFocus { - get => (bool?) this.GetValue(CompleteEditOnTextBoxLostFocusProperty); - set => this.SetValue(CompleteEditOnTextBoxLostFocusProperty, value.BoxNullable()); - } - - public Orientation Orientation { - get => (Orientation) this.GetValue(OrientationProperty); - set => this.SetValue(OrientationProperty, value); - } - - public HorizontalIncrement HorizontalIncrement { - get => (HorizontalIncrement) this.GetValue(HorizontalIncrementProperty); - set => this.SetValue(HorizontalIncrementProperty, value); - } - - public VerticalIncrement VerticalIncrement { - get => (VerticalIncrement) this.GetValue(VerticalIncrementProperty); - set => this.SetValue(VerticalIncrementProperty, value); - } - - public bool IsEditingTextBox { - get => (bool) this.GetValue(IsEditingTextBoxProperty); - protected set => this.SetValue(IsEditingTextBoxPropertyKey, value.Box()); - } - - /// - /// The number of digits to round the actual value to. Set to null to disable rounding. - /// - /// When is bound to non floating point, this value should be ignored - /// - /// - /// However when binding to floating point numbers, this value should ideally be 6 or 7. For doubles, - /// this should be 14 or 15. This is to combat floating point rounding issues, causing the the - /// - /// - public int? RoundedPlaces { - get => (int?) this.GetValue(RoundedPlacesProperty); - set => this.SetValue(RoundedPlacesProperty, value); - } - - /// - /// The number of digits to round the preview value to. Set to null to disable rounding. - /// - /// When is bound to non floating point, this value should be ignored - /// - /// - /// However when binding to floating point numbers, this value should ideally be 6 or 7. For doubles, - /// this should be 14 or 15. This is to combat floating point rounding issues, causing the the - /// - /// - public int? PreviewRoundedPlaces { - get => (int?) this.GetValue(PreviewRoundedPlacesProperty); - set => this.SetValue(PreviewRoundedPlacesProperty, value); - } - - /// - /// Gets or sets a value that is displayed while the value preview is active, instead of displaying the - /// actual value. A text box will still appear if the control is clicked - /// - /// This is only displayed when the value is non-null and not an empty string - /// - /// - public string PreviewDisplayTextOverride { - get => (string) this.GetValue(PreviewDisplayTextOverrideProperty); - set => this.SetValue(PreviewDisplayTextOverrideProperty, value); - } - - /// - /// A piece of text to display overtop of the editor text box when manually editing - /// the value, and there is no text in the text box - /// - public string EditingHint { - get => (string) this.GetValue(EditingHintProperty); - set => this.SetValue(EditingHintProperty, value); - } - - /// - /// Whether or not to restore the value property when the drag is cancelled. Default is true - /// - public bool RestoreValueOnCancel { - get => (bool) this.GetValue(RestoreValueOnCancelProperty); - set => this.SetValue(RestoreValueOnCancelProperty, value.Box()); - } - - public IChangeMapper ChangeMapper { - get => (IChangeMapper) this.GetValue(ChangeMapperProperty); - set => this.SetValue(ChangeMapperProperty, value); - } - - public IValuePreProcessor ValuePreProcessor { - get => (IValuePreProcessor) this.GetValue(ValuePreProcessorProperty); - set => this.SetValue(ValuePreProcessorProperty, value); - } - - public bool IsDoubleClickToEdit { - get => (bool) this.GetValue(IsDoubleClickToEditProperty); - set => this.SetValue(IsDoubleClickToEditProperty, value.Box()); - } - - public IValueFormatter PreviewValueFormatter { - get => (IValueFormatter) this.GetValue(PreviewValueFormatterProperty); - set => this.SetValue(PreviewValueFormatterProperty, value); - } - - public bool? ForcedReadOnlyState { - get => (bool?) this.GetValue(ForcedReadOnlyStateProperty); - set => this.SetValue(ForcedReadOnlyStateProperty, value.BoxNullable()); - } - - public bool IsValueReadOnly { - get { - if (this.GetValue(ForcedReadOnlyStateProperty) is bool forced) - return forced; - - Binding binding; - BindingExpression expression = this.GetBindingExpression(ValueProperty); - if (expression == null || (binding = expression.ParentBinding) == null) - return false; - - switch (binding.Mode) { - case BindingMode.OneWay: - case BindingMode.OneTime: - return true; - default: return false; - } - } - } - - #endregion - - public static readonly RoutedEvent EditStartedEvent = EventManager.RegisterRoutedEvent(nameof(EditStarted), RoutingStrategy.Bubble, typeof(EditStartEventHandler), typeof(NumberDragger)); - public static readonly RoutedEvent EditCompletedEvent = EventManager.RegisterRoutedEvent(nameof(EditCompleted), RoutingStrategy.Bubble, typeof(EditCompletedEventHandler), typeof(NumberDragger)); - - [Category("Behavior")] - public event EditStartEventHandler EditStarted { - add => this.AddHandler(EditStartedEvent, value); - remove => this.RemoveHandler(EditStartedEvent, value); - } - - [Category("Behavior")] - public event EditCompletedEventHandler EditCompleted { - add => this.AddHandler(EditCompletedEvent, value); - remove => this.RemoveHandler(EditCompletedEvent, value); - } - - private TextBlock PART_HintTextBlock; - private TextBlock PART_TextBlock; - private TextBox PART_TextBox; - private Point? lastClickPoint; - private Point? lastMouseMove; - private double? previousValue; - private bool ignoreMouseMove; - private bool isUpdatingExternalMouse; - private bool ignoreLostFocus; - private bool hasCancelled_ignoreMouseUp; - - public NumberDragger() { - this.Loaded += (s, e) => { - this.CoerceValue(IsEditingTextBoxProperty); - this.UpdateText(); - this.UpdateCursor(); - this.RequeryChangeMapper(this.Value); - }; - } - - static NumberDragger() { - ValueProperty.OverrideMetadata(typeof(NumberDragger), new FrameworkPropertyMetadata(null, (o, value) => ((NumberDragger) o).OnCoerceValue(value))); - Application.Current.Deactivated += OnApplicationFocusLost; - } - - private static void OnApplicationFocusLost(object sender, EventArgs e) { - if (Keyboard.FocusedElement is NumberDragger dragger) { - if (dragger.IsDragging) { - dragger.CancelDrag(); - } - else if (dragger.IsEditingTextBox) { - dragger.CancelInputEdit(); - } - } - } - - private object OnCoerceValue(object value) { - double src = (double) value; - double dst = Maths.Clamp(this.GetRoundedValue(src, false, out _), this.Minimum, this.Maximum); - if (this.ValuePreProcessor is IValuePreProcessor processor) { - dst = processor.Process(dst, this.Minimum, this.Maximum); - } - - return Maths.Equals(dst, src, 0.00000000001d) ? dst : value; - } - - public override void OnApplyTemplate() { - base.OnApplyTemplate(); - this.PART_TextBlock = this.GetTemplateChild("PART_TextBlock") as TextBlock ?? throw new Exception("Missing template part: " + nameof(this.PART_TextBlock)); - this.PART_TextBox = this.GetTemplateChild("PART_TextBox") as TextBox ?? throw new Exception("Missing template part: " + nameof(this.PART_TextBox)); - this.PART_HintTextBlock = this.GetTemplateChild("PART_HintTextBlock") as TextBlock; - this.PART_TextBox.Focusable = true; - this.PART_TextBox.KeyDown += this.OnTextBoxKeyDown; - this.PART_TextBox.LostFocus += (s, e) => { - if (this.IsEditingTextBox && this.CompleteEditOnTextBoxLostFocus is bool complete) { - if (!complete || !this.TryCompleteEdit()) { - this.CancelInputEdit(); - } - } - - this.IsEditingTextBox = false; - }; - - this.PART_TextBox.TextChanged += (sender, e) => this.UpdateHintVisibility(); - - this.CoerceValue(IsEditingTextBoxProperty); - } - - public double GetRoundedValue(double value, bool isPreview, out int? places) { - places = this.RoundedPlaces; - if (places.HasValue) { - value = Math.Round(value, places.Value); - } - - if (isPreview) { - int? preview = this.PreviewRoundedPlaces; - if (preview.HasValue) { - value = Math.Round(value, preview.Value); - places = preview; - } - } - - return value; - } - - protected virtual void OnIsDraggingChanged(bool oldValue, bool newValue) { } - - protected virtual void OnOrientationChanged(Orientation oldValue, Orientation newValue) { - if (this.IsDragging) { - this.CancelDrag(); - } - - this.IsEditingTextBox = false; - } - - protected virtual void OnIsEditingTextBoxChanged(bool oldValue, bool newValue) { - if (newValue && this.IsDragging) { - this.CancelDrag(); - } - - this.UpdatePreviewVisibilities(); - this.UpdateText(); - if (oldValue != newValue) { - this.ignoreLostFocus = true; - try { - this.PART_TextBox.Focus(); - this.PART_TextBox.SelectAll(); - } - finally { - this.ignoreLostFocus = false; - } - } - - this.UpdateCursor(); - this.UpdateHintVisibility(); - } - - private object OnCoerceIsEditingTextBox(object isEditing) { - if (this.PART_TextBox == null || this.PART_TextBlock == null) { - return isEditing; - } - - this.UpdatePreviewVisibilities(); - return isEditing; - } - - private void UpdateHintVisibility() { - if (this.PART_HintTextBlock != null && this.PART_TextBox != null) { - if (string.IsNullOrWhiteSpace(this.PART_TextBox.Text) && this.IsEditingTextBox && !string.IsNullOrEmpty(this.EditingHint)) { - this.PART_HintTextBlock.Visibility = Visibility.Visible; - } - else { - this.PART_HintTextBlock.Visibility = Visibility.Collapsed; - } - } - } - - private void UpdatePreviewVisibilities() { - if (this.IsEditingTextBox) { - this.PART_TextBox.Visibility = Visibility.Visible; - this.PART_TextBlock.Visibility = Visibility.Hidden; - } - else { - this.PART_TextBox.Visibility = Visibility.Hidden; - this.PART_TextBlock.Visibility = Visibility.Visible; - } - - this.PART_TextBox.IsReadOnly = this.IsValueReadOnly; - } - - public void UpdateCursor() { - if (this.IsValueReadOnly) { - if (this.IsEditingTextBox) { - if (this.PART_TextBlock != null) { - this.PART_TextBlock.ClearValue(CursorProperty); - } - else { - Debug.WriteLine(nameof(this.PART_TextBlock) + " is null?"); - } - - this.ClearValue(CursorProperty); - } - else { - this.Cursor = Cursors.No; - if (this.PART_TextBlock != null) { - this.PART_TextBlock.Cursor = Cursors.No; - } - else { - Debug.WriteLine(nameof(this.PART_TextBlock) + " is null?"); - } - } - } - else { - if (this.IsDragging) { - // hide cursor while dragging - this.Cursor = Cursors.None; - if (this.PART_TextBlock != null) { - this.PART_TextBlock.ClearValue(CursorProperty); - } - else { - Debug.WriteLine(nameof(this.PART_TextBlock) + " is null?"); - } - } - else { - if (this.IsEditingTextBox) { - if (this.PART_TextBlock != null) { - this.PART_TextBlock.ClearValue(CursorProperty); - } - else { - Debug.WriteLine(nameof(this.PART_TextBlock) + " is null?"); - } - - this.ClearValue(CursorProperty); - } - else { - Cursor cursor = this.GetCursorForOrientation(); - this.Cursor = cursor; - if (this.PART_TextBlock != null) { - this.PART_TextBlock.Cursor = cursor; - } - else { - Debug.WriteLine(nameof(this.PART_TextBlock) + " is null?"); - } - } - } - } - } - - protected virtual void OnRoundedPlacesChanged(int? oldValue, int? newValue) { - if (newValue != null) - this.UpdateText(); - } - - protected virtual void OnPreviewRoundedPlacesChanged(int? oldValue, int? newValue) { - if (newValue != null) - this.UpdateText(); - } - - protected override void OnValueChanged(double oldValue, double newValue) { - base.OnValueChanged(oldValue, newValue); - this.UpdateText(); - this.RequeryChangeMapper(newValue); - } - - private void RequeryChangeMapper(double value) { - if (this.ChangeMapper is IChangeMapper mapper) { - mapper.OnValueChanged(value, out double t, out double s, out double l, out double m); - if (!this.TinyChange.Equals(t)) - this.TinyChange = t; - if (!this.SmallChange.Equals(s)) - this.SmallChange = s; - if (!this.LargeChange.Equals(l)) - this.LargeChange = l; - if (!this.MassiveChange.Equals(m)) - this.MassiveChange = m; - } - } - - protected void UpdateText() { - if (this.PART_TextBox == null && this.PART_TextBlock == null) { - return; - } - - if (this.IsEditingTextBox) { - if (this.PART_TextBlock != null) - this.PART_TextBlock.Text = ""; - - if (this.PART_TextBox == null) - return; - // don't use preview for text box; only round to RoundedPlaces, if possible - double value = this.GetRoundedValue(this.Value, false, out int? places); - this.PART_TextBox.Text = (places.HasValue ? Math.Round(value, places.Value) : value).ToString(); - } - else { - // prevents problems where the text box could be very large due - // to an un-rounded value, affecting the entire control size - // 0.300000011920929 for example when it should be 0.3 - if (this.PART_TextBox != null) - this.PART_TextBox.Text = ""; - - if (this.PART_TextBlock == null) - return; - string text = this.PreviewDisplayTextOverride; - if (string.IsNullOrEmpty(text)) { - if (this.PreviewValueFormatter is IValueFormatter formatter) { - text = formatter.ToString(this.Value, this.PreviewRoundedPlaces); - } - else { - double value = this.GetRoundedValue(this.Value, true, out int? places); - text = places.HasValue ? value.ToString("F" + places.Value.ToString()) : value.ToString(); - } - } - - this.PART_TextBlock.Text = text; - } - } - - protected override void OnMouseLeave(MouseEventArgs e) { - base.OnMouseLeave(e); - if (e.LeftButton != MouseButtonState.Pressed && this.IsDragging) { - this.CompleteDrag(); - } - } - - private bool? canActivateInputEdit; - - protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { - if (!this.IsDragging && !this.IsValueReadOnly) { - e.Handled = true; - this.Focus(); - - this.lastMouseMove = this.lastClickPoint = e.GetPosition(this); - if (this.IsDoubleClickToEdit && e.ClickCount < 2) { - this.canActivateInputEdit = false; - } - else { - this.canActivateInputEdit = true; - this.ignoreMouseMove = true; - try { - this.CaptureMouse(); - } - finally { - this.ignoreMouseMove = false; - } - - this.UpdateCursor(); - } - } - - base.OnMouseLeftButtonDown(e); - } - - protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { - e.Handled = true; - if (this.IsDragging) { - this.CompleteDrag(); - } - else if (this.hasCancelled_ignoreMouseUp) { - this.hasCancelled_ignoreMouseUp = false; - } - else if ((!this.canActivateInputEdit.HasValue || this.canActivateInputEdit.Value) && this.IsMouseOver && !this.IsValueReadOnly) { - if (this.IsMouseCaptured) { - this.ReleaseMouseCapture(); - } - - this.OnBeginInputEdit(); - } - - this.canActivateInputEdit = false; - base.OnMouseLeftButtonUp(e); - } - - protected override void OnMouseMove(MouseEventArgs e) { - base.OnMouseMove(e); - if (this.ignoreMouseMove) { - return; - } - - if (this.isUpdatingExternalMouse) { - return; - } - - if (!(this.lastClickPoint is Point lastClick)) { - return; - } - - // System.Windows.Forms.Cursor - - if (e.LeftButton != MouseButtonState.Pressed) { - if (this.IsDragging) { - this.CompleteDrag(); - } - - return; - } - else if (!this.IsEnabled || this.IsValueReadOnly) { - // saves a bit of performance by processing these here - return; - } - - if (Keyboard.IsKeyDown(Key.Escape) && this.IsDragging) { - this.CancelDrag(); - return; - } - - if (!(this.lastMouseMove is Point lastPos)) { - return; - } - - Point mpos = e.GetPosition(this); - if (!this.IsDragging) { - if (Math.Abs(mpos.X - lastClick.X) < 5d && Math.Abs(mpos.Y - lastClick.Y) < 5d) { - return; - } - - this.BeginMouseDrag(); - } - - if (!this.IsDragging) { - return; - } - - if (this.IsEditingTextBox) { - Debug.WriteLine("IsEditingTextBox and IsDragging were both true"); - this.IsEditingTextBox = false; - } - - double change; - Orientation orientation = this.Orientation; - switch (orientation) { - case Orientation.Horizontal: { - change = mpos.X - lastPos.X; - break; - } - case Orientation.Vertical: { - change = mpos.Y - lastPos.Y; - break; - } - default: { - throw new Exception("Invalid orientation: " + orientation); - } - } - - bool isShiftDown = (Keyboard.Modifiers & ModifierKeys.Shift) != 0; - bool isCtrlDown = (Keyboard.Modifiers & ModifierKeys.Control) != 0; - - if (isShiftDown) { - if (isCtrlDown) { - change *= this.TinyChange; - } - else { - change *= this.SmallChange; - } - } - else if (isCtrlDown) { - change *= this.MassiveChange; - } - else { - change *= this.LargeChange; - } - - double newValue; - if ((orientation == Orientation.Horizontal && this.HorizontalIncrement == HorizontalIncrement.LeftDecrRightIncr) || - (orientation == Orientation.Vertical && this.VerticalIncrement == VerticalIncrement.UpDecrDownIncr)) { - newValue = this.Value + change; - } - else { - newValue = this.Value - change; - } - - double roundedValue = Maths.Clamp(this.GetRoundedValue(newValue, false, out _), this.Minimum, this.Maximum); - if (Maths.Equals(this.GetRoundedValue(this.Value, false, out _), roundedValue)) { - return; - } - - this.Value = roundedValue; - this.lastMouseMove = this.lastClickPoint; - this.isUpdatingExternalMouse = true; - try { - Point sp = this.PointToScreen(lastClick); - CursorUtils.SetCursorPos((int) Math.Round(sp.X), (int) Math.Round(sp.Y)); - } - finally { - this.isUpdatingExternalMouse = false; - } - } - - protected override void OnKeyDown(KeyEventArgs e) { - base.OnKeyDown(e); - if (!e.Handled) { - if (this.IsDragging) { - if (e.Key == Key.Escape) { - this.hasCancelled_ignoreMouseUp = true; - e.Handled = true; - this.CancelInputEdit(); - if (this.IsDragging) { - this.CancelDrag(); - } - - this.IsEditingTextBox = false; - } - } - // If the user previously edited another NumberDragger, then once they complete/cancel an edit, WPF - // auto-focused that number dragger. Then they can press tab to navigate nearby draggers, and they can - // edit them by just clicking a key. Massive convenience feature, saves having to use the mouse as much - else if (this.CanEnableAutoEdit(e.Key) && !this.IsValueReadOnly && (this.HasEffectiveKeyboardFocus || this.IsFocused)) { - if (this.IsMouseCaptured) { - Debug.WriteLine("Unexpected mouse capture for KeyDown event"); - this.ReleaseMouseCapture(); - } - - this.OnBeginInputEdit(); - } - } - } - - private bool CanEnableAutoEdit(Key k) { - return k >= Key.D0 && k <= Key.D9 || k == Key.Enter; - } - - private void OnTextBoxKeyDown(object sender, KeyEventArgs e) { - if (!e.Handled && !this.IsDragging && this.IsEditingTextBox) { - if ((e.Key == Key.Enter || e.Key == Key.Escape)) { - if (e.Key != Key.Enter || !this.TryCompleteEdit()) { - this.CancelInputEdit(); - } - - e.Handled = true; - } - } - } - - protected override void OnLostFocus(RoutedEventArgs e) { - base.OnLostFocus(e); - if (!this.ignoreLostFocus) { - if (this.IsDragging) { - this.CancelDrag(); - } - - this.IsEditingTextBox = false; - } - } - - public void OnBeginInputEdit() { - this.IsEditingTextBox = true; - this.UpdateCursor(); - } - - public bool TryCompleteEdit() { - if (this.IsValueReadOnly) { - return false; - } - - if (double.TryParse(this.PART_TextBox.Text, out double value)) { - this.CompleteInputEdit(value); - return true; - } - - using (ComplexNumericExpression.ExpressionState state = ComplexNumericExpression.DefaultParser.PushState()) { - state.SetVariable("value", this.Value); - try { - value = state.Expression.Parse(this.PART_TextBox.Text); - } - catch { - return false; - } - - this.CompleteInputEdit(value); - return true; - } - } - - public void CompleteInputEdit(double value) { - this.IsEditingTextBox = false; - // TODO: figure out "trimmed" out part (due to rounding) and use that to figure out if the value is actually different - this.Value = value; - } - - public void CancelInputEdit() { - this.IsEditingTextBox = false; - } - - public void BeginMouseDrag() { - this.IsEditingTextBox = false; - this.previousValue = this.Value; - this.Focus(); - this.CaptureMouse(); - this.IsDragging = true; - this.UpdateCursor(); - - bool fail = true; - try { - this.RaiseEvent(new EditStartEventArgs()); - fail = false; - } - finally { - if (fail) { - this.CancelDrag(); - } - } - } - - public void CompleteDrag() { - if (this.IsDragging) { - this.ProcessDragCompletion(false); - this.previousValue = null; - } - } - - public void CancelDrag() { - if (this.IsDragging) { - this.ProcessDragCompletion(true); - if (this.previousValue is double oldVal) { - this.previousValue = null; - if (this.RestoreValueOnCancel) { - this.Value = oldVal; - } - } - } - } - - private void ProcessDragCompletion(bool cancelled) { - if (this.IsMouseCaptured) - this.ReleaseMouseCapture(); - this.ClearValue(IsDraggingPropertyKey); - - this.lastMouseMove = null; - if (this.lastClickPoint is Point point) { - this.isUpdatingExternalMouse = true; - try { - Point p = this.PointToScreen(point); - CursorUtils.SetCursorPos((int) p.X, (int) p.Y); - } - finally { - this.isUpdatingExternalMouse = false; - } - } - - this.lastClickPoint = null; - this.UpdateCursor(); - - this.RaiseEvent(new EditCompletedEventArgs(cancelled)); - } - - private Cursor GetCursorForOrientation() { - Cursor cursor; - switch (this.Orientation) { - case Orientation.Horizontal: - cursor = Cursors.SizeWE; - break; - case Orientation.Vertical: - cursor = Cursors.SizeNS; - break; - default: - cursor = Cursors.Arrow; - break; - } - - return cursor; - } - } -} \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/NumberDraggerStyles.xaml b/SharpPad/Controls/Dragger/NumberDraggerStyles.xaml deleted file mode 100644 index 56315dc..0000000 --- a/SharpPad/Controls/Dragger/NumberDraggerStyles.xaml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/UnitToPercentFormatter.cs b/SharpPad/Controls/Dragger/UnitToPercentFormatter.cs deleted file mode 100644 index bd5e0f6..0000000 --- a/SharpPad/Controls/Dragger/UnitToPercentFormatter.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using SharpPad.Utils; - -namespace SharpPad.Controls.Dragger { - public class UnitToPercentFormatter : IValueFormatter { - public string ToString(double value, int? places) { - double clamped = Maths.Clamp(value * 100.0, 0, 100); - return clamped.ToString("F2") + " %"; - } - } -} \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/ValueStringFormatter.cs b/SharpPad/Controls/Dragger/ValueStringFormatter.cs deleted file mode 100644 index b79bf3d..0000000 --- a/SharpPad/Controls/Dragger/ValueStringFormatter.cs +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.Controls.Dragger { - public class ValueStringFormatter : IValueFormatter { - public string Format { get; set; } - - public string ToString(double value, int? places) { - return string.Format(this.Format ?? "{0}", value.ToString()); - } - } -} \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/VerticalIncrement.cs b/SharpPad/Controls/Dragger/VerticalIncrement.cs deleted file mode 100644 index f5d28bf..0000000 --- a/SharpPad/Controls/Dragger/VerticalIncrement.cs +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.Controls.Dragger { - public enum VerticalIncrement { - /// - /// Decrement the value when dragged up, and increment when dragged down (default) - /// - UpDecrDownIncr, - - /// - /// Increment the value when dragged up, and decrement when dragged down - /// - UpIncrDownDecr - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewAutomationPeer.cs b/SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewAutomationPeer.cs deleted file mode 100644 index 1ca3ceb..0000000 --- a/SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewAutomationPeer.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System.Windows.Automation.Peers; -using System.Windows.Automation.Provider; -using SharpPad.Controls.TreeViews.Controls; - -namespace SharpPad.Controls.TreeViews.Automation.Peers { - /// - /// Powers UI-Automation for types - /// - public class MultiSelectTreeViewAutomationPeer : ItemsControlAutomationPeer, ISelectionProvider { - #region Constructor - - public MultiSelectTreeViewAutomationPeer(MultiSelectTreeView owner) : base(owner) { } - - #endregion Constructor - - #region Public methods - - public override object GetPattern(PatternInterface patternInterface) { - if (patternInterface == PatternInterface.Selection) { - return this; - } - - // if (patternInterface == PatternInterface.Scroll) - // { - // ItemsControl itemsControl = (ItemsControl)Owner; - // if (itemsControl.ScrollHost != null) - // { - // AutomationPeer automationPeer = UIElementAutomationPeer.CreatePeerForElement(itemsControl.ScrollHost); - // if (automationPeer != null && automationPeer is IScrollProvider) - // { - // automationPeer.EventsSource = this; - // return (IScrollProvider)automationPeer; - // } - // } - // } - return base.GetPattern(patternInterface); - } - - #endregion Public methods - - #region Explicit interface methods - - IRawElementProviderSimple[] ISelectionProvider.GetSelection() { - IRawElementProviderSimple[] array = null; - - // MultiSelectTreeViewItem selectedContainer = ((MultiSelectTreeView) base.Owner).SelectedContainer; - // if (selectedContainer != null) - // { - // AutomationPeer automationPeer = UIElementAutomationPeer.FromElement(selectedContainer); - // if (automationPeer.EventsSource != null) - // { - // automationPeer = automationPeer.EventsSource; - // } - - // if (automationPeer != null) - // { - // array = new[] { this.ProviderFromPeer(automationPeer) }; - // } - // } - - // if (array == null) - // { - // array = new IRawElementProviderSimple[0]; - // } - return array; - } - - #endregion Explicit interface methods - - #region Public properties - - public bool CanSelectMultiple { - get { - return false; - } - } - - public bool IsSelectionRequired { - get { - return false; - } - } - - #endregion Public properties - - #region Methods - - /// - /// When overridden in a derived class, creates a new instance of the - /// for a data item in - /// the collection of this - /// . - /// - /// - /// The data item that is associated with this . - /// - /// - /// The new created. - /// - protected override ItemAutomationPeer CreateItemAutomationPeer(object item) { - return new MultiSelectTreeViewItemDataAutomationPeer(item, this); - } - - protected override AutomationControlType GetAutomationControlTypeCore() { - return AutomationControlType.Tree; - } - - protected override string GetClassNameCore() { - return "MultiSelectTreeView"; - } - - #endregion Methods - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewItemAutomationPeer.cs b/SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewItemAutomationPeer.cs deleted file mode 100644 index e42bd19..0000000 --- a/SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewItemAutomationPeer.cs +++ /dev/null @@ -1,344 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Windows; -using System.Windows.Automation; -using System.Windows.Automation.Peers; -using System.Windows.Automation.Provider; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Media; -using SharpPad.Controls.TreeViews.Controls; - -namespace SharpPad.Controls.TreeViews.Automation.Peers { - /// - /// Powers UI-Automation for types - /// - public class MultiSelectTreeViewItemAutomationPeer : - ItemsControlAutomationPeer, - IExpandCollapseProvider, - ISelectionItemProvider, - IScrollItemProvider, - IValueProvider, - IInvokeProvider { - #region Public properties - - public ExpandCollapseState ExpandCollapseState { - get { - MultiSelectTreeViewItem treeViewItem = (MultiSelectTreeViewItem) this.Owner; - if (!treeViewItem.HasItems) { - return ExpandCollapseState.LeafNode; - } - - if (!treeViewItem.IsExpanded) { - return ExpandCollapseState.Collapsed; - } - - return ExpandCollapseState.Expanded; - } - } - - #endregion Public properties - - #region Constructor - - /// - /// Initializes a new instance of the - /// class. - /// - /// - /// Das , das diesem - /// - /// zugeordnet ist. - /// - public MultiSelectTreeViewItemAutomationPeer(MultiSelectTreeViewItem owner) : base(owner) { } - - #endregion Constructor - - #region IInvokeProvider members - - public void Invoke() { - ((MultiSelectTreeViewItem) this.Owner).InvokeMouseDown(); - } - - #endregion IInvokeProvider members - - protected override Rect GetBoundingRectangleCore() { - MultiSelectTreeViewItem treeViewItem = (MultiSelectTreeViewItem) this.Owner; - ContentPresenter contentPresenter = GetContentPresenter(treeViewItem); - if (contentPresenter != null) { - Vector offset = VisualTreeHelper.GetOffset(contentPresenter); - Point p = new Point(offset.X, offset.Y); - p = contentPresenter.PointToScreen(p); - return new Rect(p.X, p.Y, contentPresenter.ActualWidth, contentPresenter.ActualHeight); - } - - return base.GetBoundingRectangleCore(); - } - - protected override Point GetClickablePointCore() { - MultiSelectTreeViewItem treeViewItem = (MultiSelectTreeViewItem) this.Owner; - ContentPresenter contentPresenter = GetContentPresenter(treeViewItem); - if (contentPresenter != null) { - Vector offset = VisualTreeHelper.GetOffset(contentPresenter); - Point p = new Point(offset.X, offset.Y); - p = contentPresenter.PointToScreen(p); - return p; - } - - return base.GetClickablePointCore(); - } - - private static ContentPresenter GetContentPresenter(MultiSelectTreeViewItem treeViewItem) { - ContentPresenter contentPresenter = treeViewItem.Template.FindName("PART_Header", treeViewItem) as ContentPresenter; - return contentPresenter; - } - - /// - /// Overridden because original wpf tree does show the expander button and the contents of the - /// header as children, too. That was requested by the users. - /// - /// Returns a list of children. - protected override List GetChildrenCore() { - //System.Diagnostics.Trace.WriteLine("MultiSelectTreeViewItemAutomationPeer.GetChildrenCore()"); - MultiSelectTreeViewItem owner = (MultiSelectTreeViewItem) this.Owner; - - List children = new List(); - ToggleButton button = owner.Template.FindName("Expander", owner) as ToggleButton; - AddAutomationPeer(children, button); - //System.Diagnostics.Trace.WriteLine("- Adding ToggleButton, " + (button == null ? "IS" : "is NOT") + " null, now " + children.Count + " items"); - - ContentPresenter contentPresenter = GetContentPresenter(owner); - - if (contentPresenter != null) { - int childrenCount = VisualTreeHelper.GetChildrenCount(contentPresenter); - for (int i = 0; i < childrenCount; i++) { - UIElement child = VisualTreeHelper.GetChild(contentPresenter, i) as UIElement; - AddAutomationPeer(children, child); - //System.Diagnostics.Trace.WriteLine("- Adding child UIElement, " + (child == null ? "IS" : "is NOT") + " null, now " + children.Count + " items"); - } - } - - ItemCollection items = owner.Items; - for (int i = 0; i < items.Count; i++) { - MultiSelectTreeViewItem treeViewItem = owner.ItemContainerGenerator.ContainerFromIndex(i) as MultiSelectTreeViewItem; - AddAutomationPeer(children, treeViewItem); - //System.Diagnostics.Trace.WriteLine("- Adding MultiSelectTreeViewItem, " + (treeViewItem == null ? "IS" : "is NOT") + " null, now " + children.Count + " items"); - } - - if (children.Count > 0) { - //System.Diagnostics.Trace.WriteLine("MultiSelectTreeViewItemAutomationPeer.GetChildrenCore(): returning " + children.Count + " children"); - //for (int i = 0; i < children.Count; i++) - //{ - // System.Diagnostics.Trace.WriteLine("- Item " + i + " " + (children[i] == null ? "IS" : "is NOT") + " null"); - //} - return children; - } - - //System.Diagnostics.Trace.WriteLine("MultiSelectTreeViewItemAutomationPeer.GetChildrenCore(): returning null"); - return null; - } - - private static void AddAutomationPeer(List children, UIElement child) { - if (child != null) { - AutomationPeer peer = FromElement(child); - if (peer == null) { - peer = CreatePeerForElement(child); - } - - if (peer != null) { - // In the array that GetChildrenCore returns, which is used by AutomationPeer.EnsureChildren, - // no null entries are allowed or a NullReferenceException will be thrown from the guts of WPF. - // This has reproducibly been observed null on certain systems so the null check was added. - // This may mean that some child controls are missing for automation, but at least the - // application doesn't crash in normal usage. - children.Add(peer); - } - } - } - - #region Explicit interface properties - - bool ISelectionItemProvider.IsSelected { - get { - return ((MultiSelectTreeViewItem) this.Owner).IsSelected; - } - } - - IRawElementProviderSimple ISelectionItemProvider.SelectionContainer { - get { - ItemsControl parentItemsControl = ((MultiSelectTreeViewItem) this.Owner).ParentTreeView; - if (parentItemsControl != null) { - AutomationPeer automationPeer = FromElement(parentItemsControl); - if (automationPeer != null) { - return this.ProviderFromPeer(automationPeer); - } - } - - return null; - } - } - - #endregion Explicit interface properties - - #region Public methods - - public void Collapse() { - if (!this.IsEnabled()) { - throw new ElementNotEnabledException(); - } - - MultiSelectTreeViewItem treeViewItem = (MultiSelectTreeViewItem) this.Owner; - if (!treeViewItem.HasItems) { - throw new InvalidOperationException("Cannot collapse because item has no children."); - } - - treeViewItem.IsExpanded = false; - } - - public void Expand() { - if (!this.IsEnabled()) { - throw new ElementNotEnabledException(); - } - - MultiSelectTreeViewItem treeViewItem = (MultiSelectTreeViewItem) this.Owner; - if (!treeViewItem.HasItems) { - throw new InvalidOperationException("Cannot expand because item has no children."); - } - - treeViewItem.IsExpanded = true; - } - - public override object GetPattern(PatternInterface patternInterface) { - if (patternInterface == PatternInterface.ExpandCollapse) { - return this; - } - - if (patternInterface == PatternInterface.SelectionItem) { - return this; - } - - if (patternInterface == PatternInterface.ScrollItem) { - return this; - } - - if (patternInterface == PatternInterface.Value) { - return this; - } - - return base.GetPattern(patternInterface); - } - - #endregion Public methods - - #region Explicit interface methods - - void IScrollItemProvider.ScrollIntoView() { - ((MultiSelectTreeViewItem) this.Owner).BringIntoView(); - } - - void ISelectionItemProvider.AddToSelection() { - throw new NotImplementedException(); - } - - void ISelectionItemProvider.RemoveFromSelection() { - throw new NotImplementedException(); - } - - void ISelectionItemProvider.Select() { - ((MultiSelectTreeViewItem) this.Owner).ParentTreeView.Selection.SelectCore((MultiSelectTreeViewItem) this.Owner); - } - - #endregion Explicit interface methods - - #region Methods - - protected override ItemAutomationPeer CreateItemAutomationPeer(object item) { - return new MultiSelectTreeViewItemDataAutomationPeer(item, this); - } - - protected override AutomationControlType GetAutomationControlTypeCore() { - return AutomationControlType.TreeItem; - } - - protected override string GetClassNameCore() { - return "MultiSelectTreeViewItem"; - } - - #endregion Methods - - #region IValueProvider members - - public bool IsReadOnly { - get { return false; } - } - - string requestedValue; - - public void SetValue(string value) { - try { - if (String.IsNullOrWhiteSpace(value)) - return; - - string[] ids = value.Split(new[] {';'}); - - object obj; - if (ids.Length > 0 && ids[0] == "Context") { - MultiSelectTreeViewItem treeViewItem = (MultiSelectTreeViewItem) this.Owner; - obj = treeViewItem.DataContext; - } - else { - obj = this.Owner; - } - - if (ids.Length < 2) { - this.requestedValue = obj.ToString(); - } - else { - Type type = obj.GetType(); - PropertyInfo pi = type.GetProperty(ids[1]); - this.requestedValue = pi.GetValue(obj, null).ToString(); - } - } - catch (Exception ex) { - this.requestedValue = ex.ToString(); - } - } - - public string Value { - get { - if (this.requestedValue == null) { - MultiSelectTreeViewItem treeViewItem = (MultiSelectTreeViewItem) this.Owner; - return treeViewItem.DataContext.ToString(); - } - - return this.requestedValue; - } - } - - #endregion IValueProvider members - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewItemDataAutomationPeer.cs b/SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewItemDataAutomationPeer.cs deleted file mode 100644 index 8bccea2..0000000 --- a/SharpPad/Controls/TreeViews/Automation/Peers/MultiSelectTreeViewItemDataAutomationPeer.cs +++ /dev/null @@ -1,202 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System; -using System.Windows; -using System.Windows.Automation; -using System.Windows.Automation.Peers; -using System.Windows.Automation.Provider; -using System.Windows.Controls; - -namespace SharpPad.Controls.TreeViews.Automation.Peers { - public class MultiSelectTreeViewItemDataAutomationPeer : - ItemAutomationPeer, - ISelectionItemProvider, - IScrollItemProvider, - IExpandCollapseProvider, - IValueProvider { - #region Properties - - private MultiSelectTreeViewItemAutomationPeer ItemPeer { - get { - AutomationPeer automationPeer = null; - UIElement wrapper = this.GetWrapper(); - if (wrapper != null) { - automationPeer = UIElementAutomationPeer.CreatePeerForElement(wrapper); - if (automationPeer == null) { - if (wrapper is FrameworkElement) { - automationPeer = new FrameworkElementAutomationPeer((FrameworkElement) wrapper); - } - else { - automationPeer = new UIElementAutomationPeer(wrapper); - } - } - } - - MultiSelectTreeViewItemAutomationPeer treeViewItemAutomationPeer = automationPeer as MultiSelectTreeViewItemAutomationPeer; - - if (treeViewItemAutomationPeer == null) { - throw new InvalidOperationException("Could not find parent automation peer."); - } - - return treeViewItemAutomationPeer; - } - } - - #endregion Properties - - #region Constructor - - public MultiSelectTreeViewItemDataAutomationPeer(object item, ItemsControlAutomationPeer itemsControlAutomationPeer) : base(item, itemsControlAutomationPeer) { } - - #endregion Constructor - - #region Public methods - - public override object GetPattern(PatternInterface patternInterface) { - if (patternInterface == PatternInterface.ExpandCollapse) { - return this; - } - - if (patternInterface == PatternInterface.SelectionItem) { - return this; - } - - if (patternInterface == PatternInterface.ScrollItem) { - return this; - } - - if (patternInterface == PatternInterface.Value) { - return this; - } - - if (patternInterface == PatternInterface.ItemContainer - || patternInterface == PatternInterface.SynchronizedInput) { - return this.ItemPeer; - } - - return base.GetPattern(patternInterface); - } - - #endregion Public methods - - #region Explicit interface properties - - ExpandCollapseState IExpandCollapseProvider.ExpandCollapseState { - get { - return this.ItemPeer.ExpandCollapseState; - } - } - - bool ISelectionItemProvider.IsSelected { - get { - return ((ISelectionItemProvider) this.ItemPeer).IsSelected; - } - } - - IRawElementProviderSimple ISelectionItemProvider.SelectionContainer { - get { - // TreeViewItemAutomationPeer treeViewItemAutomationPeer = GetWrapperPeer() as TreeViewItemAutomationPeer; - // if (treeViewItemAutomationPeer != null) - // { - // ISelectionItemProvider selectionItemProvider = treeViewItemAutomationPeer; - // return selectionItemProvider.SelectionContainer; - // } - - // this.ThrowElementNotAvailableException(); - return null; - } - } - - #endregion Explicit interface properties - - #region Explicit interface methods - - void IExpandCollapseProvider.Collapse() { - this.ItemPeer.Collapse(); - } - - void IExpandCollapseProvider.Expand() { - this.ItemPeer.Expand(); - } - - void IScrollItemProvider.ScrollIntoView() { - ((IScrollItemProvider) this.ItemPeer).ScrollIntoView(); - } - - void ISelectionItemProvider.AddToSelection() { - ((ISelectionItemProvider) this.ItemPeer).AddToSelection(); - } - - void ISelectionItemProvider.RemoveFromSelection() { - ((ISelectionItemProvider) this.ItemPeer).RemoveFromSelection(); - } - - void ISelectionItemProvider.Select() { - ((ISelectionItemProvider) this.ItemPeer).Select(); - } - - #endregion Explicit interface methods - - #region Methods - - protected override AutomationControlType GetAutomationControlTypeCore() { - return AutomationControlType.TreeItem; - } - - protected override string GetClassNameCore() { - return "TreeViewItem"; - } - - private UIElement GetWrapper() { - UIElement result = null; - ItemsControlAutomationPeer itemsControlAutomationPeer = this.ItemsControlAutomationPeer; - if (itemsControlAutomationPeer != null) { - ItemsControl itemsControl = (ItemsControl) itemsControlAutomationPeer.Owner; - if (itemsControl != null) { - result = itemsControl.ItemContainerGenerator.ContainerFromItem(this.Item) as UIElement; - } - } - - return result; - } - - #endregion Methods - - #region IValueProvider members - - bool IValueProvider.IsReadOnly { - get { return ((IValueProvider) this.ItemPeer).IsReadOnly; } - } - - void IValueProvider.SetValue(string value) { - ((IValueProvider) this.ItemPeer).SetValue(value); - } - - string IValueProvider.Value { - get { return ((IValueProvider) this.ItemPeer).Value; } - } - - #endregion IValueProvider members - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Controls/BorderSelectionLogic.cs b/SharpPad/Controls/TreeViews/Controls/BorderSelectionLogic.cs deleted file mode 100644 index d89807b..0000000 --- a/SharpPad/Controls/TreeViews/Controls/BorderSelectionLogic.cs +++ /dev/null @@ -1,277 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; - -namespace SharpPad.Controls.TreeViews.Controls { - internal class BorderSelectionLogic : IDisposable { - #region Private fields - - private MultiSelectTreeView treeView; - private readonly Border border; - private readonly ScrollViewer scrollViewer; - private readonly ItemsPresenter content; - - private bool isFirstMove; - private bool mouseDown; - private Point startPoint; - private DateTime lastScrollTime; - private HashSet initialSelection; - - public static (int millis, int lines) AutoScrollData = (25, 2); - - #endregion Private fields - - #region Constructor - - public BorderSelectionLogic(MultiSelectTreeView treeView, Border selectionBorder, ScrollViewer scrollViewer, ItemsPresenter content) { - this.treeView = treeView ?? throw new ArgumentNullException(nameof(treeView)); - this.border = selectionBorder ?? throw new ArgumentNullException(nameof(selectionBorder)); - this.scrollViewer = scrollViewer ?? throw new ArgumentNullException(nameof(scrollViewer)); - this.content = content ?? throw new ArgumentNullException(nameof(content)); - - treeView.MouseDown += this.OnMouseDown; - treeView.MouseMove += this.OnMouseMove; - treeView.MouseUp += this.OnMouseUp; - treeView.KeyDown += this.OnKeyDown; - treeView.KeyUp += this.OnKeyUp; - } - - #endregion Constructor - - #region Public methods - - public void Dispose() { - if (this.treeView != null) { - this.treeView.MouseDown -= this.OnMouseDown; - this.treeView.MouseMove -= this.OnMouseMove; - this.treeView.MouseUp -= this.OnMouseUp; - this.treeView.KeyDown -= this.OnKeyDown; - this.treeView.KeyUp -= this.OnKeyUp; - this.treeView = null; - } - - GC.SuppressFinalize(this); - } - - #endregion Public methods - - #region Methods - - private void OnMouseDown(object sender, MouseButtonEventArgs e) { - this.mouseDown = true; - this.startPoint = Mouse.GetPosition(this.content); - - // Debug.WriteLine("Initialize drwawing"); - this.isFirstMove = true; - // Capture the mouse right now so that the MouseUp event will not be missed - Mouse.Capture(this.treeView); - - IList selection = this.treeView.SelectedItems; - this.initialSelection = selection == null ? new HashSet() : new HashSet(selection.Cast()); - } - - private void OnMouseMove(object sender, MouseEventArgs e) { - if (this.mouseDown) { - if (DateTime.UtcNow > this.lastScrollTime.AddMilliseconds(AutoScrollData.millis)) { - Point currentPointWin = Mouse.GetPosition(this.scrollViewer); - if (currentPointWin.Y < 16) { - for (int i = AutoScrollData.lines; i > 0; i--) - this.scrollViewer.LineUp(); - this.scrollViewer.UpdateLayout(); - this.lastScrollTime = DateTime.UtcNow; - } - - if (currentPointWin.Y > this.scrollViewer.ActualHeight - 16) { - for (int i = AutoScrollData.lines; i > 0; i--) - this.scrollViewer.LineDown(); - this.scrollViewer.UpdateLayout(); - this.lastScrollTime = DateTime.UtcNow; - } - } - - Point currentPoint = Mouse.GetPosition(this.content); - double width = currentPoint.X - this.startPoint.X + 1; - double height = currentPoint.Y - this.startPoint.Y + 1; - double left = this.startPoint.X; - double top = this.startPoint.Y; - - if (this.isFirstMove) { - if (Math.Abs(width) <= SystemParameters.MinimumHorizontalDragDistance && Math.Abs(height) <= SystemParameters.MinimumVerticalDragDistance) { - return; - } - - this.isFirstMove = false; - if (!SelectionMultiple.IsControlKeyDown) { - if (!this.treeView.ClearSelectionByRectangle()) { - this.EndAction(); - return; - } - } - } - - // Debug.WriteLine(string.Format("Drawing: {0};{1};{2};{3}",startPoint.X,startPoint.Y,width,height)); - if (width < 1) { - width = Math.Abs(width - 1) + 1; - left = this.startPoint.X - width + 1; - } - - if (height < 1) { - height = Math.Abs(height - 1) + 1; - top = this.startPoint.Y - height + 1; - } - - this.border.Width = width; - Canvas.SetLeft(this.border, left); - this.border.Height = height; - Canvas.SetTop(this.border, top); - - this.border.Visibility = Visibility.Visible; - - double right = left + width - 1; - double bottom = top + height - 1; - - // Debug.WriteLine(string.Format("left:{1};right:{2};top:{3};bottom:{4}", null, left, right, top, bottom)); - SelectionMultiple selection = (SelectionMultiple) this.treeView.Selection; - bool foundFocusItem = false; - - IList selectedItems = this.treeView.SelectedItems; - foreach (MultiSelectTreeViewItem item in MultiSelectTreeView.GetEntireTreeRecursive(this.treeView, false, false)) { - FrameworkElement itemContent = (FrameworkElement) item.Template.FindName("headerBorder", item); - Point p = itemContent.TransformToAncestor(this.content).Transform(new Point()); - double itemLeft = p.X; - double itemRight = p.X + itemContent.ActualWidth - 1; - double itemTop = p.Y; - double itemBottom = p.Y + itemContent.ActualHeight - 1; - - // Debug.WriteLine(string.Format("element:{0};itemleft:{1};itemright:{2};itemtop:{3};itembottom:{4}",item,itemLeft,itemRight,itemTop,itemBottom)); - - // Compute the current input states for determining the new selection state of the item - bool intersect = !(itemLeft > right || itemRight < left || itemTop > bottom || itemBottom < top); - bool initialSelected = this.initialSelection != null && this.initialSelection.Contains(item); - bool ctrl = SelectionMultiple.IsControlKeyDown; - - // Decision matrix: - // If the Ctrl key is pressed, each intersected item will be toggled from its initial selection. - // Without the Ctrl key, each intersected item is selected, others are deselected. - // - // newSelected - // ─────────┬─────────────────────── - // │ intersect - // │ 0 │ 1 - // ├───────────┴─────────── - // │ initial - // │ 0 │ 1 │ 0 │ 1 - // ─────────┼─────┼─────┼─────┼───── - // ctrl 0 │ 0 │ 0 │ 1 │ 1 = intersect - // ─────────┼─────┼─────┼─────┼───── - // 1 │ 0 │ 1 │ 1 │ 0 = intersect XOR initial - // - bool newSelected = intersect ^ (initialSelected && ctrl); - - // The new selection state for this item has been determined. Apply it. - if (newSelected) { - // The item shall be selected - if (selectedItems == null || !selectedItems.Contains(item)) { - // The item is not currently selected. Try to select it. - if (!selection.SelectByRectangle(item)) { - if (selection.LastCancelAll) { - this.EndAction(); - return; - } - } - } - } - else { - // The item shall be deselected - if (selectedItems != null && selectedItems.Contains(item)) { - // The item is currently selected. Try to deselect it. - if (!selection.DeselectByRectangle(item)) { - if (selection.LastCancelAll) { - this.EndAction(); - return; - } - } - } - } - - // Always focus and bring into view the item under the mouse cursor - if (!foundFocusItem && - currentPoint.X >= itemLeft && currentPoint.X <= itemRight && - currentPoint.Y >= itemTop && currentPoint.Y <= itemBottom) { - FocusHelper.Focus(item, true); - this.scrollViewer.UpdateLayout(); - foundFocusItem = true; - } - } - - if (e != null) { - e.Handled = true; - } - } - } - - private void OnMouseUp(object sender, MouseButtonEventArgs e) { - this.EndAction(); - - // Clear selection if this was a non-ctrl click outside of any item (i.e. in the background) - Point currentPoint = e.GetPosition(this.content); - double width = currentPoint.X - this.startPoint.X + 1; - double height = currentPoint.Y - this.startPoint.Y + 1; - if (Math.Abs(width) <= SystemParameters.MinimumHorizontalDragDistance && - Math.Abs(height) <= SystemParameters.MinimumVerticalDragDistance && - !SelectionMultiple.IsControlKeyDown) { - this.treeView.ClearSelection(); - } - } - - private void OnKeyDown(object sender, KeyEventArgs e) { - // The mouse move handler reads the Ctrl key so is dependent on it. - // If the key state has changed, the selection needs to be updated. - this.OnMouseMove(null, null); - } - - private void OnKeyUp(object sender, KeyEventArgs e) { - // The mouse move handler reads the Ctrl key so is dependent on it. - // If the key state has changed, the selection needs to be updated. - this.OnMouseMove(null, null); - } - - private void EndAction() { - Mouse.Capture(null); - this.mouseDown = false; - this.border.Visibility = Visibility.Collapsed; - this.initialSelection = null; - - // Debug.WriteLine("End drawing"); - } - - #endregion Methods - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Controls/EditTextBox.cs b/SharpPad/Controls/TreeViews/Controls/EditTextBox.cs deleted file mode 100644 index 628efa2..0000000 --- a/SharpPad/Controls/TreeViews/Controls/EditTextBox.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Input; - -namespace SharpPad.Controls.TreeViews.Controls { - /// - /// Text box which focuses itself on load and selects all text in it. - /// - public class EditTextBox : TextBox { - #region Private fields - - private string startText; - - #endregion Private fields - - #region Constructor - - static EditTextBox() { - DefaultStyleKeyProperty.OverrideMetadata(typeof(EditTextBox), new FrameworkPropertyMetadata(typeof(EditTextBox))); - } - - public EditTextBox() { - this.Loaded += this.OnTreeViewEditTextBoxLoaded; - } - - #endregion Constructor - - #region Methods - - protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) { - base.OnGotKeyboardFocus(e); - this.startText = this.Text; - this.SelectAll(); - } - - protected override void OnKeyDown(KeyEventArgs e) { - base.OnKeyDown(e); - if (!e.Handled) { - Key key = e.Key; - switch (key) { - case Key.Escape: - this.Text = this.startText; - break; - } - } - } - - private void OnTreeViewEditTextBoxLoaded(object sender, RoutedEventArgs e) { - BindingExpression be = this.GetBindingExpression(TextProperty); - if (be != null) - be.UpdateTarget(); - FocusHelper.Focus(this); - } - - #endregion - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Controls/FocusHelper.cs b/SharpPad/Controls/TreeViews/Controls/FocusHelper.cs deleted file mode 100644 index f1737af..0000000 --- a/SharpPad/Controls/TreeViews/Controls/FocusHelper.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System.Threading; -using System.Windows; -using System.Windows.Threading; - -namespace SharpPad.Controls.TreeViews.Controls { - /// - /// Helper methods to focus. - /// - public static class FocusHelper { - #region Public methods - - public static void Focus(EditTextBox element) { - //System.Diagnostics.Debug.WriteLine("Focus textbox with helper:" + element.Text); - FocusCore(element); - element.BringIntoView(); - } - - public static void Focus(MultiSelectTreeViewItem element, bool bringIntoView = false) { - //System.Diagnostics.Debug.WriteLine("FocusHelper focusing " + (bringIntoView ? "[into view] " : "") + element); - FocusCore(element); - if (bringIntoView) { - FrameworkElement itemContent = (FrameworkElement) element.Template.FindName("headerBorder", element); - if (itemContent != null) // May not be rendered yet... - { - itemContent.BringIntoView(); - } - } - } - - public static void Focus(MultiSelectTreeView element) { - //System.Diagnostics.Debug.WriteLine("Focus Tree with helper"); - FocusCore(element); - element.BringIntoView(); - } - - private static void FocusCore(FrameworkElement element) { - //System.Diagnostics.Debug.WriteLine("Focusing element " + element.ToString()); - //System.Diagnostics.Debug.WriteLine(Environment.StackTrace); - if (!element.Focus()) { - //System.Diagnostics.Debug.WriteLine("- Element could not be focused, invoking in dispatcher thread"); - element.Dispatcher.BeginInvoke(DispatcherPriority.Input, new ThreadStart(() => element.Focus())); - } - -#if DEBUG - // no good idea, seems to block sometimes - int i = 0; - while (i < 5) { - if (element.IsFocused) { - return; - } - - Thread.Sleep(20); - i++; - } -#endif - } - - #endregion Public methods - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Controls/ISelectionStrategy.cs b/SharpPad/Controls/TreeViews/Controls/ISelectionStrategy.cs deleted file mode 100644 index b79e663..0000000 --- a/SharpPad/Controls/TreeViews/Controls/ISelectionStrategy.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System; - -namespace SharpPad.Controls.TreeViews.Controls { - internal interface ISelectionStrategy : IDisposable { - event EventHandler PreviewSelectionChanged; - - void ApplyTemplate(); - bool SelectCore(MultiSelectTreeViewItem owner); - bool Deselect(MultiSelectTreeViewItem item, bool bringIntoView = false); - bool SelectPreviousFromKey(); - bool SelectNextFromKey(); - bool SelectFirstFromKey(); - bool SelectLastFromKey(); - bool SelectPageUpFromKey(); - bool SelectPageDownFromKey(); - bool SelectAllFromKey(); - bool SelectParentFromKey(); - bool SelectCurrentBySpace(); - bool Select(MultiSelectTreeViewItem treeViewItem); - } - - public class PreviewSelectionChangedEventArgs : EventArgs { - /// - /// Gets a value indicating whether the item was selected or deselected. - /// - public bool Selecting { get; private set; } - - /// - /// Gets the item that is being selected or deselected. - /// - public object Item { get; private set; } - - /// - /// Gets or sets a value indicating whether the selection change of this item shall be - /// cancelled. - /// - public bool CancelThis { get; set; } - - /// - /// Gets or sets a value indicating whether the selection change of this item and all other - /// affected items shall be cancelled. - /// - public bool CancelAll { get; set; } - - /// - /// Gets a value indicating whether any of the Cancel flags is set. - /// - public bool CancelAny { get { return this.CancelThis || this.CancelAll; } } - - public PreviewSelectionChangedEventArgs(bool selecting, object item) { - this.Selecting = selecting; - this.Item = item; - } - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Controls/ListUtils.cs b/SharpPad/Controls/TreeViews/Controls/ListUtils.cs deleted file mode 100644 index 1afc5d6..0000000 --- a/SharpPad/Controls/TreeViews/Controls/ListUtils.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System.Collections; - -namespace SharpPad.Controls.TreeViews.Controls { - internal static class ListUtils { - internal static object Last(this IList list) { - if (list.Count < 1) { - return null; - } - - return list[list.Count - 1]; - } - - internal static object First(this IList list) { - if (list.Count < 1) { - return null; - } - - return list[0]; - } - - internal static object FirstNonNull(this IList list) { - if (list == null) - return null; - foreach (object value in list) { - if (value != null) - return value; - } - - return null; - } - - // How the list RemoveAll function actually works - // Putting this here so i understand how it works property lol. - // It seems like if all values match the predicate, i = 0, and j just sits there getting incremented at L8. - // L9 statement is false, and L6 statement is also false, so it's effectively just traversing the entire list - // and no writing is done - // public static int RemoveAll(IList list, Predicate match) { - // L0: int i = 0; - // L1: while (i < list.Count && !match(list[i])) - // L2: ++i; - // L3: if (i >= list.Count) - // L4: return 0; - // L5: int j = i + 1; - // L6: while (j < list.Count) { - // L7: while (j < list.Count && match(list[j])) - // L8: ++j; - // L9: if (j < list.Count) - // LA: list[i++] = list[j++]; - // LB: } - // LC: Array.Clear((Array) list, i, list.Count - i); - // LD: int num = list.Count - i; - // LE: list.Count = i; - // LF: return num; - // } - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Controls/MultiSelectTreeView.cs b/SharpPad/Controls/TreeViews/Controls/MultiSelectTreeView.cs deleted file mode 100644 index 5f8bb4f..0000000 --- a/SharpPad/Controls/TreeViews/Controls/MultiSelectTreeView.cs +++ /dev/null @@ -1,565 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Windows; -using System.Windows.Automation.Peers; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Media; -using SharpPad.Controls.TreeViews.Automation.Peers; -using SharpPad.Utils; - -namespace SharpPad.Controls.TreeViews.Controls { - [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(MultiSelectTreeViewItem))] - public class MultiSelectTreeView : ItemsControl { - #region Constants and Fields - - public event EventHandler PreviewSelectionChanged; - public event EventHandler SelectionChanged; - public static readonly DependencyProperty BackgroundSelectionRectangleProperty = DependencyProperty.Register("BackgroundSelectionRectangle", typeof(Brush), typeof(MultiSelectTreeView), new PropertyMetadata(new SolidColorBrush(Color.FromArgb(0x60, 0x33, 0x99, 0xFF)))); - public static readonly DependencyProperty BorderBrushSelectionRectangleProperty = DependencyProperty.Register("BorderBrushSelectionRectangle", typeof(Brush), typeof(MultiSelectTreeView), new PropertyMetadata(new SolidColorBrush(Color.FromRgb(0x33, 0x99, 0xFF)))); - public static readonly DependencyProperty HoverHighlightingProperty = DependencyProperty.Register("HoverHighlighting", typeof(bool), typeof(MultiSelectTreeView), new PropertyMetadata(BoolBox.True)); - public static readonly DependencyProperty VerticalRulersProperty = DependencyProperty.Register("VerticalRulers", typeof(bool), typeof(MultiSelectTreeView), new PropertyMetadata(BoolBox.False)); - public static readonly DependencyProperty ItemIndentProperty = DependencyProperty.Register("ItemIndent", typeof(int), typeof(MultiSelectTreeView), new PropertyMetadata(13)); - public static readonly DependencyProperty IsKeyboardModeProperty = DependencyProperty.Register("IsKeyboardMode", typeof(bool), typeof(MultiSelectTreeView), new PropertyMetadata(BoolBox.False)); - public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register("SelectedItems", typeof(IList), typeof(MultiSelectTreeView), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsPropertyChanged)); - public static readonly DependencyProperty AllowEditItemsProperty = DependencyProperty.Register("AllowEditItems", typeof(bool), typeof(MultiSelectTreeView), new PropertyMetadata(BoolBox.False)); - private static readonly DependencyPropertyKey LastSelectedItemPropertyKey = DependencyProperty.RegisterReadOnly("LastSelectedItem", typeof(object), typeof(MultiSelectTreeView), new PropertyMetadata(null)); - - #endregion - - #region Public Properties - - public Brush BackgroundSelectionRectangle { - get => (Brush) this.GetValue(BackgroundSelectionRectangleProperty); - set => this.SetValue(BackgroundSelectionRectangleProperty, value); - } - - public Brush BorderBrushSelectionRectangle { - get => (Brush) this.GetValue(BorderBrushSelectionRectangleProperty); - set => this.SetValue(BorderBrushSelectionRectangleProperty, value); - } - - public bool HoverHighlighting { - get => (bool) this.GetValue(HoverHighlightingProperty); - set => this.SetValue(HoverHighlightingProperty, value.Box()); - } - - public bool VerticalRulers { - get => (bool) this.GetValue(VerticalRulersProperty); - set => this.SetValue(VerticalRulersProperty, value.Box()); - } - - public int ItemIndent { - get => (int) this.GetValue(ItemIndentProperty); - set => this.SetValue(ItemIndentProperty, value); - } - - [Browsable(false)] - public bool IsKeyboardMode { - get => (bool) this.GetValue(IsKeyboardModeProperty); - set => this.SetValue(IsKeyboardModeProperty, value.Box()); - } - - public bool AllowEditItems { - get => (bool) this.GetValue(AllowEditItemsProperty); - set => this.SetValue(AllowEditItemsProperty, value.Box()); - } - - /// - /// Gets the last selected item. - /// - public object LastSelectedItem { - get => this.GetValue(LastSelectedItemPropertyKey.DependencyProperty); - private set => this.SetValue(LastSelectedItemPropertyKey, value); - } - - private MultiSelectTreeViewItem lastFocusedItem; - - /// - /// Gets the last focused item. - /// - internal MultiSelectTreeViewItem LastFocusedItem { - get => this.lastFocusedItem; - set { - // Only the last focused MultiSelectTreeViewItem may have IsTabStop = true - // so that the keyboard focus only stops a single time for the MultiSelectTreeView control. - if (this.lastFocusedItem != null) { - this.lastFocusedItem.IsTabStop = false; - } - - this.lastFocusedItem = value; - if (this.lastFocusedItem != null) { - this.lastFocusedItem.IsTabStop = true; - } - - // The MultiSelectTreeView control only has the tab stop if none of its items has it. - this.IsTabStop = this.lastFocusedItem == null; - } - } - - /// - /// Gets or sets a list of selected items and can be bound to another list. If the source list - /// implements the changes are automatically taken over. - /// - public IList SelectedItems { - get => (IList) this.GetValue(SelectedItemsProperty); - set => this.SetValue(SelectedItemsProperty, value); - } - - internal ISelectionStrategy Selection { get; private set; } - - #endregion - - #region Constructors and Destructors - - static MultiSelectTreeView() { - DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiSelectTreeView), new FrameworkPropertyMetadata(typeof(MultiSelectTreeView))); - } - - public MultiSelectTreeView() { - this.SelectedItems = new ObservableCollection(); - this.Selection = new SelectionMultiple(this); - this.Selection.PreviewSelectionChanged += (s, e) => { - this.OnPreviewSelectionChanged(e); - }; - } - - #endregion - - #region Public Methods and Operators - - public override void OnApplyTemplate() { - base.OnApplyTemplate(); - - this.Selection.ApplyTemplate(); - } - - public bool ClearSelection() { - if (this.SelectedItems.Count > 0) { - // Make a copy of the list and ignore changes to the selection while raising events - foreach (object selItem in new ArrayList(this.SelectedItems)) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(false, selItem); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) { - return false; - } - } - - this.SelectedItems.Clear(); - } - - return true; - } - - public bool SelectNextItem() { - return this.Selection.SelectNextFromKey(); - } - - public bool SelectPreviousItem() { - return this.Selection.SelectPreviousFromKey(); - } - - public bool SelectFirstItem() { - return this.Selection.SelectFirstFromKey(); - } - - public bool SelectLastItem() { - return this.Selection.SelectLastFromKey(); - } - - public bool SelectAllItems() { - return this.Selection.SelectAllFromKey(); - } - - public bool SelectParentItem() { - return this.Selection.SelectParentFromKey(); - } - - #endregion - - #region Methods - - internal bool DeselectRecursive(MultiSelectTreeViewItem item, bool includeSelf) { - List selectedChildren = new List(); - if (includeSelf) { - if (item.IsSelected) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(false, item); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) { - return false; - } - - selectedChildren.Add(item); - } - } - - if (!this.CollectDeselectRecursive(item, selectedChildren)) { - return false; - } - - foreach (MultiSelectTreeViewItem child in selectedChildren) { - child.IsSelected = false; - } - - return true; - } - - private bool CollectDeselectRecursive(MultiSelectTreeViewItem item, List selectedChildren) { - foreach (MultiSelectTreeViewItem child in item.Items) { - if (child.IsSelected) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(false, child); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) { - return false; - } - - selectedChildren.Add(child); - } - - if (!this.CollectDeselectRecursive(child, selectedChildren)) { - return false; - } - } - - return true; - } - - internal bool ClearSelectionByRectangle() { - foreach (object item in new ArrayList(this.SelectedItems)) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(false, item); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) - return false; - } - - this.SelectedItems.Clear(); - return true; - } - - internal static MultiSelectTreeViewItem GetNextItem(MultiSelectTreeViewItem item, List items) { - int indexOfCurrent = item != null ? items.IndexOf(item) : -1; - for (int i = indexOfCurrent + 1; i < items.Count; i++) { - if (items[i].IsVisible) { - return items[i]; - } - } - - return null; - } - - internal static MultiSelectTreeViewItem GetPreviousItem(MultiSelectTreeViewItem item, List items) { - int indexOfCurrent = item != null ? items.IndexOf(item) : -1; - for (int i = indexOfCurrent - 1; i >= 0; i--) { - if (items[i].IsVisible) { - return items[i]; - } - } - - return null; - } - - internal static MultiSelectTreeViewItem GetFirstItem(List items) { - for (int i = 0; i < items.Count; i++) { - if (items[i].IsVisible) { - return items[i]; - } - } - - return null; - } - - internal static MultiSelectTreeViewItem GetLastItem(List items) { - for (int i = items.Count - 1; i >= 0; i--) { - if (items[i].IsVisible) { - return items[i]; - } - } - - return null; - } - - protected override DependencyObject GetContainerForItemOverride() { - return new MultiSelectTreeViewItem(); - } - - protected override bool IsItemItsOwnContainerOverride(object item) { - return item is MultiSelectTreeViewItem; - } - - protected override AutomationPeer OnCreateAutomationPeer() { - return new MultiSelectTreeViewAutomationPeer(this); - } - - private static void OnSelectedItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - MultiSelectTreeView treeView = (MultiSelectTreeView) d; - if (e.OldValue != null) { - if (e.OldValue is INotifyCollectionChanged collection) { - collection.CollectionChanged -= treeView.OnSelectedItemsChanged; - } - } - - if (e.NewValue != null) { - if (e.NewValue is INotifyCollectionChanged collection) { - collection.CollectionChanged += treeView.OnSelectedItemsChanged; - } - } - } - - protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) { - switch (e.Action) { - case NotifyCollectionChangedAction.Remove: - case NotifyCollectionChangedAction.Replace: - if (e.OldItems != null && this.SelectedItems is IList selection) { - foreach (object item in e.OldItems) { - selection.Remove(item); - // Don't preview and ask, it is already gone so it must be removed from - // the SelectedItems list - } - } - - break; - case NotifyCollectionChangedAction.Reset: - // If the items list has considerably changed, the selection is probably - // useless anyway, clear it entirely. - this.SelectedItems?.Clear(); - break; - } - - base.OnItemsChanged(e); - } - - internal static List GetEntireTreeRecursive(ItemsControl parent, bool includeInvisible, bool includeDisabled = true) { - List list = new List(128); - GetEntireTreeRecursive(list, parent, includeInvisible, includeDisabled); - return list; - } - - internal static void GetEntireTreeRecursive(List dst, ItemsControl parent, bool includeInvisible, bool includeDisabled = true) { - ItemCollection items = parent.Items; - for (int i = 0, count = items.Count; i < count; i++) { - MultiSelectTreeViewItem tve = (MultiSelectTreeViewItem) items[i]; - if ((includeInvisible || tve.IsVisible) && (includeDisabled || tve.IsEnabled)) { - dst.Add(tve); - if (includeInvisible || tve.IsExpanded) { - GetEntireTreeRecursive(dst, tve, includeInvisible, includeDisabled); - } - } - } - } - - public static void ProcessTree(Action item, ItemsControl parent, bool includeInvisible, bool includeDisabled = true, bool useBottomToTop = false) { - ItemCollection items = parent.Items; - for (int i = 0, count = items.Count; i < count; i++) { - MultiSelectTreeViewItem tve = (MultiSelectTreeViewItem) items[i]; - if ((includeInvisible || tve.IsVisible) && (includeDisabled || tve.IsEnabled)) { - if (!useBottomToTop) - item(tve); - - if (includeInvisible || tve.IsExpanded) - ProcessTree(item, tve, includeInvisible, includeDisabled, useBottomToTop); - - if (useBottomToTop) - item(tve); - } - } - } - - internal static IEnumerable EnumerableTreeRecursive(ItemsControl parent, bool includeInvisible, bool includeDisabled = true) { - ItemCollection items = parent.Items; - for (int i = 0, count = items.Count; i < count; i++) { - MultiSelectTreeViewItem tve = (MultiSelectTreeViewItem) items[i]; - if ((includeInvisible || tve.IsVisible) && (includeDisabled || tve.IsEnabled)) { - yield return tve; - if (includeInvisible || tve.IsExpanded) { - foreach (MultiSelectTreeViewItem tvi in EnumerableTreeRecursive(tve, includeInvisible, includeDisabled)) { - yield return tvi; - } - } - } - } - } - - internal static MultiSelectTreeViewItem EnumerableTreeRecursiveFirst(Predicate accept, ItemsControl parent, bool includeInvisible, bool includeDisabled = true) { - ItemCollection items = parent.Items; - for (int i = 0, count = items.Count; i < count; i++) { - MultiSelectTreeViewItem tve = (MultiSelectTreeViewItem) items[i]; - if ((includeInvisible || tve.IsVisible) && (includeDisabled || tve.IsEnabled)) { - if (accept(tve)) { - return tve; - } - - if (includeInvisible || tve.IsExpanded) { - MultiSelectTreeViewItem item = EnumerableTreeRecursiveFirst(accept, tve, includeInvisible, includeDisabled); - if (item != null) { - return item; - } - } - } - } - - return null; - } - - internal IEnumerable GetNodesToSelectBetween(MultiSelectTreeViewItem firstNode, MultiSelectTreeViewItem lastNode) { - List allNodes = GetEntireTreeRecursive(this, false, false); - int firstIndex = allNodes.IndexOf(firstNode); - int lastIndex = allNodes.IndexOf(lastNode); - - if (firstIndex >= allNodes.Count) { - throw new InvalidOperationException("First node index " + firstIndex + "greater or equal than count " + allNodes.Count + "."); - } - - if (lastIndex >= allNodes.Count) { - throw new InvalidOperationException("Last node index " + lastIndex + " greater or equal than count " + allNodes.Count + "."); - } - - List nodesToSelect = new List(); - - if (lastIndex == firstIndex) { - return new List {firstNode}; - } - - if (lastIndex > firstIndex) { - for (int i = firstIndex; i <= lastIndex; i++) { - if (allNodes[i].IsVisible) { - nodesToSelect.Add(allNodes[i]); - } - } - } - else { - for (int i = firstIndex; i >= lastIndex; i--) { - if (allNodes[i].IsVisible) { - nodesToSelect.Add(allNodes[i]); - } - } - } - - return nodesToSelect; - } - - private void OnSelectedItemsChanged(object sender, NotifyCollectionChangedEventArgs e) { - switch (e.Action) { - case NotifyCollectionChangedAction.Add: - if (e.NewItems.Count > 0) { - foreach (MultiSelectTreeViewItem item in e.NewItems) { - if (!item.IsSelected) { - item.IsSelected = true; - } - } - - this.LastSelectedItem = e.NewItems[e.NewItems.Count - 1]; - } - - break; - case NotifyCollectionChangedAction.Remove: - foreach (MultiSelectTreeViewItem item in e.OldItems) { - item.IsSelected = false; - if (item == this.LastSelectedItem) { - IList selection = this.SelectedItems; - if (selection.Count > 0) { - this.LastSelectedItem = selection[selection.Count - 1]; - } - else { - this.LastSelectedItem = null; - } - } - } - - break; - case NotifyCollectionChangedAction.Reset: - foreach (MultiSelectTreeViewItem item in GetEntireTreeRecursive(this, true)) { - if (item.IsSelected) { - item.IsSelected = false; - } - } - - this.LastSelectedItem = null; - break; - default: throw new InvalidOperationException(); - } - - this.OnSelectionChanged(); - } - - protected override void OnPreviewKeyDown(KeyEventArgs e) { - base.OnPreviewKeyDown(e); - if (!this.IsKeyboardMode) { - this.IsKeyboardMode = true; - } - } - - protected override void OnPreviewKeyUp(KeyEventArgs e) { - base.OnPreviewKeyDown(e); - if (!this.IsKeyboardMode) { - this.IsKeyboardMode = true; - } - } - - protected override void OnPreviewMouseDown(MouseButtonEventArgs e) { - base.OnPreviewMouseDown(e); - if (this.IsKeyboardMode) { - this.IsKeyboardMode = false; - } - } - - protected override void OnGotFocus(RoutedEventArgs e) { - base.OnGotFocus(e); - - // If the MultiSelectTreeView control has gotten the focus, it needs to pass it to an - // item instead. If there was an item focused before, return to that. Otherwise just - // focus this first item in the list if any. If there are no items at all, the - // MultiSelectTreeView control just keeps the focus. - // In any case, the focussing must occur when the current event processing is finished, - // i.e. be queued in the dispatcher. Otherwise the TreeView could keep its focus - // because other focus things are still going on and interfering this final request. - - MultiSelectTreeViewItem lastFocused = this.LastFocusedItem; - if (lastFocused != null) { - this.Dispatcher.BeginInvoke((Action) (() => FocusHelper.Focus(lastFocused))); - } - else { - MultiSelectTreeViewItem firstNode = EnumerableTreeRecursiveFirst((x) => true, this, false); - if (firstNode != null) { - this.Dispatcher.BeginInvoke((Action) (() => FocusHelper.Focus(firstNode))); - } - } - } - - protected void OnPreviewSelectionChanged(PreviewSelectionChangedEventArgs e) { - this.PreviewSelectionChanged?.Invoke(this, e); - } - - protected void OnSelectionChanged() { - this.SelectionChanged?.Invoke(this, EventArgs.Empty); - } - - #endregion - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Controls/MultiSelectTreeViewItem.cs b/SharpPad/Controls/TreeViews/Controls/MultiSelectTreeViewItem.cs deleted file mode 100644 index 9472e05..0000000 --- a/SharpPad/Controls/TreeViews/Controls/MultiSelectTreeViewItem.cs +++ /dev/null @@ -1,500 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Windows; -using System.Windows.Automation.Peers; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Media; -using SharpPad.Controls.TreeViews.Automation.Peers; -using SharpPad.Utils; - -namespace SharpPad.Controls.TreeViews.Controls { - [TemplatePart(Name = "ItemsHost", Type = typeof(ItemsPresenter))] - [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(MultiSelectTreeViewItem))] - public class MultiSelectTreeViewItem : HeaderedItemsControl { - public static readonly DependencyProperty BackgroundFocusedProperty = DependencyProperty.Register("BackgroundFocused", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(SystemColors.HighlightBrush, null)); - public static readonly DependencyProperty BackgroundSelectedHoveredProperty = DependencyProperty.Register("BackgroundSelectedHovered", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(Brushes.DarkGray, null)); - public static readonly DependencyProperty BackgroundSelectedProperty = DependencyProperty.Register("BackgroundSelected", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(SystemColors.HighlightBrush, null)); - public static readonly DependencyProperty ForegroundSelectedProperty = DependencyProperty.Register("ForegroundSelected", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(SystemColors.HighlightTextBrush, null)); - public static readonly DependencyProperty BackgroundHoveredProperty = DependencyProperty.Register("BackgroundHovered", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(Brushes.LightGray, null)); - public static readonly DependencyProperty BackgroundInactiveProperty = DependencyProperty.Register("BackgroundInactive", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(SystemColors.ControlBrush, null)); - public static readonly DependencyProperty ForegroundInactiveProperty = DependencyProperty.Register("ForegroundInactive", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(SystemColors.ControlTextBrush, null)); - public static readonly DependencyProperty BorderBrushHoveredProperty = DependencyProperty.Register("BorderBrushHovered", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(Brushes.Transparent, null)); - public static readonly DependencyProperty BorderBrushFocusedProperty = DependencyProperty.Register("BorderBrushFocused", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(Brushes.Transparent, null)); - public static readonly DependencyProperty BorderBrushInactiveProperty = DependencyProperty.Register("BorderBrushInactive", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(Brushes.Black, null)); - public static readonly DependencyProperty BorderBrushSelectedProperty = DependencyProperty.Register("BorderBrushSelected", typeof(Brush), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(Brushes.Transparent, null)); - public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(BoolBox.False)); - public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(bool), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(true)); - public new static readonly DependencyProperty IsVisibleProperty = DependencyProperty.Register("IsVisible", typeof(bool), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(true)); - public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof(bool), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(OnIsSelectedChanged)); - public static readonly DependencyProperty IsEditingProperty = DependencyProperty.Register("IsEditing", typeof(bool), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(BoolBox.False)); - public static readonly DependencyProperty ContentTemplateEditProperty = DependencyProperty.Register("ContentTemplateEdit", typeof(DataTemplate), typeof(MultiSelectTreeViewItem)); - public static readonly DependencyProperty DisplayNameProperty = DependencyProperty.Register("DisplayName", typeof(string), typeof(MultiSelectTreeViewItem)); - public static readonly DependencyProperty HoverHighlightingProperty = DependencyProperty.Register("HoverHighlighting", typeof(bool), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(BoolBox.False)); - public static readonly DependencyProperty ItemIndentProperty = DependencyProperty.Register("ItemIndent", typeof(int), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(13)); - public static readonly DependencyProperty IsKeyboardModeProperty = DependencyProperty.Register("IsKeyboardMode", typeof(bool), typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(BoolBox.False)); - public static readonly DependencyProperty RemarksProperty = DependencyProperty.Register("Remarks", typeof(string), typeof(MultiSelectTreeViewItem)); - public static readonly DependencyProperty RemarksTemplateProperty = DependencyProperty.Register("RemarksTemplate", typeof(DataTemplate), typeof(MultiSelectTreeViewItem)); - private static readonly DependencyPropertyKey IsLeftMousePressedPropertyKey = DependencyProperty.RegisterReadOnly("IsLeftMousePressed", typeof(bool), typeof(MultiSelectTreeViewItem), new PropertyMetadata(BoolBox.False)); - public static readonly DependencyProperty IsLeftMousePressedProperty = IsLeftMousePressedPropertyKey.DependencyProperty; - - public Brush BackgroundFocused { - get => (Brush) this.GetValue(BackgroundFocusedProperty); - set => this.SetValue(BackgroundFocusedProperty, value); - } - - public Brush BackgroundSelected { - get => (Brush) this.GetValue(BackgroundSelectedProperty); - set => this.SetValue(BackgroundSelectedProperty, value); - } - - public Brush ForegroundSelected { - get => (Brush) this.GetValue(ForegroundSelectedProperty); - set => this.SetValue(ForegroundSelectedProperty, value); - } - - public Brush BackgroundSelectedHovered { - get => (Brush) this.GetValue(BackgroundSelectedHoveredProperty); - set => this.SetValue(BackgroundSelectedHoveredProperty, value); - } - - public Brush BackgroundHovered { - get => (Brush) this.GetValue(BackgroundHoveredProperty); - set => this.SetValue(BackgroundHoveredProperty, value); - } - - public Brush BackgroundInactive { - get => (Brush) this.GetValue(BackgroundInactiveProperty); - set => this.SetValue(BackgroundInactiveProperty, value); - } - - public Brush ForegroundInactive { - get => (Brush) this.GetValue(ForegroundInactiveProperty); - set => this.SetValue(ForegroundInactiveProperty, value); - } - - public Brush BorderBrushInactive { - get => (Brush) this.GetValue(BorderBrushInactiveProperty); - set => this.SetValue(BorderBrushInactiveProperty, value); - } - - public Brush BorderBrushHovered { - get => (Brush) this.GetValue(BorderBrushHoveredProperty); - set => this.SetValue(BorderBrushHoveredProperty, value); - } - - public Brush BorderBrushFocused { - get => (Brush) this.GetValue(BorderBrushFocusedProperty); - set => this.SetValue(BorderBrushFocusedProperty, value); - } - - public Brush BorderBrushSelected { - get => (Brush) this.GetValue(BorderBrushSelectedProperty); - set => this.SetValue(BorderBrushSelectedProperty, value); - } - - public DataTemplate ContentTemplateEdit { - get => (DataTemplate) this.GetValue(ContentTemplateEditProperty); - set => this.SetValue(ContentTemplateEditProperty, value); - } - - public bool IsExpanded { - get => (bool) this.GetValue(IsExpandedProperty); - set => this.SetValue(IsExpandedProperty, value.Box()); - } - - public bool IsEditable { - get => (bool) this.GetValue(IsEditableProperty); - set => this.SetValue(IsEditableProperty, value.Box()); - } - - public new bool IsVisible { - get => (bool) this.GetValue(IsVisibleProperty); - set => this.SetValue(IsVisibleProperty, value.Box()); - } - - public bool IsEditing { - get => (bool) this.GetValue(IsEditingProperty); - set => this.SetValue(IsEditingProperty, value.Box()); - } - - public bool IsSelected { - get => (bool) this.GetValue(IsSelectedProperty); - set => this.SetValue(IsSelectedProperty, value.Box()); - } - - public string DisplayName { - get => (string) this.GetValue(DisplayNameProperty); - set => this.SetValue(DisplayNameProperty, value); - } - - public bool HoverHighlighting { - get => (bool) this.GetValue(HoverHighlightingProperty); - set => this.SetValue(HoverHighlightingProperty, value.Box()); - } - - public int ItemIndent { - get => (int) this.GetValue(ItemIndentProperty); - set => this.SetValue(ItemIndentProperty, value); - } - - public bool IsKeyboardMode { - get => (bool) this.GetValue(IsKeyboardModeProperty); - set => this.SetValue(IsKeyboardModeProperty, value.Box()); - } - - public string Remarks { - get => (string) this.GetValue(RemarksProperty); - set => this.SetValue(RemarksProperty, value); - } - - public DataTemplate RemarksTemplate { - get => (DataTemplate) this.GetValue(RemarksTemplateProperty); - set => this.SetValue(RemarksTemplateProperty, value); - } - - public bool IsLeftMousePressed => (bool) this.GetValue(IsLeftMousePressedProperty); - - private MultiSelectTreeView lastParentTreeView; - - internal MultiSelectTreeView ParentTreeView { - get { - for (ItemsControl ic = this.ParentItemsControl; ic != null; ic = ItemsControlFromItemContainer(ic)) { - if (ic is MultiSelectTreeView treeView) { - return this.lastParentTreeView = treeView; - } - } - - return null; - } - } - - private static bool IsControlKeyDown => (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control; - - private static bool IsShiftKeyDown => (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift; - - private bool CanExpand => this.HasItems; - - private bool CanExpandOnInput => this.CanExpand && this.IsEnabled; - - private ItemsControl ParentItemsControl => ItemsControlFromItemContainer(this); - - public MultiSelectTreeViewItem() { } - - static MultiSelectTreeViewItem() { - DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(typeof(MultiSelectTreeViewItem))); - VirtualizingPanel.IsVirtualizingProperty.OverrideMetadata(typeof(MultiSelectTreeViewItem), new FrameworkPropertyMetadata(BoolBox.False)); - } - - internal void InvokeMouseDown() { - this.OnMouseDown(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Right) { - RoutedEvent = Mouse.MouseDownEvent - }); - } - - protected static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - // The item has been selected through its IsSelected property. Update the SelectedItems - // list accordingly (this is the authoritative collection). No PreviewSelectionChanged - // event is fired - the item is already selected. - if (d is MultiSelectTreeViewItem item && item.ParentTreeView is MultiSelectTreeView treeView) { - object obj = item; - if ((bool) e.NewValue) { - if (!treeView.SelectedItems.Contains(obj)) { - treeView.SelectedItems.Add(obj); - } - } - else { - treeView.SelectedItems.Remove(obj); - } - } - } - - protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { - if (this.ParentTreeView == null) - return; - - switch (e.Property.Name) { - case nameof(this.IsEditing): { - if ((bool) e.NewValue == false) { - this.StopEditing(); - } - - break; - } - case nameof(this.IsExpanded): { - // Bring newly expanded child nodes into view if they'd be outside of the current view - if ((bool) e.NewValue) { - if (this.VisualChildrenCount > 0) { - ((FrameworkElement) this.GetVisualChild(this.VisualChildrenCount - 1))?.BringIntoView(); - } - } - - // Deselect children of collapsed item (If one resists, don't collapse) - if ((bool) e.NewValue == false) { - if (!this.ParentTreeView.DeselectRecursive(this, false)) { - this.IsExpanded = true; - } - } - - break; - } - case nameof(UIElement.IsVisible): { - // Deselect invisible item and its children (If one resists, don't hide) - if ((bool) e.NewValue == false) { - if (!this.ParentTreeView.DeselectRecursive(this, true)) { - this.IsVisible = true; - } - } - - break; - } - } - - base.OnPropertyChanged(e); - } - - protected override AutomationPeer OnCreateAutomationPeer() { - return new MultiSelectTreeViewItemAutomationPeer(this); - } - - protected override void OnInitialized(EventArgs e) { - base.OnInitialized(e); - if (this.ParentTreeView is MultiSelectTreeView treeView && treeView.SelectedItems.Contains(this)) { - this.IsSelected = true; - } - } - - protected override void OnMouseDoubleClick(MouseButtonEventArgs e) { - base.OnMouseDoubleClick(e); - - FrameworkElement itemContent = (FrameworkElement) this.Template.FindName("headerBorder", this); - if (!itemContent.IsMouseOver) { - // A (probably disabled) child item was really clicked, do nothing here - return; - } - - if (this.IsKeyboardFocused && e.ChangedButton == MouseButton.Left) - this.IsExpanded = !this.IsExpanded; - } - - protected override void OnKeyDown(KeyEventArgs e) { - base.OnKeyDown(e); - if (!e.Handled) { - Key key = e.Key; - switch (key) { - case Key.Left: - if (this.IsExpanded) { - this.IsExpanded = false; - } - else { - this.ParentTreeView.Selection.SelectParentFromKey(); - } - - e.Handled = true; - break; - case Key.Right: - if (this.CanExpand) { - if (!this.IsExpanded) { - this.IsExpanded = true; - } - else { - this.ParentTreeView.Selection.SelectNextFromKey(); - } - } - - e.Handled = true; - break; - case Key.Up: - this.ParentTreeView.Selection.SelectPreviousFromKey(); - e.Handled = true; - break; - case Key.Down: - this.ParentTreeView.Selection.SelectNextFromKey(); - e.Handled = true; - break; - case Key.Home: - this.ParentTreeView.Selection.SelectFirstFromKey(); - e.Handled = true; - break; - case Key.End: - this.ParentTreeView.Selection.SelectLastFromKey(); - e.Handled = true; - break; - case Key.PageUp: - this.ParentTreeView.Selection.SelectPageUpFromKey(); - e.Handled = true; - break; - case Key.PageDown: - this.ParentTreeView.Selection.SelectPageDownFromKey(); - e.Handled = true; - break; - case Key.A: - if (e.KeyboardDevice.Modifiers == ModifierKeys.Control) { - this.ParentTreeView.Selection.SelectAllFromKey(); - e.Handled = true; - } - - break; - case Key.Add: - if (this.CanExpandOnInput && !this.IsExpanded) { - this.IsExpanded = true; - } - - e.Handled = true; - break; - case Key.Subtract: - if (this.CanExpandOnInput && this.IsExpanded) { - this.IsExpanded = false; - } - - e.Handled = true; - break; - case Key.F2: - if (this.ParentTreeView.AllowEditItems && this.ContentTemplateEdit != null && this.IsFocused && this.IsEditable) { - this.IsEditing = true; - e.Handled = true; - } - - break; - case Key.Escape: - this.StopEditing(); - e.Handled = true; - break; - case Key.Return: - FocusHelper.Focus(this, true); - this.IsEditing = false; - e.Handled = true; - break; - case Key.Space: - this.ParentTreeView.Selection.SelectCurrentBySpace(); - e.Handled = true; - break; - } - } - } - - private void StopEditing() { - FocusHelper.Focus(this, true); - this.IsEditing = false; - } - - protected override void OnGotFocus(RoutedEventArgs e) { - //base.OnGotFocus(e); - this.ParentTreeView.LastFocusedItem = this; - } - - protected override void OnLostFocus(RoutedEventArgs e) { - base.OnLostFocus(e); - this.IsEditing = false; - } - - protected override void OnMouseDown(MouseButtonEventArgs e) { - base.OnMouseDown(e); - - FrameworkElement itemContent = (FrameworkElement) this.Template.FindName("headerBorder", this); - if (!itemContent.IsMouseOver) { - // A (probably disabled) child item was really clicked, do nothing here - return; - } - - if (e.ChangedButton == MouseButton.Left) { - this.ParentTreeView.Selection.Select(this); - e.Handled = true; - } - - if (e.ChangedButton == MouseButton.Right) { - if (!this.IsSelected) { - this.ParentTreeView.Selection.Select(this); - } - - e.Handled = true; - } - } - - protected override void OnPreviewMouseDown(MouseButtonEventArgs e) { - base.OnPreviewMouseDown(e); - if (e.MouseDevice.LeftButton == MouseButtonState.Pressed) { - this.SetValue(IsLeftMousePressedPropertyKey, BoolBox.True); - } - else if (this.IsLeftMousePressed) { - this.ClearValue(IsLeftMousePressedPropertyKey); - } - } - - protected override void OnPreviewMouseUp(MouseButtonEventArgs e) { - base.OnPreviewMouseUp(e); - this.ClearValue(IsLeftMousePressedPropertyKey); - } - - protected override void OnPreviewMouseMove(MouseEventArgs e) { - base.OnPreviewMouseMove(e); - if (this.IsLeftMousePressed && e.MouseDevice.LeftButton != MouseButtonState.Pressed) { - this.ClearValue(IsLeftMousePressedPropertyKey); - } - } - - protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) { - MultiSelectTreeView parentTV; - switch (e.Action) { - case NotifyCollectionChangedAction.Remove: - // Remove all items from the SelectedItems list that have been removed from the Items list - parentTV = this.ParentTreeView ?? this.lastParentTreeView; - if (parentTV != null) { - foreach (object item in e.OldItems) { - parentTV.SelectedItems.Remove(item); - if (parentTV.Selection is SelectionMultiple multiselection) { - multiselection.InvalidateLastShiftRoot(item); - } - // Don't preview and ask, it is already gone so it must be removed from the SelectedItems list - } - } - - break; - case NotifyCollectionChangedAction.Reset: - // Remove all items from the SelectedItems list that are no longer in the Items list - parentTV = this.ParentTreeView ?? this.lastParentTreeView; - if (parentTV != null) { - object[] selection = new object[parentTV.SelectedItems.Count]; - parentTV.SelectedItems.CopyTo(selection, 0); - HashSet dataItems = new HashSet(MultiSelectTreeView.GetEntireTreeRecursive(parentTV, true).Select(item => item)); - foreach (object item in selection) { - if (!dataItems.Contains(item)) { - parentTV.SelectedItems.Remove(item); - // Don't preview and ask, it is already gone so it must be removed - // from the SelectedItems list - } - } - } - - break; - } - - base.OnItemsChanged(e); - } - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Controls/SelectionMultiple.cs b/SharpPad/Controls/TreeViews/Controls/SelectionMultiple.cs deleted file mode 100644 index c04f89c..0000000 --- a/SharpPad/Controls/TreeViews/Controls/SelectionMultiple.cs +++ /dev/null @@ -1,387 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012 Yves Goergen, Goroll - * - * 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 NON-INFRINGEMENT. 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. - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Media; - -namespace SharpPad.Controls.TreeViews.Controls { - /// - /// Implements the logic for the multiple selection strategy. - /// - public class SelectionMultiple : ISelectionStrategy { - private readonly MultiSelectTreeView treeView; - private BorderSelectionLogic borderSelectionLogic; - private MultiSelectTreeViewItem lastShiftRoot; - - public SelectionMultiple(MultiSelectTreeView treeView) { - this.treeView = treeView; - } - - public event EventHandler PreviewSelectionChanged; - - public void ApplyTemplate() { - this.borderSelectionLogic = new BorderSelectionLogic( - this.treeView, - this.treeView.Template.FindName("selectionBorder", this.treeView) as Border, - this.treeView.Template.FindName("scrollViewer", this.treeView) as ScrollViewer, - this.treeView.Template.FindName("content", this.treeView) as ItemsPresenter); - } - - public bool Select(MultiSelectTreeViewItem item) { - if (IsControlKeyDown) { - if (this.treeView.SelectedItems.Contains(item)) { - return this.Deselect(item, true); - } - else { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(true, item); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) { - FocusHelper.Focus(item, true); - return false; - } - - return this.SelectCore(item); - } - } - else { - if (this.treeView.SelectedItems.Count == 1 && this.treeView.SelectedItems[0] == item) { - // Requested to select the single already-selected item. Don't change the selection. - FocusHelper.Focus(item, true); - this.lastShiftRoot = item; - return true; - } - else { - return this.SelectCore(item); - } - } - } - - public bool SelectCore(MultiSelectTreeViewItem item) { - IList selection = this.treeView.SelectedItems; - if (IsControlKeyDown) { - if (!selection.Contains(item)) { - selection.Add(item); - } - - this.lastShiftRoot = item; - } - else if (IsShiftKeyDown && selection.Count > 0) { - MultiSelectTreeViewItem firstSelectedItem = (MultiSelectTreeViewItem) (this.lastShiftRoot ?? selection.First()); - List newSelection = this.treeView.GetNodesToSelectBetween(firstSelectedItem, item).Select(n => n).ToList(); - // Make a copy of the list because we're modifying it while enumerating it - object[] selectedItems = new object[selection.Count]; - selection.CopyTo(selectedItems, 0); - // Remove all items no longer selected - foreach (object selItem in selectedItems.Where(i => !newSelection.Contains(i))) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(false, selItem); - this.OnPreviewSelectionChanged(e); - if (e.CancelAll) { - FocusHelper.Focus(item); - return false; - } - - if (!e.CancelThis) { - selection.Remove(selItem); - } - } - - // Add new selected items - foreach (MultiSelectTreeViewItem newItem in newSelection.Where(i => !selectedItems.Contains(i))) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(true, newItem); - this.OnPreviewSelectionChanged(e); - if (e.CancelAll) { - FocusHelper.Focus(item, true); - return false; - } - - if (!e.CancelThis) { - selection.Add(newItem); - } - } - } - else { - if (selection.Count > 0) { - foreach (MultiSelectTreeViewItem selItem in new ArrayList(selection)) { - PreviewSelectionChangedEventArgs e2 = new PreviewSelectionChangedEventArgs(false, selItem); - this.OnPreviewSelectionChanged(e2); - if (e2.CancelAll) { - FocusHelper.Focus(item); - this.lastShiftRoot = item; - return false; - } - - if (!e2.CancelThis) { - selection.Remove(selItem); - } - } - } - - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(true, item); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) { - FocusHelper.Focus(item, true); - this.lastShiftRoot = item; - return false; - } - - selection.Add(item); - this.lastShiftRoot = item; - } - - FocusHelper.Focus(item, true); - return true; - } - - public bool SelectCurrentBySpace() { - // Another item was focused by Ctrl+Arrow key - MultiSelectTreeViewItem item = this.GetFocusedItem(); - if (this.treeView.SelectedItems.Contains(item)) { - // With Ctrl key, toggle this item selection (deselect now). - // Without Ctrl key, always select it (is already selected). - if (IsControlKeyDown) { - if (!this.Deselect(item, true)) - return false; - item.IsSelected = false; - } - } - else { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(true, item); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) { - FocusHelper.Focus(item, true); - return false; - } - - item.IsSelected = true; - if (!this.treeView.SelectedItems.Contains(item)) { - this.treeView.SelectedItems.Add(item); - } - } - - FocusHelper.Focus(item, true); - return true; - } - - public bool SelectNextFromKey() { - List items = MultiSelectTreeView.GetEntireTreeRecursive(this.treeView, false, false); - MultiSelectTreeViewItem item = MultiSelectTreeView.GetNextItem(this.GetFocusedItem(), items); - return this.SelectFromKey(item); - } - - public bool SelectPreviousFromKey() { - List items = MultiSelectTreeView.GetEntireTreeRecursive(this.treeView, false, false); - MultiSelectTreeViewItem item = MultiSelectTreeView.GetPreviousItem(this.GetFocusedItem(), items); - return this.SelectFromKey(item); - } - - public bool SelectFirstFromKey() { - List items = MultiSelectTreeView.GetEntireTreeRecursive(this.treeView, false, false); - MultiSelectTreeViewItem item = MultiSelectTreeView.GetFirstItem(items); - return this.SelectFromKey(item); - } - - public bool SelectLastFromKey() { - List items = MultiSelectTreeView.GetEntireTreeRecursive(this.treeView, false, false); - MultiSelectTreeViewItem item = MultiSelectTreeView.GetLastItem(items); - return this.SelectFromKey(item); - } - - public bool SelectPageUpFromKey() { - return this.SelectPageUpDown(false); - } - - public bool SelectPageDownFromKey() { - return this.SelectPageUpDown(true); - } - - public bool SelectAllFromKey() { - List items = MultiSelectTreeView.GetEntireTreeRecursive(this.treeView, false, false); - // Add new selected items - foreach (MultiSelectTreeViewItem item in items.Where(i => !this.treeView.SelectedItems.Contains(i))) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(true, item); - this.OnPreviewSelectionChanged(e); - if (e.CancelAll) { - return false; - } - - if (!e.CancelThis) { - this.treeView.SelectedItems.Add(item); - } - } - - return true; - } - - public bool SelectParentFromKey() { - DependencyObject parent = this.GetFocusedItem(); - while (parent != null) { - parent = VisualTreeHelper.GetParent(parent); - if (parent is MultiSelectTreeViewItem) - break; - } - - return this.SelectFromKey(parent as MultiSelectTreeViewItem); - } - - public bool Deselect(MultiSelectTreeViewItem item, bool bringIntoView = false) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(false, item); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) - return false; - - this.treeView.SelectedItems.Remove(item); - if (item == this.lastShiftRoot) { - this.lastShiftRoot = null; - } - - FocusHelper.Focus(item, bringIntoView); - return true; - } - - public void Dispose() { - if (this.borderSelectionLogic != null) { - this.borderSelectionLogic.Dispose(); - this.borderSelectionLogic = null; - } - - GC.SuppressFinalize(this); - } - - public void InvalidateLastShiftRoot(object item) { - if (this.lastShiftRoot == item) { - this.lastShiftRoot = null; - } - } - - internal bool SelectByRectangle(MultiSelectTreeViewItem item) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(true, item); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) { - this.lastShiftRoot = item; - return false; - } - - if (!this.treeView.SelectedItems.Contains(item)) { - this.treeView.SelectedItems.Add(item); - } - - this.lastShiftRoot = item; - return true; - } - - internal bool DeselectByRectangle(MultiSelectTreeViewItem item) { - PreviewSelectionChangedEventArgs e = new PreviewSelectionChangedEventArgs(false, item); - this.OnPreviewSelectionChanged(e); - if (e.CancelAny) { - this.lastShiftRoot = item; - return false; - } - - this.treeView.SelectedItems.Remove(item); - if (item == this.lastShiftRoot) { - this.lastShiftRoot = null; - } - - return true; - } - - private MultiSelectTreeViewItem GetFocusedItem() { - return MultiSelectTreeView.EnumerableTreeRecursiveFirst(x => x.IsFocused, this.treeView, false, false); - } - - private bool SelectFromKey(MultiSelectTreeViewItem item) { - if (item == null) { - return false; - } - - // If Ctrl is pressed just focus it, so it can be selected by Space. Otherwise select it. - if (IsControlKeyDown) { - FocusHelper.Focus(item, true); - return true; - } - else { - return this.SelectCore(item); - } - } - - private bool SelectPageUpDown(bool down) { - List items = MultiSelectTreeView.GetEntireTreeRecursive(this.treeView, false, false); - MultiSelectTreeViewItem item = this.GetFocusedItem(); - if (item == null) { - return down ? this.SelectLastFromKey() : this.SelectFirstFromKey(); - } - - double targetY = item.TransformToAncestor(this.treeView).Transform(new Point()).Y; - FrameworkElement itemContent = (FrameworkElement) item.Template.FindName("headerBorder", item); - double offset = this.treeView.ActualHeight - 2 * itemContent.ActualHeight; - if (!down) - offset = -offset; - targetY += offset; - while (true) { - MultiSelectTreeViewItem newItem = down ? MultiSelectTreeView.GetNextItem(item, items) : MultiSelectTreeView.GetPreviousItem(item, items); - if (newItem == null) - break; - item = newItem; - double itemY = item.TransformToAncestor(this.treeView).Transform(new Point()).Y; - if (down && itemY > targetY || - !down && itemY < targetY) { - break; - } - } - - return this.SelectFromKey(item); - } - - protected void OnPreviewSelectionChanged(PreviewSelectionChangedEventArgs e) { - EventHandler handler = this.PreviewSelectionChanged; - if (handler != null) { - handler(this, e); - this.LastCancelAll = e.CancelAll; - } - } - - #region Properties - - public bool LastCancelAll { get; private set; } - - internal static bool IsControlKeyDown { - get { - return (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control; - } - } - - private static bool IsShiftKeyDown { - get { - return (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/SOURCECODE.txt b/SharpPad/Controls/TreeViews/SOURCECODE.txt deleted file mode 100644 index 50ae4a7..0000000 --- a/SharpPad/Controls/TreeViews/SOURCECODE.txt +++ /dev/null @@ -1,8 +0,0 @@ -This is a heavy modification of the MultiSelectTreeView library, -where ItemsSource is not supported for typical MVVM usage. - -This is the Github link of an (old) fork that I created with some fixes, -but doesn't match this project's source for the tree view: -https://github.com/AngryCarrot789/MultiSelectTreeView - -The licence for the library is contained in the source files of this directory diff --git a/SharpPad/Controls/TreeViews/Themes/EditTextBox.xaml b/SharpPad/Controls/TreeViews/Themes/EditTextBox.xaml deleted file mode 100644 index ad8cae1..0000000 --- a/SharpPad/Controls/TreeViews/Themes/EditTextBox.xaml +++ /dev/null @@ -1,39 +0,0 @@ - - - - \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Themes/MultiSelectTreeView.Aero2.xaml b/SharpPad/Controls/TreeViews/Themes/MultiSelectTreeView.Aero2.xaml deleted file mode 100644 index ec68703..0000000 --- a/SharpPad/Controls/TreeViews/Themes/MultiSelectTreeView.Aero2.xaml +++ /dev/null @@ -1,67 +0,0 @@ - - - - \ No newline at end of file diff --git a/SharpPad/Controls/TreeViews/Themes/MultiSelectTreeViewItem.Aero2.xaml b/SharpPad/Controls/TreeViews/Themes/MultiSelectTreeViewItem.Aero2.xaml deleted file mode 100644 index 8c99f1e..0000000 --- a/SharpPad/Controls/TreeViews/Themes/MultiSelectTreeViewItem.Aero2.xaml +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SharpPad/Converters/BoolConverterAND.cs b/SharpPad/Converters/BoolConverterAND.cs deleted file mode 100644 index e007d1c..0000000 --- a/SharpPad/Converters/BoolConverterAND.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Globalization; -using System.Windows; -using System.Windows.Data; -using SharpPad.Utils; - -namespace SharpPad.Converters { - public class BoolConverterAND : IMultiValueConverter { - public bool EmptyArrayBool { get; set; } = false; - - public bool NonBoolBool { - get => this.NonBoolValue is bool b && b; - set => this.NonBoolValue = value.Box(); - } - - public object NonBoolValue { get; set; } = DependencyProperty.UnsetValue; - - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - // if (values == null || values.Length != 3) { - // throw new Exception("Expected 3 elements, not " + (values != null ? values.Length.ToString() : "null")); - // } - // bool a = (bool) values[0]; // showOptionAlwaysUseResult - // bool b = (bool) values[1]; // isAlwaysUseNextResult - // bool c = (bool) values[2]; // showOptionAlwaysUseResultForCurrent - // return (a && b && c).Box(); // box utils as optimisation - if (values == null) { - return this.EmptyArrayBool.Box(); - } - - foreach (object value in values) { - if (value is bool boolean) { - if (!boolean) - return BoolBox.False; - } - else { - return this.NonBoolValue; - } - } - - return BoolBox.True; - } - - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/SharpPad/Converters/DbToVolumeConverter.cs b/SharpPad/Converters/DbToVolumeConverter.cs deleted file mode 100644 index b72aaf5..0000000 --- a/SharpPad/Converters/DbToVolumeConverter.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Globalization; -using System.Windows; -using System.Windows.Data; -using SharpPad.Utils; - -namespace SharpPad.Converters { - public class DbToVolumeConverter : IValueConverter { - public static DbToVolumeConverter Instance { get; } = new DbToVolumeConverter(); - - public int? RoundedPlaces { get; set; } - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - double input; - switch (value) { - case float f: - input = f; - break; - case double d: - input = d; - break; - default: return DependencyProperty.UnsetValue; - } - - double val = AudioUtils.DbToVolume(input); - if (this.RoundedPlaces is int round) - val = Math.Round(val, round); - return value is float ? (float) val : val; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - if (value is float f) { - return AudioUtils.VolumeToDb(f); - } - else if (value is double d) { - return AudioUtils.VolumeToDb(d); - } - else { - return DependencyProperty.UnsetValue; - } - } - } -} \ No newline at end of file diff --git a/SharpPad/Converters/VolumeToDbConverter.cs b/SharpPad/Converters/VolumeToDbConverter.cs deleted file mode 100644 index 8a42b6e..0000000 --- a/SharpPad/Converters/VolumeToDbConverter.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Globalization; -using System.Windows; -using System.Windows.Data; -using SharpPad.Utils; - -namespace SharpPad.Converters { - public class VolumeToDbConverter : IValueConverter { - public static VolumeToDbConverter Instance { get; } = new VolumeToDbConverter(); - - public int? RoundedPlaces { get; set; } - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - double input; - switch (value) { - case float f: - input = f; - break; - case double d: - input = d; - break; - default: return DependencyProperty.UnsetValue; - } - - double val = AudioUtils.VolumeToDb(input); - if (this.RoundedPlaces is int round) - val = Math.Round(val, round); - return value is float ? (float) val : val; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - if (value is float f) { - return AudioUtils.DbToVolume(f); - } - else if (value is double d) { - return AudioUtils.DbToVolume(d); - } - else { - return DependencyProperty.UnsetValue; - } - } - } -} \ No newline at end of file diff --git a/SharpPad/DispatcherDelegate.cs b/SharpPad/DispatcherDelegate.cs index e951db4..5be4c24 100644 --- a/SharpPad/DispatcherDelegate.cs +++ b/SharpPad/DispatcherDelegate.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/History/ChildManagerHistoryAction.cs b/SharpPad/History/ChildManagerHistoryAction.cs index ea69303..953b99a 100644 --- a/SharpPad/History/ChildManagerHistoryAction.cs +++ b/SharpPad/History/ChildManagerHistoryAction.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/History/HistoryAction.cs b/SharpPad/History/HistoryAction.cs index e83d637..26deb84 100644 --- a/SharpPad/History/HistoryAction.cs +++ b/SharpPad/History/HistoryAction.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/History/HistoryManager.cs b/SharpPad/History/HistoryManager.cs index a3db317..2c32858 100644 --- a/SharpPad/History/HistoryManager.cs +++ b/SharpPad/History/HistoryManager.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/History/IHistoryAction.cs b/SharpPad/History/IHistoryAction.cs index 9e05d32..62cbd4b 100644 --- a/SharpPad/History/IHistoryAction.cs +++ b/SharpPad/History/IHistoryAction.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/History/MergedHistoryAction.cs b/SharpPad/History/MergedHistoryAction.cs index 86c2829..1109436 100644 --- a/SharpPad/History/MergedHistoryAction.cs +++ b/SharpPad/History/MergedHistoryAction.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/IDispatcher.cs b/SharpPad/IDispatcher.cs index 093b3fe..3a5a092 100644 --- a/SharpPad/IDispatcher.cs +++ b/SharpPad/IDispatcher.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Interactivity/Contexts/ContextDataHelper.cs b/SharpPad/Interactivity/Contexts/ContextDataHelper.cs index 6d351ad..f14a785 100644 --- a/SharpPad/Interactivity/Contexts/ContextDataHelper.cs +++ b/SharpPad/Interactivity/Contexts/ContextDataHelper.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Keymap.xml b/SharpPad/Keymap.xml index 060721e..de2225a 100644 --- a/SharpPad/Keymap.xml +++ b/SharpPad/Keymap.xml @@ -57,6 +57,14 @@ + + + + + + + + diff --git a/SharpPad/Notepads/Commands/CloseDocumentCommand.cs b/SharpPad/Notepads/Commands/CloseDocumentCommand.cs index 543dc3c..7a936e7 100644 --- a/SharpPad/Notepads/Commands/CloseDocumentCommand.cs +++ b/SharpPad/Notepads/Commands/CloseDocumentCommand.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License @@ -34,17 +34,16 @@ protected override void Execute(CommandEventArgs e) { return; int index = notepad.Editors.IndexOf(editor); - if (index == -1) + if (index < 0) { + IoC.MessageService.ShowMessage("Error", "This is weird... the editor was not stored inside the notepad..."); return; + } bool isActiveDocument = notepad.ActiveEditor == editor; notepad.RemoveEditorAt(index); - if (isActiveDocument) { - if (index > 0) - index--; - if (index < notepad.Editors.Count) - notepad.ActiveEditor = notepad.Editors[index]; + if (isActiveDocument && index < notepad.Editors.Count) { + notepad.ActiveEditor = notepad.Editors[index]; } } } diff --git a/SharpPad/Notepads/Commands/DocumentCommand.cs b/SharpPad/Notepads/Commands/DocumentCommand.cs index f017780..4896de0 100644 --- a/SharpPad/Notepads/Commands/DocumentCommand.cs +++ b/SharpPad/Notepads/Commands/DocumentCommand.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Commands/EditorCommand.cs b/SharpPad/Notepads/Commands/EditorCommand.cs index e532c0e..70b753e 100644 --- a/SharpPad/Notepads/Commands/EditorCommand.cs +++ b/SharpPad/Notepads/Commands/EditorCommand.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Commands/FindModelCommand.cs b/SharpPad/Notepads/Commands/FindModelCommand.cs index 535cf94..9132804 100644 --- a/SharpPad/Notepads/Commands/FindModelCommand.cs +++ b/SharpPad/Notepads/Commands/FindModelCommand.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Commands/FindModelCommandUsage.cs b/SharpPad/Notepads/Commands/FindModelCommandUsage.cs index 07e9a95..72fc853 100644 --- a/SharpPad/Notepads/Commands/FindModelCommandUsage.cs +++ b/SharpPad/Notepads/Commands/FindModelCommandUsage.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Controls/Dragger/EditCompletedEventArgs.cs b/SharpPad/Notepads/Commands/NewFileCommand.cs similarity index 60% rename from SharpPad/Controls/Dragger/EditCompletedEventArgs.cs rename to SharpPad/Notepads/Commands/NewFileCommand.cs index 74645cd..94ef64d 100644 --- a/SharpPad/Controls/Dragger/EditCompletedEventArgs.cs +++ b/SharpPad/Notepads/Commands/NewFileCommand.cs @@ -17,14 +17,18 @@ // along with SharpPad. If not, see . // -using System.Windows; +using SharpPad.CommandSystem; -namespace SharpPad.Controls.Dragger { - public class EditCompletedEventArgs : RoutedEventArgs { - public bool IsCancelled { get; } +namespace SharpPad.Notepads.Commands { + public class NewFileCommand : NotepadCommand { + public override Executability CanExecute(Notepad notepad, CommandEventArgs e) { + return Executability.Valid; + } - public EditCompletedEventArgs(bool cancelled) : base(NumberDragger.EditCompletedEvent) { - this.IsCancelled = cancelled; + public override void Execute(Notepad notepad, CommandEventArgs e) { + notepad.AddNewEditorForDocument(new NotepadDocument() { + DocumentName = "New Document " + (notepad.Editors.Count + 1) + }); } } } \ No newline at end of file diff --git a/SharpPad/Notepads/Commands/NotepadCommand.cs b/SharpPad/Notepads/Commands/NotepadCommand.cs index 377dd75..1bbe898 100644 --- a/SharpPad/Notepads/Commands/NotepadCommand.cs +++ b/SharpPad/Notepads/Commands/NotepadCommand.cs @@ -10,22 +10,15 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License // along with SharpPad. If not, see . // -using System; -using System.IO; -using System.Linq; -using System.Windows.Threading; -using ICSharpCode.AvalonEdit.Document; using SharpPad.CommandSystem; using SharpPad.Interactivity.Contexts; -using SharpPad.Tasks; -using SharpPad.Utils; namespace SharpPad.Notepads.Commands { public abstract class NotepadCommand : Command { @@ -44,96 +37,4 @@ protected override void Execute(CommandEventArgs e) { public abstract void Execute(Notepad notepad, CommandEventArgs e); } - - public class NewFileCommand : NotepadCommand { - public override Executability CanExecute(Notepad notepad, CommandEventArgs e) { - return Executability.Valid; - } - - public override void Execute(Notepad notepad, CommandEventArgs e) { - notepad.AddNewEditor(new NotepadDocument() { - DocumentName = "New Document " + (notepad.Editors.Count + 1) - }); - } - } - - public class OpenFilesCommand : NotepadCommand { - // lazy - private readonly WeakReference currentTask = new WeakReference(null); - - public override Executability CanExecute(Notepad notepad, CommandEventArgs e) { - if (this.currentTask.TryGetTarget(out var task) && task.IsRunning) - return Executability.ValidButCannotExecute; - return Executability.Valid; - } - - public override void Execute(Notepad notepad, CommandEventArgs e) { - if (this.currentTask.TryGetTarget(out ActivityTask activityTask) && activityTask.IsRunning) { - IoC.MessageService.ShowMessage("Processing", "Already opening files. Please wait until the last operation has completed"); - return; - } - - string[] filePaths = IoC.FilePickService.OpenMultipleFiles("Select files to open", Filters.AllAndTextType); - if (filePaths != null) - this.currentTask.SetTarget(OpenFiles(notepad, filePaths)); - } - - public static ActivityTask OpenFile(Notepad notepad, string path) => OpenFiles(notepad, new string[] {path}); - - public static ActivityTask OpenFiles(Notepad notepad, string[] paths) { - return TaskManager.Instance.RunTask(async () => { - IActivityProgress progress = TaskManager.Instance.CurrentTask.Progress; - progress.Text = "Reading files"; - - string[] textArray = new string[paths.Length]; - double percentPerFile = 1.0 / paths.Length; - using (progress.PushCompletionRange(0.0, 0.5)) { - for (int i = 0; i < paths.Length; i++) { - textArray[i] = File.ReadAllText(paths[i]); - progress.OnProgress(percentPerFile); - } - } - - progress.Text = "Creating tabs"; - NotepadEditor lastEditor = null; - using (progress.PushCompletionRange(0.5, 1.0)) { - for (int i = 0; i < paths.Length; i++) { - string path = paths[i]; - string text = textArray[i]; - // Need dispatcher because TextDocument is not thread-safe and also tracks the owner thread - TextDocument textDocument = new TextDocument(text); - textDocument.SetOwnerThread(IoC.Dispatcher.Thread); - await IoC.Dispatcher.InvokeAsync(() => { - NotepadDocument document = new NotepadDocument(textDocument) { - FilePath = path, - IsModified = false - }; - - lastEditor = notepad.AddNewEditor(document); - }, DispatcherPriority.Loaded); - - progress.OnProgress(percentPerFile); - } - } - - if (lastEditor != null) { - await IoC.Dispatcher.InvokeAsync(() => { - notepad.ActiveEditor = lastEditor; - }); - } - }); - } - } - - public class SaveAllDocumentsCommand : NotepadCommand { - public override Executability CanExecute(Notepad notepad, CommandEventArgs e) { - return Executability.Valid; - } - - public override void Execute(Notepad notepad, CommandEventArgs e) { - foreach (NotepadEditor editor in notepad.Editors.Where(x => x.Document != null)) { - SaveDocumentCommand.SaveOrSaveAs(editor.Document); - } - } - } } \ No newline at end of file diff --git a/SharpPad/Notepads/Commands/OpenFilesCommand.cs b/SharpPad/Notepads/Commands/OpenFilesCommand.cs new file mode 100644 index 0000000..6efd379 --- /dev/null +++ b/SharpPad/Notepads/Commands/OpenFilesCommand.cs @@ -0,0 +1,96 @@ +// +// Copyright (c) 2023-2024 REghZy +// +// This file is part of SharpPad. +// +// SharpPad is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// SharpPad is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SharpPad. If not, see . +// + +using System; +using System.IO; +using System.Windows.Threading; +using ICSharpCode.AvalonEdit.Document; +using SharpPad.CommandSystem; +using SharpPad.Tasks; +using SharpPad.Utils; + +namespace SharpPad.Notepads.Commands { + public class OpenFilesCommand : NotepadCommand { + // lazy + private readonly WeakReference currentTask = new WeakReference(null); + + public override Executability CanExecute(Notepad notepad, CommandEventArgs e) { + if (this.currentTask.TryGetTarget(out var task) && task.IsRunning) + return Executability.ValidButCannotExecute; + return Executability.Valid; + } + + public override void Execute(Notepad notepad, CommandEventArgs e) { + if (this.currentTask.TryGetTarget(out ActivityTask activityTask) && activityTask.IsRunning) { + IoC.MessageService.ShowMessage("Processing", "Already opening files. Please wait until the last operation has completed"); + return; + } + + string[] filePaths = IoC.FilePickService.OpenMultipleFiles("Select files to open", Filters.AllAndTextType); + if (filePaths != null) + this.currentTask.SetTarget(OpenFiles(notepad, filePaths)); + } + + public static ActivityTask OpenFile(Notepad notepad, string path) => OpenFiles(notepad, new string[] {path}); + + public static ActivityTask OpenFiles(Notepad notepad, string[] paths) { + return TaskManager.Instance.RunTask(async () => { + IActivityProgress progress = TaskManager.Instance.CurrentTask.Progress; + progress.Text = "Reading files"; + + string[] textArray = new string[paths.Length]; + double percentPerFile = 1.0 / paths.Length; + using (progress.PushCompletionRange(0.0, 0.5)) { + for (int i = 0; i < paths.Length; i++) { + textArray[i] = File.ReadAllText(paths[i]); + progress.OnProgress(percentPerFile); + } + } + + progress.Text = "Creating tabs"; + NotepadEditor lastEditor = null; + using (progress.PushCompletionRange(0.5, 1.0)) { + for (int i = 0; i < paths.Length; i++) { + string path = paths[i]; + string text = textArray[i]; + // Need dispatcher because TextDocument is not thread-safe and also tracks the owner thread + TextDocument textDocument = new TextDocument(text); + textDocument.SetOwnerThread(IoC.Dispatcher.Thread); + await IoC.Dispatcher.InvokeAsync(() => { + NotepadDocument document = new NotepadDocument(textDocument) { + FilePath = path, + IsModified = false + }; + + lastEditor = notepad.AddNewEditorForDocument(document); + }, DispatcherPriority.Loaded); + + progress.OnProgress(percentPerFile); + } + } + + if (lastEditor != null) { + await IoC.Dispatcher.InvokeAsync(() => { + notepad.ActiveEditor = lastEditor; + }); + } + }); + } + } +} \ No newline at end of file diff --git a/SharpPad/Utils/KBUtils.cs b/SharpPad/Notepads/Commands/SaveAllDocumentsCommand.cs similarity index 55% rename from SharpPad/Utils/KBUtils.cs rename to SharpPad/Notepads/Commands/SaveAllDocumentsCommand.cs index 3f72841..4099e0d 100644 --- a/SharpPad/Utils/KBUtils.cs +++ b/SharpPad/Notepads/Commands/SaveAllDocumentsCommand.cs @@ -17,12 +17,19 @@ // along with SharpPad. If not, see . // -using System.Windows.Input; +using System.Linq; +using SharpPad.CommandSystem; -namespace SharpPad.Utils { - public static class KBUtils { - public static bool AreModsPressed(ModifierKeys keys) { - return (Keyboard.Modifiers & keys) == keys; +namespace SharpPad.Notepads.Commands { + public class SaveAllDocumentsCommand : NotepadCommand { + public override Executability CanExecute(Notepad notepad, CommandEventArgs e) { + return notepad.Editors.Count > 0 ? Executability.Valid : Executability.ValidButCannotExecute; + } + + public override void Execute(Notepad notepad, CommandEventArgs e) { + foreach (NotepadEditor editor in notepad.Editors.Where(x => x.Document != null)) { + SaveDocumentCommand.SaveOrSaveAs(editor.Document); + } } } } \ No newline at end of file diff --git a/SharpPad/Notepads/Contexts/EditMenuContextGenerator.cs b/SharpPad/Notepads/Contexts/EditMenuContextGenerator.cs index 0b657e6..65661ee 100644 --- a/SharpPad/Notepads/Contexts/EditMenuContextGenerator.cs +++ b/SharpPad/Notepads/Contexts/EditMenuContextGenerator.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Contexts/NotepadTabContextGenerator.cs b/SharpPad/Notepads/Contexts/NotepadTabContextGenerator.cs index 0dd8295..048c211 100644 --- a/SharpPad/Notepads/Contexts/NotepadTabContextGenerator.cs +++ b/SharpPad/Notepads/Contexts/NotepadTabContextGenerator.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Controls/FindAndReplaceControl.cs b/SharpPad/Notepads/Controls/FindAndReplaceControl.cs index df6c49f..fdb6f30 100644 --- a/SharpPad/Notepads/Controls/FindAndReplaceControl.cs +++ b/SharpPad/Notepads/Controls/FindAndReplaceControl.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Controls/INotepadEditorUI.cs b/SharpPad/Notepads/Controls/INotepadEditorUI.cs index 76afb5a..7f47e98 100644 --- a/SharpPad/Notepads/Controls/INotepadEditorUI.cs +++ b/SharpPad/Notepads/Controls/INotepadEditorUI.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Controls/INotepadTabUI.cs b/SharpPad/Notepads/Controls/INotepadTabUI.cs index bdd28cc..e0bef7c 100644 --- a/SharpPad/Notepads/Controls/INotepadTabUI.cs +++ b/SharpPad/Notepads/Controls/INotepadTabUI.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Controls/NotepadControls.xaml b/SharpPad/Notepads/Controls/NotepadControls.xaml index c0133e8..02eacf0 100644 --- a/SharpPad/Notepads/Controls/NotepadControls.xaml +++ b/SharpPad/Notepads/Controls/NotepadControls.xaml @@ -36,11 +36,7 @@ Foreground="{DynamicResource ABrush.Foreground.Static}" ShowLineNumbers="True" FontSize="14" FontFamily="Consolas" - ap:HorizontalScrolling.UseHorizontalScrolling="True"> - - - - + ap:HorizontalScrolling.UseHorizontalScrolling="True"/> diff --git a/SharpPad/Notepads/Controls/NotepadEditorControl.cs b/SharpPad/Notepads/Controls/NotepadEditorControl.cs index 2b3669a..8bd9a3e 100644 --- a/SharpPad/Notepads/Controls/NotepadEditorControl.cs +++ b/SharpPad/Notepads/Controls/NotepadEditorControl.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License @@ -76,12 +76,13 @@ public bool IsDroppableTargetOver { // use RDA to prevent lag if the active document changes very quickly, e.g. opening files // but the active document is set during each file opened for some reason private readonly RapidDispatchAction updateActiveEditorRda; - private ColorizeSearchResultsBackgroundRenderer searchColorizor; - // private FindResultOutline findResultOutliner; + private readonly SearchResultBackgroundRenderer searchColorizor; + // private SearchResultColorizingTransformer findResultOutliner; public NotepadEditorControl() { this.contextData = new ContextData().Set(DataKeys.UINotepadEditorKey, this); this.updateActiveEditorRda = new RapidDispatchAction(this.SetActiveEditor, DispatcherPriority.Render); + this.searchColorizor = new SearchResultBackgroundRenderer(); } public override void OnApplyTemplate() { @@ -91,10 +92,9 @@ public override void OnApplyTemplate() { TemplateUtils.GetTemplateChild(this, nameof(this.PART_FindAndReplacePanel), out this.PART_FindAndReplacePanel); TemplateUtils.GetTemplateChild(this, nameof(this.PART_FindAndReplaceControl), out this.PART_FindAndReplaceControl); - this.searchColorizor = new ColorizeSearchResultsBackgroundRenderer(); this.PART_TextEditor.TextArea.TextView.BackgroundRenderers.Add(this.searchColorizor); - // this.PART_TextEditor.TextArea.TextView.LineTransformers.Add(this.findResultOutliner = new FindResultOutline(this)); + // this.PART_TextEditor.TextArea.TextView.LineTransformers.Add(this.findResultOutliner = new SearchResultColorizingTransformer(this)); this.PART_FindAndReplacePanel.Visibility = Visibility.Collapsed; if (this.activeEditor != null) this.activeEditor.TextEditor = this.PART_TextEditor; @@ -264,7 +264,7 @@ private void OnSearchResultsChanged(FindAndReplaceModel model) { private void UpdateSearchResultRender() { if (this.searchColorizor != null) { this.searchColorizor.OnSearchUpdated(this.activeFindModel?.Results); - // this.PART_TextEditor.TextArea.TextView.InvalidateLayer(KnownLayer.Selection); + this.PART_TextEditor.TextArea.TextView.InvalidateLayer(KnownLayer.Selection); } } @@ -340,105 +340,5 @@ protected override async void OnDrop(DragEventArgs e) { #endregion public void FocusFindSearchBox() => this.PART_FindAndReplaceControl?.FocusSearchText(); - - #region Search Result Outlines - - // A modified implementation from: https://stackoverflow.com/a/47955290/11034928 - public class ColorizeSearchResultsBackgroundRenderer : IBackgroundRenderer { - private static readonly Brush BgBrush; - private static readonly Pen BdPen; - private readonly TextSegmentCollection myResults = new TextSegmentCollection(); - - public KnownLayer Layer => KnownLayer.Selection; // draw behind selection - - public ColorizeSearchResultsBackgroundRenderer() { - } - - static ColorizeSearchResultsBackgroundRenderer() { - Color bgc = Colors.Orange; - Color brc = Colors.White; - BgBrush = new SolidColorBrush(new Color() {R = bgc.R, G = bgc.G, B = bgc.B, A = 175}); - BdPen = new Pen(new SolidColorBrush(new Color() {R = brc.R, G = brc.G, B = brc.B, A = 255}), 1.0); - - // big performance helper - if (BgBrush.CanFreeze) - BgBrush.Freeze(); - if (BdPen.CanFreeze) - BdPen.Freeze(); - } - - public void OnSearchUpdated(IEnumerable ranges) { - this.myResults.Clear(); - if (ranges != null) - this.myResults.AddCollectionRange(ranges.Select(x => new TextSegment() {StartOffset = x.Index, Length = x.Length})); - } - - /// Causes the background renderer to draw. - public void Draw(TextView textView, DrawingContext drawingContext) { - if (this.myResults == null || !textView.VisualLinesValid) { - return; - } - - ReadOnlyCollection visualLines = textView.VisualLines; - if (visualLines.Count == 0) { - return; - } - - int viewStart = visualLines.First().FirstDocumentLine.Offset; - int viewEnd = visualLines.Last().LastDocumentLine.EndOffset; - - foreach (TextSegment result in this.myResults.FindOverlappingSegments(viewStart, viewEnd - viewStart)) { - BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder { - AlignToWholePixels = true, BorderThickness = 1, CornerRadius = 0 - }; - - geoBuilder.AddSegment(textView, result); - Geometry geometry = geoBuilder.CreateGeometry(); - if (geometry != null) { - drawingContext.DrawGeometry(BgBrush, BdPen, geometry); - } - } - } - } - - // Old version. Works, but getting the white outline doesn't work that well - private class FindResultOutline : ColorizingTransformer { - private static readonly Brush BgBrush = new SolidColorBrush(new Color() {R = Colors.Orange.R, G = Colors.Orange.G, B = Colors.Orange.B, A = 150}); - - private readonly NotepadEditorControl control; - - public FindResultOutline(NotepadEditorControl control) { - this.control = control; - } - - static FindResultOutline() { - // big performance helper - if (BgBrush.CanFreeze) - BgBrush.Freeze(); - } - - protected override void Colorize(ITextRunConstructionContext context) { - IReadOnlyList results = this.control.activeFindModel?.Results; - if (results == null || results.Count < 1) { - return; - } - - int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset; - foreach (TextRange range in results) { - if (range.Index < lineStartOffset) { - continue; - } - - int startColumn = context.VisualLine.GetVisualColumn(range.Index - lineStartOffset); - int endColumn = context.VisualLine.GetVisualColumn(range.EndIndex - lineStartOffset); - - this.ChangeVisualElements(startColumn, endColumn, element => { - element.TextRunProperties.SetBackgroundBrush(BgBrush); - }); - } - } - } - - #endregion } } \ No newline at end of file diff --git a/SharpPad/Notepads/Controls/NotepadTabControl.cs b/SharpPad/Notepads/Controls/NotepadTabControl.cs index f2354ae..3659384 100644 --- a/SharpPad/Notepads/Controls/NotepadTabControl.cs +++ b/SharpPad/Notepads/Controls/NotepadTabControl.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Controls/NotepadTabItem.cs b/SharpPad/Notepads/Controls/NotepadTabItem.cs index dae308d..f697c3e 100644 --- a/SharpPad/Notepads/Controls/NotepadTabItem.cs +++ b/SharpPad/Notepads/Controls/NotepadTabItem.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Controls/SearchResultBackgroundRenderer.cs b/SharpPad/Notepads/Controls/SearchResultBackgroundRenderer.cs new file mode 100644 index 0000000..f30f2b4 --- /dev/null +++ b/SharpPad/Notepads/Controls/SearchResultBackgroundRenderer.cs @@ -0,0 +1,87 @@ +// +// Copyright (c) 2023-2024 REghZy +// +// This file is part of SharpPad. +// +// SharpPad is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// SharpPad is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SharpPad. If not, see . +// + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows.Media; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Rendering; +using SharpPad.Utils; + +namespace SharpPad.Notepads.Controls { + // A modified implementation from: https://stackoverflow.com/a/47955290/11034928 + public class SearchResultBackgroundRenderer : IBackgroundRenderer { + private static readonly Brush BgBrush; + private static readonly Pen BdPen; + private readonly TextSegmentCollection myResults = new TextSegmentCollection(); + + public KnownLayer Layer => KnownLayer.Selection; // draw behind selection + + public SearchResultBackgroundRenderer() { + + } + + static SearchResultBackgroundRenderer() { + Color bgc = Colors.Orange; + Color brc = Colors.LightGray; + BgBrush = new SolidColorBrush(new Color() {R = bgc.R, G = bgc.G, B = bgc.B, A = 175}); + BdPen = new Pen(new SolidColorBrush(new Color() {R = brc.R, G = brc.G, B = brc.B, A = 255}), 1.0); + + // big performance helper + if (BgBrush.CanFreeze) + BgBrush.Freeze(); + if (BdPen.CanFreeze) + BdPen.Freeze(); + } + + public void OnSearchUpdated(IEnumerable ranges) { + this.myResults.Clear(); + if (ranges != null) + this.myResults.AddCollectionRange(ranges.Select(x => new TextSegment() {StartOffset = x.Index, Length = x.Length})); + } + + /// Causes the background renderer to draw. + public void Draw(TextView textView, DrawingContext drawingContext) { + if (this.myResults.Count < 1 || !textView.VisualLinesValid) { + return; + } + + ReadOnlyCollection visualLines = textView.VisualLines; + if (visualLines.Count < 1) { + return; + } + + int viewStart = visualLines[0].FirstDocumentLine.Offset; + int viewEnd = visualLines[visualLines.Count - 1].LastDocumentLine.EndOffset; + + foreach (TextSegment result in this.myResults.FindOverlappingSegments(viewStart, viewEnd - viewStart)) { + BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder { + AlignToWholePixels = true, BorderThickness = 1, CornerRadius = 0 + }; + + geoBuilder.AddSegment(textView, result); + Geometry geometry = geoBuilder.CreateGeometry(); + if (geometry != null) { + drawingContext.DrawGeometry(BgBrush, BdPen, geometry); + } + } + } + } +} \ No newline at end of file diff --git a/SharpPad/Notepads/Controls/SearchResultColorizingTransformer.cs b/SharpPad/Notepads/Controls/SearchResultColorizingTransformer.cs new file mode 100644 index 0000000..d1bb92b --- /dev/null +++ b/SharpPad/Notepads/Controls/SearchResultColorizingTransformer.cs @@ -0,0 +1,65 @@ +// +// Copyright (c) 2023-2024 REghZy +// +// This file is part of SharpPad. +// +// SharpPad is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// SharpPad is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SharpPad. If not, see . +// + +using System.Collections.Generic; +using System.Windows.Media; +using ICSharpCode.AvalonEdit.Rendering; +using SharpPad.Utils; + +namespace SharpPad.Notepads.Controls { + // Old version. Works, but getting the white outline doesn't work that well + public class SearchResultColorizingTransformer : ColorizingTransformer { + private static readonly Brush BgBrush = new SolidColorBrush(new Color() {R = Colors.Orange.R, G = Colors.Orange.G, B = Colors.Orange.B, A = 150}); + + private readonly NotepadEditorControl control; + + public FindAndReplaceModel FindModel { get; set; } + + public SearchResultColorizingTransformer(NotepadEditorControl control) { + this.control = control; + } + + static SearchResultColorizingTransformer() { + // big performance helper + if (BgBrush.CanFreeze) + BgBrush.Freeze(); + } + + protected override void Colorize(ITextRunConstructionContext context) { + IReadOnlyList results = this.FindModel?.Results; + if (results == null || results.Count < 1) { + return; + } + + int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset; + foreach (TextRange range in results) { + if (range.Index < lineStartOffset) { + continue; + } + + int startColumn = context.VisualLine.GetVisualColumn(range.Index - lineStartOffset); + int endColumn = context.VisualLine.GetVisualColumn(range.EndIndex - lineStartOffset); + + this.ChangeVisualElements(startColumn, endColumn, element => { + element.TextRunProperties.SetBackgroundBrush(BgBrush); + }); + } + } + } +} \ No newline at end of file diff --git a/SharpPad/Notepads/FindAndReplaceModel.cs b/SharpPad/Notepads/FindAndReplaceModel.cs index 2a65ec7..5899543 100644 --- a/SharpPad/Notepads/FindAndReplaceModel.cs +++ b/SharpPad/Notepads/FindAndReplaceModel.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/Notepad.cs b/SharpPad/Notepads/Notepad.cs index 0d449e6..3ab4230 100644 --- a/SharpPad/Notepads/Notepad.cs +++ b/SharpPad/Notepads/Notepad.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License @@ -20,6 +20,8 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; +using SharpPad.Utils; namespace SharpPad.Notepads { public delegate void NotepadActiveEditorChangedEventHandler(Notepad sender, NotepadEditor oldEditor, NotepadEditor newEditor); @@ -40,11 +42,17 @@ public class Notepad { public NotepadEditor ActiveEditor { get => this.activeEditor; set { - NotepadEditor doc = this.activeEditor; - if (doc == value) + NotepadEditor oldEditor = this.activeEditor; + if (oldEditor == value) { return; + } + + if (value != null && !value.IsOwnedBy(this)) { + throw new InvalidOperationException("The new editor is not owned by this notepad. It must be added first via" + nameof(this.AddEditor)); + } + this.activeEditor = value; - this.ActiveEditorChanged?.Invoke(this, doc, value); + this.ActiveEditorChanged?.Invoke(this, oldEditor, value); } } @@ -67,9 +75,14 @@ public Notepad() { this.Editors = this.editors.AsReadOnly(); } - public NotepadEditor AddNewEditor(NotepadDocument document) { + /// + /// Creates and adds a new notepad editor using the given initial document + /// + /// The document the editor will use + /// The created editor + public NotepadEditor AddNewEditorForDocument(NotepadDocument document) { NotepadEditor editor = new NotepadEditor(document); - this.InsertEditor(this.editors.Count, editor); + this.AddEditor(editor); return editor; } @@ -79,29 +92,46 @@ public void InsertEditor(int index, NotepadEditor editor) { if (editor == null) throw new ArgumentNullException(nameof(editor)); + if (editor.Owner != null) + throw new InvalidOperationException("The editor is already associated with another notepad instance"); + this.editors.Insert(index, editor); + NotepadEditor.SetOwner(editor, this); this.EditorIndexChanged?.Invoke(this, editor, -1, index); - if (this.editors.Count == 1) + // Check it is owned by us, just in case an event handler removes it immediately for some reason... + if (this.editors.Count == 1 && editor.IsOwnedBy(this)) { this.ActiveEditor = editor; + } } - public bool RemoveEditor(NotepadEditor editor) { - if (editor == null) - throw new ArgumentNullException(nameof(editor)); + public bool RemoveEditor(NotepadEditor editorToAdd) { + if (editorToAdd == null) + throw new ArgumentNullException(nameof(editorToAdd)); + if (!editorToAdd.IsOwnedBy(this)) + return false; - int index = this.editors.IndexOf(editor); - if (index == -1) + int index = this.editors.IndexOf(editorToAdd); + if (index == -1) { + Debug.Assert(false, "Fatal error: we owned the editor but it did not exist in our list"); return false; + } this.RemoveEditorAt(index); return true; } public void RemoveEditorAt(int index) { - NotepadEditor editor = this.editors[index]; + NotepadEditor editorToRemove = this.editors[index]; + if (editorToRemove == this.activeEditor) { + // Clear or change active editor, to allow the old one to possibly be GC'd + int newActiveIndex = CollectionUtils.GetNeighbourIndex(this.editors, index); + this.ActiveEditor = newActiveIndex == -1 ? null : this.editors[newActiveIndex]; + } + this.editors.RemoveAt(index); - this.EditorIndexChanged?.Invoke(this, editor, index, -1); + NotepadEditor.SetOwner(editorToRemove, null); + this.EditorIndexChanged?.Invoke(this, editorToRemove, index, -1); } } } \ No newline at end of file diff --git a/SharpPad/Notepads/NotepadDocument.cs b/SharpPad/Notepads/NotepadDocument.cs index f29cc57..d8a0a5e 100644 --- a/SharpPad/Notepads/NotepadDocument.cs +++ b/SharpPad/Notepads/NotepadDocument.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/NotepadDropRegistry.cs b/SharpPad/Notepads/NotepadDropRegistry.cs index 869944f..3a7b5f5 100644 --- a/SharpPad/Notepads/NotepadDropRegistry.cs +++ b/SharpPad/Notepads/NotepadDropRegistry.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Notepads/NotepadEditor.cs b/SharpPad/Notepads/NotepadEditor.cs index 59900c7..33854ce 100644 --- a/SharpPad/Notepads/NotepadEditor.cs +++ b/SharpPad/Notepads/NotepadEditor.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License @@ -39,8 +39,14 @@ public class NotepadEditor { private FindAndReplaceModel findModel; private NotepadDocument document; private TextEditor textEditor; + private Notepad owner; private bool isFindPanelOpen; + /// + /// Gets the object that this editor currently exists in + /// + public Notepad Owner => this.owner; + /// /// Gets or sets the document that this editor is presenting /// @@ -127,5 +133,11 @@ public NotepadEditor(NotepadDocument document) : this() { /// The notepad /// A bool public bool IsViewedBy(Notepad notepad) => notepad.ActiveEditor == this; + + public bool IsOwnedBy(Notepad notepad) => notepad == this.owner; + + internal static void SetOwner(NotepadEditor editor, Notepad notepad) { + editor.owner = notepad; + } } } \ No newline at end of file diff --git a/SharpPad/Notepads/Serialisation/SerialisationRegistry.cs b/SharpPad/Notepads/Serialisation/SerialisationRegistry.cs deleted file mode 100644 index 5f8a4b8..0000000 --- a/SharpPad/Notepads/Serialisation/SerialisationRegistry.cs +++ /dev/null @@ -1,292 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Collections.Generic; -using SharpPad.RBC; -using SharpPad.Utils; -using SharpPad.Utils.Collections; - -namespace SharpPad.Notepads.Serialisation { - public delegate void SerialiseHandler(T obj, RBEDictionary data, SerialisationContext ctx); - - /// - /// A class which helps version-based serialisation, for serialising objects of typically the current-version into binary - /// data (RBE system) and deserialising the current-version objects based on similar version or lower version binary data - /// - public class SerialisationRegistry { - private readonly InheritanceDictionary map; - private readonly object locker; // used just in case Register is called off main thread - private volatile bool isDirty; - - public SerialisationRegistry() { - this.map = new InheritanceDictionary(); - this.locker = new object(); - } - - /// - /// Registers a serialiser and deserialiser of the given version, for the given object type. - /// The serialisers parameter order is read then write - /// - /// The version of the serialiser and deserialiser - /// Read: the deserialiser method - /// Write: the serialiser method - /// The type of object passed to the serialiser/deserialiser - public void Register(int buildVersion, SerialiseHandler deserialise, SerialiseHandler serialise) { - Type type = typeof(T); - lock (this.locker) { - this.isDirty = true; - if (!this.map.TryGetLocalValue(type, out TypeSerialiser entry)) - this.map[type] = entry = new TypeSerialiser(); - entry.RegisterSerialiser(buildVersion, serialise, deserialise); - } - } - - /// - /// Serialises the object, using it's full type as a starting point for the serialisers to target. - /// This uses the application's current version as a target serialiser version - /// - /// The object to serialise - /// The RBE dictionary, in which data is written into - /// Optional flags for the serialisation process - public void Serialise(object obj, RBEDictionary data) => this.Serialise(obj, data, ApplicationCore.Instance.CurrentBuild); - - /// - /// Serialises the object, using it's full type as a starting point for the serialisers to target - /// - /// The object to serialise - /// The RBE dictionary, in which data is written into - /// The version of the serialiser to use - /// Optional flags for the serialisation process - public void Serialise(object obj, RBEDictionary data, int version) { - this.RunSerialisersInternal(true, obj, obj.GetType(), data, version); - } - - /// - /// Deserialises the object, using it's full type as a starting point for the deserialisers to target. - /// This uses the application's current version as a target deserialiser version - /// - /// The object to serialise - /// The RBE dictionary, in which data is written into - /// Optional flags for the serialisation process - public void Deserialise(object obj, RBEDictionary data) => this.Deserialise(obj, data, ApplicationCore.Instance.CurrentBuild); - - /// - /// Deserialises the object, using it's full type as a starting point for the deserialisers to target - /// - /// The object to deserialise - /// The RBE dictionary, in which data is read from - /// The version of the deserialiser to use - /// Optional flags for the deserialisation process - public void Deserialise(object obj, RBEDictionary data, int version) { - this.RunSerialisersInternal(false, obj, obj.GetType(), data, version); - } - - private void CleanDirtyStates(Type objType) { - lock (this.locker) { - if (this.isDirty) { - this.isDirty = false; - - // fully generate type hierarchy - this.map.GetOrCreateEntry(objType); - } - } - } - - internal void RunSerialisersInternal(bool serialise, object obj, Type objType, RBEDictionary data, int version) { - while (this.isDirty) { - this.CleanDirtyStates(objType); - } - - lock (this.locker) { - // CleanDirtyStates hopefully ensures that all the hierarchy of - // objects are registered so this should be fast not slow... - ITypeEntry typeEntry = this.map.GetEntrySlowlyOrNull(objType); - if (typeEntry == null) { - return; - } - - if (!typeEntry.HasLocalValue && (typeEntry = typeEntry.NearestBaseTypeWithLocalValue) == null) { - return; - } - - SortedList versions = typeEntry.LocalValue.versionInfo; - int index = BinarySearch.IndexOf(versions.Keys, version); - if (index < 0) - index = ~index - 1; - if (index < 0) - return; - - TypeSerialiser.SerialiserList info = versions.Values[index]; - List list = serialise ? info.serialisers : info.deserialisers; - if (list != null) { - SerialisationContext context = new SerialisationContext(version, versions.Keys[index], obj, objType, this); - for (int i = 0, count = list.Count; i < count; i++) { - list[i](obj, data, context); - } - } - } - } - - private class TypeSerialiser { - public delegate void NonGenericSerialiseHandler(object obj, RBEDictionary data, SerialisationContext context); - - public readonly SortedList versionInfo; - - public TypeSerialiser() { - this.versionInfo = new SortedList(); - } - - public void RegisterSerialiser(int buildVersion, SerialiseHandler serialise, SerialiseHandler deserialise) { - if (serialise == null) - throw new ArgumentNullException(nameof(serialise)); - if (deserialise == null) - throw new ArgumentNullException(nameof(deserialise)); - if (!this.versionInfo.TryGetValue(buildVersion, out SerialiserList info)) - this.versionInfo[buildVersion] = info = new SerialiserList(); - info.AddSerialiser((o, data, context) => serialise((T) o, data, context)); - info.AddDeserialiser((o, data, context) => deserialise((T) o, data, context)); - } - - public class SerialiserList { - public List serialisers; - public List deserialisers; - - public void AddSerialiser(NonGenericSerialiseHandler handler) { - (this.serialisers ?? (this.serialisers = new List())).Add(handler); - } - - public void AddDeserialiser(NonGenericSerialiseHandler handler) { - (this.deserialisers ?? (this.deserialisers = new List())).Add(handler); - } - } - } - } - - /// - /// Stores information about the current serialisation or deserialisation operation. This is a mutable - /// class whose state is modified during each serialisation frame (old values restored during end of serialisation) - /// - public readonly struct SerialisationContext { - /// - /// Gets the target serialisation version. This is typically the version of the application. - /// This is used to determine what type of serialisers to target. Serialisation implementation - /// can differ between versions (duh) - /// - public readonly int TargetVersion; - - /// - /// Gets the version of the serialiser or deserialiser being used. This will always be less than or - /// equal to . This may differ from when the state of - /// an object doesn't necessarily change between application versions and therefore needs no higher-version - /// serialisers. - /// - /// An example is a Vector3 serialiser; there's only ever going to be 3 components so only a v1.0 serialiser - /// is all that is needed, which is the version that this property will be; 1.0.0.0. Target version may be higher, - /// not that it would be needed - /// - /// - public readonly int ActualVersion; - - /// - /// Gets the object currently being serialised/deserialised - /// - public readonly object CurrentObject; - - /// - /// Gets or sets the type being serialised/deserialised. This may be a base type of , - /// but will always be an instance of - /// - public readonly Type CurrentType; - - /// - /// Gets the registry currently involved in the serialisation process - /// - public readonly SerialisationRegistry Registry; - - public SerialisationContext(int targetVersion, int actualVersion, object currentObject, Type currentType, SerialisationRegistry registry) { - if (targetVersion < 0) - throw new InvalidOperationException("Target version cannot be negative"); - if (actualVersion < 0) - throw new InvalidOperationException("Actual version cannot be negative"); - this.TargetVersion = targetVersion; - this.ActualVersion = actualVersion; - this.CurrentObject = currentObject; - this.CurrentType = currentType; - this.Registry = registry; - } - - /// - /// Serialises our current object using its base type. Uses the field as a target version - /// - /// The RBE dictionary, which should be written into - public void SerialiseBaseType(RBEDictionary data) => this.SerialiseBaseType(data, this.TargetVersion); - - /// - /// Serialises our current object using its base type - /// - /// The RBE dictionary, which should be written into - /// The version of our current type's base type's serialiser to use - public void SerialiseBaseType(RBEDictionary data, int version) { - Type baseType = this.CurrentType.BaseType; - if (baseType != null) { - this.Registry.RunSerialisersInternal(true, this.CurrentObject, baseType, data, version); - } - } - - /// - /// Serialises the object using the nearest previous serialiser version. This should be used if you're - /// certain the previous serialiser version will work properly - /// - /// The RBE dictionary, which should be written into - public void SerialiseLastVersion(RBEDictionary data) { - if (this.ActualVersion <= 0) - throw new InvalidOperationException("Cannot serialise the previous version of our object when we are the first version"); - this.Registry.RunSerialisersInternal(true, this.CurrentObject, this.CurrentType, data, this.ActualVersion - 1); - } - - /// - /// Same as , except for deserialisation - /// - /// The RBE dictionary, which should be read from - public void DeserialiseBaseType(RBEDictionary data) => this.DeserialiseBaseType(data, this.TargetVersion); - - /// - /// Same as , except for deserialisation - /// - /// The RBE dictionary, which should be read from - /// The version of our current type's base type's serialiser to use - public void DeserialiseBaseType(RBEDictionary data, int version) { - Type baseType = this.CurrentType.BaseType; - if (baseType != null) { - this.Registry.RunSerialisersInternal(false, this.CurrentObject, baseType, data, version); - } - } - - /// - /// Same as , except for deserialisation - /// - /// The RBE dictionary, which should be read from - public void DeserialiseLastVersion(RBEDictionary data) { - if (this.ActualVersion <= 0) - throw new InvalidOperationException("Cannot deserialise the previous version of our object when we are the first version"); - this.Registry.RunSerialisersInternal(false, this.CurrentObject, this.CurrentType, data, this.ActualVersion - 1); - } - } -} \ No newline at end of file diff --git a/SharpPad/Notepads/Views/NotepadWindow.xaml.cs b/SharpPad/Notepads/Views/NotepadWindow.xaml.cs index a995e0a..e5f933c 100644 --- a/SharpPad/Notepads/Views/NotepadWindow.xaml.cs +++ b/SharpPad/Notepads/Views/NotepadWindow.xaml.cs @@ -26,6 +26,7 @@ using System.Windows.Threading; using ICSharpCode.AvalonEdit.Editing; using SharpPad.Interactivity.Contexts; +using SharpPad.Properties; using SharpPad.Tasks; using SharpPad.Themes; using SharpPad.Utils.RDA; @@ -65,9 +66,18 @@ public NotepadWindow() { if (sel.IsEmpty) { this.PART_CaretText.Text = $"{caret.Line}:{caret.Column} ({caret.Offset} offset)"; } + else if (sel is RectangleSelection rect) { + ICSharpCode.AvalonEdit.TextViewPosition end = rect.EndPosition; + int offsetA = area.Document.GetOffset(rect.StartPosition.Location); + int offsetB = area.Document.GetOffset(end.Location); + this.PART_CaretText.Text = $"{caret.Line}:{caret.Column} ({offsetA} -> {offsetB - end.Column + end.VisualColumn + 1})"; + } else { - int len = sel.Length; - this.PART_CaretText.Text = $"{caret.Line}:{caret.Column} ({caret.Offset - len} offset, {len} chars)"; + int offsetA = area.Document.GetOffset(sel.StartPosition.Location); + int offsetB = area.Document.GetOffset(sel.EndPosition.Location); + if (offsetA > offsetB) + Utils.Maths.Swap(ref offsetA, ref offsetB); + this.PART_CaretText.Text = $"{caret.Line}:{caret.Column} ({offsetA} offset, {(offsetB - offsetA)} chars)"; } } }, TimeSpan.FromSeconds(0.2)); @@ -78,6 +88,12 @@ public NotepadWindow() { taskManager.TaskCompleted += this.OnTaskCompleted; } + protected override void OnClosed(EventArgs e) { + base.OnClosed(e); + Settings.Default.NotepadWindowWidth = (int) this.Width; + Settings.Default.NotepadWindowHeight = (int) this.Height; + } + private void OnTaskStarted(TaskManager taskmanager, ActivityTask task, int index) { if (this.primaryActivity == null || this.primaryActivity.IsCompleted) { this.SetActivityTask(task); diff --git a/SharpPad/Properties/Settings.Designer.cs b/SharpPad/Properties/Settings.Designer.cs index 9eabc7e..e6b72de 100644 --- a/SharpPad/Properties/Settings.Designer.cs +++ b/SharpPad/Properties/Settings.Designer.cs @@ -9,18 +9,42 @@ //------------------------------------------------------------------------------ namespace SharpPad.Properties { - - + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.8.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; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("600")] + public int NotepadWindowWidth { + get { + return ((int)(this["NotepadWindowWidth"])); + } + set { + this["NotepadWindowWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("600")] + public int NotepadWindowHeight { + get { + return ((int)(this["NotepadWindowHeight"])); + } + set { + this["NotepadWindowHeight"] = value; + } + } } } diff --git a/SharpPad/Properties/Settings.settings b/SharpPad/Properties/Settings.settings index 033d7a5..0c7e0c2 100644 --- a/SharpPad/Properties/Settings.settings +++ b/SharpPad/Properties/Settings.settings @@ -1,7 +1,12 @@  - - - - - + + + + + 600 + + + 600 + + \ No newline at end of file diff --git a/SharpPad/RBC/BinaryUtils.cs b/SharpPad/RBC/BinaryUtils.cs deleted file mode 100644 index 6ff12b2..0000000 --- a/SharpPad/RBC/BinaryUtils.cs +++ /dev/null @@ -1,213 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SharpPad.RBC { - public static class BinaryUtils { - // Buffer.MemoryCopy implementation (on windows at least, .NET Framework) uses an internal function which accepts ulong length - // Passing int to MemoryCopy will cast to long (twice due to 'len, len') and those get cast to ulong (in a checked context) - // Just an optimisation to use ulong in these functions - public static unsafe void CopyArray(byte[] src, int srcBegin, byte* dst, int count) => Unsafe.CopyBlock(ref *dst, ref src[srcBegin], (uint) count); - - public static unsafe void WriteArray(byte* src, byte[] dst, int dstBegin, int count) => Unsafe.CopyBlock(ref dst[dstBegin], ref *src, (uint) count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ReadExact(Stream stream, byte[] buffer, int count) { - if (stream.Read(buffer, 0, count) != count) { - throw new EndOfStreamException($"Failed to read {count} bytes"); - } - } - - // public class ByteAlignment { - // public static readonly IntPtr ArrayAdjustment = MeasureArrayAdjustment(); - // private readonly byte data; - // public ByteAlignment(byte data) { - // this.data = data; - // } - // - // private static IntPtr MeasureArrayAdjustment() { - // byte[] objArray = new byte[1]; - // byte b = Unsafe.As(objArray).data; - // return new IntPtr(objArray[0] - b); - // } - // } - // - // public static void Fill(byte[] span, byte value) { - // Unsafe.InitBlockUnaligned(ref Unsafe.AddByteOffset(ref span[0], ByteAlignment.ArrayAdjustment), value, (uint) span.Length); - // } - - private const int LEN_U1 = 0b00; // unsigned byte - private const int LEN_U2 = 0b01; // unsigned short - private const int LEN_S4 = 0b10; // signed int - private const int MAX_U1 = (byte.MaxValue) >> 2; // 63 - private const int MAX_U2 = (byte.MaxValue << 8) | MAX_U1; // 65,343 - private const int MAX_S4 = (short.MaxValue << 16) | MAX_U2 | MAX_U1; // 2,147,483,455. Not using ushort because that results in integer overflow to -193 - - public static int ReadValue(BinaryReader reader, LengthReadStrategy strategy) { - if (strategy != LengthReadStrategy.SegmentedLength) { - return -1; - } - - int a = reader.ReadByte(); - int mask = a & 0b11; - switch (mask) { - case LEN_U1: return a >> 2; - case LEN_U2: return ReadU2(reader, a); - case LEN_S4: return ReadU4(reader, a); - default: throw new Exception($"Invalid length mask. Byte = {a}"); - } - } - - public static bool WriteValue(BinaryWriter writer, LengthReadStrategy strategy, int length) { - if (strategy != LengthReadStrategy.SegmentedLength) { - return false; - } - - if (length <= MAX_U1) { - writer.Write((byte) ((length << 2) | LEN_U1)); - } - else if (length <= MAX_U2) { - writer.Write((ushort) ((length << 2) | LEN_U2)); - } - else if (length <= MAX_S4) { - writer.Write((length << 2) | LEN_S4); - } - else { - throw new Exception($"Length is too large to fit into a segmented length: {length}"); - } - - return true; - } - - public static int ReadU2(BinaryReader reader, int a) { - int b = reader.ReadByte(); - return b << 8 | (a >> 2); - } - - public static int ReadU4(BinaryReader reader, int a) { - int b = reader.ReadByte(); - int c = reader.ReadUInt16(); - return (c << 16) | (b << 8) | (a >> 2); - } - - public static TEnum ToEnum8(byte value) where TEnum : unmanaged, Enum => Unsafe.As(ref value); - public static TEnum ToEnum16(short value) where TEnum : unmanaged, Enum => Unsafe.As(ref value); - public static TEnum ToEnum32(int value) where TEnum : unmanaged, Enum => Unsafe.As(ref value); - public static TEnum ToEnum64(long value) where TEnum : unmanaged, Enum => Unsafe.As(ref value); - public static byte FromEnum8(TEnum value) where TEnum : unmanaged, Enum => Unsafe.As(ref value); - public static short FromEnum16(TEnum value) where TEnum : unmanaged, Enum => Unsafe.As(ref value); - public static int FromEnum32(TEnum value) where TEnum : unmanaged, Enum => Unsafe.As(ref value); - public static long FromEnum64(TEnum value) where TEnum : unmanaged, Enum => Unsafe.As(ref value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ReadStruct(byte[] array, int offset) where T : unmanaged { - // return MemoryMarshal.Read(new ReadOnlySpan(array, offset, size)); - // T value = default; - // Unsafe.CopyBlock(ref *(byte*) &value, ref array[offset], (uint) size); - // return value; - return Unsafe.ReadUnaligned(ref array[offset]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteStruct(T value, byte[] array, int offset) where T : unmanaged { - // MemoryMarshal.Write(new Span(array, offset, size), ref value); - // byte* src = (byte*) &value; - // Unsafe.CopyBlock(ref array[offset], ref *src, (uint) sizeof(T)); - Unsafe.WriteUnaligned(ref array[offset], value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteEmpty(byte[] array, int offset, int size) { - // byte zero = 0; - // MemoryMarshal.Write(new Span(array, offset, size), ref zero); - Unsafe.InitBlock(ref array[offset], 0, (uint) size); - } - - public static unsafe void WriteStruct(this BinaryWriter writer, T value) where T : unmanaged { - switch (sizeof(T)) { - case 1: - writer.Write(Unsafe.As(ref value)); - return; - case 2: - writer.Write(Unsafe.As(ref value)); - return; - case 4: - writer.Write(Unsafe.As(ref value)); - return; - case 8: - writer.Write(Unsafe.As(ref value)); - return; - case 12: { - writer.Write(Unsafe.As(ref value)); - writer.Write(Unsafe.As(ref Unsafe.AddByteOffset(ref value, (IntPtr) 8))); - return; - } - case 16: { - writer.Write(Unsafe.As(ref value)); - writer.Write(Unsafe.As(ref Unsafe.AddByteOffset(ref value, (IntPtr) 8))); - return; - } - default: throw new ArgumentException($"sizeof({typeof(T)}) must be >= 0 && <= 16"); - } - } - - public static unsafe T ReadStruct(this BinaryReader reader) where T : unmanaged { - switch (sizeof(T)) { - case 1: { - byte v = reader.ReadByte(); - return Unsafe.As(ref v); - } - case 2: { - ushort v = reader.ReadUInt16(); - return Unsafe.As(ref v); - } - case 4: { - uint v = reader.ReadUInt32(); - return Unsafe.As(ref v); - } - case 8: { - ulong v = reader.ReadUInt64(); - return Unsafe.As(ref v); - } - case 16: { - ulong a = reader.ReadUInt64(); - ulong b = reader.ReadUInt64(); - Block16 blk = new Block16(a, b); - return Unsafe.As(ref blk); - } - default: throw new ArgumentException($"sizeof({typeof(T)}) must be a power of 2 and a maximum of 16 bytes wide"); - } - } - - [StructLayout(LayoutKind.Sequential)] - private readonly struct Block16 { - public readonly ulong a; - public readonly ulong b; - - public Block16(ulong a, ulong b) { - this.a = a; - this.b = b; - } - } - } -} \ No newline at end of file diff --git a/SharpPad/RBC/Events/ReadFromRBEEventHandler.cs b/SharpPad/RBC/Events/ReadFromRBEEventHandler.cs deleted file mode 100644 index 67e6ef7..0000000 --- a/SharpPad/RBC/Events/ReadFromRBEEventHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.RBC.Events { - public delegate void ReadFromRBEEventHandler(object sender, RBEDictionary data); -} \ No newline at end of file diff --git a/SharpPad/RBC/Events/WriteToRBEEventHandler.cs b/SharpPad/RBC/Events/WriteToRBEEventHandler.cs deleted file mode 100644 index b93bf3c..0000000 --- a/SharpPad/RBC/Events/WriteToRBEEventHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.RBC.Events { - public delegate void WriteToRBEEventHandler(object sender, RBEDictionary data); -} \ No newline at end of file diff --git a/SharpPad/RBC/RBEArray.cs b/SharpPad/RBC/RBEArray.cs deleted file mode 100644 index 8417b35..0000000 --- a/SharpPad/RBC/RBEArray.cs +++ /dev/null @@ -1,425 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.IO; -using SharpPad.Utils; - -namespace SharpPad.RBC { - public enum LengthReadStrategy { - /// - /// Appends a byte, short, int or long before the data to indicate the length. - /// This is fixed per RBE type and does not change, and is therefore the easiest to implement - /// - LazyLength, - - /// - /// Uses the first two bits to determine how many additional bytes to read to discover the length. This is used to - /// compress the data as much as possible, but is harder to implement and typically slower to read/write, and can - /// sometimes result in more data being used overall (255 >> 2 == 63, meaning if you need 64 values, you need 2 bytes to store 64) - /// - SegmentedLength - } - - /// - /// Used to store an array of bytes (unsigned) - /// - public class RBEByteArray : RBEBase { - public byte[] Array { get; set; } - - public bool IsEmpty { - get => this.Array == null || this.Array.Length == 0; - } - - public override RBEType Type => RBEType.ByteArray; - - public RBEByteArray() { } - - public RBEByteArray(byte[] array) { - this.Array = array; - } - - protected override void Read(BinaryReader reader) { - this.Array = new byte[reader.ReadInt32()]; - reader.Read(this.Array, 0, this.Array.Length); - } - - protected override void Write(BinaryWriter writer) { - if (this.Array != null) { - writer.Write(this.Array.Length); - writer.Write(this.Array); - } - else { - writer.Write(0); - } - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBEByteArray CloneCore() { - return new RBEByteArray(Arrays.CloneArrayUnsafe(this.Array)); - } - } - - public class RBEShortArray : RBEBase { - public short[] Array { get; set; } - - public bool IsEmpty { - get => this.Array == null || this.Array.Length == 0; - } - - public override RBEType Type => RBEType.ShortArray; - - public RBEShortArray() { } - - public RBEShortArray(short[] array) { - this.Array = array; - } - - protected override void Read(BinaryReader reader) { - int length = reader.ReadInt32(); - short[] array = this.Array = new short[length]; - for (int i = 0; i < length; i++) { - array[i] = reader.ReadInt16(); - } - } - - protected override void Write(BinaryWriter writer) { - if (this.Array != null) { - writer.Write(this.Array.Length); - foreach (short value in this.Array) { - writer.Write(value); - } - } - else { - writer.Write(0); - } - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBEShortArray CloneCore() { - return new RBEShortArray(Arrays.CloneArrayUnsafe(this.Array)); - } - } - - /// - /// Used to store an array of integers (signed) - /// - public class RBEIntArray : RBEBase { - public int[] Array { get; set; } - - public bool IsEmpty { - get => this.Array == null || this.Array.Length == 0; - } - - public override RBEType Type => RBEType.IntArray; - - public RBEIntArray() { } - - public RBEIntArray(int[] array) { - this.Array = array; - } - - protected override void Read(BinaryReader reader) { - int length = reader.ReadInt32(); - int[] array = this.Array = new int[length]; - for (int i = 0; i < length; i++) { - array[i] = reader.ReadInt32(); - } - - // optimise for reading long? - // for (int i = 0, end = length >> 1; i <= end; i++) { } - } - - protected override void Write(BinaryWriter writer) { - if (this.Array != null) { - writer.Write(this.Array.Length); - foreach (int value in this.Array) { - writer.Write(value); - } - } - else { - writer.Write(0); - } - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBEIntArray CloneCore() { - return new RBEIntArray(Arrays.CloneArrayUnsafe(this.Array)); - } - } - - public class RBELongArray : RBEBase { - public long[] Array { get; set; } - - public bool IsEmpty { - get => this.Array == null || this.Array.Length == 0; - } - - public override RBEType Type => RBEType.LongArray; - - public RBELongArray() { } - - public RBELongArray(long[] array) { - this.Array = array; - } - - protected override void Read(BinaryReader reader) { - int length = reader.ReadInt32(); - long[] array = this.Array = new long[length]; - for (int i = 0; i < length; i++) { - array[i] = reader.ReadInt64(); - } - - // optimise for reading long? - // for (int i = 0, end = length >> 1; i <= end; i++) { } - } - - protected override void Write(BinaryWriter writer) { - if (this.Array != null) { - writer.Write(this.Array.Length); - foreach (long value in this.Array) { - writer.Write(value); - } - } - else { - writer.Write(0); - } - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBELongArray CloneCore() { - return new RBELongArray(Arrays.CloneArrayUnsafe(this.Array)); - } - } - - public class RBEFloatArray : RBEBase { - public float[] Array { get; set; } - - public bool IsEmpty { - get => this.Array == null || this.Array.Length == 0; - } - - public override RBEType Type => RBEType.FloatArray; - - public RBEFloatArray() { } - - public RBEFloatArray(float[] array) { - this.Array = array; - } - - protected override void Read(BinaryReader reader) { - int length = reader.ReadInt32(); - float[] array = this.Array = new float[length]; - for (int i = 0; i < length; i++) { - array[i] = reader.ReadSingle(); - } - } - - protected override void Write(BinaryWriter writer) { - if (this.Array != null) { - writer.Write(this.Array.Length); - foreach (float value in this.Array) { - writer.Write(value); - } - } - else { - writer.Write(0); - } - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBEFloatArray CloneCore() { - return new RBEFloatArray(Arrays.CloneArrayUnsafe(this.Array)); - } - } - - public class RBEDoubleArray : RBEBase { - public double[] Array { get; set; } - - public bool IsEmpty { - get => this.Array == null || this.Array.Length == 0; - } - - public override RBEType Type => RBEType.DoubleArray; - - public RBEDoubleArray() { } - - public RBEDoubleArray(double[] array) { - this.Array = array; - } - - protected override void Read(BinaryReader reader) { - int length = reader.ReadInt32(); - double[] array = this.Array = new double[length]; - for (int i = 0; i < length; i++) { - array[i] = reader.ReadDouble(); - } - } - - protected override void Write(BinaryWriter writer) { - if (this.Array != null) { - writer.Write(this.Array.Length); - foreach (double value in this.Array) { - writer.Write(value); - } - } - else { - writer.Write(0); - } - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBEDoubleArray CloneCore() { - return new RBEDoubleArray(Arrays.CloneArrayUnsafe(this.Array)); - } - } - - public class RBEStringArray : RBEBase { - public override RBEType Type => RBEType.StringArray; - - public string[] Array { get; set; } - - public RBEStringArray() { } - - public RBEStringArray(string[] array) { - this.Array = array; - } - - protected override void Read(BinaryReader reader) { - int length = reader.ReadInt32(); - string[] array = this.Array = new string[length]; - for (int i = 0; i < length; i++) { - array[i] = RBEString.ReadString(reader); - } - } - - protected override void Write(BinaryWriter writer) { - if (this.Array != null) { - writer.Write(this.Array.Length); - foreach (string value in this.Array) { - RBEString.WriteString(writer, value); - } - } - else { - writer.Write(0); - } - } - - public override RBEBase Clone() => this.CloneCore(); - public RBEStringArray CloneCore() => new RBEStringArray(Arrays.CloneArrayMax(this.Array)); - } - - public class RBEStructArray : RBEBase { - private byte[] data; - - public override RBEType Type => RBEType.StructArray; - - public RBEStructArray() { } - - public static RBEStructArray ForValues(T[] value) where T : unmanaged { - RBEStructArray rbe = new RBEStructArray(); - rbe.SetValues(value); - return rbe; - } - - protected override void Read(BinaryReader reader) { - int length = reader.ReadInt32(); - this.data = new byte[length]; - if (reader.Read(this.data, 0, length) != length) { - throw new IOException("Failed to read " + length + " bytes"); - } - } - - protected override void Write(BinaryWriter writer) { - if (this.data == null) { - throw new InvalidOperationException("Array has not been set yet"); - } - - // could possibly optimise this; use 2 bits to indicate the size type: - // byte,short,int,long (long is a bit excessive though) - // then bitshift the actual size by 2, cast to size type, and write? - writer.Write(this.data.Length); - writer.Write(this.data); - } - - public T[] GetValues() where T : unmanaged { - unsafe { - byte[] array = this.data; - if (array == null) { - throw new Exception("Binary data has not been read yet"); - } - - int size = sizeof(T); - if ((array.Length % size) != 0) { - throw new Exception($"Binary data size is inconsistent with the struct size (binary({array.Length}) % struct({size})) != 0"); - } - - int len = array.Length / size; - T[] values = new T[len]; - for (int i = 0, offset = 0; i < len; i++, offset += size) { - values[i] = BinaryUtils.ReadStruct(array, offset); - } - - return values; - } - } - - public bool TryGetValues(out T[] values) where T : unmanaged { - byte[] array = this.data; - unsafe { - int size; - if (array == null || ((size = sizeof(T)) % size) != 0) { - values = null; - return false; - } - - int len = array.Length / size; - values = new T[len]; - for (int i = 0, offset = 0; i < len; i++, offset += size) { - values[i] = BinaryUtils.ReadStruct(array, offset); - } - - return true; - } - } - - public void SetValues(T[] values) where T : unmanaged { - unsafe { - int size = sizeof(T); - int length = values.Length; - byte[] array = this.data = new byte[size * length]; - for (int i = 0, offset = 0; i < length; i++, offset += size) { - BinaryUtils.WriteStruct(values[i], array, offset); - } - } - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBEStructArray CloneCore() { - return new RBEStructArray {data = Arrays.CloneArrayUnsafe(this.data)}; - } - } -} \ No newline at end of file diff --git a/SharpPad/RBC/RBEBase.cs b/SharpPad/RBC/RBEBase.cs deleted file mode 100644 index 112f67d..0000000 --- a/SharpPad/RBC/RBEBase.cs +++ /dev/null @@ -1,210 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Collections.Generic; -using System.IO; - -namespace SharpPad.RBC { - /// - /// The base class for the RBE (REghZy Binary Element... i know right what a sexy acronym) - /// - /// This is used for list/dictionary based binary structures - /// - /// - /// This is based on minecraft's NBT structure, because it's pretty good... for the most part. The - /// primary binary difference is that does not store its child type, whereas - /// minecraft does (for some reason...? lists maintain their order so the dev should know the right types) - /// - /// - public abstract class RBEBase { - private static readonly Dictionary TypeToIdTable; - - /// - /// This element's type - /// - public abstract RBEType Type { get; } - - static RBEBase() { - TypeToIdTable = new Dictionary { - {typeof(RBEDictionary), RBEType.Dictionary}, - {typeof(RBEList), RBEType.List}, - {typeof(RBEByte), RBEType.Byte}, - {typeof(RBEShort), RBEType.Short}, - {typeof(RBEInt), RBEType.Int}, - {typeof(RBELong), RBEType.Long}, - {typeof(RBEFloat), RBEType.Float}, - {typeof(RBEDouble), RBEType.Double}, - {typeof(RBEString), RBEType.String}, - {typeof(RBEStruct), RBEType.Struct}, - {typeof(RBEByteArray), RBEType.ByteArray}, - {typeof(RBEShortArray), RBEType.ShortArray}, - {typeof(RBEIntArray), RBEType.IntArray}, - {typeof(RBELongArray), RBEType.LongArray}, - {typeof(RBEFloatArray), RBEType.FloatArray}, - {typeof(RBEDoubleArray), RBEType.DoubleArray}, - {typeof(RBEStringArray), RBEType.StringArray}, - {typeof(RBEStructArray), RBEType.StructArray}, - {typeof(RBEGuid), RBEType.Guid} - }; - } - - protected RBEBase() { } - - /// - /// Reads this element's data from the given binary reader. This may be a recursive operation (for lists, dictionaries, etc) - /// - /// The reader (data source) - protected abstract void Read(BinaryReader reader); - - /// - /// Writes this element's data into the given binary writer. This may be a recursive operation (for lists, dictionaries, etc) - /// - /// The writer (data target) - protected abstract void Write(BinaryWriter writer); - - /// - /// Reads this element's data in packed form from the given binary reader. By default, this just invokes . - /// Collection based elements are the only ones that need to override this, as they form a recursive read operation - /// - /// The reader (data source) - /// - /// The dictionary which maps a key index to the actual string key (used by dictionary based elements, like ) - /// - protected virtual void ReadPacked(BinaryReader reader, Dictionary packData) { - this.Read(reader); - } - - /// - /// Writes this element's data, in packed form, into the given binary writer. By default, this just invokes . - /// Collection based elements are the only ones that need to override this, as they form a recursive write operation - /// - /// The writer (data target) - /// - /// A pre-computed dictionary which maps all string keys to an index which should - /// be written instead of the actual key (used by dictionary based elements, like ) - /// - protected virtual void WritePacked(BinaryWriter writer, Dictionary dictionary) { - this.Write(writer); - } - - /// - /// Accumulates all of the keys that this element uses. This is a recursive operation, and is invoked before any elements are - /// written via . Entries added to the dictionary should set the index as the dictionary's current count, - /// which saves passing an integer reference - /// - /// The dictionary, in which entries should be added to - protected internal virtual void AccumulatePackedEntries(Dictionary dictionary) { } - - /// - /// Creates a deep clone of this element - /// - /// A new element which contains no references (at all) to the instance that was originally cloned - public abstract RBEBase Clone(); - - public override string ToString() { - return TryGetIdByType(this.GetType(), out RBEType type) ? type.ToString() : this.GetType().ToString(); - } - - /// - /// Reads an RBE object from the given binary reader - /// - /// Binary data source - /// - public static RBEBase ReadIdAndElement(BinaryReader reader) { - byte id = reader.ReadByte(); - RBEBase element = CreateById((RBEType) id); - element.Read(reader); - return element; - } - - public static void WriteIdAndElement(BinaryWriter writer, RBEBase rbe) { - writer.Write((byte) rbe.Type); - rbe.Write(writer); - } - - public static RBEBase ReadIdAndElementPacked(BinaryReader reader, Dictionary dictionary) { - byte id = reader.ReadByte(); - RBEBase element = CreateById((RBEType) id); - element.ReadPacked(reader, dictionary); - return element; - } - - public static void WriteIdAndElementPacked(BinaryWriter writer, RBEBase rbe, Dictionary dictionary) { - writer.Write((byte) rbe.Type); - rbe.WritePacked(writer, dictionary); - } - - public static RBEBase CreateById(RBEType id) { - switch (id) { - case RBEType.Dictionary: return new RBEDictionary(); - case RBEType.List: return new RBEList(); - case RBEType.Byte: return new RBEByte(); - case RBEType.Short: return new RBEShort(); - case RBEType.Int: return new RBEInt(); - case RBEType.Long: return new RBELong(); - case RBEType.Float: return new RBEFloat(); - case RBEType.Double: return new RBEDouble(); - case RBEType.String: return new RBEString(); - case RBEType.Struct: return new RBEStruct(); - case RBEType.ByteArray: return new RBEByteArray(); - case RBEType.ShortArray: return new RBEShortArray(); - case RBEType.IntArray: return new RBEIntArray(); - case RBEType.LongArray: return new RBELongArray(); - case RBEType.FloatArray: return new RBEFloatArray(); - case RBEType.DoubleArray: return new RBEDoubleArray(); - case RBEType.StringArray: return new RBEStringArray(); - case RBEType.StructArray: return new RBEStructArray(); - case RBEType.Guid: return new RBEGuid(); - default: throw new ArgumentOutOfRangeException(nameof(id), id, "Unknown RBEType: " + id); - } - } - - public static bool TryGetIdByType(Type type, out RBEType rbeType) => TypeToIdTable.TryGetValue(type, out rbeType); - - public static Type GetTypeById(RBEType rbeType) { - switch (rbeType) { - case RBEType.Dictionary: return typeof(RBEDictionary); - case RBEType.List: return typeof(RBEList); - case RBEType.Byte: return typeof(RBEByte); - case RBEType.Short: return typeof(RBEShort); - case RBEType.Int: return typeof(RBEInt); - case RBEType.Long: return typeof(RBELong); - case RBEType.Float: return typeof(RBEFloat); - case RBEType.Double: return typeof(RBEDouble); - case RBEType.String: return typeof(RBEString); - case RBEType.Struct: return typeof(RBEStruct); - case RBEType.ByteArray: return typeof(RBEByteArray); - case RBEType.ShortArray: return typeof(RBEShortArray); - case RBEType.IntArray: return typeof(RBEIntArray); - case RBEType.LongArray: return typeof(RBELongArray); - case RBEType.FloatArray: return typeof(RBEFloatArray); - case RBEType.DoubleArray: return typeof(RBEDoubleArray); - case RBEType.StringArray: return typeof(RBEStringArray); - case RBEType.StructArray: return typeof(RBEStructArray); - case RBEType.Guid: return typeof(RBEGuid); - default: throw new ArgumentOutOfRangeException(nameof(rbeType), rbeType, "Unknown RBEType: " + rbeType); - } - } - - protected static string GetReadableTypeName(Type type) { - return TryGetIdByType(type, out RBEType rbeType) ? rbeType.ToString() : type.Name; - } - } -} \ No newline at end of file diff --git a/SharpPad/RBC/RBEDictionary.cs b/SharpPad/RBC/RBEDictionary.cs deleted file mode 100644 index 916871f..0000000 --- a/SharpPad/RBC/RBEDictionary.cs +++ /dev/null @@ -1,361 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Collections.Generic; -using System.IO; - -namespace SharpPad.RBC { - /// - /// Used to store named elements using a dictionary - /// - public class RBEDictionary : RBEBase { - public Dictionary Map { get; private set; } - - public override RBEType Type => RBEType.Dictionary; - - public RBEBase this[string key] { - get => this.Map.TryGetValue(ValidateKey(key), out RBEBase value) ? value : null; - set { - key = ValidateKey(key); - if (value != null) { - this.Map[key] = value; - } - else { - this.Map.Remove(key); - } - } - } - - public RBEDictionary() { - this.Map = new Dictionary(); - } - - public RBEDictionary(Dictionary map) { - this.Map = map ?? throw new ArgumentNullException(nameof(map), "Map cannot be null"); - } - - #region Getters And Setters (and similar util functions) - - public bool ContainsKey(string key) => this.Map.ContainsKey(ValidateKey(key)); - - public T GetElement(string key) where T : RBEBase { - if (this.TryGetElement(key, out T value)) { - return value; - } - - string readableTypeName = TryGetIdByType(typeof(T), out RBEType type) ? type.ToString() : typeof(T).ToString(); - throw new Exception($"No such entry '{key}' of type {readableTypeName}"); - } - - public bool TryGetElement(string key, out T element) where T : RBEBase { - if (this.Map.TryGetValue(ValidateKey(key), out RBEBase rbeBase) && rbeBase is T rbe) { - element = rbe; - return true; - } - - element = default; - return false; - } - - public bool TryGetElementValue(string key, Func elemToVal, out T value) where TElement : RBEBase { - if (this.TryGetElement(key, out TElement element)) { - value = elemToVal(element); - return true; - } - - value = default; - return false; - } - - public RBEDictionary GetDictionary(string key) => this.GetElement(key); - public RBEDictionary GetDictionary(string key, RBEDictionary def) => this.TryGetElement(key, out RBEDictionary rbe) ? rbe : def; - public bool TryGetDictionary(string key, out RBEDictionary value) => (value = this.GetDictionary(key, null)) != null; - - public RBEDictionary GetOrCreateDictionary(string key) { - if (!this.TryGetElement(key, out RBEDictionary dictionary)) - this[key] = dictionary = new RBEDictionary(); - return dictionary; - } - - public RBEDictionary CreateDictionary(string key) { - if (this.ContainsKey(key)) - throw new Exception("Key already in use: " + key); - RBEDictionary dictionary = new RBEDictionary(); - this[key] = dictionary; - return dictionary; - } - - public RBEList GetList(string key) => this.GetElement(key); - public RBEList GetList(string key, RBEList def) => this.TryGetElement(key, out RBEList rbe) ? rbe : def; - public bool TryGetList(string key, out RBEList value) => (value = this.GetList(key, null)) != null; - - public RBEList GetOrCreateList(string key) { - if (!this.TryGetElement(key, out RBEList dictionary)) - this[key] = dictionary = new RBEList(); - return dictionary; - } - - public RBEList CreateList(string key) { - if (this.ContainsKey(key)) - throw new Exception("Key already in use: " + key); - RBEList list = new RBEList(); - this[key] = list; - return list; - } - - public bool GetBool(string key) => this.GetByte(key) != 0; - public bool GetBool(string key, bool def) => this.TryGetElement(key, out RBEByte rbe) ? (rbe.Value != 0) : def; - public bool TryGetBool(string key, out bool value) => this.TryGetElementValue(key, e => e.Value != 0, out value); - - // These are kinda pointless since you can just cast to/from the appropriately sized integer, but still - - public T GetEnum8(string key) where T : unmanaged, Enum => BinaryUtils.ToEnum8(this.GetByte(key)); - public T GetEnum8(string key, T def) where T : unmanaged, Enum => this.TryGetElement(key, out RBEByte rbe) ? BinaryUtils.ToEnum8(rbe.Value) : def; - public bool TryGetEnum8(string key, out T value) where T : unmanaged, Enum => this.TryGetElementValue(key, e => BinaryUtils.ToEnum8(e.Value), out value); - - public T GetEnum16(string key) where T : unmanaged, Enum => BinaryUtils.ToEnum16(this.GetByte(key)); - public T GetEnum16(string key, T def) where T : unmanaged, Enum => this.TryGetElement(key, out RBEByte rbe) ? BinaryUtils.ToEnum16(rbe.Value) : def; - public bool TryGetEnum16(string key, out T value) where T : unmanaged, Enum => this.TryGetElementValue(key, e => BinaryUtils.ToEnum16(e.Value), out value); - - public T GetEnum32(string key) where T : unmanaged, Enum => BinaryUtils.ToEnum32(this.GetByte(key)); - public T GetEnum32(string key, T def) where T : unmanaged, Enum => this.TryGetElement(key, out RBEByte rbe) ? BinaryUtils.ToEnum32(rbe.Value) : def; - public bool TryGetEnum32(string key, out T value) where T : unmanaged, Enum => this.TryGetElementValue(key, e => BinaryUtils.ToEnum32(e.Value), out value); - - public T GetEnum64(string key) where T : unmanaged, Enum => BinaryUtils.ToEnum64(this.GetByte(key)); - public T GetEnum64(string key, T def) where T : unmanaged, Enum => this.TryGetElement(key, out RBEByte rbe) ? BinaryUtils.ToEnum64(rbe.Value) : def; - public bool TryGetEnum64(string key, out T value) where T : unmanaged, Enum => this.TryGetElementValue(key, e => BinaryUtils.ToEnum64(e.Value), out value); - - public byte GetByte(string key) => this.GetElement(key).Value; - public byte GetByte(string key, byte def) => this.TryGetElement(key, out RBEByte rbe) ? rbe.Value : def; - public bool TryGetByte(string key, out byte value) => this.TryGetElementValue(key, e => e.Value, out value); - - public short GetShort(string key) => this.GetElement(key).Value; - public short GetShort(string key, short def) => this.TryGetElement(key, out RBEShort rbe) ? rbe.Value : def; - public bool TryGetShort(string key, out short value) => this.TryGetElementValue(key, e => e.Value, out value); - - public int GetInt(string key) => this.GetElement(key).Value; - public int GetInt(string key, int def) => this.TryGetElement(key, out RBEInt rbe) ? rbe.Value : def; - public bool TryGetInt(string key, out int value) => this.TryGetElementValue(key, e => e.Value, out value); - - public uint GetUInt(string key) => (uint) this.GetElement(key).Value; - public uint GetUInt(string key, uint def) => this.TryGetElement(key, out RBEInt rbe) ? (uint) rbe.Value : def; - public bool TryGetUInt(string key, out uint value) => this.TryGetElementValue(key, e => (uint) e.Value, out value); - - public long GetLong(string key) => this.GetElement(key).Value; - public long GetLong(string key, long def) => this.TryGetElement(key, out RBELong rbe) ? rbe.Value : def; - public bool TryGetLong(string key, out long value) => this.TryGetElementValue(key, e => e.Value, out value); - - public ulong GetULong(string key) => (ulong) this.GetElement(key).Value; - public ulong GetULong(string key, ulong def) => this.TryGetElement(key, out RBELong rbe) ? (ulong) rbe.Value : def; - public bool TryGetULong(string key, out ulong value) => this.TryGetElementValue(key, e => (ulong) e.Value, out value); - - public float GetFloat(string key) => this.GetElement(key).Value; - public float GetFloat(string key, float def) => this.TryGetElement(key, out RBEFloat rbe) ? rbe.Value : def; - public bool TryGetFloat(string key, out float value) => this.TryGetElementValue(key, e => e.Value, out value); - - public double GetDouble(string key) => this.GetElement(key).Value; - public double GetDouble(string key, double def) => this.TryGetElement(key, out RBEDouble rbe) ? rbe.Value : def; - public bool TryGetDouble(string key, out double value) => this.TryGetElementValue(key, e => e.Value, out value); - - public string GetString(string key) => this.GetElement(key).Value; - public string GetString(string key, string def) => this.TryGetElement(key, out RBEString rbe) ? rbe.Value : def; - public bool TryGetString(string key, out string value) => this.TryGetElementValue(key, e => e.Value, out value); - - // public string GetLongString(string key) => GetString(this.GetElement(key).List); - // public string GetLongString(string key, string def) => this.TryGetElement(key, out RBEList rbe) ? GetString(rbe.List) : def; - // public bool TryGetLongString(string key, out string value) => this.TryGetElementValue(key, e => GetString(e.List), out value); - - // private static string GetString(List list) { - // StringBuilder sb = new StringBuilder(ushort.MaxValue * 2); - // foreach (RBEBase rbe in list) { - // if (!(rbe is RBEString)) - // throw new Exception("Expected list to contain only string elements"); - // sb.Append(((RBEString) rbe).Value); - // } - // return sb.ToString(); - // } - - // private static void SetString(RBEList list, string value) { - // int i = 0, j, c = value.Length; - // do { - // j = i; - // i += ushort.MaxValue; - // list.Add(new RBEString(value.JSubstring(j, Math.Min(i, c)))); - // } while (i < c); - // } - - public T GetStruct(string key) where T : unmanaged => this.GetElement(key).GetValue(); - public T GetStruct(string key, T def) where T : unmanaged => this.TryGetElement(key, out RBEStruct rbe) ? rbe.GetValue() : def; - - public bool TryGetStruct(string key, out T value) where T : unmanaged { - if (this.TryGetElement(key, out RBEStruct rbe) && rbe.TryGetValue(out value)) - return true; - value = default; - return false; - } - - public byte[] GetByteArray(string key) => this.GetElement(key).Array; - public byte[] GetByteArray(string key, byte[] def) => this.TryGetElement(key, out RBEByteArray rbe) ? rbe.Array : def; - public bool TryGetByteArray(string key, out byte[] value) => this.TryGetElementValue(key, e => e.Array, out value); - - public short[] GetShortArray(string key) => this.GetElement(key).Array; - public short[] GetShortArray(string key, short[] def) => this.TryGetElement(key, out RBEShortArray rbe) ? rbe.Array : def; - public bool TryGetShortArray(string key, out short[] value) => this.TryGetElementValue(key, e => e.Array, out value); - - public int[] GetIntArray(string key) => this.GetElement(key).Array; - public int[] GetIntArray(string key, int[] def) => this.TryGetElement(key, out RBEIntArray rbe) ? rbe.Array : def; - public bool TryGetIntArray(string key, out int[] value) => this.TryGetElementValue(key, e => e.Array, out value); - - public long[] GetLongArray(string key) => this.GetElement(key).Array; - public long[] GetLongArray(string key, long[] def) => this.TryGetElement(key, out RBELongArray rbe) ? rbe.Array : def; - public bool TryGetLongArray(string key, out long[] value) => this.TryGetElementValue(key, e => e.Array, out value); - - public float[] GetFloatArray(string key) => this.GetElement(key).Array; - public float[] GetFloatArray(string key, float[] def) => this.TryGetElement(key, out RBEFloatArray rbe) ? rbe.Array : def; - public bool TryGetFloatArray(string key, out float[] value) => this.TryGetElementValue(key, e => e.Array, out value); - - public double[] GetDoubleArray(string key) => this.GetElement(key).Array; - public double[] GetDoubleArray(string key, double[] def) => this.TryGetElement(key, out RBEDoubleArray rbe) ? rbe.Array : def; - public bool TryGetDoubleArray(string key, out double[] value) => this.TryGetElementValue(key, e => e.Array, out value); - - public string[] GetStringArray(string key) => this.GetElement(key).Array; - public string[] GetStringArray(string key, string[] def) => this.TryGetElement(key, out RBEStringArray rbe) ? rbe.Array : def; - public bool TryGetStringArray(string key, out string[] value) => this.TryGetElementValue(key, e => e.Array, out value); - - public T[] GetStructArray(string key) where T : unmanaged => this.GetElement(key).GetValues(); - public T[] GetStructArray(string key, T[] def) where T : unmanaged => this.TryGetElement(key, out RBEStructArray rbe) ? rbe.GetValues() : def; - - public bool TryGetStructArray(string key, out T[] value) where T : unmanaged { - if (this.TryGetElement(key, out RBEStructArray rbe) && rbe.TryGetValues(out value)) { - return true; - } - - value = default; - return false; - } - - public Guid GetGuid(string key) => this.GetElement(key).Value; - public Guid GetGuid(string key, Guid def) => this.TryGetElement(key, out RBEGuid rbe) ? rbe.Value : def; - public bool TryGetGuid(string key, out Guid value) => this.TryGetElementValue(key, e => e.Value, out value); - - public void SetDictionary(string key, Dictionary value) => this[key] = new RBEDictionary(value); - public void SetList(string key, List value) => this[key] = new RBEList(value); - public void SetBool(string key, bool value) => this[key] = new RBEByte((byte) (value ? 1 : 0)); - public void SetEnum8(string key, T value) where T : unmanaged, Enum => this.SetByte(key, BinaryUtils.FromEnum8(value)); - public void SetEnum16(string key, T value) where T : unmanaged, Enum => this.SetShort(key, BinaryUtils.FromEnum16(value)); - public void SetEnum32(string key, T value) where T : unmanaged, Enum => this.SetInt(key, BinaryUtils.FromEnum32(value)); - public void SetEnum64(string key, T value) where T : unmanaged, Enum => this.SetLong(key, BinaryUtils.FromEnum64(value)); - public void SetByte(string key, byte value) => this[key] = new RBEByte(value); - public void SetShort(string key, short value) => this[key] = new RBEShort(value); - public void SetInt(string key, int value) => this[key] = new RBEInt(value); - public void SetUInt(string key, uint value) => this.SetInt(key, (int) value); - public void SetLong(string key, long value) => this[key] = new RBELong(value); - public void SetULong(string key, ulong value) => this.SetLong(key, (long) value); - public void SetFloat(string key, float value) => this[key] = new RBEFloat(value); - public void SetDouble(string key, double value) => this[key] = new RBEDouble(value); - public void SetString(string key, string value) => this[key] = new RBEString(value); - public void SetStruct(string key, in T value) where T : unmanaged => this[key] = RBEStruct.ForValue(in value); - public void SetByteArray(string key, byte[] array) => this[key] = new RBEByteArray(array); - public void SetShortArray(string key, short[] array) => this[key] = new RBEShortArray(array); - public void SetIntArray(string key, int[] array) => this[key] = new RBEIntArray(array); - public void SetLongArray(string key, long[] array) => this[key] = new RBELongArray(array); - public void SetFloatArray(string key, float[] array) => this[key] = new RBEFloatArray(array); - public void SetDoubleArray(string key, double[] array) => this[key] = new RBEDoubleArray(array); - public void SetStringArray(string key, string[] array) => this[key] = new RBEStringArray(array); - public void SetStructArray(string key, T[] array) where T : unmanaged => this[key] = RBEStructArray.ForValues(array); - public void SetGuid(string key, Guid value) => this[key] = new RBEGuid(value); - - #endregion - - protected override void Read(BinaryReader reader) { - int length = reader.ReadUInt16(); - this.Map = new Dictionary(length); - for (int i = 0; i < length; i++) { - string key = new string(reader.ReadChars(reader.ReadByte())); - RBEBase element = ReadIdAndElement(reader); - this.Map[key] = element; - } - } - - protected override void ReadPacked(BinaryReader reader, Dictionary packData) { - int length = reader.ReadUInt16(); - this.Map = new Dictionary(length); - for (int i = 0; i < length; i++) { - int index = reader.ReadInt32(); - if (!packData.TryGetValue(index, out string key)) - throw new Exception($"No such key for index: {index}"); - RBEBase element = ReadIdAndElementPacked(reader, packData); - this.Map[key] = element; - } - } - - protected override void Write(BinaryWriter writer) { - writer.Write((ushort) this.Map.Count); - foreach (KeyValuePair entry in this.Map) { - int length = entry.Key.Length; - if (length > 255) - throw new Exception($"Map contained a key longer than 255 characters: {length}"); - writer.Write((byte) length); - writer.Write(entry.Key.ToCharArray()); - WriteIdAndElement(writer, entry.Value); - } - } - - protected override void WritePacked(BinaryWriter writer, Dictionary dictionary) { - writer.Write((ushort) this.Map.Count); - foreach (KeyValuePair entry in this.Map) { - if (!dictionary.TryGetValue(entry.Key, out int index)) - throw new Exception($"No such index for key: {entry.Key}"); - writer.Write(index); - WriteIdAndElementPacked(writer, entry.Value, dictionary); - } - } - - protected internal override void AccumulatePackedEntries(Dictionary dictionary) { - foreach (KeyValuePair entry in this.Map) { - if (!dictionary.ContainsKey(entry.Key)) - dictionary[entry.Key] = dictionary.Count; - entry.Value.AccumulatePackedEntries(dictionary); - } - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBEDictionary CloneCore() { - Dictionary map = new Dictionary(this.Map.Count); - foreach (KeyValuePair element in this.Map) - map[element.Key] = element.Value.Clone(); - return new RBEDictionary(map); - } - - private static string ValidateKey(string key) { - // CanonicalizeKey - if (key == null) { - return ""; - } - else if (key.Length > 255) { - throw new ArgumentNullException(nameof(key), $"Key length must be less than 256 characters: {key.Length}"); - } - else { - return key; - } - } - } -} \ No newline at end of file diff --git a/SharpPad/RBC/RBEGuid.cs b/SharpPad/RBC/RBEGuid.cs deleted file mode 100644 index 42d3406..0000000 --- a/SharpPad/RBC/RBEGuid.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.IO; - -namespace SharpPad.RBC { - public class RBEGuid : RBEBase { - public override RBEType Type => RBEType.Guid; - - public Guid Value { get; set; } - - public RBEGuid() { } - - public RBEGuid(Guid value) { - this.Value = value; - } - - // These are probably ultra slow but faster than writing/reading strings - - protected override void Read(BinaryReader reader) { - this.Value = new Guid(reader.ReadBytes(16)); - } - - protected override void Write(BinaryWriter writer) { - writer.Write(this.Value.ToByteArray()); - } - - public override RBEBase Clone() => this.CloneCore(); - public RBEGuid CloneCore() => new RBEGuid(this.Value); - } -} \ No newline at end of file diff --git a/SharpPad/RBC/RBEList.cs b/SharpPad/RBC/RBEList.cs deleted file mode 100644 index 463a919..0000000 --- a/SharpPad/RBC/RBEList.cs +++ /dev/null @@ -1,313 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace SharpPad.RBC { - /// - /// Used to store an ordered list of elements - /// - public class RBEList : RBEBase { - public List List { get; private set; } - - public override RBEType Type => RBEType.List; - - public RBEList() : this(new List()) { } - - public RBEList(List children) { - this.List = children ?? throw new ArgumentNullException(nameof(children), "List cannot be null"); - } - - /// - /// Returns an enumerable of , and pre-checks the list to ensure all values are the correc type - /// - /// - /// - /// - public IEnumerable Cast() where T : RBEBase { - if (this.List.Any(x => !(x is T))) { - throw new Exception($"Expected list to contain only {GetReadableTypeName(typeof(T))} instances"); - } - - return this.List.Cast(); - } - - private bool GetElementByType(int index, out T value) where T : RBEBase { - if (index >= 0 && index < this.List.Count) { - if (this.List[index] is T val) { - value = val; - return true; - } - else { - throw new Exception($"Incompatible types: Attempted to get element named {index} of type {typeof(T)}, but type {this.List[index]?.GetType()} was found"); - } - } - else { - value = default; - return false; - } - } - - public RBEDictionary GetDictionaryElement(int index) { - return this.GetElementByType(index, out RBEDictionary rbe) ? rbe : null; - } - - public Dictionary GetDictionary(int index, Dictionary def = null) { - return this.GetElementByType(index, out RBEDictionary rbe) ? rbe.Map : def; - } - - public bool TryGetDictionary(int index, out Dictionary value) { - RBEDictionary element = this.GetDictionaryElement(index); - value = element?.Map; - return element != null; - } - - public RBEList GetListElement(int index) { - return this.GetElementByType(index, out RBEList rbe) ? rbe : null; - } - - public List GetList(int index, List def = null) { - return this.GetElementByType(index, out RBEList rbe) ? rbe.List : def; - } - - public bool TryGetList(int index, out List value) { - RBEList element = this.GetListElement(index); - value = element?.List; - return element != null; - } - - public RBEByte GetInt8Element(int index) { - return this.GetElementByType(index, out RBEByte rbe) ? rbe : null; - } - - public byte GetInt8(int index, byte def = default) { - return this.GetElementByType(index, out RBEByte rbe) ? rbe.Value : def; - } - - public bool TryGetInt8(int index, out byte value) { - RBEByte element = this.GetInt8Element(index); - value = element?.Value ?? default; - return element != null; - } - - public RBEShort GetInt16Element(int index) { - return this.GetElementByType(index, out RBEShort rbe) ? rbe : null; - } - - public short GetInt16(int index, short def = default) { - return this.GetElementByType(index, out RBEShort rbe) ? rbe.Value : def; - } - - public bool TryGetInt16(int index, out short value) { - RBEShort element = this.GetInt16Element(index); - value = element?.Value ?? default; - return element != null; - } - - public RBEInt GetInt32Element(int index) { - return this.GetElementByType(index, out RBEInt rbe) ? rbe : null; - } - - public int GetInt32(int index, int def = default) { - return this.GetElementByType(index, out RBEInt rbe) ? rbe.Value : def; - } - - public bool TryGetInt32(int index, out int value) { - RBEInt element = this.GetInt32Element(index); - value = element?.Value ?? default; - return element != null; - } - - public RBELong GetInt64Element(int index) { - return this.GetElementByType(index, out RBELong rbe) ? rbe : null; - } - - public long GetInt64(int index, long def = default) { - return this.GetElementByType(index, out RBELong rbe) ? rbe.Value : def; - } - - public bool TryGetInt64(int index, out long value) { - RBELong element = this.GetInt64Element(index); - value = element?.Value ?? default; - return element != null; - } - - public RBEFloat GetFloatElement(int index) { - return this.GetElementByType(index, out RBEFloat rbe) ? rbe : null; - } - - public float GetFloat(int index, float def = default) { - return this.GetElementByType(index, out RBEFloat rbe) ? rbe.Value : def; - } - - public bool TryGetFloat(int index, out float value) { - RBEFloat element = this.GetFloatElement(index); - value = element?.Value ?? default; - return element != null; - } - - public RBEDouble GetDoubleElement(int index) { - return this.GetElementByType(index, out RBEDouble rbe) ? rbe : null; - } - - public double GetDouble(int index, double def = default) { - return this.GetElementByType(index, out RBEDouble rbe) ? rbe.Value : def; - } - - public bool TryGetDouble(int index, out double value) { - RBEDouble element = this.GetDoubleElement(index); - value = element?.Value ?? default; - return element != null; - } - - public RBEStruct GetStructElement(int index) { - return this.GetElementByType(index, out RBEStruct rbe) ? rbe : null; - } - - public RBEStructArray GetStructArrayElement(int index) { - return this.GetElementByType(index, out RBEStructArray rbe) ? rbe : null; - } - - public T GetStruct(int index) where T : unmanaged { - return this.GetElementByType(index, out RBEStruct value) ? value.GetValue() : default; - } - - public T GetStruct(int index, T def) where T : unmanaged { - return this.GetElementByType(index, out RBEStruct value) ? value.GetValue() : def; - } - - public bool TryGetStruct(int index, out T value) where T : unmanaged { - RBEStruct element = this.GetStructElement(index); - value = element?.GetValue() ?? default; - return element != null; - } - - public T[] GetStructArray(int index, T[] def = default) where T : unmanaged { - return this.GetElementByType(index, out RBEStructArray value) ? value.GetValues() : def; - } - - public bool TryGetStructArray(int index, out T[] value) where T : unmanaged { - RBEStructArray element = this.GetStructArrayElement(index); - value = element?.GetValues(); - return value != null; - } - - public void Add(RBEBase element) { - this.List.Add(element); - } - - public RBEDictionary AddDictionary(Dictionary value = null) { - RBEDictionary dictionary = new RBEDictionary(value ?? new Dictionary()); - this.List.Add(dictionary); - return dictionary; - } - - public RBEList AddList(List value = null) { - RBEList list = new RBEList(value ?? new List()); - this.List.Add(list); - return list; - } - - public void AddInt8(byte value) { - this.List.Add(new RBEByte(value)); - } - - public void AddInt16(short value) { - this.List.Add(new RBEShort(value)); - } - - public void AddInt32(int value) { - this.List.Add(new RBEInt(value)); - } - - public void AddInt64(long value) { - this.List.Add(new RBELong(value)); - } - - public void AddFloat(float value) { - this.List.Add(new RBEFloat(value)); - } - - public void AddDouble(double value) { - this.List.Add(new RBEDouble(value)); - } - - public void AddStruct(in T value) where T : unmanaged { - RBEStruct obj = new RBEStruct(); - obj.SetValue(value); - this.List.Add(obj); - } - - public void AddStructArray(in T[] value) where T : unmanaged { - RBEStructArray obj = new RBEStructArray(); - obj.SetValues(value); - this.List.Add(obj); - } - - protected override void Read(BinaryReader reader) { - int length = reader.ReadUInt16(); - this.List = new List(length); - for (int i = 0; i < length; i++) { - this.List.Add(ReadIdAndElement(reader)); - } - } - - protected override void ReadPacked(BinaryReader reader, Dictionary packData) { - int length = reader.ReadUInt16(); - this.List = new List(length); - for (int i = 0; i < length; i++) { - this.List.Add(ReadIdAndElementPacked(reader, packData)); - } - } - - protected override void Write(BinaryWriter writer) { - writer.Write((ushort) this.List.Count); - foreach (RBEBase child in this.List) { - WriteIdAndElement(writer, child); - } - } - - protected override void WritePacked(BinaryWriter writer, Dictionary dictionary) { - writer.Write((ushort) this.List.Count); - foreach (RBEBase child in this.List) { - WriteIdAndElementPacked(writer, child, dictionary); - } - } - - protected internal override void AccumulatePackedEntries(Dictionary dictionary) { - foreach (RBEBase rbe in this.List) { - rbe.AccumulatePackedEntries(dictionary); - } - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBEList CloneCore() { - List list = new List(this.List); - // not using Select because there's a possibility it causes a stack overflow exception, - // because there could be a huge chain of elements (lists in lists in lists etc...) - foreach (RBEBase element in this.List) - list.Add(element.Clone()); - return new RBEList(list); - } - } -} \ No newline at end of file diff --git a/SharpPad/RBC/RBEPrimitive.cs b/SharpPad/RBC/RBEPrimitive.cs deleted file mode 100644 index ea28e57..0000000 --- a/SharpPad/RBC/RBEPrimitive.cs +++ /dev/null @@ -1,256 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace SharpPad.RBC { - public class RBEByte : RBEBase { - public override RBEType Type => RBEType.Byte; - - public byte Value { get; set; } - - public RBEByte() { } - - public RBEByte(byte value) { - this.Value = value; - } - - protected override void Read(BinaryReader reader) { - this.Value = reader.ReadByte(); - } - - protected override void Write(BinaryWriter writer) { - writer.Write(this.Value); - } - - public override RBEBase Clone() => this.CloneCore(); - public RBEByte CloneCore() => new RBEByte(this.Value); - } - - public class RBEShort : RBEBase { - public override RBEType Type => RBEType.Short; - - public short Value { get; set; } - - public RBEShort() { } - - public RBEShort(short value) { - this.Value = value; - } - - protected override void Read(BinaryReader reader) { - this.Value = reader.ReadInt16(); - } - - protected override void Write(BinaryWriter writer) { - writer.Write(this.Value); - } - - public override RBEBase Clone() => this.CloneCore(); - public RBEShort CloneCore() => new RBEShort(this.Value); - } - - public class RBEInt : RBEBase { - public override RBEType Type => RBEType.Int; - - public int Value { get; set; } - - public RBEInt() { } - - public RBEInt(int value) { - this.Value = value; - } - - protected override void Read(BinaryReader reader) { - this.Value = reader.ReadInt32(); - } - - protected override void Write(BinaryWriter writer) { - writer.Write(this.Value); - } - - public override RBEBase Clone() => this.CloneCore(); - public RBEInt CloneCore() => new RBEInt(this.Value); - } - - public class RBELong : RBEBase { - public override RBEType Type => RBEType.Long; - - public long Value { get; set; } - - public RBELong() { } - - public RBELong(long value) { - this.Value = value; - } - - protected override void Read(BinaryReader reader) { - this.Value = reader.ReadInt64(); - } - - protected override void Write(BinaryWriter writer) { - writer.Write(this.Value); - } - - public override RBEBase Clone() => this.CloneCore(); - public RBELong CloneCore() => new RBELong(this.Value); - } - - public class RBEFloat : RBEBase { - public override RBEType Type => RBEType.Float; - - public float Value { get; set; } - - public RBEFloat() { } - - public RBEFloat(float value) { - this.Value = value; - } - - protected override void Read(BinaryReader reader) { - this.Value = reader.ReadSingle(); - } - - protected override void Write(BinaryWriter writer) { - writer.Write(this.Value); - } - - public override RBEBase Clone() => this.CloneCore(); - public RBEFloat CloneCore() => new RBEFloat(this.Value); - } - - public class RBEDouble : RBEBase { - public override RBEType Type => RBEType.Double; - - public double Value { get; set; } - - public RBEDouble() { } - - public RBEDouble(double value) { - this.Value = value; - } - - protected override void Read(BinaryReader reader) { - this.Value = reader.ReadDouble(); - } - - protected override void Write(BinaryWriter writer) { - writer.Write(this.Value); - } - - public override RBEBase Clone() => this.CloneCore(); - public RBEDouble CloneCore() => new RBEDouble(this.Value); - } - - /// - /// An RBE element that stores a string. Max string length is an unsigned short () - /// - public class RBEString : RBEBase { - public const int MaxValueLength = ushort.MaxValue; - private string value; - - public override RBEType Type => RBEType.String; - - public string Value { - get => this.value; - set { - if (value != null && value.Length > MaxValueLength) { - throw new Exception("Value length exceeds the maximum value of " + MaxValueLength); - } - - this.value = value; - } - } - - public RBEString() { } - - public RBEString(string value) { - this.Value = value; - } - - public static string ClampLength(string input) { - if (input != null && input.Length > MaxValueLength) - return input.Substring(0, MaxValueLength); - return input; - } - - public static void CreateStringList(RBEList list, string input) { - for (int i = 0, length = input.Length; i < length; i += MaxValueLength) { - list.Add(new RBEString(input.Substring(i, Math.Min(MaxValueLength, length - i)))); - } - } - - public static RBEList CreateStringList(string input) { - RBEList list = new RBEList(new List(input.Length / MaxValueLength + 1)); - CreateStringList(list, input); - return list; - } - - public static string ReadFromStringList(RBEList list) { - IEnumerable enumerable = list.Cast(); - StringBuilder sb = new StringBuilder(list.List.Count * MaxValueLength); - foreach (RBEString rbe in enumerable) - sb.Append(rbe.value); - return sb.ToString(); - } - - /// - /// Reads a ushort (as a length prefix) and then reads that many chars as a string - /// - /// - /// A string with more than 0 character, or null. This function does not return an empty string - public static string ReadString(BinaryReader reader) { - int length = reader.ReadUInt16(); - if (length < 1) { - return null; - } - else { - char[] chars = reader.ReadChars(length); - return new string(chars); - } - } - - /// - /// Writes a ushort (as a length prefix) and then the chars of the string. If the string is too long, the excess is not written - /// - public static void WriteString(BinaryWriter writer, string text) { - if (string.IsNullOrEmpty(text)) { - writer.Write((ushort) 0); - } - else { - writer.Write((ushort) text.Length); - writer.Write(text.ToCharArray(0, Math.Min(text.Length, ushort.MaxValue))); - } - } - - protected override void Read(BinaryReader reader) { - this.value = ReadString(reader); - } - - protected override void Write(BinaryWriter writer) { - WriteString(writer, this.value); - } - - public override RBEBase Clone() => this.CloneCore(); - public RBEString CloneCore() => new RBEString(this.value); - } -} \ No newline at end of file diff --git a/SharpPad/RBC/RBEStruct.cs b/SharpPad/RBC/RBEStruct.cs deleted file mode 100644 index 398f10a..0000000 --- a/SharpPad/RBC/RBEStruct.cs +++ /dev/null @@ -1,110 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.IO; -using System.Runtime.CompilerServices; - -namespace SharpPad.RBC { - /// - /// Used to store unmanaged structs in the little-endian format. Struct can have a max size of 65535 () bytes - /// - /// Only unmanaged structs can be stored. These are just simple structs, e.g. int, long, etc, or any custom struct that consists of those. A struct - /// that contains a reference type as a field/property is not unmanaged and cannot be stored (maybe apart from strings? not sure) - /// - /// - public class RBEStruct : RBEBase { - private byte[] data; - - public override RBEType Type => RBEType.Struct; - - public RBEStruct() { } - - public static RBEStruct ForValue(in T value) where T : unmanaged { - RBEStruct rbe = new RBEStruct(); - rbe.SetValue(value); - return rbe; - } - - protected override void Read(BinaryReader reader) { - int length = reader.ReadUInt16(); - this.data = new byte[length]; - if (reader.Read(this.data, 0, length) != length) { - throw new IOException("Failed to read " + length + " bytes"); - } - } - - protected override void Write(BinaryWriter writer) { - if (this.data == null) { - throw new InvalidOperationException("Array has not been set yet"); - } - - // no one would ever have a struct whose size is greater than 65535 - writer.Write((ushort) this.data.Length); - writer.Write(this.data); - } - - public T GetValue() where T : unmanaged { - byte[] array = this.data; - if (array == null) { - throw new Exception("Binary data has not been read yet"); - } - - int size = Unsafe.SizeOf(); - if (array.Length != size) { - throw new Exception($"Binary data size does not match struct size (binary({array.Length}) != struct({size}) for struct {typeof(T)})"); - } - - return BinaryUtils.ReadStruct(array, 0); - } - - public bool TryGetValue(out T value) where T : unmanaged { - int size; - if (this.data == null || this.data.Length != (size = Unsafe.SizeOf())) { - value = default; - return false; - } - - value = BinaryUtils.ReadStruct(this.data, 0); - return true; - } - - public void SetValue(in T value) where T : unmanaged { - int size = Unsafe.SizeOf(); - if (size > ushort.MaxValue) { - throw new Exception("Value's size is too large: " + size); - } - - this.data = new byte[size]; - BinaryUtils.WriteStruct(value, this.data, 0); - } - - public override RBEBase Clone() => this.CloneCore(); - - public RBEStruct CloneCore() { - byte[] src = this.data, dest = null; - if (src != null) { - dest = new byte[src.Length]; - Unsafe.CopyBlock(ref dest[0], ref src[0], (uint) dest.Length); - } - - return new RBEStruct {data = dest}; - } - } -} \ No newline at end of file diff --git a/SharpPad/RBC/RBEType.cs b/SharpPad/RBC/RBEType.cs deleted file mode 100644 index 5076107..0000000 --- a/SharpPad/RBC/RBEType.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -namespace SharpPad.RBC { - public enum RBEType : byte { - // In order for old data to be compatible, the existing type values - // should not be modified. There can only be 255 different types of element (1-255) - - // when adding new types, a case must be added to the RBEBase.TypeToIdTable dictionary, RBEBase.CreateById and RBEBase.GetTypeById - - Unknown = 0, - Dictionary = 1, - List = 2, - Byte = 3, - Short = 4, - Int = 5, - Long = 6, - Float = 7, - Double = 8, - String = 9, - Struct = 10, - ByteArray = 11, - ShortArray = 12, - IntArray = 13, - LongArray = 14, - FloatArray = 15, - DoubleArray = 16, - StringArray = 17, - StructArray = 18, - Guid = 19, - } -} \ No newline at end of file diff --git a/SharpPad/RBC/RBEUtils.cs b/SharpPad/RBC/RBEUtils.cs deleted file mode 100644 index c40e11b..0000000 --- a/SharpPad/RBC/RBEUtils.cs +++ /dev/null @@ -1,156 +0,0 @@ -// -// Copyright (c) 2023-2024 REghZy -// -// This file is part of SharpPad. -// -// SharpPad is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// SharpPad is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with SharpPad. If not, see . -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace SharpPad.RBC { - public static class RBEUtils { - private static Encoding defaultEncoding = Encoding.UTF8; - - public static Encoding DefaultEncoding { - get => defaultEncoding; - set => defaultEncoding = value ?? throw new ArgumentNullException(nameof(value), "Default encoding value cannot be null"); - } - - public static byte[] ToByteArray(RBEBase rbe, Encoding encoding, bool packed = false, int initialBufferSize = 2048) { - using (MemoryStream stream = new MemoryStream(initialBufferSize)) { - if (packed) { - WriteToStream(rbe, stream, encoding); - } - else { - WriteToStreamPacked(rbe, stream, encoding); - } - - return stream.ToArray(); - } - } - - public static RBEBase FromByteArray(byte[] array, Encoding encoding, bool packed = false) { - using (MemoryStream stream = new MemoryStream(array, false)) { - if (packed) { - return ReadFromStream(stream, encoding); - } - else { - return ReadFromStreamPacked(stream, encoding); - } - } - } - - public static RBEBase ReadFromFile(string filePath) { - using (Stream stream = new BufferedStream(File.OpenRead(filePath))) { - return ReadFromStream(stream); - } - } - - public static RBEBase ReadFromStream(Stream stream) { - return ReadFromStream(stream, defaultEncoding); - } - - public static RBEBase ReadFromStream(Stream stream, Encoding encoding) { - using (BinaryReader reader = new BinaryReader(stream, encoding, true)) { - return RBEBase.ReadIdAndElement(reader); - } - } - - public static RBEBase ReadFromFilePacked(string filePath) { - using (Stream stream = new BufferedStream(File.OpenRead(filePath))) { - return ReadFromStreamPacked(stream); - } - } - - public static RBEBase ReadFromStreamPacked(Stream stream) { - return ReadFromStreamPacked(stream, defaultEncoding); - } - - public static RBEBase ReadFromStreamPacked(Stream stream, Encoding encoding) { - Dictionary dictionary = new Dictionary(); - using (BinaryReader reader = new BinaryReader(stream, encoding, true)) { - ReadPackedKeys(reader, dictionary); - return RBEBase.ReadIdAndElementPacked(reader, dictionary); - } - } - - public static void WriteToFile(RBEBase rbe, string filePath) { - using (Stream stream = new BufferedStream(new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read))) { - WriteToStream(rbe, stream); - } - } - - public static void WriteToStream(RBEBase rbe, Stream stream) { - WriteToStream(rbe, stream, defaultEncoding); - } - - public static void WriteToStream(RBEBase rbe, Stream stream, Encoding encoding) { - using (BinaryWriter writer = new BinaryWriter(stream, encoding, true)) { - RBEBase.WriteIdAndElement(writer, rbe); - } - } - - public static void WriteToFilePacked(RBEBase rbe, string filePath) { - using (Stream stream = new BufferedStream(new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read))) { - WriteToStreamPacked(rbe, stream); - } - } - - public static void WriteToStreamPacked(RBEBase rbe, Stream stream) { - WriteToStreamPacked(rbe, stream, defaultEncoding); - } - - public static void WriteToStreamPacked(RBEBase rbe, Stream stream, Encoding encoding) { - // would it be more performant to, instead of running an accumulation sweep then writing, - // perform a single sweep like "WriteDynamicPacked", where keys are added to the dictionary when needed? - - // It would require the data to be written to a temporary buffer, then write the dictionary to the file, - // then write the temporary buffer to the file, so that the dictionary is first... - // I imagine that's the bane of all computer science right there. It's not even logically possible - // to do it without that temp buffer unless you have a known fixed dictionary size (e.g. 4kb, 32kb) - Dictionary dictionary = new Dictionary(); - rbe.AccumulatePackedEntries(dictionary); - using (BinaryWriter writer = new BinaryWriter(stream, encoding, true)) { - WritePacketKeys(writer, dictionary); - RBEBase.WriteIdAndElementPacked(writer, rbe, dictionary); - } - } - - private static void WritePacketKeys(BinaryWriter writer, Dictionary dictionary) { - writer.Write(dictionary.Count); - foreach (KeyValuePair entry in dictionary) { - int length = entry.Key.Length; - if (length >= byte.MaxValue) { - throw new Exception($"Key is too long ({length}). It must be {byte.MaxValue} or less"); - } - - writer.Write((byte) length); - writer.Write(entry.Key.ToCharArray()); - } - } - - private static void ReadPackedKeys(BinaryReader reader, Dictionary dictionary) { - int count = reader.ReadInt32(); - for (int i = 0; i < count; i++) { - int length = reader.ReadByte(); - char[] chars = reader.ReadChars(length); - dictionary[i] = new string(chars); - } - } - } -} \ No newline at end of file diff --git a/SharpPad/SharpPad.csproj b/SharpPad/SharpPad.csproj index 524f8ce..4c0a845 100644 --- a/SharpPad/SharpPad.csproj +++ b/SharpPad/SharpPad.csproj @@ -69,11 +69,6 @@ - - ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - True - - @@ -83,8 +78,6 @@ 4.0 - - @@ -117,29 +110,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -150,8 +120,11 @@ + + + @@ -160,23 +133,13 @@ + + - - - - - - - - - - - - @@ -235,6 +198,8 @@ + + @@ -267,14 +232,10 @@ - - - - @@ -286,78 +247,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -372,18 +294,6 @@ MSBuild:Compile Designer - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - MSBuild:Compile Designer @@ -411,12 +321,9 @@ App.xaml Code - - - @@ -435,13 +342,10 @@ - - - @@ -450,7 +354,6 @@ - NotepadWindow.xaml @@ -484,6 +387,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -561,7 +468,6 @@ SettingsSingleFileGenerator Settings.Designer.cs - diff --git a/SharpPad/Shortcuts/Managing/ShortcutInputManager.cs b/SharpPad/Shortcuts/Managing/ShortcutInputManager.cs index f3b61fe..cf97aab 100644 --- a/SharpPad/Shortcuts/Managing/ShortcutInputManager.cs +++ b/SharpPad/Shortcuts/Managing/ShortcutInputManager.cs @@ -321,7 +321,10 @@ public bool OnMouseStroke(string focusedGroup, MouseStroke stroke) { public void ProcessInputStatesForMouseUp(string focusedGroup, MouseStroke stroke) { this.AccumulateShortcuts(stroke, focusedGroup, BlockAllFilter); - ExceptionUtils.Assert(this.cachedShortcutList.Count == 0, "Expected the block all filter to work properly"); + if (this.cachedShortcutList.Count > 0) { + throw new Exception("Expected the block all filter to work properly"); + } + this.ProcessInputStates(); } diff --git a/SharpPad/Shortcuts/WPF/ShortcutStyles.xaml b/SharpPad/Shortcuts/WPF/ShortcutStyles.xaml new file mode 100644 index 0000000..4794d64 --- /dev/null +++ b/SharpPad/Shortcuts/WPF/ShortcutStyles.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SharpPad/Controls/Dragger/EditStartEventArgs.cs b/SharpPad/Shortcuts/WPF/ShortcutToolTip.cs similarity index 72% rename from SharpPad/Controls/Dragger/EditStartEventArgs.cs rename to SharpPad/Shortcuts/WPF/ShortcutToolTip.cs index 860551b..8844bd1 100644 --- a/SharpPad/Controls/Dragger/EditStartEventArgs.cs +++ b/SharpPad/Shortcuts/WPF/ShortcutToolTip.cs @@ -18,9 +18,12 @@ // using System.Windows; +using System.Windows.Controls; -namespace SharpPad.Controls.Dragger { - public class EditStartEventArgs : RoutedEventArgs { - public EditStartEventArgs() : base(NumberDragger.EditStartedEvent) { } +namespace SharpPad.Shortcuts.WPF { + public class ShortcutToolTip : ToolTip { + static ShortcutToolTip() { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ShortcutToolTip), new FrameworkPropertyMetadata(typeof(ShortcutToolTip))); + } } } \ No newline at end of file diff --git a/SharpPad/Shortcuts/WPF/ShortcutTooltipService.cs b/SharpPad/Shortcuts/WPF/ShortcutTooltipService.cs new file mode 100644 index 0000000..e355c9b --- /dev/null +++ b/SharpPad/Shortcuts/WPF/ShortcutTooltipService.cs @@ -0,0 +1,61 @@ +// +// Copyright (c) 2023-2024 REghZy +// +// This file is part of SharpPad. +// +// SharpPad is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// SharpPad is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SharpPad. If not, see . +// + + +using System.Windows; +using System.Windows.Controls; +using SharpPad.CommandSystem; +using SharpPad.Shortcuts.WPF.Converters; + +namespace SharpPad.Shortcuts.WPF { + public static class ShortcutTooltipService { + public static readonly DependencyProperty CommandIdProperty = DependencyProperty.RegisterAttached("CommandId", typeof(string), typeof(ShortcutTooltipService), new PropertyMetadata(null, OnCommandIdChanged)); + + private static readonly DependencyPropertyKey ReadableShortcutStringPropertyKey = DependencyProperty.RegisterAttachedReadOnly("ReadableShortcutString", typeof(string), typeof(ShortcutTooltipService), new PropertyMetadata(null)); + public static readonly DependencyProperty ReadableShortcutStringProperty = ReadableShortcutStringPropertyKey.DependencyProperty; + + public static void SetCommandId(DependencyObject element, string value) { + element.SetValue(CommandIdProperty, value); + } + + public static string GetCommandId(DependencyObject element) { + return (string) element.GetValue(CommandIdProperty); + } + + public static string GetReadableShortcutString(DependencyObject element) { + return (string) element.GetValue(ReadableShortcutStringProperty); + } + + private static void OnCommandIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { + if (e.OldValue is string oldCmdId && !string.IsNullOrWhiteSpace(oldCmdId)) { + d.ClearValue(ReadableShortcutStringPropertyKey); + ToolTipService.SetToolTip(d, null); + } + + if (e.NewValue is string newCmdId && !string.IsNullOrWhiteSpace(newCmdId)) { + Command cmd = CommandManager.Instance.GetCommandById(newCmdId); + if (cmd != null) { + if (CommandIdToGestureConverter.CommandIdToGesture(newCmdId, null, out string value)) { + d.SetValue(ReadableShortcutStringPropertyKey, value); + } + } + } + } + } +} \ No newline at end of file diff --git a/SharpPad/Tasks/ActivityDialog.xaml.cs b/SharpPad/Tasks/ActivityDialog.xaml.cs index 831c36d..4e873e4 100644 --- a/SharpPad/Tasks/ActivityDialog.xaml.cs +++ b/SharpPad/Tasks/ActivityDialog.xaml.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Tasks/ActivityTask.cs b/SharpPad/Tasks/ActivityTask.cs index 187099e..1e4c2ae 100644 --- a/SharpPad/Tasks/ActivityTask.cs +++ b/SharpPad/Tasks/ActivityTask.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Tasks/CompletionRange.cs b/SharpPad/Tasks/CompletionRange.cs index 01f9886..56a50b9 100644 --- a/SharpPad/Tasks/CompletionRange.cs +++ b/SharpPad/Tasks/CompletionRange.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Tasks/DefaultProgressTracker.cs b/SharpPad/Tasks/DefaultProgressTracker.cs index 1d063d6..b2e5b1d 100644 --- a/SharpPad/Tasks/DefaultProgressTracker.cs +++ b/SharpPad/Tasks/DefaultProgressTracker.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Tasks/EmptyActivityProgress.cs b/SharpPad/Tasks/EmptyActivityProgress.cs index 87f07fc..4f0517c 100644 --- a/SharpPad/Tasks/EmptyActivityProgress.cs +++ b/SharpPad/Tasks/EmptyActivityProgress.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Tasks/IActivityProgress.cs b/SharpPad/Tasks/IActivityProgress.cs index e3a1b05..6e632be 100644 --- a/SharpPad/Tasks/IActivityProgress.cs +++ b/SharpPad/Tasks/IActivityProgress.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Tasks/StandardActivityControl.cs b/SharpPad/Tasks/StandardActivityControl.cs index 194dfa0..584835b 100644 --- a/SharpPad/Tasks/StandardActivityControl.cs +++ b/SharpPad/Tasks/StandardActivityControl.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Tasks/TaskManager.cs b/SharpPad/Tasks/TaskManager.cs index c420da9..5fdc7c2 100644 --- a/SharpPad/Tasks/TaskManager.cs +++ b/SharpPad/Tasks/TaskManager.cs @@ -10,7 +10,7 @@ // // SharpPad is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License diff --git a/SharpPad/Themes/ControlColours.xaml b/SharpPad/Themes/ControlColours.xaml index 48b0d61..4fced7a 100644 --- a/SharpPad/Themes/ControlColours.xaml +++ b/SharpPad/Themes/ControlColours.xaml @@ -194,8 +194,8 @@ - - + + diff --git a/SharpPad/Themes/Controls.xaml b/SharpPad/Themes/Controls.xaml index 35eea39..314a44d 100644 --- a/SharpPad/Themes/Controls.xaml +++ b/SharpPad/Themes/Controls.xaml @@ -1023,6 +1023,9 @@ + + + @@ -1033,17 +1036,14 @@ - - - - + -