diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index e33cbf1b..3a8a9ae5 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: runtimeIdentifier: [linux-x64, osx-x64, win-x64] - project: [Cpp2IL, Cpp2IL.Gui] + project: [Cpp2IL] name: Build Single-File Artifact runs-on: ubuntu-latest steps: diff --git a/Cpp2IL.Gui/.gitignore b/Cpp2IL.Gui/.gitignore deleted file mode 100644 index 8afdcb63..00000000 --- a/Cpp2IL.Gui/.gitignore +++ /dev/null @@ -1,454 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# Tye -.tye/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# Fody - auto-generated XML schema -FodyWeavers.xsd - -## -## Visual studio for Mac -## - - -# globs -Makefile.in -*.userprefs -*.usertasks -config.make -config.status -aclocal.m4 -install-sh -autom4te.cache/ -*.tar.gz -tarballs/ -test-results/ - -# Mac bundle stuff -*.dmg -*.app - -# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# JetBrains Rider -.idea/ -*.sln.iml - -## -## Visual Studio Code -## -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json diff --git a/Cpp2IL.Gui/App.axaml b/Cpp2IL.Gui/App.axaml deleted file mode 100644 index 9132490c..00000000 --- a/Cpp2IL.Gui/App.axaml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - diff --git a/Cpp2IL.Gui/App.axaml.cs b/Cpp2IL.Gui/App.axaml.cs deleted file mode 100644 index 1b6230f1..00000000 --- a/Cpp2IL.Gui/App.axaml.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; -using Cpp2IL.Core.Logging; -using Cpp2IL.Gui.ViewModels; -using Cpp2IL.Gui.Views; - -namespace Cpp2IL.Gui -{ - public class App : Application - { - public override void Initialize() - { - Logger.InfoNewline("Loading XAML...", "GUI"); - AvaloniaXamlLoader.Load(this); - Logger.InfoNewline("Loaded XAML.", "GUI"); - } - - public override void OnFrameworkInitializationCompleted() - { - Logger.InfoNewline("Framework init complete, configuring logging sink and window", "GUI"); - Avalonia.Logging.Logger.Sink = new ConsoleAvaloniaSink(); - - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new MainWindow - { - DataContext = new MainWindowViewModel(), - }; - ((MainWindow) desktop.MainWindow).OnCreated(); - } - - base.OnFrameworkInitializationCompleted(); - } - } -} diff --git a/Cpp2IL.Gui/Assets/avalonia-logo.ico b/Cpp2IL.Gui/Assets/avalonia-logo.ico deleted file mode 100644 index 8cbdd1ec..00000000 Binary files a/Cpp2IL.Gui/Assets/avalonia-logo.ico and /dev/null differ diff --git a/Cpp2IL.Gui/ClassFileBuilder.cs b/Cpp2IL.Gui/ClassFileBuilder.cs deleted file mode 100644 index d39b517e..00000000 --- a/Cpp2IL.Gui/ClassFileBuilder.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using Cpp2IL.Core.ISIL; -using Cpp2IL.Core.Model.Contexts; -using Cpp2IL.Core.Utils; -using LibCpp2IL; -using LibCpp2IL.BinaryStructures; - -namespace Cpp2IL.Gui; - -public static class ClassFileBuilder -{ - public static string BuildCsFileForType(TypeAnalysisContext type, MethodBodyMode methodBodyMode, bool includeAttributeGenerators) - { - var sb = new StringBuilder(); - - if (!string.IsNullOrEmpty(type.Definition!.Namespace)) - { - sb.Append("namespace ").Append(type.Definition.Namespace).AppendLine(";"); - sb.AppendLine(); - } - - //Type custom attributes - sb.Append(CsFileUtils.GetCustomAttributeStrings(type, 0)); - - //Type keywords and name - sb.Append(CsFileUtils.GetKeyWordsForType(type)).Append(' ').Append(type.Definition.Name); - - //Base class - CsFileUtils.AppendInheritanceInfo(type, sb); - - //Opening brace on new line - sb.AppendLine(); - sb.AppendLine("{"); - - if (type.IsEnumType) - { - //enums - all fields that are constants are enum values - //no methods, no props, no events - foreach (var field in type.Fields) - { - if (field.BackingData!.Attributes.HasFlag(FieldAttributes.Literal)) - { - sb.Append('\t').Append(field.BackingData.Field.Name).Append(" = ").Append(field.BackingData.DefaultValue).AppendLine(","); - } - } - } - else - { - //Fields - foreach (var field in type.Fields) - { - sb.Append(CsFileUtils.GetCustomAttributeStrings(field, 1)); - - sb.Append('\t').Append(CsFileUtils.GetKeyWordsForField(field)); - sb.Append(' '); - sb.Append(CsFileUtils.GetTypeName(field.BackingData!.Field.FieldType!.ToString())).Append(' '); - sb.Append(field.BackingData.Field.Name!); - - var isConst = field.BackingData.Attributes.HasFlag(FieldAttributes.Literal); - if (isConst) - sb.Append(" = ").Append(field.BackingData.DefaultValue); - - sb.Append(';'); - - if (!isConst) - { - var offset = type.AppContext.Binary.GetFieldOffsetFromIndex(type.Definition.TypeIndex, type.Fields.IndexOf(field), field.BackingData.Field.FieldIndex, type.Definition.IsValueType, field.BackingData.Attributes.HasFlag(FieldAttributes.Static)); - sb.Append(" // C++ Field Offset: ").Append(offset).Append(" (0x").Append(offset.ToString("X")).Append(')'); - } - - sb.AppendLine(); - sb.AppendLine(); - } - - if (type.Methods.Count > 0) - sb.AppendLine(); - - //Constructors - foreach (var method in type.Methods) - { - if (method.Definition!.Name is not ".ctor" and not ".cctor") - continue; - - if (method.Definition.MethodPointer > 0) - sb.Append('\t').Append("// Method at address 0x").Append(method.Definition.MethodPointer.ToString("X")).AppendLine(); - - sb.Append(CsFileUtils.GetCustomAttributeStrings(method, 1)); - sb.Append('\t').Append(CsFileUtils.GetKeyWordsForMethod(method)).Append(' ').Append(type.Definition.Name).Append('('); - sb.Append(CsFileUtils.GetMethodParameterString(method)); - sb.Append(')'); - - sb.Append(GetMethodBodyIfPresent(method, methodBodyMode)); - } - - //Properties - foreach (var prop in type.Properties) - { - prop.AnalyzeCustomAttributeData(); - - //TODO - // sb.Append(GetCustomAttributeStrings(prop, 1)); - // - // sb.Append('\t').Append(GetKeyWordsForProperty(prop)); - // sb.Append(GetTypeName(prop.Definition.PropertyType!.ToString())).Append(' '); - // sb.Append(prop.Definition.Name!); - // - // sb.Append(' ').Append(GetMethodParameterString(prop)); - // - // sb.Append(GetMethodBodyIfPresent(prop, methodBodyMode)); - } - - //Methods - foreach (var method in type.Methods) - { - if (method.Definition!.Name is ".ctor" or ".cctor") - continue; - - if (method.Definition.MethodPointer > 0) - sb.Append('\t').Append("// Method at address 0x").Append(method.Definition.MethodPointer.ToString("X")).AppendLine(); - - sb.Append(CsFileUtils.GetCustomAttributeStrings(method, 1)); - sb.Append('\t').Append(CsFileUtils.GetKeyWordsForMethod(method)); - sb.Append(' '); - sb.Append(CsFileUtils.GetTypeName(method.Definition.ReturnType!.ToString())).Append(' '); - sb.Append(method.Definition!.Name).Append('('); - sb.Append(CsFileUtils.GetMethodParameterString(method)); - sb.Append(')'); - - sb.Append(GetMethodBodyIfPresent(method, methodBodyMode)); - } - - //Attribute generators, if enabled and available - if (includeAttributeGenerators && type.AppContext.MetadataVersion < 29f) - { - var membersWithGenerators = type.Methods.Cast().Concat(type.Fields).Concat(type.Properties).Append(type).ToList(); - - foreach (var memberWithGenerator in membersWithGenerators) - { - if (memberWithGenerator.RawIl2CppCustomAttributeData.Length == 0) - continue; - - sb.Append("\t// Custom attribute generator at address 0x").Append(memberWithGenerator.CaCacheGeneratorAnalysis!.UnderlyingPointer.ToString("X")).AppendLine(); - sb.AppendLine("\t// Expected custom attribute types (parameter+ptr contains an array of these): "); - - foreach (var il2CppType in memberWithGenerator.AttributeTypes!) - { - sb.Append("\t//\t"); - - if (il2CppType.Type is Il2CppTypeEnum.IL2CPP_TYPE_CLASS or Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE) - sb.Append(CsFileUtils.GetTypeName(il2CppType.AsClass().FullName!)); - else - sb.Append(CsFileUtils.GetTypeName(LibCpp2ILUtils.GetTypeReflectionData(il2CppType).ToString())); - - sb.AppendLine(); - } - - sb.Append("\tpublic static void GenerateCustomAttributesFor_"); - sb.Append(memberWithGenerator switch - { - MethodAnalysisContext => "Method", - FieldAnalysisContext => "Field", - PropertyAnalysisContext => "Property", - TypeAnalysisContext => "Type", - _ => throw new ArgumentOutOfRangeException() - }); - sb.Append('_').Append(memberWithGenerator.CustomAttributeOwnerName).Append("(Il2CppCustomAttributeCache customAttributes)"); - - sb.Append(GetMethodBodyIfPresent(memberWithGenerator.CaCacheGeneratorAnalysis, methodBodyMode)); - } - } - } - - sb.AppendLine("}"); //Close class - - return sb.ToString(); - } - - private static string GetMethodBodyIfPresent(MethodAnalysisContext method, MethodBodyMode mode) - { - var sb = new StringBuilder(); - - if (method.Definition?.Attributes.HasFlag(MethodAttributes.Abstract) ?? false) - return ";\n\n"; - - sb.AppendLine(); - sb.AppendLine("\t{"); - - if (mode != MethodBodyMode.Stubs) - { - try - { - if (mode != MethodBodyMode.RawAsm) - method.Analyze(); - - switch (mode) - { - case MethodBodyMode.Isil: - { - sb.AppendLine("\t\t// Method body (Instruction-Set-Independent Machine Code Representation)"); - - if (method.ConvertedIsil == null) - { - sb.AppendLine(); - sb.AppendLine($"\t\t// ERROR: No ISIL was generated, which probably means the {method.AppContext.InstructionSet.GetType().Name}\n\t\t// is not fully implemented and so does not generate control flow graphs."); - } - else - sb.Append(GetMethodBodyISIL(method.ConvertedIsil)); - - break; - } - case MethodBodyMode.RawAsm: - { - var rawBytes = method.AppContext.InstructionSet.GetRawBytesForMethod(method, false); - - sb.AppendLine($"\t\t// Method body (Raw Machine Code, {rawBytes.Length} bytes)").AppendLine(); - - sb.Append("\t\t// "); - sb.Append(method.AppContext.InstructionSet.PrintAssembly(method).Replace("\n", "\n\t\t// ")).AppendLine(); - - break; - } - case MethodBodyMode.Pseudocode: - { - sb.AppendLine("\t\t// Method body (Generated C#-Like Decompilation)").AppendLine(); - - sb.AppendLine("\t\t// TODO: Implement C#-like decompilation"); - - break; - } - } - } - catch (Exception e) - { - sb.AppendLine("\t\t// Error Analysing method: " + e.ToString().Replace("\n", "\n\t\t//")); - } - } - - sb.AppendLine("\t}\n"); - - return sb.ToString(); - } - - // ReSharper disable once InconsistentNaming - private static string GetMethodBodyISIL(List method) - { - var sb = new StringBuilder(); - - foreach (var instruction in method) - { - // foreach (var nodeStatement in node.Statements) - // { - // if (nodeStatement is IsilIfStatement ifStatement) - // { - // sb.AppendLine().Append('\t', 2).Append(ifStatement.Condition).AppendLine(";\n"); - // - // sb.Append('\t', 2).AppendLine("// True branch"); - // var tempBlock = new InstructionSetIndependentNode() {Statements = ifStatement.IfBlock}; - // sb.Append(GetMethodBodyISIL(new() {tempBlock})); - // - // if ((ifStatement.ElseBlock?.Count ?? 0) > 0) - // { - // sb.Append('\n').Append('\t', 2).AppendLine("// False branch"); - // tempBlock = new() {Statements = ifStatement.ElseBlock!}; - // sb.Append(GetMethodBodyISIL(new() {tempBlock})); - // } - // - // sb.Append('\t', 2).AppendLine("// End of if\n"); - // } - // else - sb.Append('\t', 2).Append("//").Append(instruction).AppendLine(";"); - // } - } - - return sb.ToString(); - } -} diff --git a/Cpp2IL.Gui/ConsoleAvaloniaSink.cs b/Cpp2IL.Gui/ConsoleAvaloniaSink.cs deleted file mode 100644 index 704c4d95..00000000 --- a/Cpp2IL.Gui/ConsoleAvaloniaSink.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Text; -using Avalonia.Logging; -using Avalonia.Utilities; - -namespace Cpp2IL.Gui -{ - public class ConsoleAvaloniaSink : ILogSink - { - public bool IsEnabled(LogEventLevel level, string area) => level switch - { - LogEventLevel.Information => area is not "Layout", - LogEventLevel.Warning => true, - LogEventLevel.Error => true, - LogEventLevel.Fatal => true, - _ => false, - }; - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate) - { - if (IsEnabled(level, area)) - Console.WriteLine(Format(area, messageTemplate, source)); - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0) - { - if (IsEnabled(level, area)) - Console.WriteLine(Format(area, messageTemplate, source, propertyValue0)); - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - if (IsEnabled(level, area)) - Console.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1)); - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - if (IsEnabled(level, area)) - Console.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, params object?[] propertyValues) - { - if (IsEnabled(level, area)) - Console.WriteLine(Format(area, messageTemplate, source, propertyValues)); - } - - private static string Format( - string area, - string template, - object? source, - T0? v0 = default, - T1? v1 = default, - T2? v2 = default) - { - var result = new StringBuilder(template.Length); - var r = new CharacterReader(template.AsSpan()); - var i = 0; - - result.Append('['); - result.Append(area); - result.Append("] "); - - while (!r.End) - { - var c = r.Take(); - - if (c != '{') - { - result.Append(c); - } - else - { - if (r.Peek != '{') - { - result.Append('\''); - result.Append(i++ switch - { - 0 => v0, - 1 => v1, - 2 => v2, - _ => null - }); - result.Append('\''); - r.TakeUntil('}'); - r.Take(); - } - else - { - result.Append('{'); - r.Take(); - } - } - } - - if (source != null) - { - result.Append(" ("); - result.Append(source.GetType().Name); - result.Append(" #"); - result.Append(source.GetHashCode()); - result.Append(')'); - } - - return result.ToString(); - } - - private static string Format( - string area, - string template, - object? source, - object?[] v) - { - var result = new StringBuilder(template.Length); - var r = new CharacterReader(template.AsSpan()); - var i = 0; - - result.Append('['); - result.Append(area); - result.Append(']'); - - while (!r.End) - { - var c = r.Take(); - - if (c != '{') - { - result.Append(c); - } - else - { - if (r.Peek != '{') - { - result.Append('\''); - result.Append(i < v.Length ? v[i++] : null); - result.Append('\''); - r.TakeUntil('}'); - r.Take(); - } - else - { - result.Append('{'); - r.Take(); - } - } - } - - if (source != null) - { - result.Append('('); - result.Append(source.GetType().Name); - result.Append(" #"); - result.Append(source.GetHashCode()); - result.Append(')'); - } - - return result.ToString(); - } - } -} \ No newline at end of file diff --git a/Cpp2IL.Gui/Cpp2IL.Gui.csproj b/Cpp2IL.Gui/Cpp2IL.Gui.csproj deleted file mode 100644 index 6e9df53c..00000000 --- a/Cpp2IL.Gui/Cpp2IL.Gui.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - true - Debug;Release - true - true - enable - Exe - AnyCPU;x64 - true - true - net7.0 - 2022.1.0 - - - link - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Cpp2IL.Gui/GuiUtils.cs b/Cpp2IL.Gui/GuiUtils.cs deleted file mode 100644 index 8a807710..00000000 --- a/Cpp2IL.Gui/GuiUtils.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using AsmResolver; -using AsmResolver.PE; -using AsmResolver.PE.Win32Resources; -using AssetRipper.Primitives; -using Cpp2IL.Core.Extensions; - -namespace Cpp2IL.Gui; - -public static class GuiUtils -{ - public static UnityVersion ReadFileVersionFromUnityExeXPlatform(string path) - { - var pe = PEImage.FromFile(path); - - var versionResourceDirectory = (ResourceDirectory) pe.Resources!.GetEntry(16); //ID 16 is RT_VERSION, from https://docs.microsoft.com/en-us/windows/win32/menurc/resource-types - var theSingleVersionResource = (ResourceDirectory) versionResourceDirectory.Entries.Single(); - var defaultCultureVersionResource = (ResourceData) theSingleVersionResource.Entries.Single(); - var versionResource = ((DataSegment) defaultCultureVersionResource.Contents!).Data; - - //https://docs.microsoft.com/en-us/windows/win32/menurc/vs-versioninfo - using var reader = new BinaryReader(new MemoryStream(versionResource)); - var length = reader.ReadUInt16(); - var valueLength = reader.ReadUInt16(); - var isPlainText = reader.ReadUInt16() != 0; - - var key = reader.ReadUnicodeString(); - - if(key != "VS_VERSION_INFO") - throw new ($"Invalid version resource - invalid key (got {key})"); - - //Align on 32-bit boundary - reader.BaseStream.Position = (reader.BaseStream.Position + 3) & ~3; - - //now read fixed file info - var signature = reader.ReadUInt32(); - if(signature != 0xFEEF04BD) - throw new ("Invalid version resource - invalid fixed file info signature"); - - //Another 48 bytes that I don't actually care about make up the rest of the fixed file info structure - reader.ReadBytes(0x30); - - //Align on 32-bit boundary - reader.BaseStream.Position = (reader.BaseStream.Position + 3) & ~3; - - //StringFileInfo - var sfiBytesRead = 0; - - var sfiLen = reader.ReadUInt16(); - var sfiValueLen = reader.ReadUInt16(); //Should be 0 - var sfiIsPlainText = reader.ReadUInt16() != 0; - - sfiBytesRead += 6; - - var sfiKey = reader.ReadUnicodeString(); - if(sfiKey != "StringFileInfo") - throw new ("Invalid version resource - invalid StringFileInfo key"); - - sfiBytesRead += (sfiKey.Length + 1) * 2; //+1 for null terminator, *2 for unicode - - //Align on 32-bit boundary - var oldPos = (int) reader.BaseStream.Position; - reader.BaseStream.Position = (reader.BaseStream.Position + 3) & ~3; - sfiBytesRead += (int) reader.BaseStream.Position - oldPos; - - while (sfiBytesRead < sfiLen) - { - //Probably only one table but technically more than one is allowed - var stringTableBytesRead = 0; - var stringTableLen = reader.ReadUInt16(); - var stringTableValueLen = reader.ReadUInt16(); //Should be 0 - var stringTableIsPlainText = reader.ReadUInt16() != 0; - - stringTableBytesRead += 6; - sfiBytesRead += 6; - - var stringTableKey = reader.ReadUnicodeString(); - //stringTableKey should be a 8-digit hex number with no base specifier - if(stringTableKey.Length != 8 || !int.TryParse(stringTableKey, NumberStyles.HexNumber, null, out var stringTableKeyInt)) - throw new ($"Invalid version resource - invalid StringTableKey: {stringTableKey}"); - - var langId = stringTableKeyInt >> 16 & 0xFFFF; - var codePage = stringTableKeyInt & 0xFFFF; - - sfiBytesRead += 18; //8 * 2 for the raw chars + 2 for null terminator - stringTableBytesRead += 18; - - //Align on 32-bit boundary - oldPos = (int) reader.BaseStream.Position; - reader.BaseStream.Position = (reader.BaseStream.Position + 3) & ~3; - var delta =(int) reader.BaseStream.Position - oldPos; - - sfiBytesRead += delta; - stringTableBytesRead += delta; - - var stringTable = new Dictionary(); - while (stringTableBytesRead < stringTableLen) - { - //Individual key-value pairs - var stringStructLen = reader.ReadUInt16(); - var stringValueLen = reader.ReadUInt16(); - var stringIsPlainText = reader.ReadUInt16() != 0; - - stringTableBytesRead += 6; - sfiBytesRead += 6; - - var stringKey = reader.ReadUnicodeString(); - - var keyLen = (stringKey.Length + 1) * 2; - stringTableBytesRead += keyLen; - sfiBytesRead += keyLen; - - //Align on 32-bit boundary - oldPos = (int) reader.BaseStream.Position; - reader.BaseStream.Position = (reader.BaseStream.Position + 3) & ~3; - delta = (int) reader.BaseStream.Position - oldPos; - - stringTableBytesRead += delta; - sfiBytesRead += delta; - - var stringValue = reader.ReadUnicodeString(); - var actualValueLen = stringValue.Length + 1; //StringValueLen is in unicode chars, not bytes - if(actualValueLen != stringValueLen) - throw new($"Expecting a string value length of {stringValueLen}, got string {stringValue}, which is {actualValueLen} bytes long"); - - stringTable[stringKey] = stringValue; - - stringTableBytesRead += actualValueLen * 2; - sfiBytesRead += actualValueLen * 2; - - //Align once more - oldPos = (int) reader.BaseStream.Position; - reader.BaseStream.Position = (reader.BaseStream.Position + 3) & ~3; - delta = (int) reader.BaseStream.Position - oldPos; - - stringTableBytesRead += delta; - sfiBytesRead += delta; - } - - //At least 2019.4 ships a player exe with a "Unity Version" key - if (stringTable.TryGetValue("Unity Version", out var unityVersion)) - { - var sanitized = unityVersion[..unityVersion.LastIndexOf('_')]; //strip commit hash - return UnityVersion.Parse(sanitized); - } - - //Otherwise we can fall back to FileVersion - if (stringTable.TryGetValue("FileVersion", out var fileVersion)) - { - var sanitized = fileVersion[..fileVersion.LastIndexOf('.')]; //strip last bit of build - return UnityVersion.Parse(sanitized); - } - } - - return default; - } -} diff --git a/Cpp2IL.Gui/Images/Assembly.png b/Cpp2IL.Gui/Images/Assembly.png deleted file mode 100644 index 42d74fc4..00000000 Binary files a/Cpp2IL.Gui/Images/Assembly.png and /dev/null differ diff --git a/Cpp2IL.Gui/Images/Class.png b/Cpp2IL.Gui/Images/Class.png deleted file mode 100644 index d62ac8b9..00000000 Binary files a/Cpp2IL.Gui/Images/Class.png and /dev/null differ diff --git a/Cpp2IL.Gui/Images/ImageResources.cs b/Cpp2IL.Gui/Images/ImageResources.cs deleted file mode 100644 index 49870d64..00000000 --- a/Cpp2IL.Gui/Images/ImageResources.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Avalonia.Media.Imaging; - -namespace Cpp2IL.Gui.Images; - -public class ImageResources -{ - static IBitmap LoadBitmap(string name) => new Bitmap(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream($"Cpp2IL.Gui.Images.{name}.png") ?? throw new("Could not find image resource")); - - public static readonly IBitmap Assembly = LoadBitmap("Assembly"); - public static readonly IBitmap Namespace = LoadBitmap("NameSpace"); - public static readonly IBitmap Class = LoadBitmap("Class"); - public static readonly IBitmap Method = LoadBitmap("Method"); -} diff --git a/Cpp2IL.Gui/Images/Method.png b/Cpp2IL.Gui/Images/Method.png deleted file mode 100644 index 5406c68d..00000000 Binary files a/Cpp2IL.Gui/Images/Method.png and /dev/null differ diff --git a/Cpp2IL.Gui/Images/NameSpace.png b/Cpp2IL.Gui/Images/NameSpace.png deleted file mode 100644 index b240c5e0..00000000 Binary files a/Cpp2IL.Gui/Images/NameSpace.png and /dev/null differ diff --git a/Cpp2IL.Gui/MethodBodyMode.cs b/Cpp2IL.Gui/MethodBodyMode.cs deleted file mode 100644 index 552c1cf9..00000000 --- a/Cpp2IL.Gui/MethodBodyMode.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Cpp2IL.Gui; - -public enum MethodBodyMode -{ - Stubs, - RawAsm, - Isil, - Pseudocode, -} \ No newline at end of file diff --git a/Cpp2IL.Gui/MethodBodyModes.cs b/Cpp2IL.Gui/MethodBodyModes.cs deleted file mode 100644 index 07c164ff..00000000 --- a/Cpp2IL.Gui/MethodBodyModes.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace Cpp2IL.Gui; - -public static class MethodBodyModes -{ - public static readonly List AllModes = new() - { - MethodBodyMode.Stubs, - MethodBodyMode.RawAsm, - MethodBodyMode.Isil, - MethodBodyMode.Pseudocode, - }; -} \ No newline at end of file diff --git a/Cpp2IL.Gui/Models/DroppedGame.cs b/Cpp2IL.Gui/Models/DroppedGame.cs deleted file mode 100644 index 8d831756..00000000 --- a/Cpp2IL.Gui/Models/DroppedGame.cs +++ /dev/null @@ -1,30 +0,0 @@ -using AssetRipper.Primitives; -using Cpp2IL.Core.Extensions; -using LibCpp2IL; - -namespace Cpp2IL.Gui.Models -{ - public abstract class DroppedGame - { - public static DroppedGame? ForPaths(string[] paths) - { - if (paths.Length > 2) - paths = MiscExtensions.SubArray(paths, 0, 2); - - if (LooseFilesDroppedGame.TryGet(paths) is { } lfdg) - return lfdg; - - if (DroppedWindowsGame.TryGet(paths) is { } dwg) - return dwg; - - if (DroppedSingleApkGame.TryGet(paths) is { } dsag) - return dsag; - - return null; - } - - public abstract byte[] MetadataBytes { get; } - public abstract byte[] BinaryBytes { get; } - public abstract UnityVersion? UnityVersion { get; } - } -} \ No newline at end of file diff --git a/Cpp2IL.Gui/Models/DroppedSingleApkGame.cs b/Cpp2IL.Gui/Models/DroppedSingleApkGame.cs deleted file mode 100644 index 372128b2..00000000 --- a/Cpp2IL.Gui/Models/DroppedSingleApkGame.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using AssetRipper.Primitives; -using Cpp2IL.Core.Utils; -using LibCpp2IL; -using System.IO.Compression; -using Cpp2IL.Core.Extensions; -using System.Collections.Generic; -using Cpp2IL.Core.Logging; - -namespace Cpp2IL.Gui.Models -{ - public class DroppedSingleApkGame : DroppedGame - { - private static readonly string[] Traverse = new[] { "x86_64", "x86", "arm64-v8a", "armeabi-v7a" }; // TODO: Review this list and finish if required - - public override byte[] MetadataBytes { get; } - public override byte[] BinaryBytes { get; } - public override UnityVersion? UnityVersion => null; - - private DroppedSingleApkGame(byte[] metadata, byte[] binary) - { - MetadataBytes = metadata; - BinaryBytes = binary; - } - - public static DroppedSingleApkGame? TryGet(string[] paths) - { - if (paths.Length > 1) - return null; - var path = paths[0]; - if (!File.Exists(path)) - throw new FileNotFoundException("Could not find the required file."); - - using var zip = ZipFile.OpenRead(path); - Dictionary libs = new(); - byte[]? md = null; - foreach (var e in zip.Entries) - { - if (e == null) - continue; - if (e.Name == "global-metadata.dat") - { - if (md != null) - { - Logger.WarnNewline("Found duplicate global-metadata.dat, skipping."); // TODO: Add an interactive GUI picker - continue; - } - md = e.ReadBytes(); - } - else if (e.Name == "libil2cpp.so") - { - libs.Add(e.FullName.Split("/")[^2], e); - } - } - - if (md == null) - return null; - foreach (var spec in Traverse) - { - if (libs.ContainsKey(spec)) - return new(md, libs[spec].ReadBytes()); - } - return libs.Count > 0 ? new(md, libs.First().Value.ReadBytes()) : null; - } - } -} diff --git a/Cpp2IL.Gui/Models/DroppedWindowsGame.cs b/Cpp2IL.Gui/Models/DroppedWindowsGame.cs deleted file mode 100644 index 77ed0724..00000000 --- a/Cpp2IL.Gui/Models/DroppedWindowsGame.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using AssetRipper.Primitives; -using Cpp2IL.Core.Utils; -using LibCpp2IL; - -namespace Cpp2IL.Gui.Models -{ - public class DroppedWindowsGame : DroppedGame - { - public override byte[] MetadataBytes { get; } - public override byte[] BinaryBytes { get; } - public override UnityVersion? UnityVersion { get; } - - public string RootDir { get; } - - private DroppedWindowsGame(string metadataPath, string binaryPath, string exeName) - { - MetadataBytes = File.ReadAllBytes(metadataPath); - BinaryBytes = File.ReadAllBytes(binaryPath); - RootDir = Path.GetDirectoryName(binaryPath)!; - - var playerExe = Path.Combine(RootDir, exeName + ".exe"); - if (!File.Exists(playerExe)) - throw new($"Somehow the player exe {playerExe} has disappeared"); - - UnityVersion = GuiUtils.ReadFileVersionFromUnityExeXPlatform(playerExe); - } - - public static DroppedWindowsGame? TryGet(string[] paths) - { - if (paths.Length > 1) - return null; - - var path = paths[0]; - string exeName; - if (File.Exists(path)) - { - exeName = Path.GetFileNameWithoutExtension(path); - path = Path.GetDirectoryName(path) ?? throw new("Could not get directory name"); - } - else if(Directory.Exists(path)) - { - var temp = Directory.GetFiles(path) - .Where(p => Path.GetExtension(p) == ".exe") - .FirstOrDefault(p => !MiscUtils.BlacklistedExecutableFilenames.Contains(Path.GetFileName(p))); - - if (temp == null) - return null; - - exeName = Path.GetFileNameWithoutExtension(temp); - } - else - { - throw new FileNotFoundException("Could not find the required file or directory."); - } - - var gameAssemblyPath = Path.Combine(path, "GameAssembly.dll"); - var metadataPath = Path.Combine(path, $"{exeName}_Data", "il2cpp_data", "Metadata", "global-metadata.dat"); - if (File.Exists(gameAssemblyPath) && File.Exists(metadataPath)) - return new(metadataPath, gameAssemblyPath, exeName); - - return null; - } - } -} \ No newline at end of file diff --git a/Cpp2IL.Gui/Models/FileTreeEntry.cs b/Cpp2IL.Gui/Models/FileTreeEntry.cs deleted file mode 100644 index 90a05006..00000000 --- a/Cpp2IL.Gui/Models/FileTreeEntry.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Avalonia; -using Avalonia.Media; -using Cpp2IL.Core.Model.Contexts; -using Cpp2IL.Gui.Images; -using ICSharpCode.TreeView; -using LibCpp2IL; - -namespace Cpp2IL.Gui.Models; - -public class FileTreeEntry : SharpTreeNode -{ - public HasApplicationContext? Context { get; } - public string? NamespaceName { get; } - - public FileTreeEntry(ApplicationAnalysisContext context) - { - Context = null; - NamespaceName = "Root"; - - var assemblies = context.Assemblies.ToList(); - assemblies.SortByExtractedKey(a => a.Definition.AssemblyName.Name); - - foreach (var assemblyAnalysisContext in assemblies) - Children.Add(new FileTreeEntry(assemblyAnalysisContext)); - } - - private FileTreeEntry(AssemblyAnalysisContext context) : this((HasApplicationContext) context) - { - var uniqueNamespaces = context.Types.Select(t => t.Definition!.Namespace!).Distinct(); - - //Top-level namespaces only - foreach (var ns in uniqueNamespaces.Where(n => !n.Contains('.'))) - Children.Add(new FileTreeEntry(context, ns)); - } - - private FileTreeEntry(HasApplicationContext context) - { - Context = context; - NamespaceName = null; - - if (context is TypeAnalysisContext tac) - foreach (var methodAnalysisContext in tac.Methods) - Children.Add(new FileTreeEntry(methodAnalysisContext)); - } - - private FileTreeEntry(AssemblyAnalysisContext parentCtx, string namespaceName) - { - Context = null; - NamespaceName = namespaceName; - - List allTypesInThisNamespaceAndSubNamespaces; - if (namespaceName != string.Empty) - { - //Add sub-namespaces first - var namespaceDot = $"{namespaceName}."; - allTypesInThisNamespaceAndSubNamespaces = parentCtx.Types.Where(t => t.Definition!.Namespace! == namespaceName || t.Definition.Namespace!.StartsWith(namespaceDot)).ToList(); - var uniqueSubNamespaces = allTypesInThisNamespaceAndSubNamespaces.Where(t => t.Definition!.Namespace != namespaceName).Select(t => t.Definition!.Namespace![(namespaceName.Length + 1)..]).Distinct().ToList(); - foreach (var subNs in uniqueSubNamespaces) - { - if (subNs.Contains('.')) - { - var directChildNs = subNs[..subNs.IndexOf('.')]; - if(!uniqueSubNamespaces.Contains(directChildNs)) - Children.Add(new FileTreeEntry(parentCtx, $"{namespaceDot}{directChildNs}")); - - continue; //Skip deeper-nested namespaces - } - - Children.Add(new FileTreeEntry(parentCtx, $"{namespaceDot}{subNs}")); - } - } - else - { - //Empty namespace cannot have sub namespaces - allTypesInThisNamespaceAndSubNamespaces = parentCtx.Types.Where(t => t.Definition!.Namespace == namespaceName).ToList(); - } - - allTypesInThisNamespaceAndSubNamespaces.SortByExtractedKey(t => t.Definition!.Name!); - - //Add types in this namespace - foreach (var type in allTypesInThisNamespaceAndSubNamespaces.Where(t => t.Definition!.Namespace == namespaceName)) - Children.Add(new FileTreeEntry(type)); - } - - public EntryType Type => Context switch - { - TypeAnalysisContext => EntryType.Type, - MethodAnalysisContext => EntryType.Method, - AssemblyAnalysisContext => EntryType.Assembly, - null => EntryType.Namespace, - _ => throw new ArgumentOutOfRangeException() - }; - - public bool ShouldHaveChildren => Type switch - { - EntryType.Method => false, - EntryType.Assembly => true, - EntryType.Namespace => true, - EntryType.Type => ((TypeAnalysisContext) Context!).Methods.Count > 0, - _ => throw new ArgumentOutOfRangeException() - }; - - public string DisplayName => Context switch - { - TypeAnalysisContext tac => tac.Definition!.Name!, - AssemblyAnalysisContext aac => aac.Definition.AssemblyName.Name, - MethodAnalysisContext mac => $"{mac.Definition!.Name}({string.Join(", ", mac.Parameters.Select(p => p.ReadableTypeName))})", - null => NamespaceName!.Contains('.') ? NamespaceName[(NamespaceName.LastIndexOf('.') + 1)..] : NamespaceName, - _ => throw new ArgumentOutOfRangeException() - }; - - public override string ToString() => $"FileTreeEntry: DisplayName = {DisplayName}, Type = {Type}, Context = {Context}"; - - public override object Text => DisplayName; - - public override bool IsCheckable => false; - - // public override bool ShowExpander => ShouldHaveChildren; - - public override object Icon => Type switch - { - EntryType.Assembly => ImageResources.Assembly, - EntryType.Namespace => ImageResources.Namespace, - EntryType.Type => ImageResources.Class, - EntryType.Method => ImageResources.Method, - _ => throw new ArgumentOutOfRangeException() - }; - - public override IBrush Foreground => IsSelected ? SystemColors.HighlightBrush : Brushes.Transparent; - - public enum EntryType - { - Assembly, - Namespace, - Type, - Method, - } -} \ No newline at end of file diff --git a/Cpp2IL.Gui/Models/LooseFilesDroppedGame.cs b/Cpp2IL.Gui/Models/LooseFilesDroppedGame.cs deleted file mode 100644 index b5491475..00000000 --- a/Cpp2IL.Gui/Models/LooseFilesDroppedGame.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.IO; -using System.Linq; -using AssetRipper.Primitives; -using LibCpp2IL; -using LibCpp2IL.Metadata; - -namespace Cpp2IL.Gui.Models -{ - public class LooseFilesDroppedGame : DroppedGame - { - private string MetadataPath; - private string BinaryPath; - - public static LooseFilesDroppedGame? TryGet(string[] paths) - { - if (paths.Length != 2) - //More than 2 files dropped - return null; - - if (!File.Exists(paths[0]) || !File.Exists(paths[1])) - //One of the files is missing - return null; - - //if one of the files is a global-metadata and the other one is not a directory, we can assume the dropped files constitute a loose binary + metadata - var potentialMetadata = paths.FirstOrDefault(p => p.ToLowerInvariant().EndsWith(".dat")); - if (potentialMetadata == null) - //No .dat files so we assume no metadata - return null; - - var firstFourBytes = new byte[4]; - using (var fs = File.OpenRead(potentialMetadata)) - { - fs.Read(firstFourBytes, 0, 4); - } - - if (!Il2CppMetadata.HasMetadataHeader(firstFourBytes)) - //Not a metadata file - return null; - - var potentialBinary = paths.FirstOrDefault(p => !p.ToLowerInvariant().EndsWith(".dat")); - if (potentialBinary == null) - return null; - - if (Directory.Exists(potentialBinary)) - //Other path is a directory - return null; - - return new(potentialMetadata, potentialBinary); - } - - public LooseFilesDroppedGame(string metadataPath, string binaryPath) - { - MetadataPath = metadataPath; - BinaryPath = binaryPath; - } - - public override byte[] MetadataBytes => File.ReadAllBytes(MetadataPath); - public override byte[] BinaryBytes => File.ReadAllBytes(BinaryPath); - public override UnityVersion? UnityVersion => null; - } -} \ No newline at end of file diff --git a/Cpp2IL.Gui/Program.cs b/Cpp2IL.Gui/Program.cs deleted file mode 100644 index bfb913b3..00000000 --- a/Cpp2IL.Gui/Program.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Avalonia; -using Avalonia.ReactiveUI; -using Cpp2IL.Core; -using Cpp2IL.Core.Logging; - -namespace Cpp2IL.Gui -{ - class Program - { - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - [STAThread] - public static void Main(string[] args) - { - Console.WriteLine("Starting Cpp2IL GUI. Initializing Cpp2IL Core..."); - - Cpp2IlApi.Init(); - SimpleConsoleLogger.Initialize(); - SimpleConsoleLogger.ShowVerbose = true; - // Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); - - Logger.InfoNewline("Starting Avalonia...", "GUI"); - BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); - } - - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .UsePlatformDetect() - .LogToTrace() - .UseReactiveUI(); - } -} diff --git a/Cpp2IL.Gui/ViewLocator.cs b/Cpp2IL.Gui/ViewLocator.cs deleted file mode 100644 index 94ccc415..00000000 --- a/Cpp2IL.Gui/ViewLocator.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Avalonia.Controls; -using Avalonia.Controls.Templates; -using Cpp2IL.Gui.ViewModels; - -namespace Cpp2IL.Gui -{ - public class ViewLocator : IDataTemplate - { - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "All the viewmodel types are hard referenced")] - [UnconditionalSuppressMessage("Trimming", "IL2057", Justification = "All the viewmodel types are hard referenced")] - public Control Build(object? data) - { - var name = data!.GetType().FullName!.Replace("ViewModel", "View"); - var type = Type.GetType(name); - - if (type != null) - { - return (Control) Activator.CreateInstance(type)!; - } - else - { - return new TextBlock {Text = "Not Found: " + name}; - } - } - - public bool Match(object? data) - { - return data is ViewModelBase; - } - } -} diff --git a/Cpp2IL.Gui/ViewModels/InputUnityVersionViewModel.cs b/Cpp2IL.Gui/ViewModels/InputUnityVersionViewModel.cs deleted file mode 100644 index 9059b773..00000000 --- a/Cpp2IL.Gui/ViewModels/InputUnityVersionViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ReactiveUI; - -namespace Cpp2IL.Gui.ViewModels -{ - public class InputUnityVersionViewModel : ViewModelBase - { - private string _version = ""; - - public string Version - { - get => _version; - set => this.RaiseAndSetIfChanged(ref _version, value); - } - } -} \ No newline at end of file diff --git a/Cpp2IL.Gui/ViewModels/MainWindowViewModel.cs b/Cpp2IL.Gui/ViewModels/MainWindowViewModel.cs deleted file mode 100644 index f6609610..00000000 --- a/Cpp2IL.Gui/ViewModels/MainWindowViewModel.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using System.Threading; -using AssetRipper.Primitives; -using Avalonia.Threading; -using AvaloniaEdit.Document; -using Cpp2IL.Core; -using Cpp2IL.Core.Model.Contexts; -using Cpp2IL.Gui.Models; -using Cpp2IL.Gui.Views; -using ReactiveUI; - -namespace Cpp2IL.Gui.ViewModels -{ - public class MainWindowViewModel : ViewModelBase - { - public MainWindow Window = null!; - - private string _statusText = "Drop an IL2CPP game on this window to start, or click here to open a file browser."; - private bool _hasGame; - private FileTreeEntry? _rootNode; - private MethodBodyMode _methodBodyMode = MethodBodyMode.Isil; - private bool _showAttributeGenerators; - private TextDocument _editorText = new TextDocument("Select a class to open"); - - public TypeAnalysisContext? LastSelectedType { get; set; } - - public string StatusText - { - get => _statusText; - set => this.RaiseAndSetIfChanged(ref _statusText, value); - } - - public bool HasGame - { - get => _hasGame; - set => this.RaiseAndSetIfChanged(ref _hasGame, value); - } - - public FileTreeEntry? RootNode - { - get => _rootNode; - set => this.RaiseAndSetIfChanged(ref _rootNode, value); - } - - public MethodBodyMode MethodBodyMode - { - get => _methodBodyMode; - set - { - this.RaiseAndSetIfChanged(ref _methodBodyMode, value); - UpdateEditor(); - } - } - - public bool ShowAttributeGenerators - { - get => _showAttributeGenerators; - set - { - this.RaiseAndSetIfChanged(ref _showAttributeGenerators, value); - UpdateEditor(); - } - } - - public TextDocument EditorText - { - get => _editorText; - set => this.RaiseAndSetIfChanged(ref _editorText, value); - } - - public async void OnDropped(string[]? droppedFiles) - { - if(droppedFiles == null) - return; - - var dg = DroppedGame.ForPaths(droppedFiles); - - if (dg == null) - { - StatusText = "No game found in what you just dropped"; - return; - } - - var version = dg.UnityVersion; - while (!version.HasValue) - { - Console.WriteLine("Prompting for Unity version"); - var dialog = new InputUnityVersionDialog(); - var inputVersion = await dialog.ShowDialog(Window); - - try - { - version = UnityVersion.Parse(inputVersion); - } - catch (Exception) - { - version = null; - } - } - - ContinueLoading(dg, version.Value); - } - - private void OnLoadComplete() - { - StatusText = "Building file tree..."; - - RootNode = new(Cpp2IlApi.CurrentAppContext!); - - StatusText = "Load complete"; - HasGame = true; - } - - private void OnLoadFailed(Exception exception) - { - StatusText = $"Load failed: {exception}"; - } - - private void ContinueLoading(DroppedGame droppedGame, UnityVersion version) - { - StatusText = "Loading game..."; - - new Thread(() => - { - try - { - Cpp2IlApi.InitializeLibCpp2Il(droppedGame.BinaryBytes, droppedGame.MetadataBytes, version); - } - catch (Exception e) - { - Dispatcher.UIThread.Post(() => OnLoadFailed(e)); - return; - } - - Dispatcher.UIThread.Post(OnLoadComplete); - }) - { - Name = "Game Loader", - IsBackground = true - }.Start(); - } - - public void OnItemSelected(FileTreeEntry fileTreeEntry) - { - if(fileTreeEntry.Context is not TypeAnalysisContext type) - return; - - LastSelectedType = type; - UpdateEditor(); - } - - private void UpdateEditor() - { - if(LastSelectedType == null) - return; - - EditorText = new(ClassFileBuilder.BuildCsFileForType(LastSelectedType, MethodBodyMode, ShowAttributeGenerators)); - } - } -} \ No newline at end of file diff --git a/Cpp2IL.Gui/ViewModels/ViewModelBase.cs b/Cpp2IL.Gui/ViewModels/ViewModelBase.cs deleted file mode 100644 index 57b04a7c..00000000 --- a/Cpp2IL.Gui/ViewModels/ViewModelBase.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using ReactiveUI; - -namespace Cpp2IL.Gui.ViewModels -{ - public class ViewModelBase : ReactiveObject - { - } -} \ No newline at end of file diff --git a/Cpp2IL.Gui/Views/InputUnityVersionDialog.axaml b/Cpp2IL.Gui/Views/InputUnityVersionDialog.axaml deleted file mode 100644 index 9e6bd2b5..00000000 --- a/Cpp2IL.Gui/Views/InputUnityVersionDialog.axaml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/Cpp2IL.Gui/Views/InputUnityVersionDialog.axaml.cs b/Cpp2IL.Gui/Views/InputUnityVersionDialog.axaml.cs deleted file mode 100644 index 07de1e3b..00000000 --- a/Cpp2IL.Gui/Views/InputUnityVersionDialog.axaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Interactivity; -using Avalonia.Markup.Xaml; -using Cpp2IL.Gui.ViewModels; - -namespace Cpp2IL.Gui.Views -{ - public class InputUnityVersionDialog : Window - { - private InputUnityVersionViewModel _viewModel; - public InputUnityVersionDialog() - { - DataContext = _viewModel = new(); - InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - - private void OkClick(object? sender, RoutedEventArgs e) => Close(_viewModel.Version); - } -} \ No newline at end of file diff --git a/Cpp2IL.Gui/Views/MainWindow.axaml b/Cpp2IL.Gui/Views/MainWindow.axaml deleted file mode 100644 index 85fb4ec9..00000000 --- a/Cpp2IL.Gui/Views/MainWindow.axaml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - Show Attribute Generator Functions - - - - - - - - - - - - - - - diff --git a/Cpp2IL.Gui/Views/MainWindow.axaml.cs b/Cpp2IL.Gui/Views/MainWindow.axaml.cs deleted file mode 100644 index 1a8419ab..00000000 --- a/Cpp2IL.Gui/Views/MainWindow.axaml.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Markup.Xaml; -using AvaloniaEdit; -using AvaloniaEdit.TextMate; -using Cpp2IL.Core.Logging; -using Cpp2IL.Gui.Models; -using Cpp2IL.Gui.ViewModels; -using TextMateSharp.Grammars; - -#if DEBUG -using Avalonia; -#endif - -namespace Cpp2IL.Gui.Views -{ - public partial class MainWindow : Window - { - public MainWindow() - { - Logger.InfoNewline("[MainWindow] Initializing components...", "GUI"); - InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif - } - - public void OnCreated() - { - Logger.InfoNewline("[MainWindow] OnCreated", "GUI"); - var vm = (MainWindowViewModel) DataContext!; - vm.Window = this; - - var textEditor = this.FindControl("CodeView"); - - var registryOptions = new RegistryOptions(ThemeName.DarkPlus); - var textMateInstallation = textEditor.InstallTextMate(registryOptions); - textMateInstallation.SetGrammar(registryOptions.GetScopeByLanguageId(registryOptions.GetLanguageByExtension(".cs").Id)); - - // This assumes nothing else is passed to the command line as it is a GUI build! - var commandLine = System.Environment.GetCommandLineArgs(); - if (commandLine != null && commandLine.Length > 1) { - vm.OnDropped(commandLine.Skip(1).ToArray()); - } - AddHandler(DragDrop.DropEvent, (sender, args) => vm.OnDropped(args.Data.GetFileNames()?.ToArray())); - } - - private async void OnClickDropPrompt(object? sender, PointerPressedEventArgs e) - { - var ret = await StorageProvider.OpenFilePickerAsync(new() { AllowMultiple = true, }); - var vm = (MainWindowViewModel) DataContext!; - - var paths = new List(); - foreach (var file in ret) - { - paths.Add(file.Path.LocalPath); - } - - vm.OnDropped(paths.ToArray()); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - - private void SelectingItemsControl_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) - { - if(e.AddedItems.Count < 1) - return; - - if(e.AddedItems[0] is not FileTreeEntry fte) - return; - - ((MainWindowViewModel) DataContext!).OnItemSelected(fte); - } - } -} diff --git a/Cpp2IL.sln b/Cpp2IL.sln index d0d27607..2fa414e0 100644 --- a/Cpp2IL.sln +++ b/Cpp2IL.sln @@ -26,10 +26,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cpp2IL.Core", "Cpp2IL.Core\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WasmDisassembler", "WasmDisassembler\WasmDisassembler.csproj", "{D410F8FC-0853-403B-8017-2870745EDF5B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cpp2IL.Gui", "Cpp2IL.Gui\Cpp2IL.Gui.csproj", "{B4C732B9-4144-48DC-A4A3-69E156676F99}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpTreeView", "SharpTreeView\SharpTreeView.csproj", "{50F9E14E-B328-488E-9280-9FD6A14B0A9F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StableNameDotNet", "StableNameDotNet\StableNameDotNet.csproj", "{E5890928-8F71-4B74-8332-D5A6E75AF8C2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cpp2IL.Plugin.BuildReport", "Cpp2IL.Plugin.BuildReport\Cpp2IL.Plugin.BuildReport.csproj", "{E4E1E12D-3F51-422C-8FBB-26B36C9F1FA5}" @@ -41,9 +37,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{51C7C919-3 docs\CallAnalyzer.md = docs\CallAnalyzer.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.Core.Tests", "Cpp2IL.Core.Tests\Cpp2IL.Core.Tests.csproj", "{9DA1EAC8-05D8-4CB6-80D6-AB6C304DF834}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cpp2IL.Core.Tests", "Cpp2IL.Core.Tests\Cpp2IL.Core.Tests.csproj", "{9DA1EAC8-05D8-4CB6-80D6-AB6C304DF834}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.Plugin.StrippedCodeRegSupport", "Cpp2IL.Plugin.StrippedCodeRegSupport\Cpp2IL.Plugin.StrippedCodeRegSupport.csproj", "{B2425628-0D69-44FA-AD34-997595512785}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cpp2IL.Plugin.StrippedCodeRegSupport", "Cpp2IL.Plugin.StrippedCodeRegSupport\Cpp2IL.Plugin.StrippedCodeRegSupport.csproj", "{B2425628-0D69-44FA-AD34-997595512785}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -71,14 +67,6 @@ Global {D410F8FC-0853-403B-8017-2870745EDF5B}.Debug|Any CPU.Build.0 = Debug|Any CPU {D410F8FC-0853-403B-8017-2870745EDF5B}.Release|Any CPU.ActiveCfg = Release|Any CPU {D410F8FC-0853-403B-8017-2870745EDF5B}.Release|Any CPU.Build.0 = Release|Any CPU - {B4C732B9-4144-48DC-A4A3-69E156676F99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B4C732B9-4144-48DC-A4A3-69E156676F99}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B4C732B9-4144-48DC-A4A3-69E156676F99}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B4C732B9-4144-48DC-A4A3-69E156676F99}.Release|Any CPU.Build.0 = Release|Any CPU - {50F9E14E-B328-488E-9280-9FD6A14B0A9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50F9E14E-B328-488E-9280-9FD6A14B0A9F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50F9E14E-B328-488E-9280-9FD6A14B0A9F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50F9E14E-B328-488E-9280-9FD6A14B0A9F}.Release|Any CPU.Build.0 = Release|Any CPU {E5890928-8F71-4B74-8332-D5A6E75AF8C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E5890928-8F71-4B74-8332-D5A6E75AF8C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5890928-8F71-4B74-8332-D5A6E75AF8C2}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/SharpTreeView/Consts.cs b/SharpTreeView/Consts.cs deleted file mode 100644 index 312d057b..00000000 --- a/SharpTreeView/Consts.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Avalonia.Media; -using Avalonia.Media.Immutable; - -namespace Avalonia -{ - public static class SystemParameters - { - public const double MinimumHorizontalDragDistance = 2.0; - public const double MinimumVerticalDragDistance = 2.0; - } - - public static class SystemColors - { - public static IBrush ControlTextBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFF000000)); - public static IBrush ControlDarkBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFFA0A0A0)); - public static IBrush ControlLightBrush { get; } = new ImmutableSolidColorBrush(Color.FromUInt32(0xFFE3E3E3)); - public static IBrush ControlBrush { get; } = new ImmutableSolidColorBrush(Color.FromUInt32(0xFFF0F0F0)); - public static IBrush HighlightBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFFFFFFFF)); - public static IBrush HighlightTextBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFFFFFFFF)); - public static IBrush WindowTextBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFF000000)); - public static IBrush WindowBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFFFFFFFF)); - public static IBrush GrayTextBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFF6D6D6D)); - public static IBrush InfoTextBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFF000000)); - public static IBrush InfoBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFFFFFFE1)); - public static IBrush InactiveCaptionBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFFBFCDDB)); - public static IBrush InactiveCaptionTextBrush {get;} = new ImmutableSolidColorBrush(Color.FromUInt32(0xFF000000)); - public static Color ControlLightColor {get;} = Color.FromUInt32(0xFFE3E3E3); - public static Color ControlLightLightColor {get;} = Color.FromUInt32(0xFFFFFFFF); - public static Color ControlDarkColor {get;} = Color.FromUInt32(0xFFA0A0A0); - public static Color ControlDarkDarkColor {get;} = Color.FromUInt32(0xFF696969); - public static Color HighlightColor {get;} = Color.FromUInt32(0xFF3399FF); - - // /// - // /// pull out values from system.drawing - // /// - // /// - // public static string[] GetColors() - // { - // string ToString(object obj) - // { - // if(obj is ISolidColorBrush b) - // { - // return $"new ImmutableSolidColorBrush(Color.FromUInt32(0x{b.Color.ToUint32().ToString("X")}));; - // } - // else if(obj is Color c) - // return $"Color.FromUInt32(0x{c.ToUint32().ToString("X")});; - // else - // return obj.ToString(); - // } - // return Array.ConvertAll(typeof(SystemColors).GetProperties(), p => string.Format("{0} \{get;\} = {1}", p.Name, ToString(p.GetValue(null)))); - // } - - public static Color ToAvaloniaColor(this System.Drawing.Color color) - { - return new Color(color.A, color.R, color.G, color.B); - } - - //public static IBrush ToAvaloniaBrush(this System.Drawing.Brush brush) - //{ - // if (brush is System.Drawing.SolidBrush solidbrush) { - // return new ImmutableSolidColorBrush(solidbrush.Color.ToAvaloniaColor()); - // } - // else if(brush is System.Drawing.TextureBrush textureBrush) { - // using (var imageStream = new MemoryStream()) { - // var image = textureBrush.Image; - // image.Save(imageStream, System.Drawing.Imaging.ImageFormat.Bmp); - - // var avaloniaBitmap = new Bitmap(imageStream); - // return new ImageBrush(avaloniaBitmap); - // } - - // } else { - // throw new NotSupportedException(); - // } - //} - } -} diff --git a/SharpTreeView/Converters.cs b/SharpTreeView/Converters.cs deleted file mode 100644 index 5a5b2387..00000000 --- a/SharpTreeView/Converters.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using Avalonia.Data.Converters; - -namespace ICSharpCode.TreeView -{ - public class BoolConverters - { - public static readonly IValueConverter Inverse = new FuncValueConverter(b => !b); - } -} diff --git a/SharpTreeView/EditTextBox.cs b/SharpTreeView/EditTextBox.cs deleted file mode 100644 index cf4a2f21..00000000 --- a/SharpTreeView/EditTextBox.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using Avalonia.Controls; -using Avalonia.Controls.Primitives; -using Avalonia.Input; -using Avalonia.Interactivity; - -namespace ICSharpCode.TreeView -{ - class EditTextBox : TextBox - { - public SharpTreeViewItem Item { get; set; } - - public SharpTreeNode Node => Item.Node; - - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - Init(); - } - - void Init() - { - Text = Node.LoadEditText(); - Focus(); - SelectAll(); - } - - new void SelectAll() - { - SelectionStart = 0; - SelectionEnd = Text.Length; - } - - protected override void OnKeyDown(KeyEventArgs e) - { - if (e.Key == Key.Enter) { - Commit(); - } else if (e.Key == Key.Escape) { - Node.IsEditing = false; - } - } - - protected override void OnLostFocus(RoutedEventArgs e) - { - if (Node.IsEditing) { - Commit(); - } - } - - bool commiting; - - void Commit() - { - if (!commiting) { - commiting = true; - - Node.IsEditing = false; - if (!Node.SaveEditText(Text)) { - Item.Focus(); - } - Node.RaisePropertyChanged("Text"); - - //if (Node.SaveEditText(Text)) { - // Node.IsEditing = false; - // Node.RaisePropertyChanged("Text"); - //} - //else { - // Init(); - //} - - commiting = false; - } - } - } -} diff --git a/SharpTreeView/ExtensionMethods.cs b/SharpTreeView/ExtensionMethods.cs deleted file mode 100644 index d5980d72..00000000 --- a/SharpTreeView/ExtensionMethods.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections; -using System.Linq; -using Avalonia.VisualTree; - -namespace ICSharpCode.TreeView -{ - static class ExtensionMethods - { - public static T FindAncestor(this IVisual d) where T : class - { - return d.GetVisualAncestors().OfType().FirstOrDefault(); - } - - public static void AddOnce(this IList list, object item) - { - if (!list.Contains(item)) { - list.Add(item); - } - } - - public static bool Any(this IEnumerable list) - { - var enumerator = list.GetEnumerator(); - var result = enumerator.MoveNext(); - (enumerator as IDisposable)?.Dispose(); - - return result; - } - } -} diff --git a/SharpTreeView/FlatListTreeNode.cs b/SharpTreeView/FlatListTreeNode.cs deleted file mode 100644 index 09d721ca..00000000 --- a/SharpTreeView/FlatListTreeNode.cs +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.Diagnostics; - -namespace ICSharpCode.TreeView -{ - // This part of SharpTreeNode controls the 'flat list' data structure, which emulates - // a big flat list containing the whole tree; allowing access by visible index. - partial class SharpTreeNode - { - /// The parent in the flat list - internal SharpTreeNode listParent; - /// Left/right nodes in the flat list - SharpTreeNode left, right; - - internal TreeFlattener treeFlattener; - - /// Subtree height in the flat list tree - byte height = 1; - - /// Length in the flat list, including children (children within the flat list). -1 = invalidated - int totalListLength = -1; - - int Balance => Height(right) - Height(left); - - static int Height(SharpTreeNode node) - { - return node != null ? node.height : 0; - } - - internal SharpTreeNode GetListRoot() - { - SharpTreeNode node = this; - while (node.listParent != null) - node = node.listParent; - return node; - } - - #region Debugging - [Conditional("DEBUG")] - void CheckRootInvariants() - { - GetListRoot().CheckInvariants(); - } - - [Conditional("DATACONSISTENCYCHECK")] - void CheckInvariants() - { - Debug.Assert(left == null || left.listParent == this); - Debug.Assert(right == null || right.listParent == this); - Debug.Assert(height == 1 + Math.Max(Height(left), Height(right))); - Debug.Assert(Math.Abs(Balance) <= 1); - Debug.Assert(totalListLength == -1 || totalListLength == (left != null ? left.totalListLength : 0) + (isVisible ? 1 : 0) + (right != null ? right.totalListLength : 0)); - left?.CheckInvariants(); - right?.CheckInvariants(); - } - - [Conditional("DEBUG")] - static void DumpTree(SharpTreeNode node) - { - node.GetListRoot().DumpTree(); - } - - [Conditional("DEBUG")] - void DumpTree() - { - Debug.Indent(); - left?.DumpTree(); - Debug.Unindent(); - Trace.WriteLine($"{ToString()}, totalListLength={totalListLength}, height={height}, Balance={Balance}, isVisible={isVisible}"); - Debug.Indent(); - right?.DumpTree(); - Debug.Unindent(); - } - #endregion - - #region GetNodeByVisibleIndex / GetVisibleIndexForNode - internal static SharpTreeNode GetNodeByVisibleIndex(SharpTreeNode root, int index) - { - root.GetTotalListLength(); // ensure all list lengths are calculated - Debug.Assert(index >= 0); - Debug.Assert(index < root.totalListLength); - SharpTreeNode node = root; - while (true) { - if (node.left != null && index < node.left.totalListLength) { - node = node.left; - } else { - if (node.left != null) { - index -= node.left.totalListLength; - } - if (node.isVisible) { - if (index == 0) - return node; - index--; - } - node = node.right; - } - } - } - - internal static int GetVisibleIndexForNode(SharpTreeNode node) - { - int index = node.left != null ? node.left.GetTotalListLength() : 0; - while (node.listParent != null) { - if (node == node.listParent.right) { - if (node.listParent.left != null) - index += node.listParent.left.GetTotalListLength(); - if (node.listParent.isVisible) - index++; - } - node = node.listParent; - } - return index; - } - #endregion - - #region Balancing - /// - /// Balances the subtree rooted in and recomputes the 'height' field. - /// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value. - /// - /// The new root node - static SharpTreeNode Rebalance(SharpTreeNode node) - { - Debug.Assert(node.left == null || Math.Abs(node.left.Balance) <= 1); - Debug.Assert(node.right == null || Math.Abs(node.right.Balance) <= 1); - // Keep looping until it's balanced. Not sure if this is stricly required; this is based on - // the Rope code where node merging made this necessary. - while (Math.Abs(node.Balance) > 1) { - // AVL balancing - // note: because we don't care about the identity of concat nodes, this works a little different than usual - // tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged - if (node.Balance > 1) { - if (node.right.Balance < 0) { - node.right = node.right.RotateRight(); - } - node = node.RotateLeft(); - // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that. - node.left = Rebalance(node.left); - } else if (node.Balance < -1) { - if (node.left.Balance > 0) { - node.left = node.left.RotateLeft(); - } - node = node.RotateRight(); - // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that. - node.right = Rebalance(node.right); - } - } - Debug.Assert(Math.Abs(node.Balance) <= 1); - node.height = (byte)(1 + Math.Max(Height(node.left), Height(node.right))); - node.totalListLength = -1; // mark for recalculation - // since balancing checks the whole tree up to the root, the whole path will get marked as invalid - return node; - } - - internal int GetTotalListLength() - { - if (totalListLength >= 0) - return totalListLength; - int length = (isVisible ? 1 : 0); - if (left != null) { - length += left.GetTotalListLength(); - } - if (right != null) { - length += right.GetTotalListLength(); - } - return totalListLength = length; - } - - SharpTreeNode RotateLeft() - { - /* Rotate tree to the left - * - * this right - * / \ / \ - * A right ===> this C - * / \ / \ - * B C A B - */ - SharpTreeNode b = right.left; - SharpTreeNode newTop = right; - - if (b != null) b.listParent = this; - right = b; - newTop.left = this; - newTop.listParent = listParent; - listParent = newTop; - // rebalance the 'this' node - this is necessary in some bulk insertion cases: - newTop.left = Rebalance(this); - return newTop; - } - - SharpTreeNode RotateRight() - { - /* Rotate tree to the right - * - * this left - * / \ / \ - * left C ===> A this - * / \ / \ - * A B B C - */ - SharpTreeNode b = left.right; - SharpTreeNode newTop = left; - - if (b != null) b.listParent = this; - left = b; - newTop.right = this; - newTop.listParent = listParent; - listParent = newTop; - newTop.right = Rebalance(this); - return newTop; - } - - static void RebalanceUntilRoot(SharpTreeNode pos) - { - while (pos.listParent != null) { - if (pos == pos.listParent.left) { - pos = pos.listParent.left = Rebalance(pos); - } else { - Debug.Assert(pos == pos.listParent.right); - pos = pos.listParent.right = Rebalance(pos); - } - pos = pos.listParent; - } - SharpTreeNode newRoot = Rebalance(pos); - if (newRoot != pos && pos.treeFlattener != null) { - Debug.Assert(newRoot.treeFlattener == null); - newRoot.treeFlattener = pos.treeFlattener; - pos.treeFlattener = null; - newRoot.treeFlattener.root = newRoot; - } - Debug.Assert(newRoot.listParent == null); - newRoot.CheckInvariants(); - } - #endregion - - #region Insertion - static void InsertNodeAfter(SharpTreeNode pos, SharpTreeNode newNode) - { - // newNode might be the model root of a whole subtree, so go to the list root of that subtree: - newNode = newNode.GetListRoot(); - if (pos.right == null) { - pos.right = newNode; - newNode.listParent = pos; - } else { - // insert before pos.right's leftmost: - pos = pos.right; - while (pos.left != null) - pos = pos.left; - Debug.Assert(pos.left == null); - pos.left = newNode; - newNode.listParent = pos; - } - RebalanceUntilRoot(pos); - } - #endregion - - #region Removal - void RemoveNodes(SharpTreeNode start, SharpTreeNode end) - { - // Removes all nodes from start to end (inclusive) - // All removed nodes will be reorganized in a separate tree, do not delete - // regions that don't belong together in the tree model! - - List removedSubtrees = new List(); - SharpTreeNode oldPos; - SharpTreeNode pos = start; - do { - // recalculate the endAncestors every time, because the tree might have been rebalanced - HashSet endAncestors = new HashSet(); - for (SharpTreeNode tmp = end; tmp != null; tmp = tmp.listParent) - endAncestors.Add(tmp); - - removedSubtrees.Add(pos); - if (!endAncestors.Contains(pos)) { - // we can remove pos' right subtree in a single step: - if (pos.right != null) { - removedSubtrees.Add(pos.right); - pos.right.listParent = null; - pos.right = null; - } - } - SharpTreeNode succ = pos.Successor(); - DeleteNode(pos); // this will also rebalance out the deletion of the right subtree - - oldPos = pos; - pos = succ; - } while (oldPos != end); - - // merge back together the removed subtrees: - SharpTreeNode removed = removedSubtrees[0]; - for (int i = 1; i < removedSubtrees.Count; i++) { - removed = ConcatTrees(removed, removedSubtrees[i]); - } - } - - static SharpTreeNode ConcatTrees(SharpTreeNode first, SharpTreeNode second) - { - SharpTreeNode tmp = first; - while (tmp.right != null) - tmp = tmp.right; - InsertNodeAfter(tmp, second); - return tmp.GetListRoot(); - } - - SharpTreeNode Successor() - { - if (right != null) { - SharpTreeNode node = right; - while (node.left != null) - node = node.left; - return node; - } else { - SharpTreeNode node = this; - SharpTreeNode oldNode; - do { - oldNode = node; - node = node.listParent; - // loop while we are on the way up from the right part - } while (node != null && node.right == oldNode); - return node; - } - } - - static void DeleteNode(SharpTreeNode node) - { - SharpTreeNode balancingNode; - if (node.left == null) { - balancingNode = node.listParent; - node.ReplaceWith(node.right); - node.right = null; - } else if (node.right == null) { - balancingNode = node.listParent; - node.ReplaceWith(node.left); - node.left = null; - } else { - SharpTreeNode tmp = node.right; - while (tmp.left != null) - tmp = tmp.left; - // First replace tmp with tmp.right - balancingNode = tmp.listParent; - tmp.ReplaceWith(tmp.right); - tmp.right = null; - Debug.Assert(tmp.left == null); - Debug.Assert(tmp.listParent == null); - // Now move node's children to tmp: - tmp.left = node.left; node.left = null; - tmp.right = node.right; node.right = null; - if (tmp.left != null) tmp.left.listParent = tmp; - if (tmp.right != null) tmp.right.listParent = tmp; - // Then replace node with tmp - node.ReplaceWith(tmp); - if (balancingNode == node) - balancingNode = tmp; - } - Debug.Assert(node.listParent == null); - Debug.Assert(node.left == null); - Debug.Assert(node.right == null); - node.height = 1; - node.totalListLength = -1; - if (balancingNode != null) - RebalanceUntilRoot(balancingNode); - } - - void ReplaceWith(SharpTreeNode node) - { - if (listParent != null) { - if (listParent.left == this) { - listParent.left = node; - } else { - Debug.Assert(listParent.right == this); - listParent.right = node; - } - if (node != null) - node.listParent = listParent; - listParent = null; - } else { - // this was a root node - Debug.Assert(node != null); // cannot delete the only node in the tree - node.listParent = null; - if (treeFlattener != null) { - Debug.Assert(node.treeFlattener == null); - node.treeFlattener = treeFlattener; - treeFlattener = null; - node.treeFlattener.root = node; - } - } - } - #endregion - } -} diff --git a/SharpTreeView/GeneralAdorner.cs b/SharpTreeView/GeneralAdorner.cs deleted file mode 100644 index c540e54d..00000000 --- a/SharpTreeView/GeneralAdorner.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using Avalonia; -using Avalonia.Controls.Primitives; - -namespace ICSharpCode.TreeView -{ - public class GeneralAdorner : VisualLayerManager - { - protected override Size MeasureOverride(Size constraint) - { - if (Child != null) { - Child.Measure(constraint); - return Child.DesiredSize; - } - return new Size(); - } - - protected override Size ArrangeOverride(Size finalSize) - { - if (Child != null) { - Child.Arrange(new Rect(finalSize)); - return finalSize; - } - return new Size(); - } - } -} diff --git a/SharpTreeView/InsertMarker.cs b/SharpTreeView/InsertMarker.cs deleted file mode 100644 index c5891228..00000000 --- a/SharpTreeView/InsertMarker.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using Avalonia.Controls.Primitives; - -namespace ICSharpCode.TreeView -{ - public class InsertMarker : TemplatedControl - { - } -} diff --git a/SharpTreeView/LinesRenderer.cs b/SharpTreeView/LinesRenderer.cs deleted file mode 100644 index 8511dcad..00000000 --- a/SharpTreeView/LinesRenderer.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System.Diagnostics; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Media; - -namespace ICSharpCode.TreeView -{ - class LinesRenderer : Control - { - static LinesRenderer() - { - pen = new Pen(Brushes.LightGray); - } - - static Pen pen; - - SharpTreeNodeView NodeView => TemplatedParent as SharpTreeNodeView; - - public override void Render(DrawingContext dc) - { - if (NodeView.Node == null) { - // This seems to happen sometimes with DataContext==DisconnectedItem, - // though I'm not sure why WPF would call OnRender() on a disconnected node - Trace.WriteLine($"LinesRenderer.OnRender() called with DataContext={NodeView.DataContext}"); - return; - } - - var indent = NodeView.CalculateIndent(); - var p = new Point(indent + 4.5, 0); - - if (!NodeView.Node.IsRoot || NodeView.ParentTreeView.ShowRootExpander) { - dc.DrawLine(pen, new Point(p.X, Bounds.Height / 2), new Point(p.X + 10, Bounds.Height / 2)); - } - - if (NodeView.Node.IsRoot) return; - - if (NodeView.Node.IsLast) { - dc.DrawLine(pen, p, new Point(p.X, Bounds.Height / 2)); - } - else { - dc.DrawLine(pen, p, new Point(p.X, Bounds.Height)); - } - - var current = NodeView.Node; - while (true) { - p = p.WithX(p.X - 19); - current = current.Parent; - if (p.X < 0) break; - if (!current.IsLast) { - dc.DrawLine(pen, p, new Point(p.X, Bounds.Height)); - } - } - } - } -} diff --git a/SharpTreeView/Properties/AssemblyInfo.cs b/SharpTreeView/Properties/AssemblyInfo.cs deleted file mode 100644 index 2abf7320..00000000 --- a/SharpTreeView/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System.Reflection; -using Avalonia.Metadata; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ICSharpCode.TreeView")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US english -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] - - -//[assembly: ThemeInfo( -// ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located -// //(used if a resource is not found in the page, -// // or application resource dictionaries) -// ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located -// //(used if a resource is not found in the page, -// // app, or any theme specific resource dictionaries) -//)] - - -//[assembly: XmlnsPrefix("http://icsharpcode.net/sharpdevelop/treeview", "treeview")] - -[assembly: XmlnsDefinition("http://icsharpcode.net/sharpdevelop/treeview", "ICSharpCode.TreeView")] diff --git a/SharpTreeView/Properties/GlobalAssemblyInfo.cs b/SharpTreeView/Properties/GlobalAssemblyInfo.cs deleted file mode 100644 index f58efa15..00000000 --- a/SharpTreeView/Properties/GlobalAssemblyInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -///////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////// -// // -// DO NOT EDIT GlobalAssemblyInfo.cs, it is recreated using AssemblyInfo.template whenever // -// ICSharpCode.Core is compiled. // -// // -///////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////// - -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Resources; -using System.Runtime.InteropServices; - -[assembly: ComVisible(false)] -[assembly: AssemblyCompany("ic#code")] -[assembly: AssemblyProduct("SharpDevelop")] -[assembly: AssemblyCopyright("2000-2012 AlphaSierraPapa for the SharpDevelop Team")] -[assembly: AssemblyVersion(RevisionClass.Major + "." + RevisionClass.Minor + "." + RevisionClass.Build + "." + RevisionClass.Revision)] -[assembly: AssemblyInformationalVersion(RevisionClass.FullVersion + "-ca8a8e28")] -[assembly: NeutralResourcesLanguage("en-US")] - -[assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", - Justification = "AssemblyInformationalVersion does not need to be a parsable version")] - -internal static class RevisionClass -{ - public const string Major = "4"; - public const string Minor = "2"; - public const string Build = "0"; - public const string Revision = "8752"; - public const string VersionName = "Beta 2"; - - public const string FullVersion = Major + "." + Minor + "." + Build + ".8752-Beta 2"; -} diff --git a/SharpTreeView/SharpGridView.cs b/SharpTreeView/SharpGridView.cs deleted file mode 100644 index c7b14564..00000000 --- a/SharpTreeView/SharpGridView.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using Avalonia.Controls; - -namespace ICSharpCode.TreeView -{ - public class SharpGridView : ListBox - { - //public static ResourceKey ItemContainerStyleKey { get; private set; } - - //protected override object ItemContainerDefaultStyleKey - //{ - // get - // { - // return ItemContainerStyleKey; - // } - //} - } -} diff --git a/SharpTreeView/SharpTreeNode.cs b/SharpTreeView/SharpTreeNode.cs deleted file mode 100644 index c9cadc59..00000000 --- a/SharpTreeView/SharpTreeNode.cs +++ /dev/null @@ -1,635 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq; -using Avalonia; -using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.Media; - -namespace ICSharpCode.TreeView -{ - public partial class SharpTreeNode : INotifyPropertyChanged - { - SharpTreeNodeCollection modelChildren; - internal SharpTreeNode modelParent; - bool isVisible = true; - - void UpdateIsVisible(bool parentIsVisible, bool updateFlattener) - { - bool newIsVisible = parentIsVisible && !isHidden; - if (isVisible != newIsVisible) { - isVisible = newIsVisible; - - // invalidate the augmented data - SharpTreeNode node = this; - while (node != null && node.totalListLength >= 0) { - node.totalListLength = -1; - node = node.listParent; - } - // Remember the removed nodes: - List removedNodes = null; - if (updateFlattener && !newIsVisible) { - removedNodes = VisibleDescendantsAndSelf().ToList(); - } - // also update the model children: - UpdateChildIsVisible(false); - - // Validate our invariants: - if (updateFlattener) - CheckRootInvariants(); - - // Tell the flattener about the removed nodes: - if (removedNodes != null) { - var flattener = GetListRoot().treeFlattener; - if (flattener != null) { - flattener.NodesRemoved(GetVisibleIndexForNode(this), removedNodes); - foreach (var n in removedNodes) - n.OnIsVisibleChanged(); - } - } - // Tell the flattener about the new nodes: - if (updateFlattener && newIsVisible) { - var flattener = GetListRoot().treeFlattener; - if (flattener != null) { - flattener.NodesInserted(GetVisibleIndexForNode(this), VisibleDescendantsAndSelf()); - foreach (var n in VisibleDescendantsAndSelf()) - n.OnIsVisibleChanged(); - } - } - } - } - - protected virtual void OnIsVisibleChanged() {} - - void UpdateChildIsVisible(bool updateFlattener) - { - if (modelChildren != null && modelChildren.Count > 0) { - bool showChildren = isVisible && isExpanded; - foreach (SharpTreeNode child in modelChildren) { - child.UpdateIsVisible(showChildren, updateFlattener); - } - } - } - - #region Main - - public SharpTreeNodeCollection Children { - get { - if (modelChildren == null) - modelChildren = new SharpTreeNodeCollection(this); - return modelChildren; - } - } - - public SharpTreeNode Parent => modelParent; - - public virtual object Text => null; - - public virtual IBrush Foreground => SystemColors.WindowTextBrush; - - public virtual object Icon => null; - - public virtual object ToolTip => null; - - public int Level => Parent != null ? Parent.Level + 1 : 0; - - public bool IsRoot => Parent == null; - - bool isHidden; - - public bool IsHidden - { - get => isHidden; - set { - if (isHidden != value) { - isHidden = value; - if (modelParent != null) - UpdateIsVisible(modelParent.isVisible && modelParent.isExpanded, true); - RaisePropertyChanged("IsHidden"); - Parent?.RaisePropertyChanged("ShowExpander"); - } - } - } - - /// - /// Return true when this node is not hidden and when all parent nodes are expanded and not hidden. - /// - public bool IsVisible => isVisible; - - bool isSelected; - - public bool IsSelected { - get => isSelected; - set { - if (isSelected != value) { - isSelected = value; - RaisePropertyChanged("IsSelected"); - } - } - } - - #endregion - - #region OnChildrenChanged - internal protected virtual void OnChildrenChanged(NotifyCollectionChangedEventArgs e) - { - if (e.OldItems != null) { - foreach (SharpTreeNode node in e.OldItems) { - Debug.Assert(node.modelParent == this); - node.modelParent = null; - SharpTreeNode removeEnd = node; - while (removeEnd.modelChildren != null && removeEnd.modelChildren.Count > 0) - removeEnd = removeEnd.modelChildren.Last(); - - List removedNodes = null; - int visibleIndexOfRemoval = 0; - if (node.isVisible) { - visibleIndexOfRemoval = GetVisibleIndexForNode(node); - removedNodes = node.VisibleDescendantsAndSelf().ToList(); - } - - RemoveNodes(node, removeEnd); - - if (removedNodes != null) { - var flattener = GetListRoot().treeFlattener; - flattener?.NodesRemoved(visibleIndexOfRemoval, removedNodes); - } - } - } - if (e.NewItems != null) { - SharpTreeNode insertionPos; - if (e.NewStartingIndex == 0) - insertionPos = null; - else - insertionPos = modelChildren[e.NewStartingIndex - 1]; - - foreach (SharpTreeNode node in e.NewItems) { - Debug.Assert(node.modelParent == null); - node.modelParent = this; - node.UpdateIsVisible(isVisible && isExpanded, false); - - while (insertionPos is {modelChildren: { }} && insertionPos.modelChildren.Count > 0) { - insertionPos = insertionPos.modelChildren.Last(); - } - InsertNodeAfter(insertionPos ?? this, node); - - insertionPos = node; - if (node.isVisible) { - var flattener = GetListRoot().treeFlattener; - flattener?.NodesInserted(GetVisibleIndexForNode(node), node.VisibleDescendantsAndSelf()); - } - } - } - - RaisePropertyChanged("ShowExpander"); - RaiseIsLastChangedIfNeeded(e); - } - #endregion - - #region Expanding / LazyLoading - - public virtual object ExpandedIcon => Icon; - - public virtual bool ShowExpander - { - get { return LazyLoading || Children.Any(c => !c.isHidden); } - } - - bool isExpanded; - - public bool IsExpanded - { - get => isExpanded; - set - { - if (isExpanded != value) { - isExpanded = value; - if (isExpanded) { - EnsureLazyChildren(); - OnExpanding(); - } else { - OnCollapsing(); - } - UpdateChildIsVisible(true); - RaisePropertyChanged("IsExpanded"); - } - } - } - - protected virtual void OnExpanding() {} - protected virtual void OnCollapsing() {} - - bool lazyLoading; - - public bool LazyLoading - { - get => lazyLoading; - set - { - lazyLoading = value; - if (lazyLoading) { - IsExpanded = false; - if (canExpandRecursively) { - canExpandRecursively = false; - RaisePropertyChanged("CanExpandRecursively"); - } - } - RaisePropertyChanged("LazyLoading"); - RaisePropertyChanged("ShowExpander"); - } - } - - bool canExpandRecursively = true; - - /// - /// Gets whether this node can be expanded recursively. - /// If not overridden, this property returns false if the node is using lazy-loading, and true otherwise. - /// - public virtual bool CanExpandRecursively => canExpandRecursively; - - public virtual bool ShowIcon => Icon != null; - - protected virtual void LoadChildren() - { - throw new NotSupportedException(GetType().Name + " does not support lazy loading"); - } - - /// - /// Ensures the children were initialized (loads children if lazy loading is enabled) - /// - public void EnsureLazyChildren() - { - if (LazyLoading) { - LazyLoading = false; - try { - LoadChildren(); - } catch (Exception ex) { - Trace.WriteLine(ex.Message); - Trace.WriteLine(ex.StackTrace); - throw ex; - } - } - } - - #endregion - - #region Ancestors / Descendants - - public IEnumerable Descendants() - { - return TreeTraversal.PreOrder(Children, n => n.Children); - } - - public IEnumerable DescendantsAndSelf() - { - return TreeTraversal.PreOrder(this, n => n.Children); - } - - internal IEnumerable VisibleDescendants() - { - return TreeTraversal.PreOrder(Children.Where(c => c.isVisible), n => n.Children.Where(c => c.isVisible)); - } - - internal IEnumerable VisibleDescendantsAndSelf() - { - return TreeTraversal.PreOrder(this, n => n.Children.Where(c => c.isVisible)); - } - - public IEnumerable Ancestors() - { - var node = this; - while (node.Parent != null) { - yield return node.Parent; - node = node.Parent; - } - } - - public IEnumerable AncestorsAndSelf() - { - yield return this; - foreach (var node in Ancestors()) { - yield return node; - } - } - - #endregion - - #region Editing - - public virtual bool IsEditable => false; - - bool isEditing; - - public bool IsEditing - { - get => isEditing; - set - { - if (isEditing != value) { - isEditing = value; - RaisePropertyChanged("IsEditing"); - } - } - } - - public virtual string LoadEditText() - { - return null; - } - - public virtual bool SaveEditText(string value) - { - return true; - } - - #endregion - - #region Checkboxes - - public virtual bool IsCheckable => false; - - bool? isChecked; - - public bool? IsChecked { - get => isChecked; - set => SetIsChecked(value, true); - } - - void SetIsChecked(bool? value, bool update) - { - if (isChecked != value) { - isChecked = value; - - if (update) { - if (IsChecked != null) { - foreach (var child in Descendants()) { - if (child.IsCheckable) { - child.SetIsChecked(IsChecked, false); - } - } - } - - foreach (var parent in Ancestors()) { - if (parent.IsCheckable) { - if (!parent.TryValueForIsChecked(true)) { - if (!parent.TryValueForIsChecked(false)) { - parent.SetIsChecked(null, false); - } - } - } - } - } - - RaisePropertyChanged("IsChecked"); - } - } - - bool TryValueForIsChecked(bool? value) - { - if (Children.Where(n => n.IsCheckable).All(n => n.IsChecked == value)) { - SetIsChecked(value, false); - return true; - } - return false; - } - - #endregion - - #region Cut / Copy / Paste / Delete - - public bool IsCut => false; - /* - static List cuttedNodes = new List(); - static IDataObject cuttedData; - static EventHandler requerySuggestedHandler; // for weak event - - static void StartCuttedDataWatcher() - { - requerySuggestedHandler = new EventHandler(CommandManager_RequerySuggested); - CommandManager.RequerySuggested += requerySuggestedHandler; - } - - static void CommandManager_RequerySuggested(object sender, EventArgs e) - { - if (cuttedData != null && !Clipboard.IsCurrent(cuttedData)) { - ClearCuttedData(); - } - } - - static void ClearCuttedData() - { - foreach (var node in cuttedNodes) { - node.IsCut = false; - } - cuttedNodes.Clear(); - cuttedData = null; - } - - //static public IEnumerable PurifyNodes(IEnumerable nodes) - //{ - // var list = nodes.ToList(); - // var array = list.ToArray(); - // foreach (var node1 in array) { - // foreach (var node2 in array) { - // if (node1.Descendants().Contains(node2)) { - // list.Remove(node2); - // } - // } - // } - // return list; - //} - - bool isCut; - - public bool IsCut - { - get { return isCut; } - private set - { - isCut = value; - RaisePropertyChanged("IsCut"); - } - } - - internal bool InternalCanCut() - { - return InternalCanCopy() && InternalCanDelete(); - } - - internal void InternalCut() - { - ClearCuttedData(); - cuttedData = Copy(ActiveNodesArray); - Clipboard.SetDataObject(cuttedData); - - foreach (var node in ActiveNodes) { - node.IsCut = true; - cuttedNodes.Add(node); - } - } - - internal bool InternalCanCopy() - { - return CanCopy(ActiveNodesArray); - } - - internal void InternalCopy() - { - Clipboard.SetDataObject(Copy(ActiveNodesArray)); - } - - internal bool InternalCanPaste() - { - return CanPaste(Clipboard.GetDataObject()); - } - - internal void InternalPaste() - { - Paste(Clipboard.GetDataObject()); - - if (cuttedData != null) { - DeleteCore(cuttedNodes.ToArray()); - ClearCuttedData(); - } - } - */ - - public virtual bool CanDelete() - { - return false; - } - - public virtual void Delete() - { - throw new NotSupportedException(GetType().Name + " does not support deletion"); - } - - public virtual void DeleteCore() - { - throw new NotSupportedException(GetType().Name + " does not support deletion"); - } - - public virtual IDataObject Copy(SharpTreeNode[] nodes) - { - throw new NotSupportedException(GetType().Name + " does not support copy/paste or drag'n'drop"); - } - - /* - public virtual bool CanCopy(SharpTreeNode[] nodes) - { - return false; - } - - public virtual bool CanPaste(IDataObject data) - { - return false; - } - - public virtual void Paste(IDataObject data) - { - EnsureLazyChildren(); - Drop(data, Children.Count, DropEffect.Copy); - } - */ - #endregion - - #region Drag and Drop - public virtual bool CanDrag(SharpTreeNode[] nodes) - { - return false; - } - - public virtual async void StartDrag(PointerEventArgs e, AvaloniaObject dragSource, SharpTreeNode[] nodes) - { - DragDropEffects effects = DragDropEffects.Copy | DragDropEffects.Link | DragDropEffects.Move; - if (!nodes.All(n => n.CanDelete())) - effects &= ~DragDropEffects.Move; - DragDropEffects result = await DragDrop.DoDragDrop(e, Copy(nodes), effects); - if (result == DragDropEffects.Move) { - foreach (SharpTreeNode node in nodes) - node.DeleteCore(); - } - } - - public virtual bool CanDrop(DragEventArgs e, int index) - { - return false; - } - - internal void InternalDrop(DragEventArgs e, int index) - { - if (LazyLoading) { - EnsureLazyChildren(); - index = Children.Count; - } - - Drop(e, index); - } - - public virtual void Drop(DragEventArgs e, int index) - { - throw new NotSupportedException(GetType().Name + " does not support Drop()"); - } - #endregion - - #region IsLast (for TreeView lines) - - public bool IsLast => - Parent == null || - Parent.Children[Parent.Children.Count - 1] == this; - - void RaiseIsLastChangedIfNeeded(NotifyCollectionChangedEventArgs e) - { - switch (e.Action) { - case NotifyCollectionChangedAction.Add: - if (e.NewStartingIndex == Children.Count - 1) { - if (Children.Count > 1) { - Children[Children.Count - 2].RaisePropertyChanged("IsLast"); - } - Children[Children.Count - 1].RaisePropertyChanged("IsLast"); - } - break; - case NotifyCollectionChangedAction.Remove: - if (e.OldStartingIndex == Children.Count) { - if (Children.Count > 0) { - Children[Children.Count - 1].RaisePropertyChanged("IsLast"); - } - } - break; - } - } - - #endregion - - #region INotifyPropertyChanged Members - - public event PropertyChangedEventHandler PropertyChanged; - - public void RaisePropertyChanged(string name) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); - } - - #endregion - - /// - /// Gets called when the item is double-clicked. - /// - public virtual void ActivateItem(RoutedEventArgs e) - { - } - - public override string ToString() - { - // used for keyboard navigation - object text = Text; - return text != null ? text.ToString() : string.Empty; - } - } -} diff --git a/SharpTreeView/SharpTreeNodeCollection.cs b/SharpTreeView/SharpTreeNodeCollection.cs deleted file mode 100644 index 7255b060..00000000 --- a/SharpTreeView/SharpTreeNodeCollection.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; -using System.Linq; - -namespace ICSharpCode.TreeView -{ - /// - /// Collection that validates that inserted nodes do not have another parent. - /// - public sealed class SharpTreeNodeCollection : IList, INotifyCollectionChanged - { - readonly SharpTreeNode parent; - List list = new List(); - bool isRaisingEvent; - - public SharpTreeNodeCollection(SharpTreeNode parent) - { - this.parent = parent; - } - - public event NotifyCollectionChangedEventHandler CollectionChanged; - - void OnCollectionChanged(NotifyCollectionChangedEventArgs e) - { - Debug.Assert(!isRaisingEvent); - isRaisingEvent = true; - try { - parent.OnChildrenChanged(e); - CollectionChanged?.Invoke(this, e); - } finally { - isRaisingEvent = false; - } - } - - void ThrowOnReentrancy() - { - if (isRaisingEvent) - throw new InvalidOperationException(); - } - - void ThrowIfValueIsNullOrHasParent(SharpTreeNode node) - { - if (node == null) - throw new ArgumentNullException("node"); - if (node.modelParent != null) - throw new ArgumentException("The node already has a parent", "node"); - } - - public SharpTreeNode this[int index] { - get => list[index]; - set { - ThrowOnReentrancy(); - var oldItem = list[index]; - if (oldItem == value) - return; - ThrowIfValueIsNullOrHasParent(value); - list[index] = value; - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldItem, index)); - } - } - - public int Count => list.Count; - - bool ICollection.IsReadOnly => false; - - public int IndexOf(SharpTreeNode node) - { - if (node == null || node.modelParent != parent) - return -1; - return list.IndexOf(node); - } - - public void Insert(int index, SharpTreeNode node) - { - ThrowOnReentrancy(); - ThrowIfValueIsNullOrHasParent(node); - list.Insert(index, node); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, index)); - } - - public void InsertRange(int index, IEnumerable nodes) - { - if (nodes == null) - throw new ArgumentNullException("nodes"); - ThrowOnReentrancy(); - List newNodes = nodes.ToList(); - if (newNodes.Count == 0) - return; - foreach (SharpTreeNode node in newNodes) { - ThrowIfValueIsNullOrHasParent(node); - } - list.InsertRange(index, newNodes); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newNodes, index)); - } - - public void RemoveAt(int index) - { - ThrowOnReentrancy(); - var oldItem = list[index]; - list.RemoveAt(index); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index)); - } - - public void RemoveRange(int index, int count) - { - ThrowOnReentrancy(); - if (count == 0) - return; - var oldItems = list.GetRange(index, count); - list.RemoveRange(index, count); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, index)); - } - - public void Add(SharpTreeNode node) - { - ThrowOnReentrancy(); - ThrowIfValueIsNullOrHasParent(node); - list.Add(node); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, list.Count - 1)); - } - - public void AddRange(IEnumerable nodes) - { - InsertRange(Count, nodes); - } - - public void Clear() - { - ThrowOnReentrancy(); - var oldList = list; - list = new List(); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldList, 0)); - } - - public bool Contains(SharpTreeNode node) - { - return IndexOf(node) >= 0; - } - - public void CopyTo(SharpTreeNode[] array, int arrayIndex) - { - list.CopyTo(array, arrayIndex); - } - - public bool Remove(SharpTreeNode item) - { - int pos = IndexOf(item); - if (pos >= 0) { - RemoveAt(pos); - return true; - } - - return false; - } - - public IEnumerator GetEnumerator() - { - return list.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return list.GetEnumerator(); - } - - public void RemoveAll(Predicate match) - { - if (match == null) - throw new ArgumentNullException("match"); - ThrowOnReentrancy(); - int firstToRemove = 0; - for (int i = 0; i < list.Count; i++) { - bool removeNode; - isRaisingEvent = true; - try { - removeNode = match(list[i]); - } finally { - isRaisingEvent = false; - } - if (!removeNode) { - if (firstToRemove < i) { - RemoveRange(firstToRemove, i - firstToRemove); - i = firstToRemove - 1; - } else { - firstToRemove = i + 1; - } - Debug.Assert(firstToRemove == i + 1); - } - } - if (firstToRemove < list.Count) { - RemoveRange(firstToRemove, list.Count - firstToRemove); - } - } - } -} diff --git a/SharpTreeView/SharpTreeNodeView.cs b/SharpTreeView/SharpTreeNodeView.cs deleted file mode 100644 index 46c9225c..00000000 --- a/SharpTreeView/SharpTreeNodeView.cs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Presenters; -using Avalonia.Controls.Primitives; -using Avalonia.Data; -using Avalonia.Media; - -namespace ICSharpCode.TreeView -{ - public class SharpTreeNodeView : TemplatedControl - { - public static readonly StyledProperty TextBackgroundProperty = - AvaloniaProperty.Register(nameof(TextBackground)); - - public IBrush TextBackground - { - get => GetValue(TextBackgroundProperty); - set => SetValue(TextBackgroundProperty, value); - } - - public static readonly DirectProperty IconProperty = - AvaloniaProperty.RegisterDirect(nameof(Icon), owner => { - var expanded = owner.Node?.IsExpanded; - if (!expanded.HasValue) { - return null; - } - return expanded.Value ? owner.Node?.ExpandedIcon : owner.Node?.Icon; - }); - - public object Icon => GetValue(IconProperty); - - - public SharpTreeNode Node => DataContext as SharpTreeNode; - - public SharpTreeViewItem ParentItem { get; private set; } - - public static readonly StyledProperty CellEditorProperty = - AvaloniaProperty.Register("CellEditor"); - - public Control CellEditor { - get => GetValue(CellEditorProperty); - set => SetValue(CellEditorProperty, value); - } - - public SharpTreeView ParentTreeView => ParentItem.ParentTreeView; - - internal LinesRenderer LinesRenderer; - internal Control spacer; - internal ToggleButton expander; - internal ContentPresenter icon; - internal Border textEditorContainer; - internal Border checkBoxContainer; - internal CheckBox checkBox; - internal Border textContainer; - internal ContentPresenter textContent; - List bindings = new List(); - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - ParentItem = this.FindAncestor(); - ParentItem.NodeView = this; - } - - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - - LinesRenderer = e.NameScope.Find("linesRenderer"); - spacer = e.NameScope.Find("spacer"); - expander = e.NameScope.Find("expander"); - icon = e.NameScope.Find("icon"); - textEditorContainer = e.NameScope.Find("textEditorContainer"); - checkBoxContainer = e.NameScope.Find("checkBoxContainer"); - checkBoxContainer = e.NameScope.Find("checkBoxContainer"); - checkBox = e.NameScope.Find("checkBox"); - textContainer = e.NameScope.Find("textContainer"); - textContent = e.NameScope.Find("textContent"); - - UpdateTemplate(); - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) - { - base.OnPropertyChanged(e); - if (e.Property == DataContextProperty) - { - UpdateDataContext(e.OldValue as SharpTreeNode, e.NewValue as SharpTreeNode); - } - } - - void UpdateDataContext(SharpTreeNode oldNode, SharpTreeNode newNode) - { - if (oldNode != null) - { - oldNode.PropertyChanged -= Node_PropertyChanged; - bindings.ForEach(obj => obj.Dispose()); - bindings.Clear(); - } - if (newNode != null) { - newNode.PropertyChanged += Node_PropertyChanged; - if (Template != null) { - UpdateTemplate(); - } - } - } - - void Node_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "IsEditing") { - OnIsEditingChanged(); - } else if (e.PropertyName == "IsLast") { - if (ParentTreeView.ShowLines) { - foreach (var child in Node.VisibleDescendantsAndSelf()) { - if (ParentTreeView.ContainerFromItem(child) is SharpTreeViewItem container && container.NodeView != null) { - container.NodeView.LinesRenderer.InvalidateVisual(); - } - } - } - } else if (e.PropertyName == "IsExpanded") { - RaisePropertyChanged(IconProperty, null, Icon); - if (Node.IsExpanded) - ParentTreeView.HandleExpanding(Node); - } - } - - void OnIsEditingChanged() - { - if (Node.IsEditing) { - if (CellEditor == null) - textEditorContainer.Child = new EditTextBox { Item = ParentItem }; - else - textEditorContainer.Child = CellEditor; - } - else { - textEditorContainer.Child = null; - } - } - - void UpdateTemplate() - { - if(Node != null) - { - bindings.Add(expander.Bind(IsVisibleProperty, new Binding("ShowExpander") { Source = Node })); - bindings.Add(expander.Bind(ToggleButton.IsCheckedProperty, new Binding("IsExpanded") { Source = Node })); - bindings.Add(icon.Bind(IsVisibleProperty, new Binding("ShowIcon") { Source = Node })); - bindings.Add(checkBoxContainer.Bind(IsVisibleProperty, new Binding("IsCheckable") { Source = Node })); - bindings.Add(checkBox.Bind(CheckBox.IsCheckedProperty, new Binding("IsChecked") { Source = Node })); - bindings.Add(textContainer.Bind(IsVisibleProperty, new Binding("IsEditing") { Source = Node, Converter = BoolConverters.Inverse })); - bindings.Add(textContent.Bind(ContentPresenter.ContentProperty, new Binding("Text") { Source = Node })); - RaisePropertyChanged(IconProperty, null, Icon); - } - - spacer.Width = CalculateIndent(); - - if (ParentTreeView.Root == Node && !ParentTreeView.ShowRootExpander) { - expander.IsVisible = false; - } - else { - expander.ClearValue(IsVisibleProperty); - } - } - - internal double CalculateIndent() - { - var result = 19 * Node.Level; - if (ParentTreeView.ShowRoot) { - if (!ParentTreeView.ShowRootExpander) { - if (ParentTreeView.Root != Node) { - result -= 15; - } - } - } - else { - result -= 19; - } - if (result < 0) { - Trace.WriteLine("SharpTreeNodeView.CalculateIndent() on node without correctly-set level"); - return 0; - } - return result; - } - } -} diff --git a/SharpTreeView/SharpTreeView.cs b/SharpTreeView/SharpTreeView.cs deleted file mode 100644 index 8b4cb049..00000000 --- a/SharpTreeView/SharpTreeView.cs +++ /dev/null @@ -1,766 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; -using System.Linq; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Generators; -using Avalonia.Controls.Primitives; -using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.Styling; -using Avalonia.Threading; -using AvaloniaEdit; - -namespace ICSharpCode.TreeView -{ - public class SharpTreeView : ListBox, IStyleable, IRoutedCommandBindable - { - static SharpTreeView() - { - SelectionModeProperty?.OverrideDefaultValue(SelectionMode.Multiple); - //ItemsPanelProperty.OverrideDefaultValue(new FuncTemplate(() => new VirtualizingStackPanel())); - - //AlternationCountProperty.OverrideMetadata(typeof(SharpTreeView), - // new FrameworkPropertyMetadata(2)); - - //VirtualizationModeProperty.OverrideDefaultValue(ItemVirtualizationMode.Recycling); - - DragDrop.DragEnterEvent.AddClassHandler((x, e) => x.OnDragEnter(e)); - DragDrop.DragOverEvent.AddClassHandler((x, e) => x.OnDragOver(e)); - DragDrop.DropEvent.AddClassHandler((x, e) => x.OnDrop(e)); - } - - public SharpTreeView() - { - SelectionChanged += OnSelectionChanged; - RegisterCommands(); - } - - public static readonly StyledProperty RootProperty = - AvaloniaProperty.Register(nameof(Root)); - - public SharpTreeNode Root - { - get => GetValue(RootProperty); - set => SetValue(RootProperty, value); - } - - public static readonly StyledProperty ShowRootProperty = - AvaloniaProperty.Register(nameof(ShowRoot), true); - - public bool ShowRoot - { - get => GetValue(ShowRootProperty); - set => SetValue(ShowRootProperty, value); - } - - public static readonly StyledProperty ShowRootExpanderProperty = - AvaloniaProperty.Register(nameof(ShowRootExpander)); - - public bool ShowRootExpander - { - get => GetValue(ShowRootExpanderProperty); - set => SetValue(ShowRootExpanderProperty, value); - } - - public static readonly StyledProperty AllowDropOrderProperty = - AvaloniaProperty.Register(nameof(AllowDropOrder)); - - public bool AllowDropOrder - { - get => GetValue(AllowDropOrderProperty); - set => SetValue(AllowDropOrderProperty, value); - } - - public static readonly StyledProperty ShowLinesProperty = - AvaloniaProperty.Register(nameof(ShowLines), true); - - public bool ShowLines { - get => GetValue(ShowLinesProperty); - set => SetValue(ShowLinesProperty, value); - } - - public static readonly StyledProperty IsTextSearchCaseSensitiveProperty = - AvaloniaProperty.Register(nameof(IsTextSearchCaseSensitive)); - - public bool IsTextSearchCaseSensitive { - get => GetValue(IsTextSearchCaseSensitiveProperty); - set => SetValue(IsTextSearchCaseSensitiveProperty, value); - } - - public new static readonly StyledProperty IsTextSearchEnabledProperty = - AvaloniaProperty.Register(nameof(IsTextSearchEnabled), true); - - public new bool IsTextSearchEnabled { - get => GetValue(IsTextSearchEnabledProperty); - set => SetValue(IsTextSearchEnabledProperty, value); - } - - public static bool GetShowAlternation(AvaloniaObject obj) - { - return obj.GetValue(ShowAlternationProperty); - } - - public static void SetShowAlternation(AvaloniaObject obj, bool value) - { - obj.SetValue(ShowAlternationProperty, value); - } - - public static readonly StyledProperty ShowAlternationProperty = - AvaloniaProperty.Register("ShowAlternation", false, true); - - Type IStyleable.StyleKey => typeof(ListBox); - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) - { - base.OnPropertyChanged(e); - if (e.Property == RootProperty || - e.Property == ShowRootProperty || - e.Property == ShowRootExpanderProperty) { - Reload(); - } - } - - TreeFlattener flattener; - bool updatesLocked; - - public IDisposable LockUpdates() - { - return new UpdateLock(this); - } - - class UpdateLock : IDisposable - { - SharpTreeView instance; - - public UpdateLock(SharpTreeView instance) - { - this.instance = instance; - this.instance.updatesLocked = true; - } - - public void Dispose() - { - instance.updatesLocked = false; - } - } - - void Reload() - { - flattener?.Stop(); - if (Root != null) { - if (!(ShowRoot && ShowRootExpander)) { - Root.IsExpanded = true; - } - flattener = new TreeFlattener(Root, ShowRoot); - flattener.CollectionChanged += flattener_CollectionChanged; - Items = flattener; - } - } - - void flattener_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - // Deselect nodes that are being hidden, if any remain in the tree - if (e.Action == NotifyCollectionChangedAction.Remove && Items.Any()) { - List selectedOldItems = null; - foreach (SharpTreeNode node in e.OldItems) { - if (node.IsSelected) { - if (selectedOldItems == null) - selectedOldItems = new List(); - selectedOldItems.Add(node); - } - } - if (!updatesLocked && selectedOldItems != null) { - var list = SelectedItems.Cast().Except(selectedOldItems).ToList(); - UpdateFocusedNode(list, Math.Max(0, e.OldStartingIndex - 1)); - } - } - } - - void UpdateFocusedNode(List newSelection, int topSelectedIndex) - { - if (updatesLocked) return; - SetSelectedNodes(newSelection ?? Enumerable.Empty()); - if (SelectedItem == null) { - // if we removed all selected nodes, then move the focus to the node - // preceding the first of the old selected nodes - SelectedIndex = topSelectedIndex; - if (SelectedItem != null) - FocusNode((SharpTreeNode)SelectedItem); - } - } - - protected override IItemContainerGenerator CreateItemContainerGenerator() - { - return new ItemContainerGenerator( - this, - SharpTreeViewItem.ContentProperty, - SharpTreeViewItem.ContentTemplateProperty) - { - ItemTemplate = ItemTemplate - }; - } - - protected override void OnContainersMaterialized(ItemContainerEventArgs e) - { - base.OnContainersMaterialized(e); - foreach (var item in e.Containers) { - var container = item.ContainerControl as SharpTreeViewItem; - container.ParentTreeView = this; - // Make sure that the line renderer takes into account the new bound data - container.NodeView?.LinesRenderer.InvalidateVisual(); - } - } - - protected override void OnContainersRecycled(ItemContainerEventArgs e) - { - base.OnContainersRecycled(e); - - foreach (var item in e.Containers) - { - var container = item.ContainerControl as SharpTreeViewItem; - container.ParentTreeView = this; - // Make sure that the line renderer takes into account the new bound data - container.NodeView?.LinesRenderer.InvalidateVisual(); - } - } - - internal IControl ContainerFromItem(object item) - { - int index = IndexOf(Items, item); - if (index != -1) { - return ItemContainerGenerator.ContainerFromIndex(index); - } - return null; - } - - bool doNotScrollOnExpanding; - - /// - /// Handles the node expanding event in the tree view. - /// This method gets called only if the node is in the visible region (a SharpTreeNodeView exists). - /// - internal void HandleExpanding(SharpTreeNode node) - { - if (doNotScrollOnExpanding) - return; - - SharpTreeNode lastVisibleChild = node; - while (true) { - SharpTreeNode tmp = lastVisibleChild.Children.LastOrDefault(c => c.IsVisible); - if (tmp != null) { - lastVisibleChild = tmp; - } else { - break; - } - } - if (lastVisibleChild != node) { - // Make the the expanded children are visible; but don't scroll down - // to much (keep node itself visible) - base.ScrollIntoView(lastVisibleChild); - // For some reason, this only works properly when delaying it... - Dispatcher.UIThread.InvokeAsync(delegate { - base.ScrollIntoView(node); - }, DispatcherPriority.Loaded); - } - } - - protected override void OnKeyDown(KeyEventArgs e) - { - SharpTreeViewItem container = e.Source as SharpTreeViewItem; - switch (e.Key) { - case Key.Left: - if (container != null && ItemContainerGenerator.IndexFromContainer(e.Source as IControl) != -1) { - if (container.Node.IsExpanded) { - container.Node.IsExpanded = false; - } else if (container.Node.Parent != null) { - FocusNode(container.Node.Parent); - } - e.Handled = true; - } - break; - case Key.Right: - // TODO: focus on first child - if (container != null && ItemContainerGenerator.IndexFromContainer(e.Source as IControl) != -1) { - if (!container.Node.IsExpanded && container.Node.ShowExpander) { - container.Node.IsExpanded = true; - } else if (container.Node.Children.Count > 0) { - // jump to first child: - - //container.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down)); - } - e.Handled = true; - } - break; - case Key.Return: - case Key.Space: - if (container != null && e.KeyModifiers == KeyModifiers.None && SelectedItems.Count == 1 && SelectedItem == container.Node) { - container.Node.ActivateItem(e); - } - break; - case Key.Add: - if (container != null && ItemContainerGenerator.IndexFromContainer(e.Source as IControl) != -1) { - container.Node.IsExpanded = true; - e.Handled = true; - } - break; - case Key.Subtract: - if (container != null && ItemContainerGenerator.IndexFromContainer(e.Source as IControl) != -1) { - container.Node.IsExpanded = false; - e.Handled = true; - } - break; - case Key.Multiply: - if (container != null && ItemContainerGenerator.IndexFromContainer(e.Source as IControl) != -1) { - container.Node.IsExpanded = true; - ExpandRecursively(container.Node); - e.Handled = true; - } - break; - case Key.Back: - if (IsTextSearchEnabled) { - var instance = SharpTreeViewTextSearch.GetInstance(this); - if (instance != null) { - instance.RevertLastCharacter(); - e.Handled = true; - } - } - break; - } - - foreach (var commandBinding in CommandBindings) - { - if (commandBinding.Command.Gesture?.Matches(e) == true) - { - commandBinding.Command.Execute(null, this); - e.Handled = true; - break; - } - } - - if (!e.Handled) - base.OnKeyDown(e); - } - - protected override void OnTextInput(TextInputEventArgs e) - { - if (!string.IsNullOrEmpty(e.Text) && IsTextSearchEnabled && (e.Source == this || ItemContainerGenerator.IndexFromContainer(e.Source as IControl) != -1)) { - var instance = SharpTreeViewTextSearch.GetInstance(this); - if (instance != null) { - instance.Search(e.Text); - e.Handled = true; - } - } - if (!e.Handled) - base.OnTextInput(e); - } - - void ExpandRecursively(SharpTreeNode node) - { - if (node.CanExpandRecursively) { - node.IsExpanded = true; - foreach (SharpTreeNode child in node.Children) { - ExpandRecursively(child); - } - } - } - - /// - /// Scrolls the specified node in view and sets keyboard focus on it. - /// - public void FocusNode(SharpTreeNode node) - { - if (node == null) - throw new ArgumentNullException("node"); - ScrollIntoView(node); - // WPF's ScrollIntoView() uses the same if/dispatcher construct, so we call OnFocusItem() after the item was brought into view. - //if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) { - // OnFocusItem(node); - //} else { - Dispatcher.UIThread.InvokeAsync(()=> OnFocusItem(node), DispatcherPriority.Loaded); - //} - } - - public void ScrollIntoView(SharpTreeNode node) - { - if (node == null) - throw new ArgumentNullException("node"); - doNotScrollOnExpanding = true; - foreach (SharpTreeNode ancestor in node.Ancestors()) - ancestor.IsExpanded = true; - doNotScrollOnExpanding = false; - base.ScrollIntoView(node); - } - - object OnFocusItem(object item) - { - if (ContainerFromItem(item) is Control element) { - element.Focus(); - } - return null; - } - - #region Track selection - - protected virtual void OnSelectionChanged(object source, SelectionChangedEventArgs e) - { - foreach (SharpTreeNode node in e.RemovedItems) { - node.IsSelected = false; - } - foreach (SharpTreeNode node in e.AddedItems) { - node.IsSelected = true; - } - } - - #endregion - - #region Drag and Drop - - protected virtual void OnDragEnter(DragEventArgs e) - { - OnDragOver(e); - } - - protected virtual void OnDragOver(DragEventArgs e) - { - e.DragEffects = DragDropEffects.None; - - if (Root != null && !ShowRoot) { - e.Handled = true; - Root.CanDrop(e, Root.Children.Count); - } - } - - protected virtual void OnDrop(DragEventArgs e) - { - e.DragEffects = DragDropEffects.None; - - if (Root != null && !ShowRoot) { - e.Handled = true; - Root.InternalDrop(e, Root.Children.Count); - } - } - - internal void HandleDragEnter(SharpTreeViewItem item, DragEventArgs e) - { - HandleDragOver(item, e); - } - - internal void HandleDragOver(SharpTreeViewItem item, DragEventArgs e) - { - HidePreview(); - - var target = GetDropTarget(item, e); - if (target != null) { - e.Handled = true; - ShowPreview(target.Item, target.Place); - } - } - - internal void HandleDrop(SharpTreeViewItem item, DragEventArgs e) - { - try { - HidePreview(); - - var target = GetDropTarget(item, e); - if (target != null) { - e.Handled = true; - target.Node.InternalDrop(e, target.Index); - } - } catch (Exception ex) { - Trace.WriteLine(ex.ToString()); - throw; - } - } - - internal void HandleDragLeave(SharpTreeViewItem item, RoutedEventArgs e) - { - HidePreview(); - e.Handled = true; - } - - class DropTarget - { - public SharpTreeViewItem Item; - public DropPlace Place; - public double Y; - public SharpTreeNode Node; - public int Index; - } - - DropTarget GetDropTarget(SharpTreeViewItem item, DragEventArgs e) - { - var dropTargets = BuildDropTargets(item, e); - var y = e.GetPosition(item).Y; - foreach (var target in dropTargets) { - if (target.Y >= y) { - return target; - } - } - return null; - } - - List BuildDropTargets(SharpTreeViewItem item, DragEventArgs e) - { - var result = new List(); - var node = item.Node; - - if (AllowDropOrder) { - TryAddDropTarget(result, item, DropPlace.Before, e); - } - - TryAddDropTarget(result, item, DropPlace.Inside, e); - - if (AllowDropOrder) { - if (node.IsExpanded && node.Children.Count > 0) { - var firstChildItem = ItemContainerGenerator.ContainerFromIndex(0) as SharpTreeViewItem; - //var firstChildItem = ItemContainerGenerator.ContainerFromItem(node.Children[0]) as SharpTreeViewItem; - TryAddDropTarget(result, firstChildItem, DropPlace.Before, e); - } - else { - TryAddDropTarget(result, item, DropPlace.After, e); - } - } - - var h = item.Height; - var y1 = 0.2 * h; - var y2 = h / 2; - var y3 = h - y1; - - if (result.Count == 2) { - if (result[0].Place == DropPlace.Inside && - result[1].Place != DropPlace.Inside) { - result[0].Y = y3; - } - else if (result[0].Place != DropPlace.Inside && - result[1].Place == DropPlace.Inside) { - result[0].Y = y1; - } - else { - result[0].Y = y2; - } - } - else if (result.Count == 3) { - result[0].Y = y1; - result[1].Y = y3; - } - if (result.Count > 0) { - result[result.Count - 1].Y = h; - } - return result; - } - - void TryAddDropTarget(List targets, SharpTreeViewItem item, DropPlace place, DragEventArgs e) - { - SharpTreeNode node; - int index; - - GetNodeAndIndex(item, place, out node, out index); - - if (node != null) { - e.DragEffects = DragDropEffects.None; - if (node.CanDrop(e, index)) { - DropTarget target = new DropTarget - { - Item = item, - Place = place, - Node = node, - Index = index - }; - targets.Add(target); - } - } - } - - void GetNodeAndIndex(SharpTreeViewItem item, DropPlace place, out SharpTreeNode node, out int index) - { - node = null; - index = 0; - - if (place == DropPlace.Inside) { - node = item.Node; - index = node.Children.Count; - } - else if (place == DropPlace.Before) { - if (item.Node.Parent != null) { - node = item.Node.Parent; - index = node.Children.IndexOf(item.Node); - } - } - else { - if (item.Node.Parent != null) { - node = item.Node.Parent; - index = node.Children.IndexOf(item.Node) + 1; - } - } - } - - SharpTreeNodeView previewNodeView; - InsertMarker insertMarker; - DropPlace previewPlace; - - enum DropPlace - { - Before, Inside, After - } - - void ShowPreview(SharpTreeViewItem item, DropPlace place) - { - previewNodeView = item.NodeView; - previewPlace = place; - - if (place == DropPlace.Inside) { - previewNodeView.TextBackground = SystemColors.HighlightBrush; - //previewNodeView.Foreground = SystemColors.HighlightTextBrush; - } - else { - if (insertMarker == null) { - var adornerLayer = AdornerLayer.GetAdornerLayer(this); - insertMarker = new InsertMarker(); - var adorner = new VisualLayerManager { Child = insertMarker }; - adornerLayer.Children.Add(adorner); - } - - insertMarker.IsVisible = true; - - var p1 = previewNodeView.TranslatePoint(new Point(),this).Value; - var p = new Point(p1.X + previewNodeView.CalculateIndent() + 4.5, p1.Y - 3); - - if (place == DropPlace.After) { - p = p.WithY(p.Y + previewNodeView.Height); - } - - insertMarker.Margin = new Thickness(p.X, p.Y, 0, 0); - - SharpTreeNodeView secondNodeView = null; - var index = flattener.IndexOf(item.Node); - - if (place == DropPlace.Before) { - if (index > 0) { - secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index - 1) as SharpTreeViewItem).NodeView; - } - } - else if (index + 1 < flattener.Count) { - secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index + 1) as SharpTreeViewItem).NodeView; - } - - var w = p1.X + previewNodeView.Width - p.X; - - if (secondNodeView != null) { - var p2 = secondNodeView.TranslatePoint(new Point(), this).Value; - w = Math.Max(w, p2.X + secondNodeView.Width - p.X); - } - - insertMarker.Width = w + 10; - } - } - - void HidePreview() - { - if (previewNodeView != null) { - previewNodeView.ClearValue(SharpTreeNodeView.TextBackgroundProperty); - //previewNodeView.ClearValue(SharpTreeNodeView.ForegroundProperty); - if (insertMarker != null) { - insertMarker.IsVisible = false; - } - previewNodeView = null; - } - } - #endregion - - #region Cut / Copy / Paste / Delete Commands - - public IList CommandBindings { get; } = new List(); - - void RegisterCommands() - { - CommandBindings.Add(new RoutedCommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut)); - CommandBindings.Add(new RoutedCommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy)); - CommandBindings.Add(new RoutedCommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste)); - CommandBindings.Add(new RoutedCommandBinding(ApplicationCommands.Delete, HandleExecuted_Delete, HandleCanExecute_Delete)); - } - - static void HandleExecuted_Cut(object sender, ExecutedRoutedEventArgs e) - { - - } - - static void HandleCanExecute_Cut(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = false; - } - - static void HandleExecuted_Copy(object sender, ExecutedRoutedEventArgs e) - { - - } - - static void HandleCanExecute_Copy(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = false; - } - - static void HandleExecuted_Paste(object sender, ExecutedRoutedEventArgs e) - { - - } - - static void HandleCanExecute_Paste(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = false; - } - - static void HandleExecuted_Delete(object sender, ExecutedRoutedEventArgs e) - { - SharpTreeView treeView = (SharpTreeView)sender; - treeView.updatesLocked = true; - int selectedIndex = -1; - try { - foreach (SharpTreeNode node in treeView.GetTopLevelSelection().ToArray()) { - if (selectedIndex == -1) - selectedIndex = treeView.flattener.IndexOf(node); - node.Delete(); - } - } finally { - treeView.updatesLocked = false; - treeView.UpdateFocusedNode(null, Math.Max(0, selectedIndex - 1)); - } - } - - static void HandleCanExecute_Delete(object sender, CanExecuteRoutedEventArgs e) - { - SharpTreeView treeView = (SharpTreeView)sender; - e.CanExecute = treeView.GetTopLevelSelection().All(node => node.CanDelete()); - } - - /// - /// Gets the selected items which do not have any of their ancestors selected. - /// - public IEnumerable GetTopLevelSelection() - { - var selection = SelectedItems.OfType(); - var selectionHash = new HashSet(selection); - return selection.Where(item => item.Ancestors().All(a => !selectionHash.Contains(a))); - } - - #endregion - - public void SetSelectedNodes(IEnumerable nodes) - { - SelectedItems.Clear(); - foreach (var item in nodes) { - SelectedItems.Add(item); - } - } - - public new void UnselectAll() - { - SelectedItems.Clear(); - } - } -} diff --git a/SharpTreeView/SharpTreeView.csproj b/SharpTreeView/SharpTreeView.csproj deleted file mode 100644 index cbce09e4..00000000 --- a/SharpTreeView/SharpTreeView.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - netstandard2.1 - False - false - ICSharpCode.TreeView - Debug;Release - AnyCPU;x64 - embedded - true - - - - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SharpTreeView/SharpTreeViewItem.cs b/SharpTreeView/SharpTreeViewItem.cs deleted file mode 100644 index febd050f..00000000 --- a/SharpTreeView/SharpTreeViewItem.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Linq; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Interactivity; - -namespace ICSharpCode.TreeView -{ - public class SharpTreeViewItem : ListBoxItem - { - static SharpTreeViewItem() - { - DragDrop.DragEnterEvent.AddClassHandler((x, e) => x.OnDragEnter(e)); - DragDrop.DragLeaveEvent.AddClassHandler((x, e) => x.OnDragLeave(e)); - DragDrop.DragOverEvent.AddClassHandler((x, e) => x.OnDragOver(e)); - DragDrop.DropEvent.AddClassHandler((x, e) => x.OnDrop(e)); - } - - public SharpTreeNode Node => DataContext as SharpTreeNode; - - public SharpTreeNodeView NodeView { get; internal set; } - public SharpTreeView ParentTreeView { get; internal set; } - - protected override void OnKeyDown(KeyEventArgs e) - { - switch (e.Key) { - case Key.F2: -// if (SharpTreeNode.ActiveNodes.Count == 1 && Node.IsEditable) { -// Node.IsEditing = true; -// e.Handled = true; -// } - break; - case Key.Escape: - Node.IsEditing = false; - break; - } - } - - #region Mouse - - Point startPoint; - bool wasSelected; - bool wasDoubleClick; - - protected override void OnPointerPressed(PointerPressedEventArgs e) - { - wasSelected = IsSelected; - if (!IsSelected) { - base.OnPointerPressed(e); - } - - if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { - startPoint = e.GetPosition(this); - e.Pointer.Capture(this); - - if (e.ClickCount == 2) { - wasDoubleClick = true; - } - } - } - - protected override void OnPointerMoved(PointerEventArgs e) - { - if (e.Pointer.Captured == this) { - var currentPoint = e.GetPosition(this); - if (Math.Abs(currentPoint.X - startPoint.X) >= SystemParameters.MinimumHorizontalDragDistance || - Math.Abs(currentPoint.Y - startPoint.Y) >= SystemParameters.MinimumVerticalDragDistance) { - - var selection = ParentTreeView.GetTopLevelSelection().ToArray(); - if (Node.CanDrag(selection)) { - Node.StartDrag(e, this, selection); - } - } - } else { - base.OnPointerMoved(e); - } - } - - protected override void OnPointerReleased(PointerReleasedEventArgs e) - { - - if (wasDoubleClick) { - wasDoubleClick = false; - Node.ActivateItem(e); - if (!e.Handled) { - if (!Node.IsRoot || ParentTreeView.ShowRootExpander) { - Node.IsExpanded = !Node.IsExpanded; - } - } - } - - //ReleaseMouseCapture(); - e.Pointer.Capture(null); - if (wasSelected) { - base.OnPointerReleased(e); - } - } - - #endregion - - #region Drag and Drop - - protected virtual void OnDragEnter(DragEventArgs e) - { - ParentTreeView.HandleDragEnter(this, e); - } - - protected virtual void OnDragOver(DragEventArgs e) - { - ParentTreeView.HandleDragOver(this, e); - } - - protected virtual void OnDrop(DragEventArgs e) - { - ParentTreeView.HandleDrop(this, e); - } - - protected virtual void OnDragLeave(RoutedEventArgs e) - { - ParentTreeView.HandleDragLeave(this, e); - } - - #endregion - } -} diff --git a/SharpTreeView/SharpTreeViewTextSearch.cs b/SharpTreeView/SharpTreeViewTextSearch.cs deleted file mode 100644 index da4cfaa5..00000000 --- a/SharpTreeView/SharpTreeViewTextSearch.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections; -using System.Collections.Generic; -using Avalonia; -using Avalonia.Threading; - -namespace ICSharpCode.TreeView -{ - /// - /// Custom TextSearch-implementation. - /// Fixes #67 - Moving to class member in tree view by typing in first character of member name selects parent assembly - /// - public class SharpTreeViewTextSearch : AvaloniaObject - { - const double doubleClickTime = 0.1; - - //static readonly DependencyPropertyKey TextSearchInstancePropertyKey = AvaloniaProperty.RegisterAttachedReadOnly("TextSearchInstance", - // typeof(SharpTreeViewTextSearch), typeof(SharpTreeViewTextSearch), new FrameworkPropertyMetadata(null)); - static readonly AttachedProperty TextSearchInstanceProperty = AvaloniaProperty.RegisterAttached("TextSearchInstance"); - //static readonly StyledProperty TextSearchInstanceProperty = TextSearchInstancePropertyKey.DependencyProperty; - - DispatcherTimer timer; - - bool isActive; - int lastMatchIndex; - string matchPrefix; - - readonly Stack inputStack; - readonly SharpTreeView treeView; - - private SharpTreeViewTextSearch(SharpTreeView treeView) - { - if (treeView == null) - throw new ArgumentNullException(nameof(treeView)); - this.treeView = treeView; - inputStack = new Stack(8); - ClearState(); - } - - public static SharpTreeViewTextSearch GetInstance(SharpTreeView sharpTreeView) - { - var textSearch = sharpTreeView.GetValue(TextSearchInstanceProperty); - if (textSearch == null) { - textSearch = new SharpTreeViewTextSearch(sharpTreeView); - sharpTreeView.SetValue(TextSearchInstanceProperty, textSearch); - } - return textSearch; - } - - public bool RevertLastCharacter() - { - if (!isActive || inputStack.Count == 0) - return false; - matchPrefix = matchPrefix.Substring(0, matchPrefix.Length - inputStack.Pop().Length); - ResetTimeout(); - return true; - } - - public bool Search(string nextChar) - { - IList items = (IList)treeView.Items; - int startIndex = isActive ? lastMatchIndex : Math.Max(0, treeView.SelectedIndex); - bool lookBackwards = inputStack.Count > 0 && string.Compare(inputStack.Peek(), nextChar, StringComparison.OrdinalIgnoreCase) == 0; - int nextMatchIndex = IndexOfMatch(matchPrefix + nextChar, startIndex, lookBackwards, out bool wasNewCharUsed); - if (nextMatchIndex != -1) { - if (!isActive || nextMatchIndex != startIndex) { - treeView.SelectedItem = items[nextMatchIndex]; - treeView.FocusNode((SharpTreeNode)treeView.SelectedItem); - lastMatchIndex = nextMatchIndex; - } - if (wasNewCharUsed) { - matchPrefix += nextChar; - inputStack.Push(nextChar); - } - isActive = true; - } - if (isActive) { - ResetTimeout(); - } - return nextMatchIndex != -1; - } - - int IndexOfMatch(string needle, int startIndex, bool tryBackward, out bool charWasUsed) - { - IList items = (IList)treeView.Items; - charWasUsed = false; - if (items.Count == 0 || string.IsNullOrEmpty(needle)) - return -1; - int index = -1; - int fallbackIndex = -1; - bool fallbackMatch = false; - int i = startIndex; - var comparisonType = treeView.IsTextSearchCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; - do { - var item = (SharpTreeNode)items[i]; - if (item != null && item.Text != null) { - string text = item.Text.ToString(); - if (text.StartsWith(needle, comparisonType)) { - charWasUsed = true; - index = i; - break; - } - if (tryBackward) { - if (fallbackMatch && matchPrefix != string.Empty) { - if (fallbackIndex == -1 && text.StartsWith(matchPrefix, comparisonType)) { - fallbackIndex = i; - } - } else { - fallbackMatch = true; - } - } - } - i++; - if (i >= items.Count) - i = 0; - } while (i != startIndex); - return index == -1 ? fallbackIndex : index; - } - - void ClearState() - { - isActive = false; - matchPrefix = string.Empty; - lastMatchIndex = -1; - inputStack.Clear(); - timer?.Stop(); - timer = null; - } - - void ResetTimeout() - { - if (timer == null) { - timer = new DispatcherTimer(DispatcherPriority.Normal); - timer.Tick += (sender, e) => ClearState(); - } else { - timer.Stop(); - } - timer.Interval = TimeSpan.FromMilliseconds(doubleClickTime * 2); - timer.Start(); - } - } -} \ No newline at end of file diff --git a/SharpTreeView/Themes/TreeView.xaml b/SharpTreeView/Themes/TreeView.xaml deleted file mode 100644 index 65cb7533..00000000 --- a/SharpTreeView/Themes/TreeView.xaml +++ /dev/null @@ -1,269 +0,0 @@ - - - - #FF7F9DB9 - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SharpTreeView/TreeFlattener.cs b/SharpTreeView/TreeFlattener.cs deleted file mode 100644 index 6d780328..00000000 --- a/SharpTreeView/TreeFlattener.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; - -namespace ICSharpCode.TreeView -{ - sealed class TreeFlattener : IList, INotifyCollectionChanged - { - /// - /// The root node of the flat list tree. - /// Tjis is not necessarily the root of the model! - /// - internal SharpTreeNode root; - readonly bool includeRoot; - readonly object syncRoot = new object(); - - public TreeFlattener(SharpTreeNode modelRoot, bool includeRoot) - { - root = modelRoot; - while (root.listParent != null) - root = root.listParent; - root.treeFlattener = this; - this.includeRoot = includeRoot; - } - - public event NotifyCollectionChangedEventHandler CollectionChanged; - - public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e) - { - CollectionChanged?.Invoke(this, e); - } - - public void NodesInserted(int index, IEnumerable nodes) - { - if (!includeRoot) index--; - foreach (SharpTreeNode node in nodes) { - RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, index++)); - } - } - - public void NodesRemoved(int index, IEnumerable nodes) - { - if (!includeRoot) index--; - foreach (SharpTreeNode node in nodes) { - RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, node, index)); - } - } - - public void Stop() - { - Debug.Assert(root.treeFlattener == this); - root.treeFlattener = null; - } - - public object this[int index] { - get { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException(); - return SharpTreeNode.GetNodeByVisibleIndex(root, includeRoot ? index : index + 1); - } - set => throw new NotSupportedException(); - } - - public int Count => includeRoot ? root.GetTotalListLength() : root.GetTotalListLength() - 1; - - public int IndexOf(object item) - { - if (item is SharpTreeNode node && node.IsVisible && node.GetListRoot() == root) - { - if (includeRoot) - return SharpTreeNode.GetVisibleIndexForNode(node); - return SharpTreeNode.GetVisibleIndexForNode(node) - 1; - } - - return -1; - } - - bool IList.IsReadOnly => true; - - bool IList.IsFixedSize => false; - - bool ICollection.IsSynchronized => false; - - object ICollection.SyncRoot => syncRoot; - - void IList.Insert(int index, object item) - { - throw new NotSupportedException(); - } - - void IList.RemoveAt(int index) - { - throw new NotSupportedException(); - } - - int IList.Add(object item) - { - throw new NotSupportedException(); - } - - void IList.Clear() - { - throw new NotSupportedException(); - } - - public bool Contains(object item) - { - return IndexOf(item) >= 0; - } - - public void CopyTo(Array array, int arrayIndex) - { - foreach (object item in this) - array.SetValue(item, arrayIndex++); - } - - void IList.Remove(object item) - { - throw new NotSupportedException(); - } - - public IEnumerator GetEnumerator() - { - for (int i = 0; i < Count; i++) { - yield return this[i]; - } - } - } -} diff --git a/SharpTreeView/TreeTraversal.cs b/SharpTreeView/TreeTraversal.cs deleted file mode 100644 index 06d50ea3..00000000 --- a/SharpTreeView/TreeTraversal.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; - -namespace ICSharpCode.TreeView -{ - /// - /// Static helper methods for traversing trees. - /// - static class TreeTraversal - { - /// - /// Converts a tree data structure into a flat list by traversing it in pre-order. - /// - /// The root element of the tree. - /// The function that gets the children of an element. - /// Iterator that enumerates the tree structure in pre-order. - public static IEnumerable PreOrder(T root, Func> recursion) - { - return PreOrder(new[] { root }, recursion); - } - - /// - /// Converts a tree data structure into a flat list by traversing it in pre-order. - /// - /// The root elements of the forest. - /// The function that gets the children of an element. - /// Iterator that enumerates the tree structure in pre-order. - public static IEnumerable PreOrder(IEnumerable input, Func> recursion) - { - Stack> stack = new Stack>(); - try { - stack.Push(input.GetEnumerator()); - while (stack.Count > 0) { - while (stack.Peek().MoveNext()) { - T element = stack.Peek().Current; - yield return element; - IEnumerable children = recursion(element); - if (children != null) { - stack.Push(children.GetEnumerator()); - } - } - stack.Pop().Dispose(); - } - } finally { - while (stack.Count > 0) { - stack.Pop().Dispose(); - } - } - } - } -} diff --git a/SharpTreeView/copyright.txt b/SharpTreeView/copyright.txt deleted file mode 100644 index e82a2ce1..00000000 --- a/SharpTreeView/copyright.txt +++ /dev/null @@ -1,10 +0,0 @@ -Copyright 2002-2011 by - - AlphaSierraPapa, Christoph Wille - Vordernberger Strasse 27/8 - A-8700 Leoben - Austria - - email: office@alphasierrapapa.com - court of jurisdiction: Landesgericht Leoben - diff --git a/SharpTreeView/license.txt b/SharpTreeView/license.txt deleted file mode 100644 index e8fd74ca..00000000 --- a/SharpTreeView/license.txt +++ /dev/null @@ -1,458 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS