Merge branch '10-component-section-tree' into 'main'
Resolve "Component: Section Tree" Closes #10, #17, #18, #16, #19, #21, #22, #20, and #15 See merge request open-source/dotnet/i18n-commander!5
This commit is contained in:
commit
34fb693d6b
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="test.i18nc" uuid="85bb5f37-723c-416e-bc82-e7c22635d5b1">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/../../../test.i18nc</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
@ -4,13 +4,13 @@ namespace DataModel.Database.Common;
|
||||
|
||||
public sealed class DataContext : DbContext
|
||||
{
|
||||
public DbSet<Setting>? Settings { get; set; }
|
||||
public DbSet<Setting> Settings { get; set; }
|
||||
|
||||
public DbSet<Section>? Sections { get; set; }
|
||||
public DbSet<Section> Sections { get; set; }
|
||||
|
||||
public DbSet<TextElement>? TextElements { get; set; }
|
||||
public DbSet<TextElement> TextElements { get; set; }
|
||||
|
||||
public DbSet<Translation>? Translations { get; set; }
|
||||
public DbSet<Translation> Translations { get; set; }
|
||||
|
||||
public DataContext(DbContextOptions<DataContext> contextOptions) : base(contextOptions)
|
||||
{
|
||||
@ -35,6 +35,8 @@ public sealed class DataContext : DbContext
|
||||
|
||||
modelBuilder.Entity<Section>().HasIndex(n => n.Id);
|
||||
modelBuilder.Entity<Section>().HasIndex(n => n.Name);
|
||||
modelBuilder.Entity<Section>().HasIndex(n => n.Depth);
|
||||
modelBuilder.Entity<Section>().HasIndex(n => n.DataKey);
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -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<TextElement> TextElements { get; set; }
|
||||
public int Depth { get; set; } = 0;
|
||||
|
||||
public Section? Parent { get; set; }
|
||||
|
||||
public List<TextElement> TextElements { get; set; } = new();
|
||||
}
|
184
I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.Designer.cs
generated
Normal file
184
I18N Commander/DataModel/Migrations/20220626195157_202206MadeParentNullable.Designer.cs
generated
Normal file
@ -0,0 +1,184 @@
|
||||
// <auto-generated />
|
||||
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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("BoolValue")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("GuidValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("IntegerValue")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Culture")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("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
|
||||
}
|
||||
}
|
||||
}
|
@ -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<int>(
|
||||
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<int>(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
190
I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.Designer.cs
generated
Normal file
190
I18N Commander/DataModel/Migrations/20220709094035_202207AddSectionDataKey.Designer.cs
generated
Normal file
@ -0,0 +1,190 @@
|
||||
// <auto-generated />
|
||||
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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DataKey")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("BoolValue")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("GuidValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("IntegerValue")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Culture")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("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
|
||||
}
|
||||
}
|
||||
}
|
@ -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<string>(
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
195
I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.Designer.cs
generated
Normal file
195
I18N Commander/DataModel/Migrations/20220709095404_202207AddSectionDepth.Designer.cs
generated
Normal file
@ -0,0 +1,195 @@
|
||||
// <auto-generated />
|
||||
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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DataKey")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Depth")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("BoolValue")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("GuidValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("IntegerValue")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Culture")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("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
|
||||
}
|
||||
}
|
||||
}
|
@ -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<int>(
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
@ -23,15 +23,26 @@ namespace DataModel.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DataKey")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Depth")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ParentId")
|
||||
b.Property<int?>("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");
|
||||
});
|
||||
|
@ -30,7 +30,7 @@ public static class Setup
|
||||
/// </summary>
|
||||
public static void AddDatabase(this IServiceCollection serviceCollection, string path2DataFile, bool createWhenNecessary = true)
|
||||
{
|
||||
serviceCollection.AddDbContext<DataContext>(options => options.UseSqlite($"Filename={path2DataFile};Mode={(createWhenNecessary ? DB_READ_WRITE_CREATE_MODE : DB_READ_WRITE_MODE)}"));
|
||||
serviceCollection.AddDbContext<DataContext>(options => options.UseSqlite($"Filename={path2DataFile};Mode={(createWhenNecessary ? DB_READ_WRITE_CREATE_MODE : DB_READ_WRITE_MODE)}"), ServiceLifetime.Transient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
111
I18N Commander/Processor/SectionProcessor.cs
Normal file
111
I18N Commander/Processor/SectionProcessor.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using DataModel.Database;
|
||||
using DataModel.Database.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Processor;
|
||||
|
||||
public static class SectionProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Load one layer of the tree by using the specified depth:
|
||||
/// </summary>
|
||||
public static IAsyncEnumerable<Section> LoadLayer(DataContext db, int depth)
|
||||
{
|
||||
return db.Sections.Where(n => n.Depth == depth).OrderBy(n => n.Id).AsAsyncEnumerable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine how deep the tree is.
|
||||
/// </summary>
|
||||
public static async ValueTask<int> GetDepth(DataContext db)
|
||||
{
|
||||
if(!await db.Sections.AnyAsync())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return await db.Sections.MaxAsync(s => s.Depth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the new sections key and its depth, then store the section in the database.
|
||||
/// </summary>
|
||||
public static async Task<Section> 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();
|
||||
|
||||
// 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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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<int> 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of recent projects without any extras e.g. it does not prune the list of projects.
|
||||
/// </summary>
|
||||
@ -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();
|
||||
|
||||
@ -67,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;
|
||||
}
|
||||
|
||||
@ -97,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);
|
||||
}
|
||||
|
114
I18N Commander/UI WinForms/Components/Main.Designer.cs
generated
Normal file
114
I18N Commander/UI WinForms/Components/Main.Designer.cs
generated
Normal file
@ -0,0 +1,114 @@
|
||||
namespace UI_WinForms.Components
|
||||
{
|
||||
partial class Main
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
9
I18N Commander/UI WinForms/Components/Main.cs
Normal file
9
I18N Commander/UI WinForms/Components/Main.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace UI_WinForms.Components;
|
||||
|
||||
public partial class Main : UserControl
|
||||
{
|
||||
public Main()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
60
I18N Commander/UI WinForms/Components/Main.resx
Normal file
60
I18N Commander/UI WinForms/Components/Main.resx
Normal file
@ -0,0 +1,60 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
131
I18N Commander/UI WinForms/Components/SectionTree.Designer.cs
generated
Normal file
131
I18N Commander/UI WinForms/Components/SectionTree.Designer.cs
generated
Normal file
@ -0,0 +1,131 @@
|
||||
namespace UI_WinForms.Components
|
||||
{
|
||||
partial class SectionTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
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;
|
||||
this.buttonRemove.Click += new System.EventHandler(this.buttonRemove_Click);
|
||||
//
|
||||
// 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;
|
||||
this.treeView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.treeView_NodeMouseClick);
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
184
I18N Commander/UI WinForms/Components/SectionTree.cs
Normal file
184
I18N Commander/UI WinForms/Components/SectionTree.cs
Normal file
@ -0,0 +1,184 @@
|
||||
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<DataContext>()!;
|
||||
|
||||
// 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.LoadNodes;
|
||||
}
|
||||
|
||||
private async void LoadNodes(object? sender, EventArgs e)
|
||||
{
|
||||
// A dictionary to cache all known tree nodes:
|
||||
var treeNodes = new Dictionary<string, TreeNode>();
|
||||
|
||||
// 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<TreeNode>();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Expand the tree:
|
||||
this.treeView.ExpandAll();
|
||||
}
|
||||
|
||||
private async void buttonAdd_Click(object sender, EventArgs e)
|
||||
{
|
||||
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, addRootNode ? 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(!addRootNode && 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
60
I18N Commander/UI WinForms/Components/SectionTree.resx
Normal file
60
I18N Commander/UI WinForms/Components/SectionTree.resx
Normal file
@ -0,0 +1,60 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
165
I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs
generated
Normal file
165
I18N Commander/UI WinForms/Dialogs/InputDialog.Designer.cs
generated
Normal file
@ -0,0 +1,165 @@
|
||||
namespace UI_WinForms.Dialogs
|
||||
{
|
||||
partial class InputDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
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.checkBoxQuestion = 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.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);
|
||||
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.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
|
||||
//
|
||||
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 checkBoxQuestion;
|
||||
}
|
||||
}
|
59
I18N Commander/UI WinForms/Dialogs/InputDialog.cs
Normal file
59
I18N Commander/UI WinForms/Dialogs/InputDialog.cs
Normal file
@ -0,0 +1,59 @@
|
||||
namespace UI_WinForms.Dialogs;
|
||||
|
||||
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(Options options)
|
||||
{
|
||||
using var inputDialog = new InputDialog();
|
||||
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;
|
||||
|
||||
// Ensure, that the text box is focused on load:
|
||||
inputDialog.textBoxInput.Select();
|
||||
|
||||
return new InputResult(
|
||||
inputDialog.ShowDialog(),
|
||||
inputDialog.textBoxInput.Text,
|
||||
inputDialog.checkBoxQuestion.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 AnswerToQuestion);
|
||||
}
|
60
I18N Commander/UI WinForms/Dialogs/InputDialog.resx
Normal file
60
I18N Commander/UI WinForms/Dialogs/InputDialog.resx
Normal file
@ -0,0 +1,60 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
@ -2,6 +2,8 @@
|
||||
|
||||
public partial class Loader : Form
|
||||
{
|
||||
public string DataFile { get; set; } = string.Empty;
|
||||
|
||||
public Loader()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
@ -9,7 +11,8 @@ public partial class Loader : Form
|
||||
|
||||
private void loaderStart_LoadProject(object sender, string projectFilePath)
|
||||
{
|
||||
// TODO: Call the data model
|
||||
this.DataFile = projectFilePath;
|
||||
this.DialogResult = DialogResult.OK;
|
||||
this.Close();
|
||||
}
|
||||
}
|
15
I18N Commander/UI WinForms/Main.Designer.cs
generated
15
I18N Commander/UI WinForms/Main.Designer.cs
generated
@ -28,13 +28,24 @@
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,17 +1,64 @@
|
||||
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 = null!;
|
||||
|
||||
[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());
|
||||
// Check, if the user closes the loader screen:
|
||||
if (loader.DialogResult != DialogResult.OK)
|
||||
return;
|
||||
|
||||
//
|
||||
// Create the DI system
|
||||
//
|
||||
var builder = new HostBuilder();
|
||||
|
||||
//
|
||||
// Add services
|
||||
//
|
||||
builder.ConfigureServices((hostContext, serviceCollection) =>
|
||||
{
|
||||
// The main form:
|
||||
serviceCollection.AddSingleton<Main>();
|
||||
|
||||
// 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<DataContext>())
|
||||
Setup.PerformDataMigration(database).Wait();
|
||||
|
||||
// Create the main window:
|
||||
var mainWindow = SERVICE_PROVIDER.GetService<Main>();
|
||||
|
||||
// Start the app:
|
||||
Application.Run(mainWindow);
|
||||
}
|
||||
}
|
||||
}
|
@ -60,6 +60,16 @@ namespace UI_WinForms.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icons8_add_folder_512 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icons8_add_folder_512", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
@ -70,6 +80,26 @@ namespace UI_WinForms.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icons8_cancel_512 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icons8_cancel_512", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icons8_delete_folder_512 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icons8_delete_folder_512", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
@ -80,6 +110,16 @@ namespace UI_WinForms.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icons8_documents_folder_512 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icons8_documents_folder_512", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
@ -100,6 +140,16 @@ namespace UI_WinForms.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap icons8_ok_512 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("icons8_ok_512", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
@ -118,9 +118,21 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="icons8_add_folder_512" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>icons8-add-folder-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icons8_browse_folder_512" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>icons8-browse-folder-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icons8_cancel_512" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>icons8-cancel-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icons8_delete_folder_512" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>icons8-delete-folder-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icons8_documents_folder_512" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>icons8-documents-folder-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icons8_document_512" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>icons8-document-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
@ -130,6 +142,9 @@
|
||||
<data name="icons8_new_window_512" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>icons8-new-window-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icons8_ok_512" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>icons8-ok-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="icons8_open_file_under_cursor_512" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>icons8-open-file-under-cursor-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
|
BIN
I18N Commander/UI WinForms/Resources/icons8-add-folder-512.png
Normal file
BIN
I18N Commander/UI WinForms/Resources/icons8-add-folder-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
I18N Commander/UI WinForms/Resources/icons8-cancel-512.png
Normal file
BIN
I18N Commander/UI WinForms/Resources/icons8-cancel-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
BIN
I18N Commander/UI WinForms/Resources/icons8-ok-512.png
Normal file
BIN
I18N Commander/UI WinForms/Resources/icons8-ok-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
Loading…
Reference in New Issue
Block a user