diff --git a/.idea/.idea.Composition/.idea/.gitignore b/.idea/.idea.Composition/.idea/.gitignore new file mode 100644 index 0000000..eeb5346 --- /dev/null +++ b/.idea/.idea.Composition/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/projectSettingsUpdater.xml +/.idea.Composition.iml +/contentModel.xml +/modules.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.Composition/.idea/encodings.xml b/.idea/.idea.Composition/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.Composition/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.Composition/.idea/indexLayout.xml b/.idea/.idea.Composition/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.Composition/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Composition.sln b/Composition.sln new file mode 100644 index 0000000..5315c81 --- /dev/null +++ b/Composition.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Composition", "Composition\Composition.csproj", "{EDFAC570-EDA6-463F-83A3-E2E15D0E31E8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EDFAC570-EDA6-463F-83A3-E2E15D0E31E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDFAC570-EDA6-463F-83A3-E2E15D0E31E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDFAC570-EDA6-463F-83A3-E2E15D0E31E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDFAC570-EDA6-463F-83A3-E2E15D0E31E8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Composition.sln.DotSettings.user b/Composition.sln.DotSettings.user new file mode 100644 index 0000000..1d79d31 --- /dev/null +++ b/Composition.sln.DotSettings.user @@ -0,0 +1,4 @@ + + True + True + True \ No newline at end of file diff --git a/Composition/Component/FileButtons.cs b/Composition/Component/FileButtons.cs new file mode 100644 index 0000000..e387164 --- /dev/null +++ b/Composition/Component/FileButtons.cs @@ -0,0 +1,83 @@ +using System; +using Composition.Model; +using Gtk; + +namespace Composition.Component; + +public class FileButtons: HBox +{ + /// Event to trigger when a file path is chosen for exporting + public event Action? Export; + /// Event to trigger when a file path is chosen for importing + public event Action? Import; + + private readonly Button _exportButton = new(Localization.Export_Button); + private readonly Button _importButton = new(Localization.Import_Fragment_Button); + private readonly Button _importFolderButton = new(Localization.Import_Folder_Button); + + public FileButtons(Window window) + { + Add(_exportButton); + Add(_importButton); + Add(_importFolderButton); + + const int buttonHeight = 50; + _exportButton.HeightRequest = buttonHeight; + _importButton.HeightRequest = buttonHeight; + _importFolderButton.HeightRequest = buttonHeight; + + _exportButton.Image = new Image(Stock.Save, IconSize.Button); + _importButton.Image = new Image(Stock.File, IconSize.Button); + _importFolderButton.Image = new Image(Stock.Directory, IconSize.Button); + + var exportDialog = new FileChooserDialog(Localization.Dialog_Export, window, FileChooserAction.Save, Localization.Dialog_Cancel, ResponseType.Cancel, Localization.Dialog_Export, ResponseType.Apply); + _exportButton.Clicked += (_, _) => exportDialog.Run(); + exportDialog.Response += (_, response) => + { + if (response.ResponseId == ResponseType.Apply) + { + string filePath = exportDialog.Filename; + if (filePath != null) + { + Export?.Invoke(filePath); + } + } + exportDialog.Hide(); + }; + + var fileFilter = new FileFilter(); + fileFilter.AddPattern("*.compose"); + fileFilter.Name = Localization.File_Filter_Description; + + var importDialog = new FileChooserDialog(Localization.Dialog_Import, window, FileChooserAction.Open, Localization.Dialog_Cancel, ResponseType.Cancel, Localization.Dialog_Import, ResponseType.Apply); + importDialog.AddFilter(fileFilter); + _importButton.Clicked += (_, _) => importDialog.Run(); + importDialog.Response += (_, response) => + { + if (response.ResponseId == ResponseType.Apply) + { + string filePath = importDialog.Filename; + if (filePath != null) + { + Import?.Invoke(filePath); + } + } + importDialog.Hide(); + }; + + var importFolderDialog = new FileChooserDialog(Localization.Dialog_Import, window, FileChooserAction.SelectFolder, Localization.Dialog_Cancel, ResponseType.Cancel, Localization.Dialog_Import, ResponseType.Apply); + _importFolderButton.Clicked += (_, _) => importFolderDialog.Run(); + importFolderDialog.Response += (_, response) => + { + if (response.ResponseId == ResponseType.Apply) + { + string filePath = importFolderDialog.Filename; + if (filePath != null) + { + Import?.Invoke(filePath); + } + } + importFolderDialog.Hide(); + }; + } +} \ No newline at end of file diff --git a/Composition/Component/KeysymView.cs b/Composition/Component/KeysymView.cs new file mode 100644 index 0000000..4dc2ad0 --- /dev/null +++ b/Composition/Component/KeysymView.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; +using Gtk; +using Key = Gdk.Key; + +namespace Composition.Component; + +public class KeysymView: HBox +{ + private readonly Label _noneLabel = new(); + public string NoneText + { + get => _noneLabel.Text; + set => _noneLabel.Text = value; + } + + public KeysymView() + { + + } + + public void Update(IReadOnlyCollection keys) + { + Foreach(child => Remove(child)); + + string[] keyArray = keys.ToArray(); + if (keyArray.Length > 0) + { + for (var i = 0; i < keyArray.Length; i++) + { + Label label = new(keyArray[i]); + label.Visible = true; + label.StyleContext.AddClass("sequence-key"); + + // Separator + if (i != 0) + { + Label seperator = new(" + "); + seperator.Visible = true; + seperator.StyleContext.AddClass("sequence-seperator"); + Add(seperator); + } + + // Add the label to the box + Add(label); + } + } + else + { + Label label = new("No keys chosen\nPress a key to add it"); + label.Visible = true; + label.StyleContext.AddClass("sequence-seperator"); + Add(label); + } + } +} \ No newline at end of file diff --git a/Composition/Component/SequenceInput.cs b/Composition/Component/SequenceInput.cs new file mode 100644 index 0000000..22bd91e --- /dev/null +++ b/Composition/Component/SequenceInput.cs @@ -0,0 +1,80 @@ +using System; +using Composition.Model; +using Gtk; +using Key = Gdk.Key; + +namespace Composition.Component; + +public class SequenceInput: HBox +{ + readonly SequenceBuilder builder; + private readonly Button _clear = new(); + private readonly Button _keys = new(); + private readonly KeysymView _keysymsView = new(); + private readonly Entry _character = new(); + private readonly Button _complete = new(); + + public event Action? CompletedEvent; + + public SequenceInput(SequenceBuilder builder) + { + this.builder = builder; + + Add(_clear); + Add(_keys); + _keys.Add(_keysymsView); + Add(_character); + Add(_complete); + + // Clear button + _clear.Image = new Image(Stock.Clear, IconSize.Button); + _clear.AlwaysShowImage = true; + _clear.Clicked += (_, _) => builder.ClearKeys(); + // Apply button + _complete.Image = new Image(Stock.Apply, IconSize.Button); + _complete.AlwaysShowImage = true; + _complete.Clicked += (_, _) => BuildSequence(); + // Key input + _keys.KeyPressEvent += (_, e) => e.RetVal = true; // Prevent event propagation to the window + _keys.KeyReleaseEvent += KeyInput; + _keysymsView.NoneText = "No keys chosen\nPress a key to add it"; + // Character output + _character.Alignment = 0.5f; // Center + _character.StyleContext.AddClass(Resources.CSS_sequence_character); + + this.builder.OnChanged += _ => Update(); + Update(); + } + + public void KeyInput(object _, KeyReleaseEventArgs e) + { + // Completes the sequence when enter is pressed + if (e.Event.Key == Key.Return) + { + _complete.Click(); + } + // Cancel input + else if (e.Event.Key == Key.Escape) + { + _clear.Click(); + } + // Appends the key to the sequence + else + { + builder.AddKey(e.Event.Key); + } + } + + private void BuildSequence() + { + Sequence sequence = builder.Build()!; + CompletedEvent?.Invoke(sequence); + builder.ClearKeys(); + } + + private void Update() + { + _keysymsView.Update(builder.Keys); + _character.Text = builder.Character?.ToString() ?? ""; + } +} \ No newline at end of file diff --git a/Composition/Component/SequenceTreeView.cs b/Composition/Component/SequenceTreeView.cs new file mode 100644 index 0000000..4033b11 --- /dev/null +++ b/Composition/Component/SequenceTreeView.cs @@ -0,0 +1,43 @@ +using System; +using Composition.Model; +using Gtk; + +namespace Composition.Component; + +public class SequenceTreeView: TreeView +{ + public SequenceTreeView(): base() + { + TreeViewColumn keysymsColumn = new(); + keysymsColumn.Title = "Sequence"; + CellRendererText keysymsCell = new(); + keysymsColumn.PackStart(keysymsCell, true); + + TreeViewColumn characterColumn = new(); + characterColumn.Title = "Result"; + CellRendererText characterCell = new(); + characterCell.Style = Pango.Style.Oblique; + characterColumn.PackStart(characterCell, true); + + TreeViewColumn toggleColumn = new(); + toggleColumn.Title = "Enabled"; + CellRendererToggle toggleCell = new(); + toggleColumn.PackStart(toggleCell, true); + + AppendColumn(keysymsColumn); + AppendColumn(characterColumn); + AppendColumn(toggleColumn); + + keysymsColumn.AddAttribute(keysymsCell, "text", 0); + characterColumn.AddAttribute(characterCell, "text", 1); + toggleColumn.AddAttribute(toggleCell, "active", 3); + + toggleCell.Toggled += (widget, path) => + { + TreeIter iter; + Model.GetIter(out iter, new TreePath(path.Path)); + bool enabled = (bool) Model.GetValue(iter, 3); + Model.SetValue(iter, 3, !enabled); + }; + } +} \ No newline at end of file diff --git a/Composition/Component/SequenceView.cs b/Composition/Component/SequenceView.cs new file mode 100644 index 0000000..cb47fb5 --- /dev/null +++ b/Composition/Component/SequenceView.cs @@ -0,0 +1,35 @@ +using Composition.Model; +using Gtk; + +namespace Composition.Component; + +public class SequenceView: HBox +{ + private Sequence _sequence; + public Sequence Sequence + { + get => _sequence; + set + { + _sequence = value; + Update(); + } + } + + private readonly KeysymView _keysymView = new() { Visible = true }; + private readonly Label _separator = new() { Visible = true, Text = "→" }; + private readonly Label _character = new() { Visible = true }; + + public SequenceView() + { + Add(_keysymView); + Add(_separator); + Add(_character); + } + + private void Update() + { + _keysymView.Update(_sequence.Keys); + _character.Text = _sequence.Character.ToString(); + } +} \ No newline at end of file diff --git a/Composition/Composition.csproj b/Composition/Composition.csproj new file mode 100644 index 0000000..7c7506b --- /dev/null +++ b/Composition/Composition.csproj @@ -0,0 +1,41 @@ + + + + WinExe + net6.0 + enable + + + + + + %(Filename)%(Extension) + + + ResXFileCodeGenerator + Resources.Designer.cs + + + ResXFileCodeGenerator + Localization.Designer.cs + + + + + + + + + + True + True + Resources.resx + + + True + True + Localization.resx + + + + diff --git a/Composition/Composition.csproj.DotSettings.user b/Composition/Composition.csproj.DotSettings.user new file mode 100644 index 0000000..2f2b44b --- /dev/null +++ b/Composition/Composition.csproj.DotSettings.user @@ -0,0 +1,2 @@ + + EDFAC570-EDA6-463F-83A3-E2E15D0E31E8/f:Localization.resx \ No newline at end of file diff --git a/Composition/FileParser.cs b/Composition/FileParser.cs new file mode 100644 index 0000000..fe929e1 --- /dev/null +++ b/Composition/FileParser.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Composition.Model; + +namespace Composition; + +public static class FileParser +{ + public static List Parse(string filePath) + { + var sequences = new List(); + var lines = File.ReadAllLines(filePath); + foreach (string line in lines) + { + Sequence? sequence = ParseLine(line); + if (sequence != null) + { + sequences.Add(sequence); + } + } + return sequences; + } + + private static readonly Regex _whiteSpaceRegex = new Regex("\\s+"); + private static readonly Regex _sequenceRegex = new Regex("\\s+(?<[^>]+>(\\s+<[^>]+>)*)\\s*:\\s*\"(?[^\"]+)\"(\\s+.+)?\\s*(#.+)?"); + private static Sequence? ParseLine(string line) + { + Match match = _sequenceRegex.Match(line); + + if (match.Success) + { + string[] keysyms = _whiteSpaceRegex + .Split(match.Groups["keysyms"].Value) + .Select(keysym => keysym[1..^1]) + .ToArray(); + string result = match.Groups["result"].Value; + + var sequence = new Sequence(keysyms, result); + return sequence; + } + + return null; + } +} \ No newline at end of file diff --git a/Composition/Localization.Designer.cs b/Composition/Localization.Designer.cs new file mode 100644 index 0000000..fe25b62 --- /dev/null +++ b/Composition/Localization.Designer.cs @@ -0,0 +1,102 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Composition { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Localization { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Localization() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Composition.Localization", typeof(Localization).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string Load_File_Message { + get { + return ResourceManager.GetString("Load_File_Message", resourceCulture); + } + } + + internal static string Skip_File_Message { + get { + return ResourceManager.GetString("Skip_File_Message", resourceCulture); + } + } + + internal static string Import_Fragment_Button { + get { + return ResourceManager.GetString("Import_Fragment_Button", resourceCulture); + } + } + + internal static string Export_Button { + get { + return ResourceManager.GetString("Export_Button", resourceCulture); + } + } + + internal static string Import_Folder_Button { + get { + return ResourceManager.GetString("Import_Folder_Button", resourceCulture); + } + } + + internal static string Dialog_Import { + get { + return ResourceManager.GetString("Dialog_Import", resourceCulture); + } + } + + internal static string Dialog_Cancel { + get { + return ResourceManager.GetString("Dialog_Cancel", resourceCulture); + } + } + + internal static string File_Filter_Description { + get { + return ResourceManager.GetString("File_Filter_Description", resourceCulture); + } + } + + internal static string Dialog_Export { + get { + return ResourceManager.GetString("Dialog_Export", resourceCulture); + } + } + } +} diff --git a/Composition/Localization.resx b/Composition/Localization.resx new file mode 100644 index 0000000..0312d63 --- /dev/null +++ b/Composition/Localization.resx @@ -0,0 +1,48 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Loading {0} {1}... + + + Skipping {0} {1}... + + + Import compose fragment + + + Export compose file + + + Import compose fragment folder + + + Import + + + Cancel + + + Compose fragment files + + + Export + + \ No newline at end of file diff --git a/Composition/Model/Sequence.cs b/Composition/Model/Sequence.cs new file mode 100644 index 0000000..304beef --- /dev/null +++ b/Composition/Model/Sequence.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Composition.Model; + +public class Sequence +{ + private static readonly Regex _keyRegex = new Regex("^KEY_"); + public static string GdkToKeysym(Gdk.Key key) => _keyRegex.Replace(key.ToString(), ""); + + public string[] Keys { get; } + public string Character { get; } + + public Sequence(IReadOnlyList keys, string character) + { + Keys = keys.ToArray(); + Character = character; + } + + public override string ToString() => Serialize(0); + + public string Serialize(int leftMinWidth) + { + string keys = string.Join(" ", Keys.Select(key => $"<{key}>")); + string start = $" {keys} "; + string end = $": \"{Character}\""; + return start.PadRight(leftMinWidth, ' ') + end; + } + + public int GetLeftMinWidth() + { + int length = "".Length; + foreach (string key in Keys) + { + // +3 for the < and > and the space + length += key.Length + 3; + } + length += 1; // for the space after the keys + return length; + } +} \ No newline at end of file diff --git a/Composition/Model/SequenceBuilder.cs b/Composition/Model/SequenceBuilder.cs new file mode 100644 index 0000000..698cb84 --- /dev/null +++ b/Composition/Model/SequenceBuilder.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; + +namespace Composition.Model; + +public class SequenceBuilder +{ + private readonly List _keys = new(); + + private string? _result; + public string? Character + { + get => _result; + set { + _result = value; + OnChanged?.Invoke(this); + } + } + + public event Action? OnChanged; + + public void AddKey(Gdk.Key key) + { + _keys.Add(key.ToString()); + OnChanged?.Invoke(this); + } + + public List Keys => _keys; + + public void ClearKeys() + { + _keys.Clear(); + OnChanged?.Invoke(this); + } + + public Sequence? Build() + { + if (_result == null || _keys.Count == 0) return null; + return new Sequence(_keys, _result); + } +} \ No newline at end of file diff --git a/Composition/Model/SequenceTreeStore.cs b/Composition/Model/SequenceTreeStore.cs new file mode 100644 index 0000000..10a5a41 --- /dev/null +++ b/Composition/Model/SequenceTreeStore.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Gtk; +using Path = System.IO.Path; + +namespace Composition.Model; + +public class SequenceTreeStore: TreeStore +{ + private readonly Dictionary _filePathToIter = new(); + + public SequenceTreeStore(): base(typeof(string), typeof(string), typeof(Sequence), typeof(bool)) + { + + } + + public bool UnloadFile(string filePath) + { + bool containsKey = _filePathToIter.ContainsKey(filePath); + if (containsKey) + { + TreeIter iter = _filePathToIter[filePath]; + Remove(ref iter); + } + return containsKey; + } + + public void LoadFile(string filePath) + { + bool isFolder(string path) => File.GetAttributes(path).HasFlag(FileAttributes.Directory); + + Console.WriteLine(Localization.Load_File_Message, (isFolder(filePath) ? "folder" : "file"), filePath); + if (isFolder(filePath)) + { + var files = Directory.GetFiles(filePath); + foreach (string file in files) + { + if (file.EndsWith(".compose") || isFolder(file)) + { + LoadFile(file); + } + else + { + Console.WriteLine(Localization.Skip_File_Message, (isFolder(file) ? "folder" : "file"), file); + } + } + } + else + { + UnloadFile(filePath); + + List sequences = FileParser.Parse(filePath); + + string name = Path.GetFileName(filePath); + name = Regex.Replace(name, @"\.compose$", "", RegexOptions.IgnoreCase); + TreeIter fileIter = AppendValues(name, "", true); + _filePathToIter[filePath] = fileIter; + + foreach (Sequence sequence in sequences) + { + string keysyms = string + .Join(" + ", sequence.Keys.Select(key => key.ToString())) + .Replace("Multi_key", "⎄"); + string character = sequence.Character; + TreeIter sequenceIter = AppendValues(fileIter, keysyms, character, sequence, true); + } + } + } + + public Dictionary> GetEnabledSequences() + { + var sequences = new Dictionary>(); + TreeIter iter; + if (GetIterFirst(out iter)) + { + do + { + string filePath = (string) GetValue(iter, 0); + bool fileEnabled = (bool) GetValue(iter, 3); + sequences[filePath] = new List(); + TreeIter childIter; + if (fileEnabled && IterChildren(out childIter, iter)) + { + do + { + Sequence sequence = (Sequence) GetValue(childIter, 2); + bool sequenceEnabled = (bool) GetValue(childIter, 3); + if (sequenceEnabled) + { + sequences[filePath].Add(sequence); + } + } while (IterNext(ref childIter)); + } + } while (IterNext(ref iter)); + } + return sequences; + } + + public void SaveFile(string filePath) + { + string content = Serialize(); + File.WriteAllText(filePath, content); + } + + public string Serialize() + { + Dictionary> sequences = GetEnabledSequences(); + + var sb = new StringBuilder(); + sb.AppendLine("# XCompose file generated by Composition"); + sb.AppendLine("include \"%L\""); + sb.AppendLine(""); + + foreach (string filePath in sequences.Keys) + { + if (sequences[filePath].Count > 0) + { + string fileName = Path.GetFileName(filePath); + int leftMinWidth = sequences[filePath].Max(sequence => sequence.GetLeftMinWidth()); + sb.AppendLine($"# {fileName}"); + foreach (Sequence sequence in sequences[filePath]) + { + sb.AppendLine(sequence.Serialize(leftMinWidth)); + } + sb.AppendLine(""); + } + } + + return sb.ToString(); + } +} \ No newline at end of file diff --git a/Composition/Program.cs b/Composition/Program.cs new file mode 100644 index 0000000..d01176f --- /dev/null +++ b/Composition/Program.cs @@ -0,0 +1,58 @@ +using Composition.Component; +using Composition.Model; +using Gdk; +using Gtk; +using Window = Gtk.Window; + +namespace Composition; + +class Program +{ + public static void Main(string[] args) + { + Application.Init(); + + var cssProvider = new CssProvider(); + cssProvider.LoadFromData(Resources.Style); + StyleContext.AddProviderForScreen(Screen.Default, cssProvider, 800); + + var window = new Window(Resources.Name); + window.SetSizeRequest(600, 800); + window.IconName = "input-keyboard"; + window.DeleteEvent += (_, _) => Application.Quit(); + + var store = new SequenceTreeStore(); + + var mainBox = new VBox(); + window.Add(mainBox); + + var buttonBox = new FileButtons(window); + buttonBox.Import += filePath => store.LoadFile(filePath); + buttonBox.Export += filePath => store.SaveFile(filePath); + mainBox.PackStart(buttonBox, false, false, 0); + + var scrollable = new ScrolledWindow(); + mainBox.Add(scrollable); + + var tree = new SequenceTreeView { Model = store }; + scrollable.Add(tree); + + // var box = new VBox(); + // window.Add(box); + // var builder = new SequenceBuilder(); + // builder.Character = "a"; + // + // var input = new SequenceInput(builder); + // input.HeightRequest = 50; + // input.CompletedEvent += sequence => + // { + // var view = new SequenceView { Sequence = sequence }; + // view.Visible = true; + // box.Add(view); + // }; + // box.Add(input); + + window.ShowAll(); + Application.Run(); + } +} \ No newline at end of file diff --git a/Composition/Resources.Designer.cs b/Composition/Resources.Designer.cs new file mode 100644 index 0000000..6242746 --- /dev/null +++ b/Composition/Resources.Designer.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Composition { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Composition.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string Name { + get { + return ResourceManager.GetString("Name", resourceCulture); + } + } + + internal static string CSS_sequence_character { + get { + return ResourceManager.GetString("CSS_sequence_character", resourceCulture); + } + } + + internal static string CSS_sequence_seperator { + get { + return ResourceManager.GetString("CSS_sequence-seperator", resourceCulture); + } + } + + internal static string CSS_sequence_key { + get { + return ResourceManager.GetString("CSS_sequence-key", resourceCulture); + } + } + + internal static string Style { + get { + return ResourceManager.GetString("Style", resourceCulture); + } + } + } +} diff --git a/Composition/Resources.resx b/Composition/Resources.resx new file mode 100644 index 0000000..c2c1d83 --- /dev/null +++ b/Composition/Resources.resx @@ -0,0 +1,49 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Composition + + + sequence-character + + + sequence-seperator + + + sequence-key + + + + .sequence-character { + font-weight: bolder; + font-size: 1.5em; + } + + .sequence-seperator { + opacity: 0.5; + } + + .sequence-key { + font-weight: bold; + } + + + \ No newline at end of file diff --git a/Composition/style.css b/Composition/style.css new file mode 100644 index 0000000..dbf200e --- /dev/null +++ b/Composition/style.css @@ -0,0 +1,3 @@ +.sequence-input-character { + font-weight: bolder; +} \ No newline at end of file diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..e734b06 Binary files /dev/null and b/screenshot.png differ