From c3590d3ffe7cbb0620f1181be697440f1b80d1bd Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 14:58:51 +0200 Subject: [PATCH 01/25] Added test data source for debugging --- .../.idea/.idea.I18N Commander/.idea/dataSources.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 I18N Commander/.idea/.idea.I18N Commander/.idea/dataSources.xml diff --git a/I18N Commander/.idea/.idea.I18N Commander/.idea/dataSources.xml b/I18N Commander/.idea/.idea.I18N Commander/.idea/dataSources.xml new file mode 100644 index 0000000..4c26172 --- /dev/null +++ b/I18N Commander/.idea/.idea.I18N Commander/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/../../../test.i18nc + $ProjectFileDir$ + + + \ No newline at end of file From 1ddb6ef1e71b29d9aafd716669ecce61c9d1c035 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 14:59:22 +0200 Subject: [PATCH 02/25] Update --- project for migrations.i18nc | Bin 45056 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 project for migrations.i18nc diff --git a/project for migrations.i18nc b/project for migrations.i18nc deleted file mode 100644 index fce11e30828c4fcf4aac4a9b664928fa703a4b0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45056 zcmeI&O-~v@7{KveR8W-C8|lGhvhBf*)(A#DH7VMy-6#vQsbXf6R@vh#uDtTg4d90|a z@=^#zQ9|-ql!ssD<;kpnA)nQO^GT;6Wj*^b7XPh8qd%2L-{N0lpJVx@UrQgBo-KY| zJdl+X1Q0*~0R#|0009L4pTK1_lGs^RZ)25O*}XA~&avxxmD)wqvE3W*q*QIWe$C=u z-bmYqFs)ZcQD|fPY9eb1&3}a^R8h6|+D%ii%%j5CZ0+8o zi8{4goFb<3@t4`0r8bJ^+LLqiPpU8%`+uqI!r->uF>63ff#C%qHXH}{`s15pN7 zXDljY(M&l5rez!ZMqXH1TUdp|L$Q~&j_rI})(b6n(7P`*k;|KzbpBKv7^fm#u(PHm z<7W)ZUh9UtU!S&7l##m8{HFfhAjY=$nMU4_A0n{yCid#jZ7@+Rs^1n>IoUhvUACj; zIL7Nt<)TsYD)n0PO{MA88}G+fhJub``^a0qtI&(t)(s4Hn07*-0cU^gxkkO*I`>Z8 zMpM4op|EmjW+009IL zKmY**5I_I{1Q0*~fd?h9qRwOzsf|=>V>_8jZY7hOo6h}Zy4?HDZhL)WeJd)*rN)0K z@7Qdp VhyVfzAb Date: Sat, 9 Jul 2022 15:03:18 +0200 Subject: [PATCH 03/25] Changed section data model - Made parents nullable - Added a data key for the tree - Added depth for quicker layer-wise loading --- .../DataModel/Database/Common/DataContext.cs | 2 + I18N Commander/DataModel/Database/Section.cs | 8 +- ...95157_202206MadeParentNullable.Designer.cs | 184 +++++++++++++++++ ...20220626195157_202206MadeParentNullable.cs | 56 +++++ ...094035_202207AddSectionDataKey.Designer.cs | 190 +++++++++++++++++ .../20220709094035_202207AddSectionDataKey.cs | 35 ++++ ...09095404_202207AddSectionDepth.Designer.cs | 195 ++++++++++++++++++ .../20220709095404_202207AddSectionDepth.cs | 35 ++++ .../Migrations/DataContextModelSnapshot.cs | 17 +- 9 files changed, 716 insertions(+), 6 deletions(-) create mode 100644 I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.Designer.cs create mode 100644 I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.cs create mode 100644 I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.Designer.cs create mode 100644 I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.cs create mode 100644 I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.Designer.cs create mode 100644 I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.cs diff --git a/I18N Commander/DataModel/Database/Common/DataContext.cs b/I18N Commander/DataModel/Database/Common/DataContext.cs index 3d9971d..1c4dbd3 100644 --- a/I18N Commander/DataModel/Database/Common/DataContext.cs +++ b/I18N Commander/DataModel/Database/Common/DataContext.cs @@ -35,6 +35,8 @@ public sealed class DataContext : DbContext modelBuilder.Entity
().HasIndex(n => n.Id); modelBuilder.Entity
().HasIndex(n => n.Name); + modelBuilder.Entity
().HasIndex(n => n.Depth); + modelBuilder.Entity
().HasIndex(n => n.DataKey); #endregion diff --git a/I18N Commander/DataModel/Database/Section.cs b/I18N Commander/DataModel/Database/Section.cs index b7f4c60..195bdba 100644 --- a/I18N Commander/DataModel/Database/Section.cs +++ b/I18N Commander/DataModel/Database/Section.cs @@ -9,7 +9,11 @@ public sealed class Section public string Name { get; set; } = string.Empty; - public Section Parent { get; set; } + public string DataKey { get; set; } = string.Empty; - public List TextElements { get; set; } + public int Depth { get; set; } = 0; + + public Section? Parent { get; set; } + + public List TextElements { get; set; } = new(); } \ No newline at end of file diff --git a/I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.Designer.cs b/I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.Designer.cs new file mode 100644 index 0000000..621a1c0 --- /dev/null +++ b/I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.Designer.cs @@ -0,0 +1,184 @@ +// +using System; +using DataModel.Database.Common; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace DataModel.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20220626195157_202206MadeParentNullable")] + partial class _202206MadeParentNullable + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.5"); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id"); + + b.HasIndex("Name"); + + b.HasIndex("ParentId"); + + b.ToTable("Sections"); + }); + + modelBuilder.Entity("DataModel.Database.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BoolValue") + .HasColumnType("INTEGER"); + + b.Property("GuidValue") + .HasColumnType("TEXT"); + + b.Property("IntegerValue") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TextValue") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoolValue"); + + b.HasIndex("GuidValue"); + + b.HasIndex("Id"); + + b.HasIndex("IntegerValue"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("TextValue"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SectionId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("Id"); + + b.HasIndex("SectionId"); + + b.ToTable("TextElements"); + }); + + modelBuilder.Entity("DataModel.Database.Translation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Culture") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TextElementId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Culture"); + + b.HasIndex("Id"); + + b.HasIndex("Text"); + + b.HasIndex("TextElementId"); + + b.ToTable("Translations"); + }); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.HasOne("DataModel.Database.Section", "Parent") + .WithMany() + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.HasOne("DataModel.Database.Section", "Section") + .WithMany("TextElements") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("DataModel.Database.Translation", b => + { + b.HasOne("DataModel.Database.TextElement", "TextElement") + .WithMany("Translations") + .HasForeignKey("TextElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TextElement"); + }); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.Navigation("TextElements"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.Navigation("Translations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.cs b/I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.cs new file mode 100644 index 0000000..a04faba --- /dev/null +++ b/I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace DataModel.Migrations +{ + public partial class _202206MadeParentNullable : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Sections_Sections_ParentId", + table: "Sections"); + + migrationBuilder.AlterColumn( + name: "ParentId", + table: "Sections", + type: "INTEGER", + nullable: true, + oldClrType: typeof(int), + oldType: "INTEGER"); + + migrationBuilder.AddForeignKey( + name: "FK_Sections_Sections_ParentId", + table: "Sections", + column: "ParentId", + principalTable: "Sections", + principalColumn: "Id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Sections_Sections_ParentId", + table: "Sections"); + + migrationBuilder.AlterColumn( + name: "ParentId", + table: "Sections", + type: "INTEGER", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "INTEGER", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Sections_Sections_ParentId", + table: "Sections", + column: "ParentId", + principalTable: "Sections", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.Designer.cs b/I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.Designer.cs new file mode 100644 index 0000000..b0818d3 --- /dev/null +++ b/I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.Designer.cs @@ -0,0 +1,190 @@ +// +using System; +using DataModel.Database.Common; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace DataModel.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20220709094035_202207AddSectionDataKey")] + partial class _202207AddSectionDataKey + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.5"); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DataKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DataKey"); + + b.HasIndex("Id"); + + b.HasIndex("Name"); + + b.HasIndex("ParentId"); + + b.ToTable("Sections"); + }); + + modelBuilder.Entity("DataModel.Database.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BoolValue") + .HasColumnType("INTEGER"); + + b.Property("GuidValue") + .HasColumnType("TEXT"); + + b.Property("IntegerValue") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TextValue") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoolValue"); + + b.HasIndex("GuidValue"); + + b.HasIndex("Id"); + + b.HasIndex("IntegerValue"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("TextValue"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SectionId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("Id"); + + b.HasIndex("SectionId"); + + b.ToTable("TextElements"); + }); + + modelBuilder.Entity("DataModel.Database.Translation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Culture") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TextElementId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Culture"); + + b.HasIndex("Id"); + + b.HasIndex("Text"); + + b.HasIndex("TextElementId"); + + b.ToTable("Translations"); + }); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.HasOne("DataModel.Database.Section", "Parent") + .WithMany() + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.HasOne("DataModel.Database.Section", "Section") + .WithMany("TextElements") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("DataModel.Database.Translation", b => + { + b.HasOne("DataModel.Database.TextElement", "TextElement") + .WithMany("Translations") + .HasForeignKey("TextElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TextElement"); + }); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.Navigation("TextElements"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.Navigation("Translations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.cs b/I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.cs new file mode 100644 index 0000000..a29ef69 --- /dev/null +++ b/I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace DataModel.Migrations +{ + public partial class _202207AddSectionDataKey : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DataKey", + table: "Sections", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.CreateIndex( + name: "IX_Sections_DataKey", + table: "Sections", + column: "DataKey"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Sections_DataKey", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "DataKey", + table: "Sections"); + } + } +} diff --git a/I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.Designer.cs b/I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.Designer.cs new file mode 100644 index 0000000..b3e601f --- /dev/null +++ b/I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.Designer.cs @@ -0,0 +1,195 @@ +// +using System; +using DataModel.Database.Common; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace DataModel.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20220709095404_202207AddSectionDepth")] + partial class _202207AddSectionDepth + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.5"); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DataKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Depth") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DataKey"); + + b.HasIndex("Depth"); + + b.HasIndex("Id"); + + b.HasIndex("Name"); + + b.HasIndex("ParentId"); + + b.ToTable("Sections"); + }); + + modelBuilder.Entity("DataModel.Database.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BoolValue") + .HasColumnType("INTEGER"); + + b.Property("GuidValue") + .HasColumnType("TEXT"); + + b.Property("IntegerValue") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TextValue") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoolValue"); + + b.HasIndex("GuidValue"); + + b.HasIndex("Id"); + + b.HasIndex("IntegerValue"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("TextValue"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SectionId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("Id"); + + b.HasIndex("SectionId"); + + b.ToTable("TextElements"); + }); + + modelBuilder.Entity("DataModel.Database.Translation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Culture") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TextElementId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Culture"); + + b.HasIndex("Id"); + + b.HasIndex("Text"); + + b.HasIndex("TextElementId"); + + b.ToTable("Translations"); + }); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.HasOne("DataModel.Database.Section", "Parent") + .WithMany() + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.HasOne("DataModel.Database.Section", "Section") + .WithMany("TextElements") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("DataModel.Database.Translation", b => + { + b.HasOne("DataModel.Database.TextElement", "TextElement") + .WithMany("Translations") + .HasForeignKey("TextElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TextElement"); + }); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.Navigation("TextElements"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.Navigation("Translations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.cs b/I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.cs new file mode 100644 index 0000000..7829b3e --- /dev/null +++ b/I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace DataModel.Migrations +{ + public partial class _202207AddSectionDepth : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Depth", + table: "Sections", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateIndex( + name: "IX_Sections_Depth", + table: "Sections", + column: "Depth"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Sections_Depth", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "Depth", + table: "Sections"); + } + } +} diff --git a/I18N Commander/DataModel/Migrations/DataContextModelSnapshot.cs b/I18N Commander/DataModel/Migrations/DataContextModelSnapshot.cs index fd8d8b5..6a285f4 100644 --- a/I18N Commander/DataModel/Migrations/DataContextModelSnapshot.cs +++ b/I18N Commander/DataModel/Migrations/DataContextModelSnapshot.cs @@ -23,15 +23,26 @@ namespace DataModel.Migrations .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); + b.Property("DataKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Depth") + .HasColumnType("INTEGER"); + b.Property("Name") .IsRequired() .HasColumnType("TEXT"); - b.Property("ParentId") + b.Property("ParentId") .HasColumnType("INTEGER"); b.HasKey("Id"); + b.HasIndex("DataKey"); + + b.HasIndex("Depth"); + b.HasIndex("Id"); b.HasIndex("Name"); @@ -140,9 +151,7 @@ namespace DataModel.Migrations { b.HasOne("DataModel.Database.Section", "Parent") .WithMany() - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("ParentId"); b.Navigation("Parent"); }); From fb0bb2b178bd26f532331a5fcfbed4d7d17e3190 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:03:50 +0200 Subject: [PATCH 04/25] Expect that all tables are available --- I18N Commander/DataModel/Database/Common/DataContext.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/I18N Commander/DataModel/Database/Common/DataContext.cs b/I18N Commander/DataModel/Database/Common/DataContext.cs index 1c4dbd3..08fe633 100644 --- a/I18N Commander/DataModel/Database/Common/DataContext.cs +++ b/I18N Commander/DataModel/Database/Common/DataContext.cs @@ -4,13 +4,13 @@ namespace DataModel.Database.Common; public sealed class DataContext : DbContext { - public DbSet? Settings { get; set; } + public DbSet Settings { get; set; } - public DbSet
? Sections { get; set; } + public DbSet
Sections { get; set; } - public DbSet? TextElements { get; set; } + public DbSet TextElements { get; set; } - public DbSet? Translations { get; set; } + public DbSet Translations { get; set; } public DataContext(DbContextOptions contextOptions) : base(contextOptions) { From 9015e08518f22dbf96d3ecfe317322a54345e971 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:04:21 +0200 Subject: [PATCH 05/25] Changed database at the DI system to be transient --- I18N Commander/DataModel/Setup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I18N Commander/DataModel/Setup.cs b/I18N Commander/DataModel/Setup.cs index 44e32d8..ed1388d 100644 --- a/I18N Commander/DataModel/Setup.cs +++ b/I18N Commander/DataModel/Setup.cs @@ -30,7 +30,7 @@ public static class Setup /// public static void AddDatabase(this IServiceCollection serviceCollection, string path2DataFile, bool createWhenNecessary = true) { - serviceCollection.AddDbContext(options => options.UseSqlite($"Filename={path2DataFile};Mode={(createWhenNecessary ? DB_READ_WRITE_CREATE_MODE : DB_READ_WRITE_MODE)}")); + serviceCollection.AddDbContext(options => options.UseSqlite($"Filename={path2DataFile};Mode={(createWhenNecessary ? DB_READ_WRITE_CREATE_MODE : DB_READ_WRITE_MODE)}"), ServiceLifetime.Transient); } /// From c90dfa1ac072ec7111595b6bce9dc11919a29c71 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:04:46 +0200 Subject: [PATCH 06/25] Added the selection processor for adding & loading data --- I18N Commander/Processor/SectionProcessor.cs | 74 ++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 I18N Commander/Processor/SectionProcessor.cs diff --git a/I18N Commander/Processor/SectionProcessor.cs b/I18N Commander/Processor/SectionProcessor.cs new file mode 100644 index 0000000..dfd064e --- /dev/null +++ b/I18N Commander/Processor/SectionProcessor.cs @@ -0,0 +1,74 @@ +using DataModel.Database; +using DataModel.Database.Common; +using Microsoft.EntityFrameworkCore; + +namespace Processor; + +public static class SectionProcessor +{ + /// + /// Load one layer of the tree by using the specified depth: + /// + public static IAsyncEnumerable
LoadLayer(DataContext db, int depth) + { + return db.Sections.Where(n => n.Depth == depth).OrderBy(n => n.DataKey).AsAsyncEnumerable(); + } + + /// + /// Determine how deep the tree is. + /// + public static async ValueTask GetDepth(DataContext db) + { + if(!await db.Sections.AnyAsync()) + { + return 0; + } + + return await db.Sections.MaxAsync(s => s.Depth); + } + + /// + /// Compute the new sections key and its depth, then store the section in the database. + /// + public static async Task
AddSection(DataContext db, string text, string? parentKey) + { + // Remove any whitespaces from the section name, regardless of how many e.g. spaces the user typed: + var key = string.Join('_', text.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)).ToUpperInvariant(); + + // In the case, when the user adds a section to the root, handle the insert differently: + if (string.IsNullOrEmpty(parentKey)) + { + var rootSection = new Section + { + Depth = 0, + DataKey = key, + Parent = null, + Name = text.Trim(), + TextElements = new(), + }; + + db.Sections.Add(rootSection); + await db.SaveChangesAsync(); + return rootSection; + } + + // Read the parent from the database: + var parent = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == parentKey); + if (parent is null) + throw new ArgumentException($"The section's parent with key {parentKey} does not exist in the database."); + + // Add the new section to the database: + var section = new Section + { + Name = text.Trim(), + DataKey = key, + Parent = parent, + TextElements = new(), + Depth = parent.Depth + 1, + }; + + db.Sections.Add(section); + await db.SaveChangesAsync(); + return section; + } +} \ No newline at end of file From 5d19a96c070f09f71c47e15c9f15e050357728fe Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:05:19 +0200 Subject: [PATCH 07/25] Added more icons --- .../UI WinForms/Resources/Icons.Designer.cs | 50 ++++++++++++++++++ .../UI WinForms/Resources/Icons.resx | 15 ++++++ .../Resources/icons8-add-folder-512.png | Bin 0 -> 2766 bytes .../Resources/icons8-cancel-512.png | Bin 0 -> 2990 bytes .../Resources/icons8-delete-folder-512.png | Bin 0 -> 2841 bytes .../Resources/icons8-documents-folder-512.png | Bin 0 -> 3538 bytes .../UI WinForms/Resources/icons8-ok-512.png | Bin 0 -> 3045 bytes 7 files changed, 65 insertions(+) create mode 100644 I18N Commander/UI WinForms/Resources/icons8-add-folder-512.png create mode 100644 I18N Commander/UI WinForms/Resources/icons8-cancel-512.png create mode 100644 I18N Commander/UI WinForms/Resources/icons8-delete-folder-512.png create mode 100644 I18N Commander/UI WinForms/Resources/icons8-documents-folder-512.png create mode 100644 I18N Commander/UI WinForms/Resources/icons8-ok-512.png diff --git a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs index 32a2e64..8d0147b 100644 --- a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs +++ b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs @@ -60,6 +60,16 @@ namespace UI_WinForms.Resources { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_add_folder_512 { + get { + object obj = ResourceManager.GetObject("icons8_add_folder_512", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// @@ -70,6 +80,26 @@ namespace UI_WinForms.Resources { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_cancel_512 { + get { + object obj = ResourceManager.GetObject("icons8_cancel_512", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_delete_folder_512 { + get { + object obj = ResourceManager.GetObject("icons8_delete_folder_512", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// @@ -80,6 +110,16 @@ namespace UI_WinForms.Resources { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_documents_folder_512 { + get { + object obj = ResourceManager.GetObject("icons8_documents_folder_512", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// @@ -100,6 +140,16 @@ namespace UI_WinForms.Resources { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_ok_512 { + get { + object obj = ResourceManager.GetObject("icons8_ok_512", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/I18N Commander/UI WinForms/Resources/Icons.resx b/I18N Commander/UI WinForms/Resources/Icons.resx index 17944cf..4fb8808 100644 --- a/I18N Commander/UI WinForms/Resources/Icons.resx +++ b/I18N Commander/UI WinForms/Resources/Icons.resx @@ -118,9 +118,21 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + icons8-add-folder-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + icons8-browse-folder-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + icons8-cancel-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + icons8-delete-folder-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + icons8-documents-folder-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + icons8-document-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -130,6 +142,9 @@ icons8-new-window-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + icons8-ok-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + icons8-open-file-under-cursor-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/I18N Commander/UI WinForms/Resources/icons8-add-folder-512.png b/I18N Commander/UI WinForms/Resources/icons8-add-folder-512.png new file mode 100644 index 0000000000000000000000000000000000000000..3a24a0eb494487640b736051905c25676cb14800 GIT binary patch literal 2766 zcmai0d0dj|7RJ)XB`bIBRuWE5p|a*iAQ_O$2rhx4IR?U~P;9XXT58v_Mz4<9jHQN_ zrKMzPHCC3|Ewi$6OwqDinx*B`$itL^7O;0GraqfxmUzNEmdABHv1YF{wdNl2{5rT@kJb6wJ#QikEVuaUtaOAB@2j z0mhTdB{(EfsZ=7AScF&_gG76JdLmI6BnIOGGF)VFA~{>-B9b}iB&IdUfQ%#MOXPg9 z2&&U$M~M}30t^P?&=2Bre)O!mNcQ3QU?Pw@1c^qVkbk8ED*hMfI^+|&geR7ZWjwKD z<^UhLn1Mer1k?9<6BS$XIWm>{zv)8ZN5_!KDY0N8J|y*{nErGiqsK`AWC$P=E2JEN z5(|jr4pW)X{tej3=l*?PCnQklD!~x(LD!gB6Xq;NfVpFk z=vgT2vyd*;xFEg?5HQJnAs~{0sxwi{f?;0(Nn(Lmx&dr!fB>8JHB$%N6h;+uK?pk) z#w_GVA_Yms(R=}j$wGYALH#HsH?*gln+papEdv$}2io$Zo2fVQ?tal=2iCybD~moc;g!x{ik8K?^Ds=gXJL#nQN`t?4%30BoCHO)mxf zss4sSr>cx&b97lJz$8*JSHS_eQ_exTX;dbTmMhs(z&i#kFahQr9nA;p69)~_buJW* zK%)@T)t#|W@&Hin%V$G=hzzN_aesz}M}87Kev0syD+=08?}1kzoFT}WAp$OD#tb9&;&ueGi?b*mi zV{fyt!*>_D5+es1iyA^7!uep^4Z zxvZ$NtfG2}(rp7hv7b|WJ?mEE?iI0V$|Dw0=@5jY?egyrC+^{ncGvrqmzC^)GL}!I zMoO)68U8%yEErWUvxHJwurJ4wu-t*v@cR9e+E)#Fo2xE4Kq?pR8EP>dH64#|PajLD z8^=Q)5!BD&6B7j69aaf7N2`>Dd@I^7PT@ODXb+A|(lYpG#0`!js^ilMLv=8(X7`>6 zX;b2;XLZW0v>j~*f`PS}YH|4Z9Aa}!YbZe|1YZ|M65lWx1Y%;Ld-NcAYFqI2;^s&8 zrZZFea+7!r!1g9_U=ppoP`QS4r>ZTW11@y<%JcY-E8vtQeY9)eUNc|YQJ*BsWN)ax z0VPP^yU@dF4)&$}iNc2qlhr?2G&cq#&SqpI#(wMUJKwRr;6zeCVa=nOM|r*dFIe^$ zAOBu3T>E60&=l7>Ht1#ZA~&WWPBgDu@W+VJoeVa1(M2N{ysN{?cbP%PAh7!XY%Gt z#2$rJ7T+tD+}d6$7SsUnu>hkaSCvnXKcBM*nAidB98?j8vV5AJPrSs8YC0U>2CVc8R7mf?PFo$EUTz5cQYk|fxU zRZP*^_FuDIqtGsX`MP??ZOs-YGh!aR-{708Zj)Wd9u27yR0*}EZ{NE|H={fPem{2> zBfTo`GW_k7*zIc0OV345^?&d^5pz6dj!%&yO?1I}fmViY8MfFKn;V!Jz&>=pDtB3~ z`_i-XlhWhI(XV^2?b}-rd<%Px@X&h1`(JN%2K59k8@rNoey8@SU_poB;PAeKL6&6a z))2bB*}{R?_%OSQ_Hav%&l$>TPGI(s+M1R0L|CmL91i|=kr{NN-VJ`w9HX`$Y{rz( zUucAnIzb<3;#iS9f1rNRfjuQv_9=60HD z0*^X3GKr|oT$<1^KYe$uiZnDzVT}!*JQ0uAMy|?;=!iw7zZ%v^ zbL^^t`4Ox4P)XYZW7o#R-(#b?eobK}*3CV^aMk#)(ip6CG;2F!=%k5X`rL%2~ Rc1-s-)Q>_VpI*mG{x2QOS^WS2 literal 0 HcmV?d00001 diff --git a/I18N Commander/UI WinForms/Resources/icons8-cancel-512.png b/I18N Commander/UI WinForms/Resources/icons8-cancel-512.png new file mode 100644 index 0000000000000000000000000000000000000000..df1ba1735b1b68d9ef6f186f6f1d82fa1e07abba GIT binary patch literal 2990 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 literal 0 HcmV?d00001 diff --git a/I18N Commander/UI WinForms/Resources/icons8-delete-folder-512.png b/I18N Commander/UI WinForms/Resources/icons8-delete-folder-512.png new file mode 100644 index 0000000000000000000000000000000000000000..6457dc54f3669d0ea4ffe3987f9e0213489253cb GIT binary patch literal 2841 zcmai0dpwiv8=qH4a!Q0+t;buWY%@7*jLIgbX_!Mwj6G(tL)+M<9HtObABk#tRj7m# zMNZLykY7%Pekz{`ogH41Nd2DC%iCMMzdxRB_x=4|-|M=s>$wkQ{J+Y$lRQWBYVE`=!c~NmKJj6LL4N(1o>2ikQv0{ z;e>c#S{DcXmTsc}#59E;hzC4KWP}x)3nGk=Mo2VZA%`&M()@AG1nY0c&wp3z9g8=gUxC{=T!Db<(np9tQ zFdq*95RUjJ&S&_~sk3FermLXva?> zX{vE#h7b($ATXF9iwCLBMllEO_yuUi4q|g%ptc6_z>KfiI^d=;5}O7=hSOopLAF*_ zWG>sE5d>j8=WSaNwnQsq1FW&JJ_b1>0~HMi*)se^1S%h5VbD--qK)+p3@HX!oS_NM z2(tl=!J*O9%F}Gi^?QI~rjQS&&FL8wQWh=8C4bX4>2Z+&i45C(Sw@;BeWrU_X#H?HrPuL3wO{zJSUFx6q*iPfv%qDl(Vr7H?DU)L1<~EUdCvLOXM@X+(q{eEDgAXnK=udw^U0 z>tKtzz64LxWt_%){b^%5TAXG_r7I;Fxh_csx<>D()DtxGZ~9;EeEFrQzb5NS(#$KxhTMNQM)mfKCxUUyx>?1 z_s*Z(r={--olB2?I+jWcUk9|T?Mie!sVtM`fVNY%oLbWGuC@UdR+wR3Ij9pMiR#tE z-*evt(Cg&?8Td>GN~0~kzuNpb}_?yeKfpd zefC9;>^G{qs$|8^IFTdSr{vkuJ(`iTwzxY;wrrrSMEz@0P)|EWZugPfDhG~i2#}yY z<`p)zzzH3_xkxH~d1&AMw|7h;eCfko8!M$Rl*JsI9XuFJURCktLhsErsrm2X&nt-P$Ux4-?EJ z(eCy8`WB-5?&Hg{*4ZLJ&D7JKwK*E4<$Zew${HI*XD%i@!}3D@XyaQrKek-ipLXiw z=O*^5FM}iLhO(JjZQ0hrhM3z2c&jo0)ku{cK0UPbxtZ%&`{i?0x_s@rSc`Ji4vorr z{p!zOUMhTctsTtd(jTi=>mbb52$xHM>zJ#(iq3uEbQLHt6_8GEdoaG>mU_)Y zje&a_mSx1t*H33(IJei>@2%G6^PIaXy;dgpGA*5D>y{~c2Y!xlSu9=ygU4AnaButS zlG}H#HqKo+$|QH^{g#4kbU?-ameL-$q`YWZWF>q99;afT+af$}Z*jdVeOTA>`UQo~ z_dZXw>sBka`XTg8*V~WpR?<0tvbyc^_*b#pRUJQJtk=6!=+=v8DDc)vtAlSg zCTXrp!me5;Q7YSY%56x|^;p*l7)oBUkEll2ylik2%`x^+y3zyarAQpC>_Ke4jXIl?opMw~g#7fNOCC&&#cS%^tdgr(0aJ(jsEU@hC3_hz zXNGW7om)u{;{_$$Md$*Jq=j_9J+(_@)@iN9L?~V;R;LucNQ^g+ejJAlNIAxWj?zSk zp_rOyCXHW6to9C5Nw!ni-+%8=zW27i6{k7w#b=GlVM(@nox6(do{VX5qt_%2bvizW ze~lg*Wl(~I>N&Zbjyz!Ov2SmFmF?i<{nhz^PmNPS16OsJ>|(3q8!CwZPfN9{rtziU z)mC^eZaN*!G!q+wOIurrgQ=HkM^=6iFYM7qbnYk?+Tuu7k-dO*q?+%UTmV zKU=_e54m{v-+FYBRF_9y>oiJsAJtJ2EoccSR@x?6g_y809NVRHb7yhSRCv5Sn|)Dl zVCg9=TtJk+Iapz`sUv5f+$45n0sq{mI5WzqD#4$=T+zi$T>#3z4qG^U1pe`MTCeF- zHT|qA)6MIKEHdC{?e8y8Xudf#oS0LlppxwM`ceNxi1A$!AS3%iaH;jpuCLBD$vFd? zKWMA9-Lf;uvOm0N&&;TimDsQuDk?P7N);HhvM!Umf=|5Uqq9ftTR-dT<0h*(PkN^Mx literal 0 HcmV?d00001 diff --git a/I18N Commander/UI WinForms/Resources/icons8-documents-folder-512.png b/I18N Commander/UI WinForms/Resources/icons8-documents-folder-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d6eebf0fcef2d7aa5b5e65a8bbf5d9250d138e40 GIT binary patch literal 3538 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 literal 0 HcmV?d00001 From 715159a6d9712c591f2d3f39766d2989c8c4c9cd Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:06:30 +0200 Subject: [PATCH 08/25] Added the main screen user component --- .../UI WinForms/Components/Main.Designer.cs | 114 ++++++++++++++++++ I18N Commander/UI WinForms/Components/Main.cs | 9 ++ .../UI WinForms/Components/Main.resx | 60 +++++++++ 3 files changed, 183 insertions(+) create mode 100644 I18N Commander/UI WinForms/Components/Main.Designer.cs create mode 100644 I18N Commander/UI WinForms/Components/Main.cs create mode 100644 I18N Commander/UI WinForms/Components/Main.resx diff --git a/I18N Commander/UI WinForms/Components/Main.Designer.cs b/I18N Commander/UI WinForms/Components/Main.Designer.cs new file mode 100644 index 0000000..6e3aef1 --- /dev/null +++ b/I18N Commander/UI WinForms/Components/Main.Designer.cs @@ -0,0 +1,114 @@ +namespace UI_WinForms.Components +{ + partial class Main + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayout = new System.Windows.Forms.TableLayoutPanel(); + this.flowLayoutBottom = new System.Windows.Forms.FlowLayoutPanel(); + this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.sectionTree = new UI_WinForms.Components.SectionTree(); + this.tableLayout.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); + this.splitContainer.Panel1.SuspendLayout(); + this.splitContainer.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayout + // + this.tableLayout.ColumnCount = 1; + this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayout.Controls.Add(this.flowLayoutBottom, 0, 1); + this.tableLayout.Controls.Add(this.splitContainer, 0, 0); + this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayout.Location = new System.Drawing.Point(0, 0); + this.tableLayout.Name = "tableLayout"; + this.tableLayout.RowCount = 2; + this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 66F)); + this.tableLayout.Size = new System.Drawing.Size(965, 603); + this.tableLayout.TabIndex = 0; + // + // flowLayoutBottom + // + this.flowLayoutBottom.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutBottom.Location = new System.Drawing.Point(0, 537); + this.flowLayoutBottom.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutBottom.Name = "flowLayoutBottom"; + this.flowLayoutBottom.Size = new System.Drawing.Size(965, 66); + this.flowLayoutBottom.TabIndex = 0; + // + // splitContainer + // + this.splitContainer.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; + this.splitContainer.Location = new System.Drawing.Point(3, 3); + this.splitContainer.Name = "splitContainer"; + // + // splitContainer.Panel1 + // + this.splitContainer.Panel1.Controls.Add(this.sectionTree); + this.splitContainer.Panel1MinSize = 300; + this.splitContainer.Size = new System.Drawing.Size(959, 531); + this.splitContainer.SplitterDistance = 319; + this.splitContainer.TabIndex = 1; + // + // sectionTree + // + this.sectionTree.Dock = System.Windows.Forms.DockStyle.Fill; + this.sectionTree.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.sectionTree.Location = new System.Drawing.Point(0, 0); + this.sectionTree.Name = "sectionTree"; + this.sectionTree.Size = new System.Drawing.Size(317, 529); + this.sectionTree.TabIndex = 0; + // + // Main + // + this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.tableLayout); + this.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.Name = "Main"; + this.Size = new System.Drawing.Size(965, 603); + this.tableLayout.ResumeLayout(false); + this.splitContainer.Panel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); + this.splitContainer.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private TableLayoutPanel tableLayout; + private FlowLayoutPanel flowLayoutBottom; + private SplitContainer splitContainer; + private SectionTree sectionTree; + } +} diff --git a/I18N Commander/UI WinForms/Components/Main.cs b/I18N Commander/UI WinForms/Components/Main.cs new file mode 100644 index 0000000..a25e38f --- /dev/null +++ b/I18N Commander/UI WinForms/Components/Main.cs @@ -0,0 +1,9 @@ +namespace UI_WinForms.Components; + +public partial class Main : UserControl +{ + public Main() + { + this.InitializeComponent(); + } +} \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Components/Main.resx b/I18N Commander/UI WinForms/Components/Main.resx new file mode 100644 index 0000000..b5ae26c --- /dev/null +++ b/I18N Commander/UI WinForms/Components/Main.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file From 4ef0d91510b6d0586e81f599215ad659a5830586 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:06:49 +0200 Subject: [PATCH 09/25] Added the section user control --- .../Components/SectionTree.Designer.cs | 129 ++++++++++++++++ .../UI WinForms/Components/SectionTree.cs | 140 ++++++++++++++++++ .../UI WinForms/Components/SectionTree.resx | 60 ++++++++ 3 files changed, 329 insertions(+) create mode 100644 I18N Commander/UI WinForms/Components/SectionTree.Designer.cs create mode 100644 I18N Commander/UI WinForms/Components/SectionTree.cs create mode 100644 I18N Commander/UI WinForms/Components/SectionTree.resx diff --git a/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs b/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs new file mode 100644 index 0000000..9229662 --- /dev/null +++ b/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs @@ -0,0 +1,129 @@ +namespace UI_WinForms.Components +{ + partial class SectionTree + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayout = new System.Windows.Forms.TableLayoutPanel(); + this.flowLayoutBottom = new System.Windows.Forms.FlowLayoutPanel(); + this.buttonAdd = new System.Windows.Forms.Button(); + this.buttonRemove = new System.Windows.Forms.Button(); + this.treeView = new System.Windows.Forms.TreeView(); + this.tableLayout.SuspendLayout(); + this.flowLayoutBottom.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayout + // + this.tableLayout.ColumnCount = 1; + this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayout.Controls.Add(this.flowLayoutBottom, 0, 1); + this.tableLayout.Controls.Add(this.treeView, 0, 0); + this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayout.Location = new System.Drawing.Point(0, 0); + this.tableLayout.Name = "tableLayout"; + this.tableLayout.RowCount = 2; + this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 66F)); + this.tableLayout.Size = new System.Drawing.Size(296, 511); + this.tableLayout.TabIndex = 0; + // + // flowLayoutBottom + // + this.flowLayoutBottom.Controls.Add(this.buttonAdd); + this.flowLayoutBottom.Controls.Add(this.buttonRemove); + this.flowLayoutBottom.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutBottom.Location = new System.Drawing.Point(0, 445); + this.flowLayoutBottom.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutBottom.Name = "flowLayoutBottom"; + this.flowLayoutBottom.Size = new System.Drawing.Size(296, 66); + this.flowLayoutBottom.TabIndex = 0; + // + // buttonAdd + // + this.buttonAdd.AutoSize = true; + this.buttonAdd.Image = global::UI_WinForms.Resources.Icons.icons8_add_folder_512; + this.buttonAdd.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonAdd.Location = new System.Drawing.Point(3, 3); + this.buttonAdd.Name = "buttonAdd"; + this.buttonAdd.Size = new System.Drawing.Size(138, 60); + this.buttonAdd.TabIndex = 0; + this.buttonAdd.Text = "Add"; + this.buttonAdd.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.buttonAdd.UseVisualStyleBackColor = true; + this.buttonAdd.Click += new System.EventHandler(this.buttonAdd_Click); + // + // buttonRemove + // + this.buttonRemove.AutoSize = true; + this.buttonRemove.Enabled = false; + this.buttonRemove.Image = global::UI_WinForms.Resources.Icons.icons8_delete_folder_512; + this.buttonRemove.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonRemove.Location = new System.Drawing.Point(147, 3); + this.buttonRemove.Name = "buttonRemove"; + this.buttonRemove.Size = new System.Drawing.Size(138, 60); + this.buttonRemove.TabIndex = 1; + this.buttonRemove.Text = "Remove"; + this.buttonRemove.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.buttonRemove.UseVisualStyleBackColor = true; + // + // treeView + // + this.treeView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.treeView.Dock = System.Windows.Forms.DockStyle.Fill; + this.treeView.HideSelection = false; + this.treeView.ItemHeight = 50; + this.treeView.Location = new System.Drawing.Point(3, 3); + this.treeView.Name = "treeView"; + this.treeView.Size = new System.Drawing.Size(290, 439); + this.treeView.TabIndex = 1; + // + // SectionTree + // + this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.tableLayout); + this.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.Name = "SectionTree"; + this.Size = new System.Drawing.Size(296, 511); + this.tableLayout.ResumeLayout(false); + this.flowLayoutBottom.ResumeLayout(false); + this.flowLayoutBottom.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private TableLayoutPanel tableLayout; + private FlowLayoutPanel flowLayoutBottom; + private Button buttonAdd; + private Button buttonRemove; + private TreeView treeView; + } +} diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs new file mode 100644 index 0000000..5858830 --- /dev/null +++ b/I18N Commander/UI WinForms/Components/SectionTree.cs @@ -0,0 +1,140 @@ +using DataModel.Database; +using DataModel.Database.Common; +using Microsoft.Extensions.DependencyInjection; +using Processor; +using UI_WinForms.Dialogs; +using UI_WinForms.Resources; + +namespace UI_WinForms.Components; + +public partial class SectionTree : UserControl +{ + private readonly DataContext db; + + public SectionTree() + { + this.InitializeComponent(); + + // Get the DI context from the main form: + this.db = Program.SERVICE_PROVIDER.GetService()!; + + // Dispose of the context when the control is disposed: + this.Disposed += (_, _) => this.db.Dispose(); + + // Create an image list from a resource: + var imgList = new ImageList(); + imgList.ImageSize = new Size(45, 45); + imgList.ColorDepth = ColorDepth.Depth32Bit; + imgList.Images.Add(Icons.icons8_documents_folder_512); + + // Set the image list to the tree view: + this.treeView.ImageList = imgList; + + // Subscribe to the load event: + this.Load += this.OnLoad; + } + + private async void OnLoad(object? sender, EventArgs e) + { + // A dictionary to cache all known tree nodes: + var treeNodes = new Dictionary(); + + // Get the max. depth of the tree: + var maxDepth = await SectionProcessor.GetDepth(this.db); + + // Store nodes, where we cannot find the parents: + var missingParents = new List(); + + // Populate the tree view out of the database, layer by layer: + for (var i = 0; i <= maxDepth; i++) + { + await foreach (var section in SectionProcessor.LoadLayer(this.db, i)) + { + // Create the tree node: + var node = new TreeNode + { + Name = section.DataKey, // [sic] name is the key + Text = section.Name, + StateImageIndex = 1, + }; + + // Cache the node: + treeNodes.Add(section.DataKey, node); + + // Is this a root node? + if (section.Depth is 0) + + // Set the root node: + this.treeView.Nodes.Add(node); + + // Otherwise, attach this section to its parent node: + else + { + // Get the parent from our cache within O(1): + treeNodes.TryGetValue(section.Parent?.DataKey ?? string.Empty, out var parent); + + // If the parent node is not found, skip this section: + if (parent is null) + { + missingParents.Add(node); + continue; + } + + // Add the node to the parent: + parent.Nodes.Add(node); + } + } + } + + // If we found any missing parents, show a dialog: + if (missingParents.Any()) + { + MessageBox.Show($"In {missingParents.Count} case(s) we could not found the matching parent. We added these nodes to a special root node, though.", "Parent not found", MessageBoxButtons.OK, MessageBoxIcon.Error); + + // Create a root node for all missing parents: + var rootMissedParents = new TreeNode + { + Name = "MISSING_PARENTS", + Text = "Missing Parents", + StateImageIndex = 1, + }; + + // Add the root node to the tree: + this.treeView.Nodes.Add(rootMissedParents); + + // Add all missing parents to the root node: + foreach (var node in missingParents) + rootMissedParents.Nodes.Add(node); + } + } + + private async void buttonAdd_Click(object sender, EventArgs e) + { + var result = InputDialog.Show("Please type the desired section name.", "Add a section", "My next section"); + if(result.DialogResult == DialogResult.Cancel) + return; + + // Get the currently selected section as parent: + var selectedNode = this.treeView.SelectedNode; + + // Add the new section to the database: + var addedSection = await SectionProcessor.AddSection(this.db, result.Text, result.IsRoot ? null : selectedNode?.Name); + + // Add the new section to the tree control: + var node = new TreeNode + { + Name = addedSection.DataKey, // [sic] name is the key + Text = addedSection.Name, + StateImageIndex = 1, + }; + + if(!result.IsRoot && selectedNode is not null) + selectedNode.Nodes.Add(node); + else + this.treeView.Nodes.Add(node); + + // Ensure, that the added node is visible and gets the focus: + node.EnsureVisible(); + this.treeView.SelectedNode = node; + } +} \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Components/SectionTree.resx b/I18N Commander/UI WinForms/Components/SectionTree.resx new file mode 100644 index 0000000..b5ae26c --- /dev/null +++ b/I18N Commander/UI WinForms/Components/SectionTree.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file From 5d09cb7e57a8515d534367293f0643eab89358b6 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:07:07 +0200 Subject: [PATCH 10/25] Added the input dialog --- .../Dialogs/InputDialog.Designer.cs | 166 ++++++++++++++++++ .../UI WinForms/Dialogs/InputDialog.cs | 39 ++++ .../UI WinForms/Dialogs/InputDialog.resx | 60 +++++++ 3 files changed, 265 insertions(+) create mode 100644 I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs create mode 100644 I18N Commander/UI WinForms/Dialogs/InputDialog.cs create mode 100644 I18N Commander/UI WinForms/Dialogs/InputDialog.resx diff --git a/I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs b/I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs new file mode 100644 index 0000000..f1b97b3 --- /dev/null +++ b/I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs @@ -0,0 +1,166 @@ +namespace UI_WinForms.Dialogs +{ + partial class InputDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayout = new System.Windows.Forms.TableLayoutPanel(); + this.labelHead = new System.Windows.Forms.Label(); + this.flowLayoutBottom = new System.Windows.Forms.FlowLayoutPanel(); + this.buttonOk = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.textBoxInput = new System.Windows.Forms.TextBox(); + this.checkBoxIsRoot = new System.Windows.Forms.CheckBox(); + this.tableLayout.SuspendLayout(); + this.flowLayoutBottom.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayout + // + this.tableLayout.AutoSize = true; + this.tableLayout.ColumnCount = 1; + this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayout.Controls.Add(this.labelHead, 0, 0); + this.tableLayout.Controls.Add(this.flowLayoutBottom, 0, 2); + this.tableLayout.Controls.Add(this.textBoxInput, 0, 1); + this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayout.Location = new System.Drawing.Point(0, 0); + this.tableLayout.Name = "tableLayout"; + this.tableLayout.RowCount = 4; + this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 66F)); + this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayout.Size = new System.Drawing.Size(800, 139); + this.tableLayout.TabIndex = 0; + // + // labelHead + // + this.labelHead.AutoSize = true; + this.labelHead.Dock = System.Windows.Forms.DockStyle.Fill; + this.labelHead.Location = new System.Drawing.Point(3, 0); + this.labelHead.Name = "labelHead"; + this.labelHead.Size = new System.Drawing.Size(794, 28); + this.labelHead.TabIndex = 0; + this.labelHead.Text = "header"; + // + // flowLayoutBottom + // + this.flowLayoutBottom.Controls.Add(this.buttonOk); + this.flowLayoutBottom.Controls.Add(this.buttonCancel); + this.flowLayoutBottom.Controls.Add(this.checkBoxIsRoot); + this.flowLayoutBottom.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutBottom.Location = new System.Drawing.Point(3, 68); + this.flowLayoutBottom.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); + this.flowLayoutBottom.Name = "flowLayoutBottom"; + this.flowLayoutBottom.Size = new System.Drawing.Size(797, 66); + this.flowLayoutBottom.TabIndex = 0; + // + // buttonOk + // + this.buttonOk.AutoSize = true; + this.buttonOk.Image = global::UI_WinForms.Resources.Icons.icons8_ok_512; + this.buttonOk.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonOk.Location = new System.Drawing.Point(3, 3); + this.buttonOk.Name = "buttonOk"; + this.buttonOk.Size = new System.Drawing.Size(114, 60); + this.buttonOk.TabIndex = 1; + this.buttonOk.Text = "Ok"; + this.buttonOk.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.buttonOk.UseVisualStyleBackColor = true; + this.buttonOk.Click += new System.EventHandler(this.buttonOk_Click); + // + // buttonCancel + // + this.buttonCancel.AutoSize = true; + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Image = global::UI_WinForms.Resources.Icons.icons8_cancel_512; + this.buttonCancel.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonCancel.Location = new System.Drawing.Point(123, 3); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(124, 60); + this.buttonCancel.TabIndex = 2; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.buttonCancel.UseVisualStyleBackColor = true; + // + // textBoxInput + // + this.textBoxInput.Dock = System.Windows.Forms.DockStyle.Fill; + this.textBoxInput.Location = new System.Drawing.Point(6, 31); + this.textBoxInput.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); + this.textBoxInput.Name = "textBoxInput"; + this.textBoxInput.Size = new System.Drawing.Size(791, 34); + this.textBoxInput.TabIndex = 1; + this.textBoxInput.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxInput_KeyUp); + // + // checkBoxIsRoot + // + this.checkBoxIsRoot.AutoSize = true; + this.checkBoxIsRoot.Dock = System.Windows.Forms.DockStyle.Left; + this.checkBoxIsRoot.Location = new System.Drawing.Point(253, 3); + this.checkBoxIsRoot.Name = "checkBoxIsRoot"; + this.checkBoxIsRoot.Size = new System.Drawing.Size(458, 60); + this.checkBoxIsRoot.TabIndex = 3; + this.checkBoxIsRoot.Text = "Add a root node (i.e. ignoring the selected node)"; + this.checkBoxIsRoot.UseVisualStyleBackColor = true; + // + // InputDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.AutoSize = true; + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(800, 139); + this.Controls.Add(this.tableLayout); + this.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "InputDialog"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "InputDialog"; + this.tableLayout.ResumeLayout(false); + this.tableLayout.PerformLayout(); + this.flowLayoutBottom.ResumeLayout(false); + this.flowLayoutBottom.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private TableLayoutPanel tableLayout; + private Label labelHead; + private FlowLayoutPanel flowLayoutBottom; + private TextBox textBoxInput; + private Button buttonOk; + private Button buttonCancel; + private CheckBox checkBoxIsRoot; + } +} \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Dialogs/InputDialog.cs b/I18N Commander/UI WinForms/Dialogs/InputDialog.cs new file mode 100644 index 0000000..bce00a3 --- /dev/null +++ b/I18N Commander/UI WinForms/Dialogs/InputDialog.cs @@ -0,0 +1,39 @@ +namespace UI_WinForms.Dialogs; + +public partial class InputDialog : Form +{ + private InputDialog() + { + this.InitializeComponent(); + } + + public static InputResult Show(string message, string title, string placeholder = "", string preloadedText = "", string okButtonText = "Ok", string cancelButtonText = "Cancel") + { + using var inputDialog = new InputDialog(); + inputDialog.labelHead.Text = message; + inputDialog.Text = title; + inputDialog.textBoxInput.PlaceholderText = placeholder; + inputDialog.textBoxInput.Text = preloadedText; + inputDialog.buttonOk.Text = okButtonText; + inputDialog.buttonCancel.Text = cancelButtonText; + + return new InputResult(inputDialog.ShowDialog(), inputDialog.textBoxInput.Text, inputDialog.checkBoxIsRoot.Checked); + } + + private void textBoxInput_KeyUp(object sender, KeyEventArgs e) + { + if (e.KeyCode is Keys.Enter or Keys.Return) + this.buttonOk.PerformClick(); + } + + private void buttonOk_Click(object sender, EventArgs e) + { + if (!string.IsNullOrWhiteSpace(this.textBoxInput.Text)) + { + this.DialogResult = DialogResult.OK; + this.Close(); + } + } + + public readonly record struct InputResult(DialogResult DialogResult, string Text, bool IsRoot); +} \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Dialogs/InputDialog.resx b/I18N Commander/UI WinForms/Dialogs/InputDialog.resx new file mode 100644 index 0000000..b5ae26c --- /dev/null +++ b/I18N Commander/UI WinForms/Dialogs/InputDialog.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file From 2f77db1a1652fb2a72f03201fa39b3a01fe77a68 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:07:34 +0200 Subject: [PATCH 11/25] Added the selected data file --- I18N Commander/UI WinForms/Loader.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/I18N Commander/UI WinForms/Loader.cs b/I18N Commander/UI WinForms/Loader.cs index b18c284..4f525ca 100644 --- a/I18N Commander/UI WinForms/Loader.cs +++ b/I18N Commander/UI WinForms/Loader.cs @@ -2,6 +2,8 @@ public partial class Loader : Form { + public string DataFile { get; set; } = string.Empty; + public Loader() { this.InitializeComponent(); @@ -9,7 +11,7 @@ public partial class Loader : Form private void loaderStart_LoadProject(object sender, string projectFilePath) { - // TODO: Call the data model + this.DataFile = projectFilePath; this.Close(); } } \ No newline at end of file From 85431a7ec39314d3d1873d6966b870886cd2060d Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:07:51 +0200 Subject: [PATCH 12/25] Changed to use the main user component --- I18N Commander/UI WinForms/Main.Designer.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/I18N Commander/UI WinForms/Main.Designer.cs b/I18N Commander/UI WinForms/Main.Designer.cs index f8b002f..b15a91a 100644 --- a/I18N Commander/UI WinForms/Main.Designer.cs +++ b/I18N Commander/UI WinForms/Main.Designer.cs @@ -28,13 +28,24 @@ ///
private void InitializeComponent() { + this.mainComponent = new UI_WinForms.Components.Main(); this.SuspendLayout(); // + // mainComponent + // + this.mainComponent.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainComponent.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.mainComponent.Location = new System.Drawing.Point(0, 0); + this.mainComponent.Name = "mainComponent"; + this.mainComponent.Size = new System.Drawing.Size(1071, 755); + this.mainComponent.TabIndex = 0; + // // Main // this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(800, 450); + this.ClientSize = new System.Drawing.Size(1071, 755); + this.Controls.Add(this.mainComponent); this.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.Name = "Main"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; @@ -44,5 +55,7 @@ } #endregion + + private Components.Main mainComponent; } } \ No newline at end of file From 91fabb658ddab6d3f1cb4880153e46b5e2831feb Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:08:25 +0200 Subject: [PATCH 13/25] Implement the change between loader & main screen while using the DI --- I18N Commander/UI WinForms/Program.cs | 50 +++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/I18N Commander/UI WinForms/Program.cs b/I18N Commander/UI WinForms/Program.cs index 50c87bb..fbb744a 100644 --- a/I18N Commander/UI WinForms/Program.cs +++ b/I18N Commander/UI WinForms/Program.cs @@ -1,17 +1,61 @@ +using DataModel; +using DataModel.Database.Common; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + namespace UI_WinForms; internal static class Program { + internal const string VERSION = "v0.1.0"; + internal static IServiceProvider SERVICE_PROVIDER; + + [STAThread] private static void Main() { ApplicationConfiguration.Initialize(); - + Application.EnableVisualStyles(); + // Start the loader screen: var loader = new Loader(); Application.Run(loader); - // Start the main app: - Application.Run(new Main()); + // + // Create the DI system + // + var builder = new HostBuilder(); + + // + // Add services + // + builder.ConfigureServices((hostContext, serviceCollection) => + { + // The main form: + serviceCollection.AddSingleton
(); + + // The database: + serviceCollection.AddDatabase(loader.DataFile, true); + }); + + // Get the host out of the DI system: + var host = builder.Build(); + + // Create a service scope: + using (var scope = host.Services.CreateScope()) + { + // Get a service provider: + SERVICE_PROVIDER = scope.ServiceProvider; + + // Apply database migrations: + using (var database = SERVICE_PROVIDER.GetRequiredService()) + Setup.PerformDataMigration(database).Wait(); + + // Create the main window: + var mainWindow = SERVICE_PROVIDER.GetService
(); + + // Start the app: + Application.Run(mainWindow); + } } } \ No newline at end of file From 97b7f823c8225a549426faee9a9aec719394ea46 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:29:22 +0200 Subject: [PATCH 14/25] Refactored input component to handle checkbox optionally closes #15 --- .../UI WinForms/Components/SectionTree.cs | 15 ++++++-- .../Dialogs/InputDialog.Designer.cs | 21 ++++++----- .../UI WinForms/Dialogs/InputDialog.cs | 35 ++++++++++++++----- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs index 5858830..81c0df4 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.cs @@ -110,15 +110,24 @@ public partial class SectionTree : UserControl private async void buttonAdd_Click(object sender, EventArgs e) { - var result = InputDialog.Show("Please type the desired section name.", "Add a section", "My next section"); + var result = InputDialog.Show(new InputDialog.Options( + Message: "Please type the desired section name.", + Title: "Add a section", + Placeholder: "My next section", + ShowQuestionCheckbox: true, + QuestionCheckboxText: "Add a root node (i.e. ignoring the selected node)" + )); + if(result.DialogResult == DialogResult.Cancel) return; + + var addRootNode = result.AnswerToQuestion; // Get the currently selected section as parent: var selectedNode = this.treeView.SelectedNode; // Add the new section to the database: - var addedSection = await SectionProcessor.AddSection(this.db, result.Text, result.IsRoot ? null : selectedNode?.Name); + var addedSection = await SectionProcessor.AddSection(this.db, result.Text, addRootNode ? null : selectedNode?.Name); // Add the new section to the tree control: var node = new TreeNode @@ -128,7 +137,7 @@ public partial class SectionTree : UserControl StateImageIndex = 1, }; - if(!result.IsRoot && selectedNode is not null) + if(!addRootNode && selectedNode is not null) selectedNode.Nodes.Add(node); else this.treeView.Nodes.Add(node); diff --git a/I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs b/I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs index f1b97b3..7426653 100644 --- a/I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs +++ b/I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs @@ -34,7 +34,7 @@ this.buttonOk = new System.Windows.Forms.Button(); this.buttonCancel = new System.Windows.Forms.Button(); this.textBoxInput = new System.Windows.Forms.TextBox(); - this.checkBoxIsRoot = new System.Windows.Forms.CheckBox(); + this.checkBoxQuestion = new System.Windows.Forms.CheckBox(); this.tableLayout.SuspendLayout(); this.flowLayoutBottom.SuspendLayout(); this.SuspendLayout(); @@ -72,7 +72,7 @@ // this.flowLayoutBottom.Controls.Add(this.buttonOk); this.flowLayoutBottom.Controls.Add(this.buttonCancel); - this.flowLayoutBottom.Controls.Add(this.checkBoxIsRoot); + this.flowLayoutBottom.Controls.Add(this.checkBoxQuestion); this.flowLayoutBottom.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutBottom.Location = new System.Drawing.Point(3, 68); this.flowLayoutBottom.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); @@ -120,14 +120,13 @@ // // checkBoxIsRoot // - this.checkBoxIsRoot.AutoSize = true; - this.checkBoxIsRoot.Dock = System.Windows.Forms.DockStyle.Left; - this.checkBoxIsRoot.Location = new System.Drawing.Point(253, 3); - this.checkBoxIsRoot.Name = "checkBoxIsRoot"; - this.checkBoxIsRoot.Size = new System.Drawing.Size(458, 60); - this.checkBoxIsRoot.TabIndex = 3; - this.checkBoxIsRoot.Text = "Add a root node (i.e. ignoring the selected node)"; - this.checkBoxIsRoot.UseVisualStyleBackColor = true; + this.checkBoxQuestion.AutoSize = true; + this.checkBoxQuestion.Dock = System.Windows.Forms.DockStyle.Left; + this.checkBoxQuestion.Location = new System.Drawing.Point(253, 3); + this.checkBoxQuestion.Name = "checkBoxQuestion"; + this.checkBoxQuestion.Size = new System.Drawing.Size(458, 60); + this.checkBoxQuestion.TabIndex = 3; + this.checkBoxQuestion.UseVisualStyleBackColor = true; // // InputDialog // @@ -161,6 +160,6 @@ private TextBox textBoxInput; private Button buttonOk; private Button buttonCancel; - private CheckBox checkBoxIsRoot; + private CheckBox checkBoxQuestion; } } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Dialogs/InputDialog.cs b/I18N Commander/UI WinForms/Dialogs/InputDialog.cs index bce00a3..55f19f0 100644 --- a/I18N Commander/UI WinForms/Dialogs/InputDialog.cs +++ b/I18N Commander/UI WinForms/Dialogs/InputDialog.cs @@ -2,22 +2,39 @@ public partial class InputDialog : Form { + public readonly record struct Options( + string Message, + string Title, + string Placeholder = "", + string PreloadedText = "", + string OkButtonText = "Ok", + string CancelButtonText = "Cancel", + bool ShowQuestionCheckbox = false, + string QuestionCheckboxText = "" + ); + private InputDialog() { this.InitializeComponent(); } - public static InputResult Show(string message, string title, string placeholder = "", string preloadedText = "", string okButtonText = "Ok", string cancelButtonText = "Cancel") + public static InputResult Show(Options options) { using var inputDialog = new InputDialog(); - inputDialog.labelHead.Text = message; - inputDialog.Text = title; - inputDialog.textBoxInput.PlaceholderText = placeholder; - inputDialog.textBoxInput.Text = preloadedText; - inputDialog.buttonOk.Text = okButtonText; - inputDialog.buttonCancel.Text = cancelButtonText; + inputDialog.labelHead.Text = options.Message; + inputDialog.Text = options.Title; + inputDialog.textBoxInput.PlaceholderText = options.Placeholder; + inputDialog.textBoxInput.Text = options.PreloadedText; + inputDialog.buttonOk.Text = options.OkButtonText; + inputDialog.buttonCancel.Text = options.CancelButtonText; + inputDialog.checkBoxQuestion.Visible = options.ShowQuestionCheckbox; + inputDialog.checkBoxQuestion.Text = options.QuestionCheckboxText; - return new InputResult(inputDialog.ShowDialog(), inputDialog.textBoxInput.Text, inputDialog.checkBoxIsRoot.Checked); + return new InputResult( + inputDialog.ShowDialog(), + inputDialog.textBoxInput.Text, + inputDialog.checkBoxQuestion.Checked + ); } private void textBoxInput_KeyUp(object sender, KeyEventArgs e) @@ -35,5 +52,5 @@ public partial class InputDialog : Form } } - public readonly record struct InputResult(DialogResult DialogResult, string Text, bool IsRoot); + public readonly record struct InputResult(DialogResult DialogResult, string Text, bool AnswerToQuestion); } \ No newline at end of file From e45f7df622a11a380bd1572f3002f28457831131 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:46:19 +0200 Subject: [PATCH 15/25] Ensure to not duplicate the latest project in history Closes #20 --- I18N Commander/UI WinForms/Components/LoaderStart.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/I18N Commander/UI WinForms/Components/LoaderStart.cs b/I18N Commander/UI WinForms/Components/LoaderStart.cs index ad55a5f..00726f7 100644 --- a/I18N Commander/UI WinForms/Components/LoaderStart.cs +++ b/I18N Commander/UI WinForms/Components/LoaderStart.cs @@ -43,7 +43,15 @@ public partial class LoaderStart : UserControl private static void UpdateRecentProjectsWithPruning(string latestUsedProjectPath) { var previousRecentList = LoaderStart.RecentProjects; + + // Check, if the latest project is identical to the next project we open. In that case, we don't add it to the list. + if (previousRecentList.Any() && previousRecentList[0] == latestUsedProjectPath) + return; + + // Add the next project to the list: previousRecentList.Insert(0, latestUsedProjectPath); + + // Prune the list: if (previousRecentList.Count > 6) previousRecentList = previousRecentList.Take(6).ToList(); From ad8a08f5dafd6d783af07a984fcc5d2a99e0a0ec Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 15:54:33 +0200 Subject: [PATCH 16/25] Ensures, that the text box of input component is selected Closes #22 --- I18N Commander/UI WinForms/Dialogs/InputDialog.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/I18N Commander/UI WinForms/Dialogs/InputDialog.cs b/I18N Commander/UI WinForms/Dialogs/InputDialog.cs index 55f19f0..25afee0 100644 --- a/I18N Commander/UI WinForms/Dialogs/InputDialog.cs +++ b/I18N Commander/UI WinForms/Dialogs/InputDialog.cs @@ -29,6 +29,9 @@ public partial class InputDialog : Form inputDialog.buttonCancel.Text = options.CancelButtonText; inputDialog.checkBoxQuestion.Visible = options.ShowQuestionCheckbox; inputDialog.checkBoxQuestion.Text = options.QuestionCheckboxText; + + // Ensure, that the text box is focused on load: + inputDialog.textBoxInput.Select(); return new InputResult( inputDialog.ShowDialog(), From eac5fa3268620d2a69d5421f190d665269505c3f Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 16:00:23 +0200 Subject: [PATCH 17/25] Fixed app starting, when user just closes the loading screen Closes #21 --- I18N Commander/UI WinForms/Components/LoaderStart.cs | 4 ++-- I18N Commander/UI WinForms/Loader.cs | 1 + I18N Commander/UI WinForms/Program.cs | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/I18N Commander/UI WinForms/Components/LoaderStart.cs b/I18N Commander/UI WinForms/Components/LoaderStart.cs index 00726f7..ebc99d2 100644 --- a/I18N Commander/UI WinForms/Components/LoaderStart.cs +++ b/I18N Commander/UI WinForms/Components/LoaderStart.cs @@ -13,10 +13,10 @@ public partial class LoaderStart : UserControl this.InitializeComponent(); } - private static RegistryKey I18NCommanderKey => Registry.CurrentUser.OpenSubKey("Software", RegistryKeyPermissionCheck.ReadWriteSubTree)!.CreateSubKey("I18N Commander", RegistryKeyPermissionCheck.ReadWriteSubTree); - #region Recent Projects + private static RegistryKey I18NCommanderKey => Registry.CurrentUser.OpenSubKey("Software", RegistryKeyPermissionCheck.ReadWriteSubTree)!.CreateSubKey("I18N Commander", RegistryKeyPermissionCheck.ReadWriteSubTree); + /// /// Gets or sets the list of recent projects without any extras e.g. it does not prune the list of projects. /// diff --git a/I18N Commander/UI WinForms/Loader.cs b/I18N Commander/UI WinForms/Loader.cs index 4f525ca..38065c7 100644 --- a/I18N Commander/UI WinForms/Loader.cs +++ b/I18N Commander/UI WinForms/Loader.cs @@ -12,6 +12,7 @@ public partial class Loader : Form private void loaderStart_LoadProject(object sender, string projectFilePath) { this.DataFile = projectFilePath; + this.DialogResult = DialogResult.OK; this.Close(); } } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Program.cs b/I18N Commander/UI WinForms/Program.cs index fbb744a..4f3d673 100644 --- a/I18N Commander/UI WinForms/Program.cs +++ b/I18N Commander/UI WinForms/Program.cs @@ -21,6 +21,10 @@ internal static class Program var loader = new Loader(); Application.Run(loader); + // Check, if the user closes the loader screen: + if (loader.DialogResult != DialogResult.OK) + return; + // // Create the DI system // From ff87378012f482a7513626b8bfefb2935b4599b5 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 16:16:06 +0200 Subject: [PATCH 18/25] Fixes wired & wrong parent directory for history list Closes #19 --- I18N Commander/UI WinForms/Components/LoaderStart.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/I18N Commander/UI WinForms/Components/LoaderStart.cs b/I18N Commander/UI WinForms/Components/LoaderStart.cs index ebc99d2..40f614f 100644 --- a/I18N Commander/UI WinForms/Components/LoaderStart.cs +++ b/I18N Commander/UI WinForms/Components/LoaderStart.cs @@ -75,7 +75,12 @@ public partial class LoaderStart : UserControl foreach (var recentProject in recentProjects) { var fileInfo = new FileInfo(recentProject); - var item = this.contextMenuRecentProjects.Items.Add($"{fileInfo.Directory.GetDirectories().Last()}/{fileInfo.Name}", Resources.Icons.icons8_document_512, (innerSender, args) => this.OpenRecentProject(innerSender)); + + // Split the file's path into each folder's name: + var folderNames = fileInfo.DirectoryName!.Split(Path.DirectorySeparatorChar); + + // Render this entry: + var item = this.contextMenuRecentProjects.Items.Add($"{folderNames.Last()}: {fileInfo.Name}", Resources.Icons.icons8_document_512, (innerSender, args) => this.OpenRecentProject(innerSender)); item.Tag = recentProject; } From 1d7e1db8e514a6e414592a27436aed1c982a0b20 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 21:02:08 +0200 Subject: [PATCH 19/25] Fixed creating new project over an existing one doesn't replaces the old one Closes #16 --- I18N Commander/UI WinForms/Components/LoaderStart.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/I18N Commander/UI WinForms/Components/LoaderStart.cs b/I18N Commander/UI WinForms/Components/LoaderStart.cs index 40f614f..052fe29 100644 --- a/I18N Commander/UI WinForms/Components/LoaderStart.cs +++ b/I18N Commander/UI WinForms/Components/LoaderStart.cs @@ -110,6 +110,12 @@ public partial class LoaderStart : UserControl return; var destinationFilePath = saveDialog.FileName; + + // When the user chose an existing file, we delete it: + // (note: the user already accepts overwriting the file) + if (File.Exists(destinationFilePath)) + File.Delete(destinationFilePath); + LoaderStart.UpdateRecentProjectsWithPruning(destinationFilePath); this.OpenProject(destinationFilePath); } From aa34f541cf48a7ed738a291c079c7f3948068b6b Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 21:14:24 +0200 Subject: [PATCH 20/25] Fixed duplicated key issue Closes #18 --- I18N Commander/Processor/SectionProcessor.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/I18N Commander/Processor/SectionProcessor.cs b/I18N Commander/Processor/SectionProcessor.cs index dfd064e..6f9b197 100644 --- a/I18N Commander/Processor/SectionProcessor.cs +++ b/I18N Commander/Processor/SectionProcessor.cs @@ -34,6 +34,17 @@ public static class SectionProcessor { // Remove any whitespaces from the section name, regardless of how many e.g. spaces the user typed: var key = string.Join('_', text.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)).ToUpperInvariant(); + + // Check, if this key already exists: + if (await db.Sections.AnyAsync(n => n.DataKey == key)) + { + var rng = new Random(); + while (await db.Sections.AnyAsync(n => n.DataKey == key)) + { + // Add a random number to the end of the key: + key += $"_{rng.Next(1, 10_000)}"; + } + } // In the case, when the user adds a section to the root, handle the insert differently: if (string.IsNullOrEmpty(parentKey)) From e8711de0d607c39028060bb39dbabdb45b35bf0d Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 21:14:57 +0200 Subject: [PATCH 21/25] Fixed order of items on layer to be the same as created --- I18N Commander/Processor/SectionProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I18N Commander/Processor/SectionProcessor.cs b/I18N Commander/Processor/SectionProcessor.cs index 6f9b197..bb8f74f 100644 --- a/I18N Commander/Processor/SectionProcessor.cs +++ b/I18N Commander/Processor/SectionProcessor.cs @@ -11,7 +11,7 @@ public static class SectionProcessor /// public static IAsyncEnumerable
LoadLayer(DataContext db, int depth) { - return db.Sections.Where(n => n.Depth == depth).OrderBy(n => n.DataKey).AsAsyncEnumerable(); + return db.Sections.Where(n => n.Depth == depth).OrderBy(n => n.Id).AsAsyncEnumerable(); } /// From 271ae2a1cf04a6b7f422f76ce7a6d5745002ad82 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 21:24:09 +0200 Subject: [PATCH 22/25] Implemented the deletion function Closes #17 --- I18N Commander/Processor/SectionProcessor.cs | 26 +++++++++++++ .../Components/SectionTree.Designer.cs | 2 + .../UI WinForms/Components/SectionTree.cs | 37 ++++++++++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/I18N Commander/Processor/SectionProcessor.cs b/I18N Commander/Processor/SectionProcessor.cs index bb8f74f..be52f29 100644 --- a/I18N Commander/Processor/SectionProcessor.cs +++ b/I18N Commander/Processor/SectionProcessor.cs @@ -82,4 +82,30 @@ public static class SectionProcessor await db.SaveChangesAsync(); return section; } + + public static async Task RemoveSection(DataContext db, string selectedKey) + { + // Remove the section from the database: + var section2Delete = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == selectedKey); + if (section2Delete is null) + throw new ArgumentException($"The section with key {selectedKey} does not exist in the database."); + + // Next, remove all children of the section, and the children's children, etc.: + var children = await db.Sections.Where(n => n.Parent == section2Delete).ToListAsync(); + foreach (var child in children) + await RemoveSection(db, child.DataKey); + + db.Sections.Remove(section2Delete); + await db.SaveChangesAsync(); + } + + public static async Task NumberChildren(DataContext db, string selectedKey) + { + // Read the section from the database: + var section = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == selectedKey); + if (section is null) + throw new ArgumentException($"The section with key {selectedKey} does not exist in the database."); + + return await db.Sections.CountAsync(n => n.Parent == section); + } } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs b/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs index 9229662..9cf2bb7 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs @@ -91,6 +91,7 @@ this.buttonRemove.Text = "Remove"; this.buttonRemove.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; this.buttonRemove.UseVisualStyleBackColor = true; + this.buttonRemove.Click += new System.EventHandler(this.buttonRemove_Click); // // treeView // @@ -102,6 +103,7 @@ this.treeView.Name = "treeView"; this.treeView.Size = new System.Drawing.Size(290, 439); this.treeView.TabIndex = 1; + this.treeView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.treeView_NodeMouseClick); // // SectionTree // diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs index 81c0df4..85308b7 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.cs @@ -31,10 +31,10 @@ public partial class SectionTree : UserControl this.treeView.ImageList = imgList; // Subscribe to the load event: - this.Load += this.OnLoad; + this.Load += this.LoadNodes; } - private async void OnLoad(object? sender, EventArgs e) + private async void LoadNodes(object? sender, EventArgs e) { // A dictionary to cache all known tree nodes: var treeNodes = new Dictionary(); @@ -146,4 +146,37 @@ public partial class SectionTree : UserControl node.EnsureVisible(); this.treeView.SelectedNode = node; } + + private async void buttonRemove_Click(object sender, EventArgs e) + { + // Get the currently selected section, which will be removed: + var selectedNode = this.treeView.SelectedNode; + + // Get the number of children: + // (notice, that the node's name is its key) + var numberChildren = await SectionProcessor.NumberChildren(this.db, selectedNode.Name); + + // Ask the user, if he really wants to remove the section: + if(MessageBox.Show(numberChildren > 0 ? $"Are you sure, you want to remove the section '{selectedNode.Text}', its {numberChildren} children and so on?" : $"Are you sure, you want to remove the section '{selectedNode.Text}'?", "Remove section", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) + return; + + // Remove the section from the database: + // (notice, that the node's name is its key) + await SectionProcessor.RemoveSection(this.db, selectedNode.Name); + + // Remove all nodes from the tree control: + this.treeView.Nodes.Clear(); + + // Reload the tree: + this.LoadNodes(this, EventArgs.Empty); + } + + private void treeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) + { + // Get the currently selected section: + var selectedNode = this.treeView.SelectedNode; + + // If the selected node is not null, enable the remove button: + this.buttonRemove.Enabled = selectedNode is not null; + } } \ No newline at end of file From 57a7568dbb00311a4ed57c3d83170c05dd422e89 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 22:23:10 +0200 Subject: [PATCH 23/25] Change to expand the tree --- I18N Commander/UI WinForms/Components/SectionTree.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs index 85308b7..1ecf74a 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.cs @@ -1,5 +1,4 @@ -using DataModel.Database; -using DataModel.Database.Common; +using DataModel.Database.Common; using Microsoft.Extensions.DependencyInjection; using Processor; using UI_WinForms.Dialogs; @@ -106,6 +105,9 @@ public partial class SectionTree : UserControl foreach (var node in missingParents) rootMissedParents.Nodes.Add(node); } + + // Expand the tree: + this.treeView.ExpandAll(); } private async void buttonAdd_Click(object sender, EventArgs e) From d0b466430aeffdfbaf699135c8bd7404cb41bea5 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 22:23:55 +0200 Subject: [PATCH 24/25] Formatting --- I18N Commander/UI WinForms/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/I18N Commander/UI WinForms/Program.cs b/I18N Commander/UI WinForms/Program.cs index 4f3d673..ca12ea2 100644 --- a/I18N Commander/UI WinForms/Program.cs +++ b/I18N Commander/UI WinForms/Program.cs @@ -9,7 +9,6 @@ internal static class Program { internal const string VERSION = "v0.1.0"; internal static IServiceProvider SERVICE_PROVIDER; - [STAThread] private static void Main() From a22a5328f50b72354637b6405ae122b11f53ed0f Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 9 Jul 2022 22:25:18 +0200 Subject: [PATCH 25/25] Ignore not initialized warning --- I18N Commander/UI WinForms/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I18N Commander/UI WinForms/Program.cs b/I18N Commander/UI WinForms/Program.cs index ca12ea2..5c21401 100644 --- a/I18N Commander/UI WinForms/Program.cs +++ b/I18N Commander/UI WinForms/Program.cs @@ -8,7 +8,7 @@ namespace UI_WinForms; internal static class Program { internal const string VERSION = "v0.1.0"; - internal static IServiceProvider SERVICE_PROVIDER; + internal static IServiceProvider SERVICE_PROVIDER = null!; [STAThread] private static void Main()