56 Commits

Author SHA1 Message Date
fierke 9becedbd97 [chore:] better file- and folder creating 2026-04-27 09:04:16 +02:00
fierke 16982c3d95 [fix:] adding ".md" if not added by user (wiki) 2026-04-27 08:49:53 +02:00
fierke 93771dd110 [fix:] trying to fix one-item-too-much-issue 2026-04-27 08:49:27 +02:00
fierke 3e14731429 Merge pull request 'export-margin-options implementation' (#33) from export-margin-options into main
Reviewed-on: #33
2026-04-23 14:22:58 +00:00
fierke df6c187a00 [chore:] combining export margin options gui with settings and PdfBuilder.cs 2026-04-23 12:30:35 +02:00
fierke 98b5198f6f [chore:] added smallFontSize and fixed usage of fontSize and smallFontSize 2026-04-23 09:03:25 +02:00
fierke 1a6459d60b [chore:] grid visibility (calc man :D) 2026-04-23 09:03:00 +02:00
fierke 43e6c35beb [gui:] export margin options gui 2026-04-23 09:02:45 +02:00
fierke 9777c6b5a2 [fix:] naming window showed nothing 2026-04-22 08:09:28 +02:00
fierke 9ad378c800 [fix:] temporarily fiex application crash if no customer is selected when start combining (more than union, which was fixed before) 2026-04-16 13:29:59 +02:00
fierke 5ccd4a4e99 [fix:] csv-import now recognizes quotation marks 2026-04-16 12:49:45 +02:00
fierke ac7b23cc28 [fix:] temporarily fixed application crash if no customer is seleceted when starting 2026-04-16 12:49:10 +02:00
fierke 16ed64dbf4 [chore:] there was a space missing that triggered my monk 2026-04-16 11:44:10 +02:00
fierke cc48c0ae2c [chore:] separator option (it's better like that :D) 2026-03-19 14:39:54 +01:00
fierke ba640c602a [file:] better folder structuring 2026-03-19 14:34:36 +01:00
fierke e8738a2eab [fix:] now, they get unique id's when importing 2026-03-19 14:20:59 +01:00
Elias Fierke afbb4626a0 [feat:] implemented renaming functionality 2026-01-19 16:25:33 +01:00
Elias Fierke ee83aca490 [feat:] included context menu for Address-SetListBox 2026-01-19 16:25:20 +01:00
Elias Fierke b40ddfbf2e [fix:] NamingWindow.axaml had a wrong size 2026-01-19 16:17:06 +01:00
Elias Fierke 5172de332c [feat:] added "new file" button for wiki and therefore
implemented the usage of NamingWindow
2026-01-18 16:15:25 +01:00
Elias Fierke d478fd5129 [feat:] simple MessageBox-lik naming window 2026-01-18 16:14:37 +01:00
Elias Fierke 5dcb44aa2a [fix:] added default config- and wiki-paths 2026-01-18 16:13:40 +01:00
Elias Fierke ea31637bdb [feat:] implemented a basic editor for the wiki 2026-01-18 14:36:52 +01:00
Elias Fierke 2c909820d3 Merge remote-tracking branch 'origin/main' 2026-01-18 14:36:07 +01:00
Elias Fierke 96eb122ff8 [fix:] fixed an issue where new lines were rendered as "MarkDig.Blah.Sülz.LineBreakInline" 2026-01-18 14:35:59 +01:00
fierke fed817a6dc [fix:] corrected account information 2026-01-18 13:03:42 +00:00
fierke d5073465b2 [file:] added README 2026-01-18 11:39:49 +00:00
Elias Fierke 10b0eb5bcd [chore:] font resolver included + various changes 2026-01-18 11:38:16 +01:00
Elias Fierke 013bd4a070 [file:] added font-asset with example font 2026-01-18 11:36:41 +01:00
Elias Fierke 2c4eb1fcef [proj:] including assets/fonts to build directory 2026-01-18 11:35:15 +01:00
Elias Fierke 0395537a55 [init:] added StableFontResolver.cs 2026-01-18 09:20:13 +01:00
Elias Fierke 313cd58fc7 [feat:] filter-button to prevent live reloading 2026-01-15 11:35:08 +01:00
Elias Fierke 723722ba47 [chore:] introducing KasPerson.id (refsid remains but isn't used in the mgmt-backend anymore) 2026-01-15 11:21:08 +01:00
Elias Fierke 58964896ad [fix:] fixed creation of empty lines 2026-01-15 10:12:40 +01:00
Elias Fierke af1c3ff8cc [fix:] single address lines were too long 2026-01-14 11:47:54 +01:00
Elias Fierke 48852e4505 [chore:] removed unused methods 2026-01-14 11:35:37 +01:00
Elias Fierke 60cde86efe [fix:] one-line addresses were visible in pdf creation 2026-01-14 11:29:21 +01:00
Elias Fierke 67cfe10f5f [chore:] removed unused methods 2026-01-14 11:24:18 +01:00
Elias Fierke e186070f05 [chore:] ui consistency improvements 2026-01-13 21:05:41 +01:00
Elias Fierke d36314b724 [chore:] text change in EditorWindow.axaml 2026-01-13 20:48:27 +01:00
Elias Fierke beed5decbf [fix:] wrong ui implementation of the wiki-path-elements 2026-01-13 20:47:57 +01:00
Elias Fierke 71859fa978 [chore:] code cleanup by IDE 2026-01-13 20:47:19 +01:00
Elias Fierke f05470249a [chore:] code cleanup 2026-01-13 20:46:22 +01:00
Elias Fierke f4918aa8ee [chore:] removed ArticleStore (wanted to do it another way) 2026-01-13 20:45:49 +01:00
Elias Fierke 4e1f08883a [chore:] removed NamePromtWindow which was useless 2026-01-13 20:45:23 +01:00
fierke 99b35c0aaf [feat:] forgot to git-add the wiki source files... here they are 2026-01-13 18:33:35 +01:00
fierke aeb4092f28 [feat:] initial wiki service implementation (basic, no editing, wrong directory- and saving-logic) 2026-01-13 18:31:41 +01:00
fierke c760ef0936 [chore:] added refsid to pdf-creation 2026-01-13 18:30:58 +01:00
fierke e750b4c757 [chore:] initial article store implementation for Settings.cs 2026-01-13 18:30:12 +01:00
Elias Fierke 3877d91af4 [chore:] usage of Country (and possible import of ISO3166-Countries) 2026-01-02 18:02:08 +01:00
Elias Fierke 7af6444da2 [file:] imported ISO3166-dll from nuget 2026-01-02 16:50:15 +01:00
Elias Fierke 4cfbcd0ab4 [feat:] added Country implementation for country code editing (unused) 2026-01-01 14:45:57 +01:00
Elias Fierke b82473ada2 [chore:] PdfBuilder.cs skips empty addresses 2025-12-21 11:43:41 +01:00
Elias Fierke 1cba67253a [chore:] selectable pdf file path for label creation 2025-12-21 11:32:38 +01:00
Elias Fierke 63c1559110 [chore:] added "Postfach" to address creation 2025-12-21 11:25:48 +01:00
Elias Fierke b670ba11fa [chore:] sender address usage in PdfBuilder.cs 2025-12-15 10:17:10 +01:00
28 changed files with 2131 additions and 659 deletions
-60
View File
@@ -1,60 +0,0 @@
using System.Collections.Generic;
using System.Text;
namespace Logof_Client;
public class CsvBuilder
{
private readonly string Header;
private readonly List<object> Instances;
private readonly KasAddressList KasAddressList;
public CsvBuilder(string header, List<object> instances)
{
Header = header;
Instances = instances;
}
public CsvBuilder(string header, KasAddressList instances)
{
Header = header;
KasAddressList = instances;
}
public string? BuildKas()
{
var result = new StringBuilder();
result.AppendLine(Header);
foreach (var l in KasAddressList.KasPersons)
result.AppendLine(
l.refsid + "," +
l.anrede + "," +
l.titel + "," +
l.vorname + "," +
l.adel + "," +
l.name + "," +
l.namezus + "," +
l.anredzus + "," +
l.strasse + "," +
l.strasse2 + "," +
l.plz + "," +
l.ort + "," +
l.land + "," +
l.pplz + "," +
l.postfach + "," +
l.name1 + "," +
l.name2 + "," +
l.name3 + "," +
l.name4 + "," +
l.name5 + "," +
l.funktion + "," +
l.funktion2 + "," +
l.abteilung + "," +
l.funktionad);
// weitere Cases
return result.ToString();
}
}
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
namespace Logof_Client; namespace Logof_Client;
@@ -32,11 +33,16 @@ public class KasAddressList //Address-Set
this.owner_id = owner_id; this.owner_id = owner_id;
} }
public static string GenerateName(string basic_type, bool? is_rest = false) public static async Task<string> GenerateName(string basic_type, bool? is_rest = false)
{ {
string pre = "";
if (is_rest == true) if (is_rest == true)
return basic_type + " - " + DateTime.Now.ToShortDateString() + " - Rest"; return basic_type + " - " + DateTime.Now.ToShortDateString() + " - Rest";
return basic_type + " - " + DateTime.Now.ToShortDateString(); pre = basic_type + " - " + DateTime.Now.ToShortDateString();
var result = await NamingWindow.Show(MainWindow._instance, pre);
return string.IsNullOrWhiteSpace(result) ? pre : result;
} }
// public void UpdateErrorList(List<KasPersonError> errorList) // public void UpdateErrorList(List<KasPersonError> errorList)
@@ -62,6 +68,7 @@ public class KasPerson
public KasPerson() public KasPerson()
{ {
id = GenerateNewID(0);
refsid = 0; refsid = 0;
anrede = ""; anrede = "";
titel = ""; titel = "";
@@ -88,7 +95,7 @@ public class KasPerson
funktionad = ""; funktionad = "";
} }
public KasPerson(int refsid, public KasPerson(int id, int refsid,
string anrede, string anrede,
string titel, string titel,
string vorname, string vorname,
@@ -113,6 +120,7 @@ public class KasPerson
string abteilung, string abteilung,
string funktionad) string funktionad)
{ {
this.id = id;
this.refsid = refsid; this.refsid = refsid;
this.anrede = anrede; this.anrede = anrede;
this.titel = titel; this.titel = titel;
@@ -139,6 +147,7 @@ public class KasPerson
this.funktionad = funktionad; this.funktionad = funktionad;
} }
public int id { get; set; }
public int refsid { get; set; } public int refsid { get; set; }
public string anrede { get; set; } public string anrede { get; set; }
public string titel { get; set; } public string titel { get; set; }
@@ -163,6 +172,22 @@ public class KasPerson
public string funktion2 { get; set; } public string funktion2 { get; set; }
public string abteilung { get; set; } public string abteilung { get; set; }
public string funktionad { get; set; } public string funktionad { get; set; }
public static int GenerateNewID(int base_id)
{
//var newid = 100000 + base_id;
int highest = 0;
foreach (var set in Settings._instance.addressSets.addresses)
{
foreach (var add in set.KasPersons)
{
if(add.id >= highest) highest = add.id+1;
}
}
return highest + base_id + 1;
}
} }
public class KasPersonError public class KasPersonError
+66 -1
View File
@@ -10,6 +10,7 @@ public class Settings
public static Settings _instance = new(); public static Settings _instance = new();
public AddressSets addressSets = new(); public AddressSets addressSets = new();
public Customers customers = new(); public Customers customers = new();
public PdfExportSettings pdfExport { get; set; } = new();
public string settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), public string settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"logofclient", "config.json"); "logofclient", "config.json");
@@ -52,6 +53,25 @@ public class Settings
} }
} }
public class PdfExportSettings
{
public double cellPaddingTopMm { get; set; } = 5;
public double cellPaddingBottomMm { get; set; } = 5;
public double cellPaddingLeftMm { get; set; } = 5;
public double cellPaddingRightMm { get; set; } = 5;
public double pageMarginTopMm { get; set; } = 0;
public double pageMarginBottomMm { get; set; } = 0;
public double pageMarginLeftMm { get; set; } = 0;
public double pageMarginRightMm { get; set; } = 0;
public int rowsPerPage { get; set; } = 7;
public int columnsPerPage { get; set; } = 3;
public double fontSize { get; set; } = 9;
public double smallFontSize { get; set; } = 6;
}
public class Global public class Global
{ {
public static Global _instance; public static Global _instance;
@@ -61,7 +81,18 @@ public class Global
_instance = this; _instance = this;
} }
public string config_path { get; set; } = ""; public string config_path { get; set; } = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"logofclient",
"config.json");
public string wiki_storage_path { get; set; } = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"logofclient",
"wiki");
public List<Country> countries { get; set; } = new();
public string font_path { get; set; } = Path.Combine(AppContext.BaseDirectory, "assets", "fonts");
public static void Save() public static void Save()
{ {
@@ -147,3 +178,37 @@ public class AddressSets
return null; return null;
} }
} }
public class Country
{
public Country(string name, string translation, List<string> alternatives)
{
this.name = name;
this.translation = translation;
this.alternatives = alternatives;
}
public Country(string name)
{
this.name = name;
translation = "";
alternatives = new List<string>();
}
public Country()
{
}
public string? name { get; set; }
public string translation { get; set; }
public List<string> alternatives { get; set; }
public static Country GetByName(string name)
{
foreach (var country in Global._instance.countries)
if (country.name == name)
return country;
return null;
}
}
+20
View File
@@ -19,11 +19,23 @@
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets> <IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets> <PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="ISO3166" Version="1.0.4"/>
<PackageReference Include="Lucide.Avalonia" Version="0.1.35"/> <PackageReference Include="Lucide.Avalonia" Version="0.1.35"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4"/> <PackageReference Include="Newtonsoft.Json" Version="13.0.4"/>
<PackageReference Include="PdfSharp" Version="6.1.1"/> <PackageReference Include="PdfSharp" Version="6.1.1"/>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Markdig" Version="0.30.3"/>
<PackageReference Include="AvaloniaEdit" Version="0.10.12"/>
</ItemGroup>
<ItemGroup>
<None Include="wiki\**\*.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="assets\icon.ico"/> <None Remove="assets\icon.ico"/>
<AvaloniaResource Include="assets\icon.ico"> <AvaloniaResource Include="assets\icon.ico">
@@ -38,4 +50,12 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</AvaloniaResource> </AvaloniaResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="assets\fonts\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Folder Include="assets\fonts\"/>
</ItemGroup>
</Project> </Project>
+261 -40
View File
@@ -28,7 +28,7 @@
<Label FontSize="20" Content="Addressverwaltung" VerticalContentAlignment="Center" /> <Label FontSize="20" Content="Addressverwaltung" VerticalContentAlignment="Center" />
</StackPanel> </StackPanel>
</TabItem.Header> </TabItem.Header>
<Grid RowDefinitions="2*,*,*"> <Grid RowDefinitions="3*,80,2*">
<Grid ColumnDefinitions="*,*" Grid.Row="0"> <Grid ColumnDefinitions="*,*" Grid.Row="0">
<!-- Kunden --> <!-- Kunden -->
<Grid Margin="30,30,10,30"> <Grid Margin="30,30,10,30">
@@ -67,7 +67,28 @@
SelectionChanged="LstCustomerAdressSets_OnSelectionChanged" SelectionChanged="LstCustomerAdressSets_OnSelectionChanged"
Background="AliceBlue" Background="AliceBlue"
Margin="0,5,0,5" Margin="0,5,0,5"
SelectionMode="Multiple,Toggle" /> SelectionMode="Multiple,Toggle">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="MnIAdSetRename" Click="MnIAdSetRename_OnClick">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<LucideIcon Kind="Pencil" Width="12" Height="12" Size="12" />
<Label Content="Umbenennen" VerticalContentAlignment="Center" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem x:Name="MnIAdSetDelete" Click="MnIAdSetRename_OnClick" IsEnabled="False">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<LucideIcon Kind="Trash" Width="12" Height="12" Size="12" />
<Label Content="Löschen" VerticalContentAlignment="Center" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
<Button Grid.Row="2" HorizontalAlignment="Stretch" x:Name="BtnCustomerAddressSetImport" <Button Grid.Row="2" HorizontalAlignment="Stretch" x:Name="BtnCustomerAddressSetImport"
Click="BtnCustomerAddressSetImport_OnClick"> Click="BtnCustomerAddressSetImport_OnClick">
@@ -78,23 +99,10 @@
</Button> </Button>
</Grid> </Grid>
</Grid> </Grid>
<!-- <Grid Grid.Row="0"> --> <!-- <Grid ColumnDefinitions="*,*,*" Grid.Row="1"> -->
<!-- <StackPanel Orientation="Horizontal" VerticalAlignment="Center" --> <StackPanel Grid.Row="1" Orientation="Horizontal" Spacing="10" HorizontalAlignment="Center"
<!-- HorizontalAlignment="Center"> -->
<!-- <TextBox x:Name="TbFilename" Watermark="Dateipfad" Width="400" -->
<!-- VerticalContentAlignment="Center" /> -->
<!-- <Button x:Name="BtnChooseFile" Margin="10,0,0,0" Click="BtnChooseFile_OnClick"> -->
<!-- <StackPanel Orientation="Horizontal"> -->
<!-- <LucideIcon Kind="FolderOpen" Width="16" Height="16" Size="16" /> -->
<!-- <Label Content="Öffnen" VerticalContentAlignment="Center" /> -->
<!-- </StackPanel> -->
<!-- </Button> -->
<!-- </StackPanel> -->
<!-- </Grid> -->
<Grid Grid.ColumnDefinitions="*,*" Grid.Row="1">
<StackPanel Grid.Column="0" Width="250" Orientation="Vertical" HorizontalAlignment="Right"
Margin="0,0,5,0"> Margin="0,0,5,0">
<Button HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" <Button Width="250" HorizontalContentAlignment="Center"
Margin="0,0,0,10" IsEnabled="False" Margin="0,0,0,10" IsEnabled="False"
x:Name="BtnCheck" Click="BtnCheck_OnClick"> x:Name="BtnCheck" Click="BtnCheck_OnClick">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@@ -103,7 +111,7 @@
FontWeight="Bold" /> FontWeight="Bold" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button HorizontalAlignment="Stretch" IsEnabled="False" <Button Width="250" IsEnabled="False"
HorizontalContentAlignment="Center" HorizontalContentAlignment="Center"
Click="BtnCombine_OnClick" x:Name="BtnCombine" Click="BtnCombine_OnClick" x:Name="BtnCombine"
Margin="0,0,0,10"> Margin="0,0,0,10">
@@ -113,19 +121,7 @@
FontWeight="Bold" /> FontWeight="Bold" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button HorizontalAlignment="Stretch" IsEnabled="False" <Button Width="250" IsEnabled="False"
HorizontalContentAlignment="Center" x:Name="BtnRepair"
Margin="0,0,0,10">
<StackPanel Orientation="Horizontal">
<LucideIcon Kind="Hammer" Width="36" Height="36" />
<Label Content="Reparieren" VerticalContentAlignment="Center" FontSize="15"
FontWeight="Bold" />
</StackPanel>
</Button>
</StackPanel>
<StackPanel Grid.Column="1" Width="250" Orientation="Vertical" HorizontalAlignment="Left"
Margin="5,0,0,0">
<Button HorizontalAlignment="Stretch" IsEnabled="False"
HorizontalContentAlignment="Center" x:Name="BtnShorten" HorizontalContentAlignment="Center" x:Name="BtnShorten"
Margin="0,0,0,10"> Margin="0,0,0,10">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@@ -134,7 +130,7 @@
FontWeight="Bold" /> FontWeight="Bold" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button HorizontalAlignment="Stretch" IsEnabled="False" <Button Width="250" IsEnabled="False"
Click="BtnGenerateLabels_OnClick" Click="BtnGenerateLabels_OnClick"
HorizontalContentAlignment="Center" x:Name="BtnGenerateLabels" HorizontalContentAlignment="Center" x:Name="BtnGenerateLabels"
Margin="0,0,0,10"> Margin="0,0,0,10">
@@ -145,8 +141,17 @@
FontWeight="Bold" /> FontWeight="Bold" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button Width="250" IsEnabled="False"
HorizontalContentAlignment="Center" x:Name="BtnRepair"
Margin="0,0,0,10">
<StackPanel Orientation="Horizontal">
<LucideIcon Kind="Hammer" Width="36" Height="36" />
<Label Content="Reparieren" VerticalContentAlignment="Center" FontSize="15"
FontWeight="Bold" />
</StackPanel> </StackPanel>
</Grid> </Button>
</StackPanel>
<!-- </Grid> -->
<Grid Grid.Row="2" Margin="20" IsVisible="True" x:Name="GrdCalcMan"> <Grid Grid.Row="2" Margin="20" IsVisible="True" x:Name="GrdCalcMan">
<Image Source="assets/calc_man.png" VerticalAlignment="Center" HorizontalAlignment="Center" /> <Image Source="assets/calc_man.png" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid> </Grid>
@@ -221,6 +226,71 @@
<CheckBox HorizontalAlignment="Center" x:Name="CbMergeExportUnmerged" IsChecked="False">Speichere Unverarbeitete in neuem Verteiler</CheckBox> <CheckBox HorizontalAlignment="Center" x:Name="CbMergeExportUnmerged" IsChecked="False">Speichere Unverarbeitete in neuem Verteiler</CheckBox>
</StackPanel> </StackPanel>
</Grid> </Grid>
<Grid Grid.Row="2" Margin="20" ColumnDefinitions="*,5*,*" IsVisible="False" x:Name="GrdExportMarginOptions">
<StackPanel Grid.Column="1" Orientation="Vertical" Spacing="20">
<Grid ColumnDefinitions="*,*,*" ColumnSpacing="20">
<StackPanel Orientation="Vertical" Spacing="10">
<Grid ColumnDefinitions="*,*">
<Label Grid.Column="0" Content="Zellenrand oben (mm)"></Label>
<NumericUpDown Grid.Column="1" x:Name="NudExpMargCellPaddingTop" Minimum="0" Maximum="20" Value="5"></NumericUpDown>
</Grid>
<Grid ColumnDefinitions="*,*">
<Label Grid.Column="0" Content="Zellenrand unten (mm)"></Label>
<NumericUpDown Grid.Column="1" x:Name="NudExpMargCellPaddingBot" Minimum="0" Maximum="20" Value="5"></NumericUpDown>
</Grid>
<Grid ColumnDefinitions="*,*">
<Label Grid.Column="0" Content="Zellenrand links (mm)"></Label>
<NumericUpDown Grid.Column="1" x:Name="NudExpMargCellPaddingLeft" Minimum="0" Maximum="20" Value="5"></NumericUpDown>
</Grid>
<Grid ColumnDefinitions="*,*">
<Label Grid.Column="0" Content="Zellenrand rechts (mm)"></Label>
<NumericUpDown Grid.Column="1" x:Name="NudExpMargCellPaddingRight" Minimum="0" Maximum="20" Value="5"></NumericUpDown>
</Grid>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Vertical" Spacing="10">
<Grid ColumnDefinitions="*,*">
<Label Content="Zellenabstand oben"></Label>
<NumericUpDown Grid.Column="1" x:Name="TbExpMargMarginTop" Minimum="0" Maximum="20" Value="0"></NumericUpDown>
</Grid>
<Grid ColumnDefinitions="*,*">
<Label Content="Zellenabstand unten"></Label>
<NumericUpDown Grid.Column="1" x:Name="TbExpMargMarginBottom" Minimum="0" Maximum="20" Value="0"></NumericUpDown>
</Grid>
<Grid ColumnDefinitions="*,*">
<Label Content="Zellenabstand rechts"></Label>
<NumericUpDown Grid.Column="1" x:Name="TbExpMargMarginRight" Minimum="0" Maximum="20" Value="0"></NumericUpDown>
</Grid>
<Grid ColumnDefinitions="*,*">
<Label Content="Zellenabstand links"></Label>
<NumericUpDown Grid.Column="1" x:Name="TbExpMargMarginLeft" Minimum="0" Maximum="20" Value="0"></NumericUpDown>
</Grid>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Vertical" Spacing="10">
<Grid ColumnDefinitions="*,*">
<Label Content="Zeilen pro Seite"></Label>
<NumericUpDown Grid.Column="1" x:Name="TbExpMargRowsPerPage" Minimum="1" Maximum="10" Value="7"></NumericUpDown>
</Grid>
<Grid ColumnDefinitions="*,*">
<Label Grid.Column="0" Content="Spalten pro Seite"></Label>
<NumericUpDown Grid.Column="1" x:Name="NudExpMargColumnsPerPage" Minimum="1" Maximum="8" Value="3"></NumericUpDown>
</Grid>
<Grid ColumnDefinitions="*,*">
<Label Grid.Column="0" Content="Schriftgröße (groß)"></Label>
<NumericUpDown Grid.Column="1" x:Name="NudExpMargFontSize" Minimum="5" Maximum="30" Value="9"></NumericUpDown>
</Grid>
<Grid ColumnDefinitions="*,*">
<Label Grid.Column="0" Content="Schriftgröße (klein)"></Label>
<NumericUpDown Grid.Column="1" x:Name="NudExpMargSmallFontSize" Minimum="3" Maximum="30" Value="6"></NumericUpDown>
</Grid>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Start" Click="BtnStartGenerateLabels_OnClick"></Button>
</StackPanel>
</StackPanel>
</Grid>
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem IsEnabled="False"> <TabItem IsEnabled="False">
@@ -247,13 +317,48 @@
</StackPanel> </StackPanel>
</TabItem.Header> </TabItem.Header>
</TabItem> </TabItem>
<TabItem IsEnabled="False"> <TabItem IsEnabled="True">
<TabItem.Header> <TabItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<LucideIcon Kind="LibraryBig" Width="32" Height="32" Size="32" /> <LucideIcon Kind="LibraryBig" Width="32" Height="32" Size="32" />
<Label FontSize="20" Content="Wiki" VerticalContentAlignment="Center" /> <Label FontSize="20" Content="Wiki" VerticalContentAlignment="Center" />
</StackPanel> </StackPanel>
</TabItem.Header> </TabItem.Header>
<Grid ColumnDefinitions="300,*">
<Border Grid.Column="0" Background="#FFF" BorderBrush="#DDD" BorderThickness="0,0,1,0">
<StackPanel>
<StackPanel Spacing="10" Orientation="Horizontal" Margin="10">
<Button Content="+ Datei" x:Name="BtnWikiAddFile"
Click="BtnWikiAddFile_OnClick" />
<Button Content="+ Ordner" x:Name="BtnWikiAddFolder"
Click="BtnWikiAddFolder_OnClick" />
</StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<TreeView Name="NavTree" Margin="10" />
</ScrollViewer>
</StackPanel>
</Border>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="8" Grid.Row="0">
<Button Name="EditButton" Content="Edit" HorizontalContentAlignment="Center"
IsEnabled="False" Width="80" Margin="0,0,8,0" />
<Button Name="OpenFolderButton" HorizontalContentAlignment="Center"
Content="Open Folder" Width="100" />
</StackPanel>
<Border Grid.Row="1" Margin="8" BorderBrush="#DDD" BorderThickness="1" CornerRadius="4">
<ScrollViewer>
<StackPanel Name="PreviewPanel" Margin="8" />
</ScrollViewer>
</Border>
</Grid>
</Grid>
</TabItem> </TabItem>
<TabItem IsEnabled="True"> <TabItem IsEnabled="True">
<TabItem.Header> <TabItem.Header>
@@ -270,13 +375,14 @@
<Label FontSize="16" Content="Global" VerticalContentAlignment="Center" /> <Label FontSize="16" Content="Global" VerticalContentAlignment="Center" />
</StackPanel> </StackPanel>
</TabItem.Header> </TabItem.Header>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical" Spacing="10">
<Grid ColumnDefinitions="400,*"> <Grid ColumnDefinitions="400,*">
<Label Grid.Column="0">config-Datei</Label> <Label Grid.Column="0">config-Datei</Label>
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="5"> <StackPanel Grid.Column="1" Orientation="Vertical" Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBox x:Name="TbConfigPath" HorizontalAlignment="Stretch" <TextBox x:Name="TbConfigPath" HorizontalAlignment="Stretch"
Watermark="/home/username/.config/logofclient/config.json" /> Watermark="/home/username/.config/logofclient/config.json" />
<Button> <Button x:Name="BtnConfigPath" HorizontalAlignment="Right">
<Button.Content> <Button.Content>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<LucideIcon Kind="File" Width="16" Height="16" Size="16" /> <LucideIcon Kind="File" Width="16" Height="16" Size="16" />
@@ -285,6 +391,41 @@
</Button.Content> </Button.Content>
</Button> </Button>
</StackPanel> </StackPanel>
</StackPanel>
</Grid>
<Grid ColumnDefinitions="400,*">
<Label Grid.Column="0">Wiki-Pfad</Label>
<StackPanel Grid.Column="1" Orientation="Vertical" Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBox x:Name="TbWikiPath" HorizontalAlignment="Stretch"
Watermark="/home/username/.config/logofclient/wiki" />
<Button IsEnabled="True" x:Name="BtnWikiPath" HorizontalAlignment="Right">
<Button.Content>
<StackPanel Orientation="Horizontal">
<LucideIcon Kind="Folder" Width="16" Height="16" Size="16" />
<Label Content="Öffnen..." VerticalContentAlignment="Center" />
</StackPanel>
</Button.Content>
</Button>
</StackPanel>
</StackPanel>
</Grid>
<Grid ColumnDefinitions="400,*">
<Label Grid.Column="0">Font-Pfad</Label>
<StackPanel Grid.Column="1" Orientation="Vertical" Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBox x:Name="TbFontPath" HorizontalAlignment="Stretch"
Watermark="[App-Direcotry]/assets/fonts/" />
<Button IsEnabled="True" x:Name="BtnFontPath" HorizontalAlignment="Right">
<Button.Content>
<StackPanel Orientation="Horizontal">
<LucideIcon Kind="Folder" Width="16" Height="16" Size="16" />
<Label Content="Öffnen..." VerticalContentAlignment="Center" />
</StackPanel>
</Button.Content>
</Button>
</StackPanel>
</StackPanel>
</Grid> </Grid>
</StackPanel> </StackPanel>
</TabItem> </TabItem>
@@ -385,9 +526,89 @@
<Label FontSize="16" Content="Länder" VerticalContentAlignment="Center" /> <Label FontSize="16" Content="Länder" VerticalContentAlignment="Center" />
</StackPanel> </StackPanel>
</TabItem.Header> </TabItem.Header>
<StackPanel Orientation="Vertical"> <Grid ColumnDefinitions="*,*">
<Grid ColumnDefinitions="400,*" /> <Grid Grid.Column="0" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="CountryList" Grid.Row="0"
SelectionChanged="CountryList_OnSelectionChanged" />
<Grid Grid.Row="1" ColumnDefinitions="*,250" Margin="0,5,0,0">
<TextBox x:Name="TbSettingsNewCountry" />
<Button Grid.Column="1" x:Name="BtnSettingsNewCountry"
HorizontalAlignment="Stretch" Click="BtnSettingsNewCountry_OnClick"
HorizontalContentAlignment="Center" Content="+ Hinzufügen"
Margin="5,0,0,0" />
</Grid>
</Grid>
<Grid Grid.Column="1" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,5"
HorizontalAlignment="Right">
<Button x:Name="BtnSettingsInsertDefaultCountries"
Content="Standard-Länder laden (Englisch)"
Click="BtnSettingsInsertDefaultCountries_OnClick" />
</StackPanel> </StackPanel>
<Grid Grid.Row="1" ColumnDefinitions="300,*" Margin="0,0,0,5">
<TextBlock Text="Name:" VerticalAlignment="Center" />
<TextBox Grid.Column="1" x:Name="TbSettingsCountryName"
TextChanged="TbSettingsCountryName_OnTextChanged" />
</Grid>
<Grid ColumnDefinitions="300,*" Margin="0,0,0,5" Grid.Row="2">
<TextBlock Text="Übersetzung:" VerticalAlignment="Center" />
<TextBox Grid.Column="1" x:Name="TbSettingsCountryTranslation"
TextChanged="TbSettingsCountryTranslation_OnTextChanged" />
</Grid>
<Grid Grid.Row="3">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0" x:Name="LbSettingsAlternatives" SelectionMode="Multiple" />
<Grid Grid.Row="1" ColumnDefinitions="*,200" Margin="0,5,0,0">
<TextBox x:Name="TbSettingsNewCountryAlternative"
Watermark="Kürzel/Alternative" />
<Button Grid.Column="1" x:Name="BtnSettingsNewCountryAlternative"
Content="+ Hinzufügen" Margin="5 0 0 0"
HorizontalContentAlignment="Center"
Click="BtnSettingsNewCountryAlternative_OnClick"
HorizontalAlignment="Stretch" />
</Grid>
<Button x:Name="BtnSettingsRemoveSelectedAlternatives" Grid.Row="2"
Content="Ausgewählte Entfernen" Background="#99963434"
HorizontalContentAlignment="Center"
HorizontalAlignment="Stretch"
Click="BtnSettingsRemoveSelectedAlternatives_OnClick"
Margin="0,5,0,0" />
<Button Grid.Row="3"
Content="Land Entfernen" Background="#99963434"
HorizontalContentAlignment="Center" x:Name="BtnSettingsRemoveCountry"
HorizontalAlignment="Stretch" Click="BtnSettingsRemoveCountry_OnClick"
Margin="0,5,0,0" />
</Grid>
</Grid>
</Grid>
</TabItem> </TabItem>
</TabControl> </TabControl>
</TabItem> </TabItem>
+604 -50
View File
@@ -1,16 +1,25 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Controls.Primitives;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Logof_Client.Wiki;
namespace Logof_Client; namespace Logof_Client;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
public static MainWindow _instance; public static MainWindow _instance;
private Country _selectedCountry;
private string _selectedWikiFilePath;
private bool _suppressCountryRefresh;
private WikiService _wikiService;
public Uri filePath; public Uri filePath;
public MainWindow() public MainWindow()
@@ -25,6 +34,43 @@ public partial class MainWindow : Window
WindowState = WindowState.Maximized; WindowState = WindowState.Maximized;
Global.Load(); Global.Load();
Settings.Load(); Settings.Load();
LoadPdfExportOptions();
HookPdfExportOptionEvents();
RefreshCountryView();
// Initialize wiki integration
_wikiService = new WikiService();
try
{
PopulateNavTree();
NavTree.SelectionChanged += NavTree_SelectionChanged;
OpenFolderButton.Click += OpenFolderButton_Click;
EditButton.Click += EditButton_Click;
// populate wiki settings UI if present
try
{
if (!string.IsNullOrWhiteSpace(Global._instance.wiki_storage_path))
TbWikiPath.Text = Global._instance.wiki_storage_path;
}
catch
{
}
try
{
BtnWikiPath.Click += BtnWikiPath_Click;
BtnFontPath.Click += BtnFontPath_Click;
BtnConfigPath.Click += BtnConfigPath_Click;
}
catch
{
}
}
catch
{
}
//Thread.Sleep(3000); //Thread.Sleep(3000);
//Show(); //Show();
@@ -54,23 +100,23 @@ public partial class MainWindow : Window
//await MessageBox.Show(_instance, $"{result.Count} Einträge fehlerhaft.", "Fertig"); //await MessageBox.Show(_instance, $"{result.Count} Einträge fehlerhaft.", "Fertig");
} }
private async void StartAddressRepair(Uri path) // private async void StartAddressRepair(Uri path)
{ // {
var addresses = DataImport.ImportKasAddressList(path); // Ihr Code hier // var addresses = DataImport.ImportKasAddressList(path); // Ihr Code hier
var progressWindow = new ProgressWindow(); // var progressWindow = new ProgressWindow();
//
progressWindow.Show(_instance); // progressWindow.Show(_instance);
//
var processor = new AddressRepair(progressWindow); // var processor = new AddressRepair(progressWindow);
//var result = await processor.Perform(addresses.Item2, errors); // //var result = await processor.Perform(addresses.Item2, errors);
//
//
progressWindow.Close(); // progressWindow.Close();
//
//
//new ResultWindow(result, addresses.Item2).Show(); // //new ResultWindow(result, addresses.Item2).Show();
//await MessageBox.Show(_instance, $"{result.Count} Einträge fehlerhaft.", "Fertig"); // //await MessageBox.Show(_instance, $"{result.Count} Einträge fehlerhaft.", "Fertig");
} // }
private void MnuExit_OnClick(object? sender, RoutedEventArgs e) private void MnuExit_OnClick(object? sender, RoutedEventArgs e)
{ {
@@ -115,7 +161,6 @@ public partial class MainWindow : Window
new FilePickerFileType(".csv-Datei") new FilePickerFileType(".csv-Datei")
{ {
Patterns = new[] { "*.csv" } Patterns = new[] { "*.csv" }
//Patterns = new[] { "*" }
} }
} }
}); });
@@ -134,41 +179,27 @@ public partial class MainWindow : Window
return; return;
} }
//var set = new KasAddressList("");
//foreach (var adset in Settings._instance.addressSets.addresses)
//if (adset.ID == KasAddressList.GetIDByAddressSetListItem(LstCustomerAdressSets.SelectedItem.ToString()))
StartAddressCheck(KasAddressList.GetIDByAddressSetListItem(LstCustomerAdressSets.SelectedItem.ToString())); StartAddressCheck(KasAddressList.GetIDByAddressSetListItem(LstCustomerAdressSets.SelectedItem.ToString()));
// var result = DataImport.ImportKasAddressList(filePath);
// if (result.Item1)
// {
// var check_result = new AddressCheck().Perform(result.Item2);
// foreach (var item in check_result.Result)
// {
// Console.WriteLine();
// Console.Write(item.Item1 + " ");
// foreach (var error in item.Item2) Console.Write(error + ", ");
// }
// }
} }
private void BtnCombine_OnClick(object? sender, RoutedEventArgs e) private void BtnCombine_OnClick(object? sender, RoutedEventArgs e)
{ {
GrdCalcMan.IsVisible = false; GrdCalcMan.IsVisible = false;
GrdCombineTypes.IsVisible = true; GrdCombineTypes.IsVisible = true;
GrdExportMarginOptions.IsVisible = false;
} }
private void MakeCalcManVisible() private void MakeCalcManVisible()
{ {
GrdCalcMan.IsVisible = true; GrdCalcMan.IsVisible = true;
GrdCombineTypes.IsVisible = false; GrdCombineTypes.IsVisible = false;
GrdExportMarginOptions.IsVisible = false;
} }
private async void StartCombine(Uri path) private async void StartCombine(Uri path)
{ {
MakeCalcManVisible(); MakeCalcManVisible();
var addresses = DataImport.ImportKasAddressList(path); var addresses = await DataImport.ImportKasAddressList(path);
var progressWindow = new ProgressWindow(); var progressWindow = new ProgressWindow();
var address_list = new List<KasAddressList> { addresses.Item2 }; var address_list = new List<KasAddressList> { addresses.Item2 };
@@ -190,8 +221,7 @@ public partial class MainWindow : Window
if (file == null) return; if (file == null) return;
//filePath = file[0].Path; //filePath = file[0].Path;
foreach (var f in file) address_list.Add(DataImport.ImportKasAddressList(f.Path).Item2); foreach (var f in file) address_list.Add((await DataImport.ImportKasAddressList(f.Path)).Item2);
progressWindow.Show(_instance); progressWindow.Show(_instance);
@@ -200,10 +230,189 @@ public partial class MainWindow : Window
progressWindow.Close(); progressWindow.Close();
//File.WriteAllText(OpenSettingsSaveAsDialog().Result, }
//new CsvBuilder(
//"refsid,anrede,titel,vorname,adel,name,namezus,anredzus,strasse,strasse2,plz,ort,land,pplz,postfach,name1,name2,name3,name4,name5,funktion,funktion2,abteilung,funktionad,lastupdate", private async void NavTree_SelectionChanged(object? sender, SelectionChangedEventArgs e)
//result).BuildKas()); {
if (NavTree.SelectedItem is TreeViewItem t && t.Tag is WikiItem item && !item.IsFolder)
{
_selectedWikiFilePath = item.Path;
var text = await _wikiService.LoadFileContentAsync(item.Path);
try
{
PreviewPanel.Children.Clear();
var rendered = MarkdownRenderer.Render(text ?? string.Empty);
PreviewPanel.Children.Add(rendered);
}
catch
{
// fallback: plain text
PreviewPanel.Children.Clear();
PreviewPanel.Children.Add(new TextBlock { Text = text ?? string.Empty });
}
EditButton.IsEnabled = true;
}
else
{
_selectedWikiFilePath = null;
EditButton.IsEnabled = false;
}
}
public void PopulateNavTree(string? expandToPath = null, string? selectPath = null)
{
var roots = _wikiService.GetRootItems();
var nodes = new List<TreeViewItem>();
foreach (var r in roots) nodes.Add(BuildNode(r));
NavTree.ItemsSource = nodes;
if (!string.IsNullOrWhiteSpace(expandToPath))
ExpandAndFindNode(nodes, expandToPath, out _);
if (!string.IsNullOrWhiteSpace(selectPath) &&
ExpandAndFindNode(nodes, selectPath, out var selectedNode) &&
selectedNode != null)
NavTree.SelectedItem = selectedNode;
}
private TreeViewItem BuildNode(WikiItem item)
{
var node = new TreeViewItem { Header = item.Name, Tag = item };
if (item.IsFolder)
foreach (var c in item.Children)
node.Items.Add(BuildNode(c));
return node;
}
private string GetSelectedWikiTargetDirectory()
{
var wikiRoot = Global._instance.wiki_storage_path;
if (NavTree.SelectedItem is TreeViewItem treeItem && treeItem.Tag is WikiItem selectedItem)
{
if (selectedItem.IsFolder) return selectedItem.Path;
var parentDir = Path.GetDirectoryName(selectedItem.Path);
if (!string.IsNullOrWhiteSpace(parentDir)) return parentDir;
}
return wikiRoot;
}
private static bool PathsEqual(string left, string right)
{
var normalizedLeft = Path.GetFullPath(left)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var normalizedRight = Path.GetFullPath(right)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var comparison = OperatingSystem.IsWindows()
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
return string.Equals(normalizedLeft, normalizedRight, comparison);
}
private bool ExpandAndFindNode(IEnumerable<TreeViewItem> nodes, string targetPath, out TreeViewItem? foundNode)
{
foreach (var node in nodes)
if (TryExpandToPath(node, targetPath, out foundNode))
return true;
foundNode = null;
return false;
}
private bool TryExpandToPath(TreeViewItem node, string targetPath, out TreeViewItem? foundNode)
{
if (node.Tag is WikiItem item && PathsEqual(item.Path, targetPath))
{
foundNode = node;
return true;
}
foreach (var childRaw in node.Items)
if (childRaw is TreeViewItem childNode &&
TryExpandToPath(childNode, targetPath, out foundNode))
{
node.IsExpanded = true;
return true;
}
foundNode = null;
return false;
}
private void OpenFolderButton_Click(object? sender, RoutedEventArgs e)
{
var path = Global._instance.wiki_storage_path;
if (!Directory.Exists(path)) return;
try
{
Process.Start(new ProcessStartInfo
{
FileName = path,
UseShellExecute = true
});
}
catch
{
}
}
private async void BtnWikiPath_Click(object? sender, RoutedEventArgs e)
{
var top = GetTopLevel(this);
var folder = await top.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = "Wiki Pfad wählen",
AllowMultiple = false
});
if (folder == null || folder.Count == 0) return;
var chosen = folder[0].Path;
TbWikiPath.Text = chosen.ToString();
Global._instance.wiki_storage_path = chosen.ToString();
Global.Save();
// reinit wiki service and reload tree
_wikiService = new WikiService();
PopulateNavTree();
}
private async void BtnFontPath_Click(object? sender, RoutedEventArgs e)
{
var top = GetTopLevel(this);
var folder = await top.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = "Font Pfad wählen",
AllowMultiple = false
});
if (folder == null || folder.Count == 0) return;
var chosen = folder[0].Path;
TbFontPath.Text = chosen.ToString();
Global._instance.font_path = chosen.ToString();
Global.Save();
}
private async void BtnConfigPath_Click(object? sender, RoutedEventArgs e)
{
var top = GetTopLevel(this);
var folder = await top.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = "Config Pfad wählen",
AllowMultiple = false
});
if (folder == null || folder.Count == 0) return;
var chosen = folder[0].Path;
TbConfigPath.Text = chosen.ToString();
Global._instance.config_path = chosen.ToString();
Global.Save();
MessageBox.Show(this, "Bitte starten Sie das Programm neu, um die Änderungen wirksam zu machen.", "Achtung");
} }
private async Task<string> OpenSettingsSaveAsDialog() private async Task<string> OpenSettingsSaveAsDialog()
@@ -312,6 +521,20 @@ public partial class MainWindow : Window
} }
} }
private async void EditButton_Click(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(_selectedWikiFilePath))
{
await MessageBox.Show(this, "Bitte wählen Sie zunächst eine Wiki-Datei aus.", "Keine Datei ausgewählt");
return;
}
EditorWindow ew = new(_selectedWikiFilePath);
ew.Show();
//await MessageBox.Show(this, "Edit feature is currently disabled.", "Edit Disabled");
}
public void RefreshCustomerItems(int index = 0) public void RefreshCustomerItems(int index = 0)
{ {
if (LstCustomers.Items.Count > 0) if (LstCustomers.Items.Count > 0)
@@ -389,7 +612,7 @@ public partial class MainWindow : Window
{ {
if (customer.patch == null) if (customer.patch == null)
{ {
var got = DataImport.ImportKasAddressList(selected_path, null, customer.separator); var got = await DataImport.ImportKasAddressList(selected_path, null, customer.separator);
if (!got.Item1) if (!got.Item1)
{ {
Console.WriteLine("Error while importing. Please try another file."); Console.WriteLine("Error while importing. Please try another file.");
@@ -405,7 +628,7 @@ public partial class MainWindow : Window
} }
else else
{ {
var got = DataImport.ImportKasAddressList(selected_path, customer.patch, customer.separator); var got = await DataImport.ImportKasAddressList(selected_path, customer.patch, customer.separator);
if (!got.Item1) if (!got.Item1)
{ {
Console.WriteLine("Error while importing. Please try another file."); Console.WriteLine("Error while importing. Please try another file.");
@@ -440,7 +663,7 @@ public partial class MainWindow : Window
foreach (var k in Settings._instance.addressSets.addresses) foreach (var k in Settings._instance.addressSets.addresses)
foreach (var customer in Settings._instance.customers.customers) foreach (var customer in Settings._instance.customers.customers)
if (customer.ID == k.owner_id && customer.ID == customer_id) if (customer.ID == k.owner_id && customer.ID == customer_id)
LstCustomerAdressSets.Items.Add(k.ID + " - " + k.Name + "(" + k.KasPersons.Count + " Einträge)"); LstCustomerAdressSets.Items.Add(k.ID + " - " + k.Name + " (" + k.KasPersons.Count + " Einträge)");
} }
private void LstCustomerAdressSets_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) private void LstCustomerAdressSets_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
@@ -522,9 +745,15 @@ public partial class MainWindow : Window
foreach (var item in LstCustomerAdressSets.SelectedItems) foreach (var item in LstCustomerAdressSets.SelectedItems)
list.Add(Settings._instance.addressSets.GetAddressSetByID( list.Add(Settings._instance.addressSets.GetAddressSetByID(
Convert.ToInt32(item.ToString().Split(" - ")[0]))); Convert.ToInt32(item.ToString().Split(" - ")[0])));
try
{
StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "difference"); StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "difference");
} }
catch
{
}
}
private void BtnCombineUnion_OnClick(object? sender, RoutedEventArgs e) private void BtnCombineUnion_OnClick(object? sender, RoutedEventArgs e)
{ {
@@ -532,9 +761,16 @@ public partial class MainWindow : Window
foreach (var item in LstCustomerAdressSets.SelectedItems) foreach (var item in LstCustomerAdressSets.SelectedItems)
list.Add(Settings._instance.addressSets.GetAddressSetByID( list.Add(Settings._instance.addressSets.GetAddressSetByID(
Convert.ToInt32(item.ToString().Split(" - ")[0]))); Convert.ToInt32(item.ToString().Split(" - ")[0])));
try
{
StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "union"); StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "union");
} }
catch
{
}
}
private void BtnCombineIntersection_OnClick(object? sender, RoutedEventArgs e) private void BtnCombineIntersection_OnClick(object? sender, RoutedEventArgs e)
{ {
@@ -542,9 +778,15 @@ public partial class MainWindow : Window
foreach (var item in LstCustomerAdressSets.SelectedItems) foreach (var item in LstCustomerAdressSets.SelectedItems)
list.Add(Settings._instance.addressSets.GetAddressSetByID( list.Add(Settings._instance.addressSets.GetAddressSetByID(
Convert.ToInt32(item.ToString().Split(" - ")[0]))); Convert.ToInt32(item.ToString().Split(" - ")[0])));
try
{
StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "intersection"); StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "intersection");
} }
catch
{
}
}
private void BtnCombineSymmetricDifference_OnClick(object? sender, RoutedEventArgs e) private void BtnCombineSymmetricDifference_OnClick(object? sender, RoutedEventArgs e)
{ {
@@ -556,15 +798,117 @@ public partial class MainWindow : Window
StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "symdiff"); StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "symdiff");
} }
private void BtnGenerateLabels_OnClick(object? sender, RoutedEventArgs e) private async void BtnGenerateLabels_OnClick(object? sender, RoutedEventArgs e)
{ {
var builder = new PdfBuilder(); GrdCalcMan.IsVisible = false;
GrdCombineTypes.IsVisible = false;
GrdExportMarginOptions.IsVisible = true;
LoadPdfExportOptions();
}
private async void BtnStartGenerateLabels_OnClick(object? sender, RoutedEventArgs e)
{
SavePdfExportOptions();
var saveDialog = new SaveFileDialog
{
DefaultExtension = "pdf",
Filters = { new FileDialogFilter { Name = "PDF-Dateien", Extensions = { "pdf" } } }
};
var filePath = await saveDialog.ShowAsync(this);
if (!string.IsNullOrEmpty(filePath))
{
var builder = new PdfBuilder(Settings._instance.pdfExport);
builder.CreateAddressLabelPdfFromAddressSetWithPlaceholder( builder.CreateAddressLabelPdfFromAddressSetWithPlaceholder(
Convert.ToInt32(LstCustomerAdressSets.SelectedItems[0].ToString().Split(" - ")[0]), Convert.ToInt32(LstCustomerAdressSets.SelectedItems[0].ToString().Split(" - ")[0]),
"Company Logo/Info", "Company Logo/Info",
"output.pdf" filePath
); );
//return true;
}
}
private void HookPdfExportOptionEvents()
{
var controls = GetPdfExportControls();
foreach (var control in controls) control.ValueChanged += PdfExportOption_OnValueChanged;
}
private void LoadPdfExportOptions()
{
var options = Settings._instance.pdfExport ?? new PdfExportSettings();
NudExpMargCellPaddingTop.Value = (decimal)options.cellPaddingTopMm;
NudExpMargCellPaddingBot.Value = (decimal)options.cellPaddingBottomMm;
NudExpMargCellPaddingLeft.Value = (decimal)options.cellPaddingLeftMm;
NudExpMargCellPaddingRight.Value = (decimal)options.cellPaddingRightMm;
TbExpMargMarginTop.Value = (decimal)options.pageMarginTopMm;
TbExpMargMarginBottom.Value = (decimal)options.pageMarginBottomMm;
TbExpMargMarginLeft.Value = (decimal)options.pageMarginLeftMm;
TbExpMargMarginRight.Value = (decimal)options.pageMarginRightMm;
TbExpMargRowsPerPage.Value = options.rowsPerPage;
NudExpMargColumnsPerPage.Value = options.columnsPerPage;
NudExpMargFontSize.Value = (decimal)options.fontSize;
NudExpMargSmallFontSize.Value = (decimal)options.smallFontSize;
}
private void SavePdfExportOptions()
{
Settings._instance.pdfExport = new PdfExportSettings
{
cellPaddingTopMm = ReadDouble(NudExpMargCellPaddingTop),
cellPaddingBottomMm = ReadDouble(NudExpMargCellPaddingBot),
cellPaddingLeftMm = ReadDouble(NudExpMargCellPaddingLeft),
cellPaddingRightMm = ReadDouble(NudExpMargCellPaddingRight),
pageMarginTopMm = ReadDouble(TbExpMargMarginTop),
pageMarginBottomMm = ReadDouble(TbExpMargMarginBottom),
pageMarginLeftMm = ReadDouble(TbExpMargMarginLeft),
pageMarginRightMm = ReadDouble(TbExpMargMarginRight),
rowsPerPage = ReadInt(TbExpMargRowsPerPage, 7),
columnsPerPage = ReadInt(NudExpMargColumnsPerPage, 3),
fontSize = ReadDouble(NudExpMargFontSize),
smallFontSize = ReadDouble(NudExpMargSmallFontSize)
};
Settings.Save();
}
private void PdfExportOption_OnValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
{
SavePdfExportOptions();
}
private double ReadDouble(NumericUpDown control)
{
return (double)(control.Value ?? 0);
}
private int ReadInt(NumericUpDown control, int fallback)
{
return (int)Math.Round(control.Value ?? fallback);
}
private NumericUpDown[] GetPdfExportControls()
{
return
[
NudExpMargCellPaddingTop,
NudExpMargCellPaddingBot,
NudExpMargCellPaddingLeft,
NudExpMargCellPaddingRight,
TbExpMargMarginTop,
TbExpMargMarginBottom,
TbExpMargMarginLeft,
TbExpMargMarginRight,
TbExpMargRowsPerPage,
NudExpMargColumnsPerPage,
NudExpMargFontSize,
NudExpMargSmallFontSize
];
} }
private void TbSettingsCustomerSenderAddress_OnTextChanged(object? sender, TextChangedEventArgs e) private void TbSettingsCustomerSenderAddress_OnTextChanged(object? sender, TextChangedEventArgs e)
@@ -574,4 +918,214 @@ public partial class MainWindow : Window
if (customer.ID == Settings._instance.customers.current) if (customer.ID == Settings._instance.customers.current)
customer.sender_address = TbSettingsCustomerSenderAddress.Text; customer.sender_address = TbSettingsCustomerSenderAddress.Text;
} }
// Country Section
private void BtnSettingsRemoveCountry_OnClick(object? sender, RoutedEventArgs e)
{
if (_selectedCountry != null)
{
Global._instance.countries.Remove(_selectedCountry);
_selectedCountry = null;
}
RefreshCountryView();
}
public void RefreshCountryView()
{
if (_suppressCountryRefresh) return;
try
{
_suppressCountryRefresh = true;
Global.Save();
var alt_index = LbSettingsAlternatives.SelectedIndex;
var country_index = CountryList.SelectedIndex;
LbSettingsAlternatives.Items.Clear();
try
{
CountryList.Items.Clear();
}
catch
{
}
foreach (var c in Global._instance.countries) CountryList.Items.Add(c.name);
try
{
CountryList.SelectedIndex = country_index;
}
catch
{
}
try
{
_selectedCountry = Country.GetByName(CountryList.SelectedItems[0].ToString());
}
catch
{
_selectedCountry = null;
}
if (_selectedCountry == null) return;
//Console.WriteLine("Refreshing alternatives...");
foreach (var a in _selectedCountry.alternatives) LbSettingsAlternatives.Items.Add(a);
//Console.WriteLine(a);
TbSettingsCountryName.Text = _selectedCountry.name;
TbSettingsCountryTranslation.Text = _selectedCountry.translation;
try
{
LbSettingsAlternatives.SelectedIndex = alt_index;
}
catch
{
}
}
finally
{
_suppressCountryRefresh = false;
}
}
private void BtnSettingsRemoveSelectedAlternatives_OnClick(object? sender, RoutedEventArgs e)
{
foreach (var selected in LbSettingsAlternatives.SelectedItems)
try
{
_selectedCountry.alternatives.Remove(selected.ToString());
}
catch
{
Console.WriteLine("Error while removing country alternative");
}
RefreshCountryView();
}
private void BtnSettingsNewCountryAlternative_OnClick(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(TbSettingsNewCountryAlternative.Text)) return;
_selectedCountry.alternatives.Add(TbSettingsNewCountryAlternative.Text.Trim());
RefreshCountryView();
}
private void TbSettingsCountryTranslation_OnTextChanged(object? sender, TextChangedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(TbSettingsCountryTranslation.Text))
{
_selectedCountry.translation = TbSettingsCountryTranslation.Text.Trim();
RefreshCountryView();
}
}
private void TbSettingsCountryName_OnTextChanged(object? sender, TextChangedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(TbSettingsCountryName.Text))
{
_selectedCountry.name = TbSettingsCountryName.Text.Trim();
RefreshCountryView();
}
}
private void BtnSettingsNewCountry_OnClick(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(TbSettingsNewCountry.Text)) return;
Global._instance.countries.Add(new Country(TbSettingsNewCountry.Text));
RefreshCountryView();
}
private void CountryList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
_selectedCountry = Country.GetByName(CountryList.SelectedItems[0].ToString());
RefreshCountryView();
}
private void BtnSettingsInsertDefaultCountries_OnClick(object? sender, RoutedEventArgs e)
{
ISO3166.Country[] countries = ISO3166.Country.List;
foreach (var countryie in countries)
Global._instance.countries.Add(new Country(countryie.Name, countryie.Name,
new List<string> { countryie.ThreeLetterCode, countryie.TwoLetterCode }));
RefreshCountryView();
}
private async void BtnWikiAddFile_OnClick(object? sender, RoutedEventArgs e)
{
var result = await NamingWindow.Show(this);
if (string.IsNullOrWhiteSpace(result)) return;
result = result.Trim();
if (!result.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) result += ".md";
if (!Directory.Exists(Global._instance.wiki_storage_path))
Directory.CreateDirectory(Global._instance.wiki_storage_path);
try
{
var targetDirectory = GetSelectedWikiTargetDirectory();
Directory.CreateDirectory(targetDirectory);
var newFilePath = Path.Combine(targetDirectory, result);
File.WriteAllText(newFilePath, "");
PopulateNavTree(targetDirectory, newFilePath);
}
catch (Exception ex)
{
MessageBox.Show(this, ex.StackTrace, "Fehler");
}
}
private async void BtnWikiAddFolder_OnClick(object? sender, RoutedEventArgs e)
{
var result = await NamingWindow.Show(this);
if (string.IsNullOrWhiteSpace(result)) return;
result = result.Trim();
if (!Directory.Exists(Global._instance.wiki_storage_path))
Directory.CreateDirectory(Global._instance.wiki_storage_path);
try
{
var targetDirectory = GetSelectedWikiTargetDirectory();
var newFolderPath = Path.Combine(targetDirectory, result);
Directory.CreateDirectory(newFolderPath);
PopulateNavTree(targetDirectory, newFolderPath);
}
catch (Exception ex)
{
MessageBox.Show(this, ex.StackTrace, "Fehler");
}
}
private async void MnIAdSetRename_OnClick(object? sender, RoutedEventArgs e)
{
if (LstCustomerAdressSets.SelectedItems.Count > 0)
try
{
var result = await NamingWindow.Show(this);
if (result != null)
{
var id = KasAddressList.GetIDByAddressSetListItem(LstCustomerAdressSets.SelectedItems[0]
.ToString());
var cus_id = Customer.GetIDByCustomerListItem(LstCustomers.SelectedItems[0].ToString());
foreach (var set in Settings._instance.addressSets.addresses)
if (set.ID == id)
{
set.Name = result;
Settings.Save();
RefreshAddressSetListItems(cus_id);
break;
}
}
}
catch (Exception ex)
{
MessageBox.Show(this, ex.StackTrace, "Fehler");
}
}
} }
+19
View File
@@ -0,0 +1,19 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" SizeToContent="WidthAndHeight"
x:Class="Logof_Client.NamingWindow"
Title="NamingWindow">
<StackPanel Orientation="Vertical">
<TextBlock Name="Text" Margin="10" TextWrapping="Wrap" />
<TextBox Name="Input" Margin="10" />
<StackPanel HorizontalAlignment="Right" Margin="5" Orientation="Horizontal" Name="Buttons">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="5" />
</Style>
</StackPanel.Styles>
</StackPanel>
</StackPanel>
</Window>
+57
View File
@@ -0,0 +1,57 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls;
namespace Logof_Client;
public partial class NamingWindow : Window
{
public NamingWindow()
{
InitializeComponent();
}
public static Task<string> Show(Window parent, string input = "", string info = "Bitte geben Sie einen Namen ein:")
{
try
{
var wind = new NamingWindow
{
Title = "Name eingeben"
};
wind.FindControl<TextBlock>("Text").Text = info;
var buttonPanel = wind.FindControl<StackPanel>("Buttons");
var inputBox = wind.FindControl<TextBox>("Input");
inputBox.Text = input;
string res = null;
void AddButton(string caption)
{
var btn = new Button { Content = caption };
btn.Click += (_, __) =>
{
res = inputBox.Text;
wind.Close();
};
buttonPanel.Children.Add(btn);
}
AddButton("Ok");
var tcs = new TaskCompletionSource<string>();
wind.Closed += delegate { tcs.TrySetResult(res); };
if (parent != null)
wind.ShowDialog(parent);
else wind.Show();
return tcs.Task;
}
catch (Exception ex)
{
Console.WriteLine("Error while showing naming window: " + ex.Message);
return Task.FromResult<string>(null!);
}
}
}
+8
View File
@@ -0,0 +1,8 @@
# Logofclient
Free and open source software suite to manage customers, prices, employees, etc.
## Bugs
Please report any bugs you find to fierke@mypapertown.de, thank you!
## Contributing
Feel free to contribute to this project using your MyPaperCloud-Account (request it via fierke@mypapertown.de) or a local git.mypapercloud.de-Account you are able to create.
+3 -2
View File
@@ -12,8 +12,9 @@
x:Name="LblResultCount" /> x:Name="LblResultCount" />
<StackPanel x:Name="StpFilterOptions" Orientation="Vertical" HorizontalAlignment="Stretch" <StackPanel x:Name="StpFilterOptions" Orientation="Vertical" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="10,80,10,0" /> VerticalAlignment="Stretch" Margin="10,80,10,0" />
<!-- <Button x:Name="BtnUpdateFilter" Content="Aktualisieren" HorizontalAlignment="Stretch" --> <Button Content="Filter anwenden" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"
<!-- VerticalAlignment="Bottom" Margin="10,0,10,10" Click="BtnUpdateFilter_OnClick" /> --> x:Name="BtnExecuteFilter" Click="BtnExecuteFilter_OnClick"
Margin="10,10,10,50" />
<Button Content="Ausgewählte Anzeigen" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" <Button Content="Ausgewählte Anzeigen" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"
x:Name="BtnShowSelected" Click="BtnShowSelected_OnClick" x:Name="BtnShowSelected" Click="BtnShowSelected_OnClick"
Margin="10,10,10,10" /> Margin="10,10,10,10" />
+16 -10
View File
@@ -46,13 +46,13 @@ public partial class ResultWindow : Window
Background = new SolidColorBrush(Color.Parse("#F0F0F0")) Background = new SolidColorBrush(Color.Parse("#F0F0F0"))
}; };
// Refsid // ID
grid.Children.Add(new TextBlock grid.Children.Add(new TextBlock
{ {
Text = "refsid: ", Text = "id: ",
FontWeight = FontWeight.Bold, Margin = new Thickness(5) FontWeight = FontWeight.Bold, Margin = new Thickness(5)
}); });
grid.Children.Add(new TextBlock { Text = person.refsid.ToString(), Margin = new Thickness(5) }); grid.Children.Add(new TextBlock { Text = person.id.ToString(), Margin = new Thickness(5) });
Grid.SetColumn(grid.Children[1], 1); Grid.SetColumn(grid.Children[1], 1);
// PLZ // PLZ
@@ -70,10 +70,10 @@ public partial class ResultWindow : Window
return grid; return grid;
} }
private void ViewSingle(int refsid) private void ViewSingle(int id)
{ {
foreach (var result in ur_addresses.KasPersons) foreach (var result in ur_addresses.KasPersons)
if (result.refsid == refsid) if (result.id == id)
{ {
var wind = new Window(); var wind = new Window();
var stp = new StackPanel(); var stp = new StackPanel();
@@ -82,8 +82,8 @@ public partial class ResultWindow : Window
var tb = new TextBlock(); var tb = new TextBlock();
var tb2 = new TextBlock(); var tb2 = new TextBlock();
tb.Text = tb.Text =
"refsid:\nanrede:\ntitel:\nvorname:\nadel:\nname:\nnamezus:\nanredzus:\nstrasse:\nstrasse2:\nplz:\nort:\nland:\npplz:\npostfach:\nname1:\nname2:\nname3:\nname4:\nname5:\nfunktion:\nfunktion2:\nabteilung:\nfunktionad:"; "id:\nanrede:\ntitel:\nvorname:\nadel:\nname:\nnamezus:\nanredzus:\nstrasse:\nstrasse2:\nplz:\nort:\nland:\npplz:\npostfach:\nname1:\nname2:\nname3:\nname4:\nname5:\nfunktion:\nfunktion2:\nabteilung:\nfunktionad:";
tb2.Text = result.refsid + "\n" + result.anrede + "\n" + result.titel + "\n" + result.vorname + "\n" + tb2.Text = result.id + "\n" + result.anrede + "\n" + result.titel + "\n" + result.vorname + "\n" +
result.adel + "\n" + result.name + "\n" + result.namezus + "\n" + result.anredzus + "\n" + result.adel + "\n" + result.name + "\n" + result.namezus + "\n" + result.anredzus + "\n" +
result.strasse + "\n" + result.strasse2 + "\n" + result.plz + "\n" + result.ort + "\n" + result.strasse + "\n" + result.strasse2 + "\n" + result.plz + "\n" + result.ort + "\n" +
result.land + "\n" + result.pplz + "\n" + result.postfach + "\n" + result.name1 + "\n" + result.land + "\n" + result.pplz + "\n" + result.postfach + "\n" + result.name1 + "\n" +
@@ -124,7 +124,7 @@ public partial class ResultWindow : Window
var cb = new CheckBox(); var cb = new CheckBox();
cb.IsChecked = true; cb.IsChecked = true;
cb.Content = errtype.ToString(); cb.Content = errtype.ToString();
cb.Click += (sender, e) => UpdateFilter(); //cb.Click += (sender, e) => UpdateFilter();
errortypecheckboxes.Add(cb); errortypecheckboxes.Add(cb);
StpFilterOptions.Children.Add(cb); StpFilterOptions.Children.Add(cb);
} }
@@ -134,7 +134,7 @@ public partial class ResultWindow : Window
var cb = new CheckBox(); var cb = new CheckBox();
cb.IsChecked = true; cb.IsChecked = true;
cb.Content = wartype.ToString(); cb.Content = wartype.ToString();
cb.Click += (sender, e) => UpdateFilter(); //cb.Click += (sender, e) => UpdateFilter();
warningtypecheckboxes.Add(cb); warningtypecheckboxes.Add(cb);
StpFilterOptions.Children.Add(cb); StpFilterOptions.Children.Add(cb);
} }
@@ -212,11 +212,17 @@ public partial class ResultWindow : Window
// try // try
// { // {
// var _asKas = (KasPerson)selected; // var _asKas = (KasPerson)selected;
// ViewSingle(_asKas.refsid); // ViewSingle(_asKas.id);
// } // }
// catch (Exception ex) // catch (Exception ex)
// { // {
// Console.WriteLine(ex.Message); // Console.WriteLine(ex.Message);
// } // }
} }
private void BtnExecuteFilter_OnClick(object? sender, RoutedEventArgs e)
{
Console.WriteLine("Updating filter...");
UpdateFilter();
}
} }
+1 -1
View File
@@ -247,7 +247,7 @@ public class AddressCheck
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
{ {
if (hasFaults) if (hasFaults)
_progress.AddToLog($"Person mit refsid {person.refsid} ist fehlerhaft"); _progress.AddToLog($"Person mit id {person.id} ist fehlerhaft");
_progress.ChangePercentage(percent); _progress.ChangePercentage(percent);
}); });
+46 -69
View File
@@ -5,56 +5,6 @@ namespace Logof_Client;
public static class AddressCreator public static class AddressCreator
{ {
/// <summary>
/// Creates max-seven-lines-long Markdown address-string. Analyzes the KasPerson-Instance to find the best result.
/// </summary>
/// <param name="refsid">KasPerson-ID</param>
/// <returns>A Markdown string with the address that is maximum seven lines long</returns>
public static string? CreateFinalMarkdownString2(int refsid)
{
// Maximum seven lines of information
// find the address
KasPerson? address = null;
var string_address = "";
var address_line_count = 0;
foreach (var set in Settings._instance.addressSets.addresses)
{
var temp = set.KasPersons.FirstOrDefault(obj => obj.refsid == refsid);
if (temp != null)
{
address = temp;
break;
}
}
// no address found
if (address == null) return null;
// let's get started: the name (and anrede), block-line-count: 1
if (!string.IsNullOrEmpty(address.anrede))
{
if (address.anrede == "Herr") string_address += "Herrn";
else string_address += address.anrede;
address_line_count++;
}
if (!string.IsNullOrEmpty(address.name) && !string.IsNullOrEmpty(address.vorname))
{
string_address += " " + address.vorname + " " + address.name;
}
else if (!string.IsNullOrEmpty(address.name))
{
}
else // Anrede but no name? to the trash!
{
string_address = "";
address_line_count = 0;
}
return "Hier könnte eine\nAdresse stehen";
}
//+++ Aufbau +++ //+++ Aufbau +++
// //
// Von unten anfangen, max. 7 Zeilen // Von unten anfangen, max. 7 Zeilen
@@ -90,7 +40,7 @@ public static class AddressCreator
/// </summary> /// </summary>
/// <param name="refsid">KasPerson-ID</param> /// <param name="refsid">KasPerson-ID</param>
/// <returns>A Markdown string with the address that is maximum seven lines long</returns> /// <returns>A Markdown string with the address that is maximum seven lines long</returns>
public static string? CreateFinalMarkdownString(int refsid) public static string? CreateFinalMarkdownString(int id)
{ {
// Maximum seven lines of information // Maximum seven lines of information
@@ -100,7 +50,7 @@ public static class AddressCreator
var address_line_count = 0; var address_line_count = 0;
foreach (var set in Settings._instance.addressSets.addresses) foreach (var set in Settings._instance.addressSets.addresses)
{ {
var temp = set.KasPersons.FirstOrDefault(obj => obj.refsid == refsid); var temp = set.KasPersons.FirstOrDefault(obj => obj.id == id);
if (temp != null) if (temp != null)
{ {
address = temp; address = temp;
@@ -112,24 +62,53 @@ public static class AddressCreator
if (address == null) return null; if (address == null) return null;
// let's get started: we start from the bottom // let's get started: we start from the bottom
// if the country is not germany, set it // if the country is not Germany, set it; try to map via Global countries alternatives -> translation
// LAND: GER oder DE var trimmedLand = (address.land ?? "").Trim();
if (address.land.ToLower().Trim() != "germany" && address.land.ToLower().Trim() != "ger" && var trimmedLowerLand = trimmedLand.ToLower();
address.land.ToLower().Trim() != "" && address.land.ToLower().Trim() != "de" && var isGermany = trimmedLowerLand == "germany" || trimmedLowerLand == "ger" ||
address.land.ToLower().Trim() != "deutschland") trimmedLowerLand == "" || trimmedLowerLand == "de" ||
trimmedLowerLand == "deutschland";
if (!isGermany)
{ {
string_address = "\n**" + address.land.Trim() + "**"; // Needs to be bold var countryToShow = trimmedLand; // default: use raw land value
if (!string.IsNullOrEmpty(trimmedLand))
// search for matching country via alternatives using for-loops
for (var ci = 0; ci < Global._instance.countries.Count; ci++)
{
var country = Global._instance.countries[ci];
for (var ai = 0; ai < country.alternatives.Count; ai++)
try
{
var alt = (country.alternatives[ai] ?? "").Trim();
if (string.Equals(alt, trimmedLand, StringComparison.OrdinalIgnoreCase))
{
countryToShow = string.IsNullOrWhiteSpace(country.translation)
? country.name
: country.translation;
goto CountryFound;
}
}
catch
{
// ignore malformed alternative
}
}
CountryFound:
string_address = "**" + countryToShow + "**"; // Needs to be bold
address_line_count++; address_line_count++;
} }
// Alternative A: pplz valid and city existing // Alternative A: pplz valid and city existing
if (!string.IsNullOrEmpty(address.ort) && CheckPLZ(address.pplz, address.land)) if (!string.IsNullOrEmpty(address.ort) && CheckPLZ(address.pplz, address.land))
{ {
string_address = address.pplz + " " + address.ort + string_address; string_address = address.pplz + " " + address.ort + "\n" + string_address;
address_line_count++; address_line_count++;
if (!string.IsNullOrWhiteSpace(address.postfach)) if (!string.IsNullOrWhiteSpace(address.postfach))
{ {
string_address = address.postfach.Trim() + "\n" + string_address; string_address = "Postfach " + address.postfach.Trim() + "\n" + string_address;
address_line_count++; address_line_count++;
} }
else if (!string.IsNullOrWhiteSpace(address.strasse.Trim())) else if (!string.IsNullOrWhiteSpace(address.strasse.Trim()))
@@ -143,7 +122,7 @@ public static class AddressCreator
if (!string.IsNullOrWhiteSpace(nameline)) if (!string.IsNullOrWhiteSpace(nameline))
{ {
string_address = nameline + string_address; string_address = nameline + "\n" + string_address;
address_line_count++; address_line_count++;
} }
@@ -180,7 +159,7 @@ public static class AddressCreator
} }
else if (!string.IsNullOrWhiteSpace(address.postfach.Trim())) else if (!string.IsNullOrWhiteSpace(address.postfach.Trim()))
{ {
string_address = address.postfach.Trim() + "\n" + string_address; string_address = "Postfach " + address.postfach.Trim() + "\n" + string_address;
address_line_count++; address_line_count++;
} }
@@ -189,7 +168,7 @@ public static class AddressCreator
if (!string.IsNullOrWhiteSpace(nameline)) if (!string.IsNullOrWhiteSpace(nameline))
{ {
string_address = nameline + string_address; string_address = nameline + "\n" + string_address;
address_line_count++; address_line_count++;
} }
@@ -215,8 +194,8 @@ public static class AddressCreator
string_address = names + "\n" + string_address; string_address = names + "\n" + string_address;
} // Error-Handling? } // Error-Handling?
return string_address; if (address_line_count > 1) return string_address;
return "Hier könnte eine\nAdresse stehen"; return null;
} }
public static string CreateNameLine(string anredezus, string anrede, string titel, string vorname, string adel, public static string CreateNameLine(string anredezus, string anrede, string titel, string vorname, string adel,
@@ -227,16 +206,14 @@ public static class AddressCreator
new[] { anredezus, titel, vorname, adel, name } new[] { anredezus, titel, vorname, adel, name }
.Where(s => !string.IsNullOrWhiteSpace(s)) .Where(s => !string.IsNullOrWhiteSpace(s))
) )
+ (string.IsNullOrWhiteSpace(namezus) ? "" : $" ({namezus.Trim()})") + (string.IsNullOrWhiteSpace(namezus) ? "" : $" ({namezus.Trim()})");
+ "\n";
// else // else
return string.Join(" ", return string.Join(" ",
new[] { anrede, titel, vorname, adel, name } new[] { anrede, titel, vorname, adel, name }
.Where(s => !string.IsNullOrWhiteSpace(s)) .Where(s => !string.IsNullOrWhiteSpace(s))
) )
+ (string.IsNullOrWhiteSpace(namezus) ? "" : $" ({namezus.Trim()})") + (string.IsNullOrWhiteSpace(namezus) ? "" : $" ({namezus.Trim()})");
+ "\n";
} }
/// <summary> /// <summary>
+41 -59
View File
@@ -16,39 +16,15 @@ public class CombineAddresses
_progress = progressWindow; _progress = progressWindow;
} }
public async Task<(KasAddressList,KasAddressList)> Perform(List<KasAddressList> address_lists, string type, bool? exportUnused) public async Task<(KasAddressList, KasAddressList)> Perform(List<KasAddressList> address_lists, string type,
bool? exportUnused)
{ {
var res = await Task.Run(async () => if (type == "difference") return await Difference(address_lists, exportUnused);
{ if (type == "union") return await Union(address_lists, exportUnused);
if (type == "difference") return Difference(address_lists, exportUnused); if (type == "intersection") return await Intersection(address_lists, exportUnused);
if (type == "union") return Union(address_lists, exportUnused); if (type == "symdiff") return await SymmetricDifference(address_lists, exportUnused);
if (type == "intersection") return Intersection(address_lists, exportUnused);
if (type == "symdiff") return SymmetricDifference(address_lists, exportUnused);
return null; return (null, null);
});
return res.Result;
// KasAddressList result = new("Ergebnis_" + DateTime.Now.ToString("ddMMyy_HHmmss"));
// await Task.Run(async () =>
// {
// for (var i = 0; i < address_lists.Count; i++)
// if (i == 0)
// lock (result)
// {
// result = address_lists[i];
// }
// else
// lock (result)
// {
// result = Merge(result, address_lists[i], i + 1, address_lists.Count).Result;
// }
// });
// return result;
return (null,null);
} }
@@ -61,7 +37,8 @@ public class CombineAddresses
/// <returns></returns> /// <returns></returns>
public bool CompareAddresses(KasPerson first, KasPerson second, bool only_refsid = false) public bool CompareAddresses(KasPerson first, KasPerson second, bool only_refsid = false)
{ {
if (first.refsid == second.refsid) return true; // A refsid of 0 means "missing", so it must not collapse unrelated entries.
if (first.refsid != 0 && second.refsid != 0 && first.refsid == second.refsid) return true;
if (!only_refsid) if (!only_refsid)
if (first.name == second.name && if (first.name == second.name &&
first.anrede == second.anrede && first.anrede == second.anrede &&
@@ -91,11 +68,12 @@ public class CombineAddresses
return false; return false;
} }
public async Task<(KasAddressList,KasAddressList)> Difference(List<KasAddressList> address_lists, bool? return_unused, public async Task<(KasAddressList, KasAddressList)> Difference(List<KasAddressList> address_lists,
bool? return_unused,
Progress? progress = null) Progress? progress = null)
{ {
if (address_lists == null || address_lists.Count == 0) if (address_lists == null || address_lists.Count == 0)
return (new KasAddressList(KasAddressList.GenerateName("difference")),null); return (new KasAddressList(await KasAddressList.GenerateName("difference")), null);
progress ??= new Progress progress ??= new Progress
{ {
@@ -107,8 +85,8 @@ public class CombineAddresses
var restUnion = new List<KasPerson>(); var restUnion = new List<KasPerson>();
for (var i = 1; i < address_lists.Count; i++) for (var i = 1; i < address_lists.Count; i++)
restUnion.AddRange(address_lists[i].KasPersons); restUnion.AddRange(address_lists[i].KasPersons);
var result = new KasAddressList(KasAddressList.GenerateName("difference")); var result = new KasAddressList(await KasAddressList.GenerateName("difference"));
var second_result = new KasAddressList(KasAddressList.GenerateName("difference_rest")); var second_result = new KasAddressList(await KasAddressList.GenerateName("difference_rest", false));
foreach (var person in address_lists[0].KasPersons) foreach (var person in address_lists[0].KasPersons)
{ {
@@ -122,21 +100,23 @@ public class CombineAddresses
progress.Increment(); progress.Increment();
if (progress.LogAction == null) continue; if (progress.LogAction == null) continue;
var logMessage = var logMessage =
$"Person mit refsid {person.refsid} verglichen mit {restUnion.Count} Personen des Restes."; $"Person mit id {person.id} verglichen mit {restUnion.Count} Personen des Restes.";
await Dispatcher.UIThread.InvokeAsync(() => progress.LogAction?.Invoke(logMessage)); await Dispatcher.UIThread.InvokeAsync(() => progress.LogAction?.Invoke(logMessage));
} }
if(return_unused == true) return (result,second_result);
else return (result, null); if (return_unused == true) return (result, second_result);
return (result, null);
} }
public async Task<(KasAddressList,KasAddressList)> Union(List<KasAddressList> address_lists, bool? return_unused, Progress progress = null) public async Task<(KasAddressList, KasAddressList)> Union(List<KasAddressList> address_lists, bool? return_unused,
Progress progress = null)
{ {
var result = new KasAddressList(KasAddressList.GenerateName("union")); var result = new KasAddressList(await KasAddressList.GenerateName("union"));
var second_result = new KasAddressList(KasAddressList.GenerateName("union_rest")); var second_result = new KasAddressList(await KasAddressList.GenerateName("union_rest", false));
if (address_lists == null || address_lists.Count == 0) if (address_lists == null || address_lists.Count == 0)
return (result,null); return (result, null);
var total = address_lists.Sum(l => l.KasPersons.Count); var total = address_lists.Sum(l => l.KasPersons.Count);
var processed = 0; var processed = 0;
@@ -152,7 +132,7 @@ public class CombineAddresses
processed++; processed++;
var percent = processed / (double)total * 100; var percent = processed / (double)total * 100;
var logMessage = var logMessage =
$"{percent:F1}%: Person mit {person.refsid} hinzugefügt (aktuell {result.KasPersons.Count} Einträge)"; $"{percent:F1}%: Person mit {person.id} hinzugefügt (aktuell {result.KasPersons.Count} Einträge)";
if (progress == null) continue; if (progress == null) continue;
if (Dispatcher.UIThread.CheckAccess()) if (Dispatcher.UIThread.CheckAccess())
@@ -161,8 +141,8 @@ public class CombineAddresses
Dispatcher.UIThread.Post(() => progress.LogAction?.Invoke(logMessage)); Dispatcher.UIThread.Post(() => progress.LogAction?.Invoke(logMessage));
} }
if(return_unused == true) return (result,second_result); if (return_unused == true) return (result, second_result);
else return (result, null); return (result, null);
} }
@@ -171,13 +151,14 @@ public class CombineAddresses
return null; return null;
} }
public async Task<(KasAddressList,KasAddressList)> Intersection(List<KasAddressList> address_lists, bool? return_unused, Progress progress = null) public async Task<(KasAddressList, KasAddressList)> Intersection(List<KasAddressList> address_lists,
bool? return_unused, Progress progress = null)
{ {
var result = new KasAddressList(KasAddressList.GenerateName("intersection")); var result = new KasAddressList(await KasAddressList.GenerateName("intersection"));
var second_result = new KasAddressList(KasAddressList.GenerateName("intersection_rest")); var second_result = new KasAddressList(await KasAddressList.GenerateName("intersection_rest", false));
if (address_lists == null || address_lists.Count == 0) if (address_lists == null || address_lists.Count == 0)
return (result,null); return (result, null);
// Nur die erste Liste als Ausgangspunkt verwenden // Nur die erste Liste als Ausgangspunkt verwenden
var baseList = address_lists[0]; var baseList = address_lists[0];
@@ -200,7 +181,7 @@ public class CombineAddresses
processed++; processed++;
var percent = processed / (double)total * 100; var percent = processed / (double)total * 100;
var logMessage = var logMessage =
$"{percent:F1}%: Person mit {person.refsid} geprüft {(isInAll ? "in allen enthalten" : "nicht überall vorhanden")}"; $"{percent:F1}%: Person mit {person.id} geprüft {(isInAll ? "in allen enthalten" : "nicht überall vorhanden")}";
// Sicher und nicht blockierend loggen // Sicher und nicht blockierend loggen
if (progress != null) if (progress != null)
@@ -212,15 +193,16 @@ public class CombineAddresses
} }
} }
if(return_unused == true) return (result,second_result); if (return_unused == true) return (result, second_result);
else return (result, null); return (result, null);
} }
public async Task<(KasAddressList,KasAddressList)> SymmetricDifference(List<KasAddressList> address_lists, bool? return_unused, Progress progress = null) public async Task<(KasAddressList, KasAddressList)> SymmetricDifference(List<KasAddressList> address_lists,
bool? return_unused, Progress progress = null)
{ {
var result = new KasAddressList(KasAddressList.GenerateName("symmetric_difference")); var result = new KasAddressList(await KasAddressList.GenerateName("symmetric_difference"));
var second_result = new KasAddressList(KasAddressList.GenerateName("symmetric_rest")); var second_result = new KasAddressList(await KasAddressList.GenerateName("symmetric_rest", false));
if (address_lists == null || address_lists.Count == 0) if (address_lists == null || address_lists.Count == 0)
return (result, null); return (result, null);
@@ -251,7 +233,7 @@ public class CombineAddresses
processed++; processed++;
var percent = processed / (double)total * 100; var percent = processed / (double)total * 100;
var logMessage = var logMessage =
$"{percent:F1}%: Person mit {person.refsid} verarbeitet (Zwischengröße {allPersons.Count})"; $"{percent:F1}%: Person mit {person.id} verarbeitet (Zwischengröße {allPersons.Count})";
if (progress != null) if (progress != null)
{ {
@@ -269,8 +251,8 @@ public class CombineAddresses
else else
second_result.KasPersons.Add(person); second_result.KasPersons.Add(person);
if(return_unused == true) return (result,second_result); if (return_unused == true) return (result, second_result);
else return (result, null); return (result, null);
} }
+75
View File
@@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Logof_Client;
public class CsvBuilder
{
private readonly string Header;
private readonly List<object> Instances;
private readonly KasAddressList KasAddressList;
private readonly char Separator;
public CsvBuilder(string header, List<object> instances, char separator = ',')
{
Header = header;
Instances = instances;
Separator = separator;
}
public CsvBuilder(string header, KasAddressList instances, char separator = ',')
{
Header = header;
KasAddressList = instances;
Separator = separator;
}
public string? BuildKas()
{
var result = new StringBuilder();
result.AppendLine(Header);
foreach (var l in KasAddressList.KasPersons)
result.AppendLine(string.Join(Separator, new[]
{
EscapeCsvField(l.refsid.ToString()),
EscapeCsvField(l.anrede),
EscapeCsvField(l.titel),
EscapeCsvField(l.vorname),
EscapeCsvField(l.adel),
EscapeCsvField(l.name),
EscapeCsvField(l.namezus),
EscapeCsvField(l.anredzus),
EscapeCsvField(l.strasse),
EscapeCsvField(l.strasse2),
EscapeCsvField(l.plz),
EscapeCsvField(l.ort),
EscapeCsvField(l.land),
EscapeCsvField(l.pplz),
EscapeCsvField(l.postfach),
EscapeCsvField(l.name1),
EscapeCsvField(l.name2),
EscapeCsvField(l.name3),
EscapeCsvField(l.name4),
EscapeCsvField(l.name5),
EscapeCsvField(l.funktion),
EscapeCsvField(l.funktion2),
EscapeCsvField(l.abteilung),
EscapeCsvField(l.funktionad)
}));
// weitere Cases
return result.ToString();
}
private string EscapeCsvField(string? value)
{
var field = value ?? string.Empty;
var mustQuote = field.Contains(Separator) || field.Contains('"') || field.Contains('\r') || field.Contains('\n');
if (!mustQuote)
return field;
return "\"" + field.Replace("\"", "\"\"") + "\"";
}
}
+69 -37
View File
@@ -3,20 +3,22 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Logof_Client; namespace Logof_Client;
public class DataImport public class DataImport
{ {
public static (bool, KasAddressList) ImportKasAddressList(Uri pathToCsv, AddressPatch patch = null, public static async Task<(bool, KasAddressList)> ImportKasAddressList(Uri pathToCsv, AddressPatch patch = null,
char separator = ',') char separator = ',')
{ {
if (patch == null) if (patch == null)
return ImportKasAddressListWithoutPatch(pathToCsv, separator); return await ImportKasAddressListWithoutPatch(pathToCsv, separator);
return ImportKasAddressListWithPatch(pathToCsv, patch, separator); return await ImportKasAddressListWithPatch(pathToCsv, patch, separator);
} }
private static (bool, KasAddressList) ImportKasAddressListWithoutPatch(Uri pathToCsv, char separator) private static async Task<(bool, KasAddressList)> ImportKasAddressListWithoutPatch(Uri pathToCsv, char separator)
{ {
if (!File.Exists(pathToCsv.LocalPath)) if (!File.Exists(pathToCsv.LocalPath))
{ {
@@ -32,7 +34,8 @@ public class DataImport
return (false, null); return (false, null);
} }
var imported = new KasAddressList(KasAddressList.GenerateName(Path.GetFileNameWithoutExtension(pathToCsv.LocalPath))); var imported = new KasAddressList(
await KasAddressList.GenerateName(Path.GetFileNameWithoutExtension(pathToCsv.LocalPath)));
while (!reader.EndOfStream) while (!reader.EndOfStream)
{ {
@@ -40,7 +43,7 @@ public class DataImport
if (string.IsNullOrWhiteSpace(line)) if (string.IsNullOrWhiteSpace(line))
continue; continue;
var parts = line.Split(separator).Select(p => p.Trim()).ToArray(); var parts = ParseCsvLine(line, separator);
if (parts.Length < 24) if (parts.Length < 24)
{ {
@@ -50,7 +53,7 @@ public class DataImport
try try
{ {
var person = new KasPerson( var person = new KasPerson(KasPerson.GenerateNewID(imported.KasPersons.Count),
ParseInt(parts[0]), ParseInt(parts[0]),
parts[1], parts[1],
parts[2], parts[2],
@@ -89,7 +92,7 @@ public class DataImport
return (true, imported); return (true, imported);
} }
private static (bool, KasAddressList) ImportKasAddressListWithPatch(Uri pathToCsv, AddressPatch patch, private static async Task<(bool, KasAddressList)> ImportKasAddressListWithPatch(Uri pathToCsv, AddressPatch patch,
char separator) char separator)
{ {
if (!File.Exists(pathToCsv.LocalPath)) if (!File.Exists(pathToCsv.LocalPath))
@@ -106,9 +109,10 @@ public class DataImport
return (false, null); return (false, null);
} }
var headers = headerLine.Split(separator).Select(h => h.Trim()).ToArray(); var headers = ParseCsvLine(headerLine, separator);
var imported = new KasAddressList(KasAddressList.GenerateName(Path.GetFileNameWithoutExtension(pathToCsv.LocalPath))); var imported = new KasAddressList(
await KasAddressList.GenerateName(Path.GetFileNameWithoutExtension(pathToCsv.LocalPath)));
var patchType = typeof(AddressPatch); var patchType = typeof(AddressPatch);
var binding = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase; var binding = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;
@@ -116,7 +120,7 @@ public class DataImport
.Where(p => p.PropertyType == typeof(bool) && p.Name.StartsWith("has_", StringComparison.OrdinalIgnoreCase)) .Where(p => p.PropertyType == typeof(bool) && p.Name.StartsWith("has_", StringComparison.OrdinalIgnoreCase))
.ToArray(); .ToArray();
var last_refsid = 1000000; //var last_refsid = 1000000;
while (!reader.EndOfStream) while (!reader.EndOfStream)
{ {
@@ -124,11 +128,11 @@ public class DataImport
if (string.IsNullOrWhiteSpace(line)) if (string.IsNullOrWhiteSpace(line))
continue; continue;
var parts = line.Split(separator).Select(p => p.Trim()).ToArray(); var parts = ParseCsvLine(line, separator);
var fieldValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); var fieldValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var refsid_existing = false; //var refsid_existing = false;
foreach (var hasProp in hasProperties) foreach (var hasProp in hasProperties)
{ {
@@ -177,16 +181,13 @@ public class DataImport
return fieldValues.TryGetValue(name, out var v) ? v : ""; return fieldValues.TryGetValue(name, out var v) ? v : "";
} }
var new_refsid = last_refsid; var refsid = 0;
if (!patch.has_refsid) if (patch.has_refsid)
new_refsid = GenerateNewRefsid(); refsid = ParseInt(GetField("refsid"));
else
new_refsid = ParseInt(GetField("refsid"));
try try
{ {
var person = new KasPerson( var person = new KasPerson(KasPerson.GenerateNewID(imported.KasPersons.Count), refsid,
new_refsid,
GetField("anrede"), GetField("anrede"),
GetField("titel"), GetField("titel"),
GetField("vorname"), GetField("vorname"),
@@ -224,23 +225,17 @@ public class DataImport
return (true, imported); return (true, imported);
int GenerateNewRefsid() // int GenerateNewRefsid()
{ // {
// var biggest = last_refsid;
int biggest = last_refsid; // foreach (var set in Settings._instance.addressSets.addresses)
foreach (var set in Settings._instance.addressSets.addresses) // foreach (var address in set.KasPersons)
{ // if (biggest < address.id)
foreach (var address in set.KasPersons) // biggest = address.id + 1;
{ //
if (biggest < address.refsid) // last_refsid = biggest + 1;
{ // return last_refsid;
biggest = address.refsid+1; // }
}
}
}
last_refsid = biggest+1;
return last_refsid;
}
} }
@@ -248,4 +243,41 @@ public class DataImport
{ {
return int.TryParse(input, out var result) ? result : 0; return int.TryParse(input, out var result) ? result : 0;
} }
private static string[] ParseCsvLine(string line, char separator)
{
var fields = new List<string>();
var current = new StringBuilder();
var inQuotes = false;
for (var i = 0; i < line.Length; i++)
{
var c = line[i];
if (c == '"')
{
if (inQuotes && i + 1 < line.Length && line[i + 1] == '"')
{
current.Append('"');
i++;
continue;
}
inQuotes = !inQuotes;
continue;
}
if (c == separator && !inQuotes)
{
fields.Add(current.ToString().Trim());
current.Clear();
continue;
}
current.Append(c);
}
fields.Add(current.ToString().Trim());
return fields.ToArray();
}
} }
+180 -115
View File
@@ -1,60 +1,64 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using PdfSharp; using PdfSharp;
using PdfSharp.Drawing; using PdfSharp.Drawing;
using PdfSharp.Fonts;
using PdfSharp.Pdf; using PdfSharp.Pdf;
namespace Logof_Client; namespace Logof_Client;
public class PdfBuilder public class PdfBuilder
{ {
// Table layout private readonly PdfExportSettings _settings;
private const int CellsPerRow = 3; private readonly XFont _boldFont;
private const int CellsPerPage = 21; // 3 columns × 7 rows private readonly XFont _regularFont;
private readonly XFont _boldFont = new("Arial", 9, XFontStyleEx.Bold); private readonly XFont _smallFont;
private readonly double _cellHeight = 42.43; // mm
private readonly double _cellPaddingBottom = 5; // mm
// Padding inside cells public PdfBuilder(PdfExportSettings? settings = null)
private readonly double _cellPaddingLeft = 5; // mm
private readonly double _cellPaddingTop = 5; // mm
// Cell dimensions (in mm)
private readonly double _cellWidth = 70; // mm
// Font settings
private readonly double _fontSize = 9;
private readonly double _marginBottom = 1; // mm
// Paper and layout settings
private readonly double _marginLeft = 0; // mm
private readonly double _marginRight = 0; // mm
private readonly double _marginTop = 0; // mm
private readonly XFont _regularFont = new("Arial", 9, XFontStyleEx.Regular);
/// <summary>
/// Creates a PDF document with address stickers from an AddressSet in a 3×7 grid layout on A4 pages.
/// </summary>
/// <param name="addressSetId">The ID of the AddressSet to use</param>
/// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdfFromAddressSet(int addressSetId, string outputPath)
{ {
// Find the AddressSet by ID EnsureFontResolverRegistered();
var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId); _settings = settings ?? new PdfExportSettings();
if (addressSet == null)
throw new ArgumentException($"AddressSet with ID {addressSetId} not found");
if (addressSet.KasPersons == null || addressSet.KasPersons.Count == 0) // Select first font from build output fonts folder (AppContext.BaseDirectory/fonts)
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses"); var chosenFamily = "Arial";
try
// Generate markdown addresses from all KasPersons in the set {
var addresses = new string?[addressSet.KasPersons.Count]; if (Directory.Exists(Global._instance.font_path))
for (var i = 0; i < addressSet.KasPersons.Count; i++) {
addresses[i] = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].refsid); var first = Directory.EnumerateFiles(Global._instance.font_path, "*.ttf").FirstOrDefault();
if (!string.IsNullOrEmpty(first))
CreateAddressLabelPdf(addresses, outputPath); chosenFamily = StripStyleSuffix(Path.GetFileNameWithoutExtension(first)) ?? chosenFamily;
} }
}
catch
{
chosenFamily = "Arial";
}
_boldFont = new XFont(chosenFamily, _settings.fontSize, XFontStyleEx.Bold);
_regularFont = new XFont(chosenFamily, _settings.fontSize, XFontStyleEx.Regular);
_smallFont = new XFont(chosenFamily, _settings.smallFontSize, XFontStyleEx.Regular);
}
private static void EnsureFontResolverRegistered()
{
if (GlobalFontSettings.FontResolver != null) return;
//var fontsDir = Path.Combine(AppContext.BaseDirectory, "fonts");
GlobalFontSettings.FontResolver = new StableFontResolver(Global._instance.font_path);
}
private static string StripStyleSuffix(string name)
{
if (string.IsNullOrEmpty(name)) return name;
var idx = name.IndexOf('-');
if (idx < 0) idx = name.IndexOf('_');
if (idx > 0)
return name.Substring(0, idx);
return name;
}
/// <summary> /// <summary>
/// Creates a PDF document with address stickers from an AddressSet with a placeholder in the first cell. /// Creates a PDF document with address stickers from an AddressSet with a placeholder in the first cell.
@@ -74,51 +78,44 @@ public class PdfBuilder
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses"); throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses");
// Generate markdown addresses from all KasPersons in the set // Generate markdown addresses from all KasPersons in the set
var addresses = new string?[addressSet.KasPersons.Count]; //var addresses = new string?[addressSet.KasPersons.Count];
var addresses = new List<string>();
// find customer (owner) to include sender_address
string senderLine = null;
try
{
var owner = Settings._instance.customers.customers.FirstOrDefault(c => c.ID == addressSet.owner_id);
if (owner != null && !string.IsNullOrWhiteSpace(owner.sender_address))
senderLine = "<font6>" + owner.sender_address.Replace("\n", " ").Trim() + "</font6>\n";
}
catch
{
senderLine = null;
}
for (var i = 0; i < addressSet.KasPersons.Count; i++) for (var i = 0; i < addressSet.KasPersons.Count; i++)
addresses[i] = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].refsid); {
var addr = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].id);
if (string.IsNullOrWhiteSpace(addr)) continue;
if (!string.IsNullOrEmpty(senderLine))
addresses.Add(senderLine + (addr ?? ""));
else
addresses.Add(addr);
}
CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath); CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath);
} }
/// <summary>
/// Creates a PDF document with address stickers in a 3×7 grid layout on A4 pages.
/// </summary>
/// <param name="addresses">Array of addresses (from CreateFinalMarkdownString)</param>
/// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdf(string?[] addresses, string outputPath)
{
if (addresses == null || addresses.Length == 0)
throw new ArgumentException("Addresses array cannot be null or empty");
var document = new PdfDocument();
var addressIndex = 0;
while (addressIndex < addresses.Length)
{
var page = document.AddPage();
page.Size = PageSize.A4;
using (var gfx = XGraphics.FromPdfPage(page))
{
// Draw the grid and fill cells
DrawPage(gfx, addresses, ref addressIndex);
}
}
// Save the document
document.Save(outputPath);
}
/// <summary> /// <summary>
/// Creates a PDF document with a single placeholder cell for other information. /// Creates a PDF document with a single placeholder cell for other information.
/// </summary> /// </summary>
/// <param name="addresses">Array of addresses</param> /// <param name="addresses">Array of addresses</param>
/// <param name="placeholderText">Text for the first cell (top-left)</param> /// <param name="placeholderText">Text for the first cell (top-left)</param>
/// <param name="outputPath">Path where the PDF should be saved</param> /// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdfWithPlaceholder(string?[] addresses, string placeholderText, string outputPath) public void CreateAddressLabelPdfWithPlaceholder(List<string> addresses, string placeholderText, string outputPath)
{ {
if (addresses == null || addresses.Length == 0) if (addresses == null || addresses.Count == 0)
throw new ArgumentException("Addresses array cannot be null or empty"); throw new ArgumentException("Addresses array cannot be null or empty");
var document = new PdfDocument(); var document = new PdfDocument();
@@ -126,7 +123,7 @@ public class PdfBuilder
var addressIndex = 0; var addressIndex = 0;
var isFirstCell = true; var isFirstCell = true;
while (addressIndex < addresses.Length || isFirstCell) while (addressIndex < addresses.Count || isFirstCell)
{ {
var page = document.AddPage(); var page = document.AddPage();
page.Size = PageSize.A4; page.Size = PageSize.A4;
@@ -140,38 +137,33 @@ public class PdfBuilder
document.Save(outputPath); document.Save(outputPath);
} }
private void DrawPage(XGraphics gfx, string?[] addresses, ref int addressIndex) private void DrawPage(XGraphics gfx, List<string> addresses, ref int addressIndex)
{ {
var cellCount = 0; for (var row = 0; row < _settings.rowsPerPage; row++)
{
for (var col = 0; col < _settings.columnsPerPage; col++)
{
if (addressIndex >= addresses.Count) break;
for (var row = 0; row < 7; row++) var x = MmToPoints(_settings.pageMarginLeftMm + col * GetCellWidthMm());
{ var y = MmToPoints(_settings.pageMarginTopMm + row * GetCellHeightMm());
for (var col = 0; col < 3; col++)
{
if (addressIndex >= addresses.Length) break;
var x = MmToPoints(_marginLeft + col * _cellWidth);
var y = MmToPoints(_marginTop + row * _cellHeight);
DrawCell(gfx, x, y, addresses[addressIndex]); DrawCell(gfx, x, y, addresses[addressIndex]);
addressIndex++; addressIndex++;
cellCount++;
} }
if (addressIndex >= addresses.Length) break; if (addressIndex >= addresses.Count) break;
} }
} }
private void DrawPageWithPlaceholder(XGraphics gfx, string?[] addresses, ref int addressIndex, private void DrawPageWithPlaceholder(XGraphics gfx, List<string> addresses, ref int addressIndex,
ref bool isFirstCell, string placeholderText) ref bool isFirstCell, string placeholderText)
{ {
var cellCount = 0; for (var row = 0; row < _settings.rowsPerPage; row++)
for (var col = 0; col < _settings.columnsPerPage; col++)
for (var row = 0; row < 7; row++)
for (var col = 0; col < 3; col++)
{ {
var x = MmToPoints(_marginLeft + col * _cellWidth); var x = MmToPoints(_settings.pageMarginLeftMm + col * GetCellWidthMm());
var y = MmToPoints(_marginTop + row * _cellHeight); var y = MmToPoints(_settings.pageMarginTopMm + row * GetCellHeightMm());
// First cell: placeholder // First cell: placeholder
if (isFirstCell) if (isFirstCell)
@@ -179,7 +171,7 @@ public class PdfBuilder
DrawCell(gfx, x, y, placeholderText); DrawCell(gfx, x, y, placeholderText);
isFirstCell = false; isFirstCell = false;
} }
else if (addressIndex < addresses.Length) else if (addressIndex < addresses.Count)
{ {
DrawCell(gfx, x, y, addresses[addressIndex]); DrawCell(gfx, x, y, addresses[addressIndex]);
addressIndex++; addressIndex++;
@@ -188,15 +180,13 @@ public class PdfBuilder
{ {
DrawEmptyCell(gfx, x, y); DrawEmptyCell(gfx, x, y);
} }
cellCount++;
} }
} }
private void DrawCell(XGraphics gfx, double x, double y, string? address) private void DrawCell(XGraphics gfx, double x, double y, string? address)
{ {
var cellWidthPoints = MmToPoints(_cellWidth); var cellWidthPoints = MmToPoints(GetCellWidthMm());
var cellHeightPoints = MmToPoints(_cellHeight); var cellHeightPoints = MmToPoints(GetCellHeightMm());
// Draw cell border // Draw cell border
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints); var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
@@ -208,8 +198,8 @@ public class PdfBuilder
private void DrawEmptyCell(XGraphics gfx, double x, double y) private void DrawEmptyCell(XGraphics gfx, double x, double y)
{ {
var cellWidthPoints = MmToPoints(_cellWidth); var cellWidthPoints = MmToPoints(GetCellWidthMm());
var cellHeightPoints = MmToPoints(_cellHeight); var cellHeightPoints = MmToPoints(GetCellHeightMm());
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints); var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
gfx.DrawRectangle(XPens.Black, rect); gfx.DrawRectangle(XPens.Black, rect);
@@ -217,11 +207,12 @@ public class PdfBuilder
private void DrawMarkdownText(XGraphics gfx, string text, double x, double y, double cellWidth, double cellHeight) private void DrawMarkdownText(XGraphics gfx, string text, double x, double y, double cellWidth, double cellHeight)
{ {
var paddingLeftPoints = MmToPoints(_cellPaddingLeft); var paddingLeftPoints = MmToPoints(_settings.cellPaddingLeftMm);
var paddingTopPoints = MmToPoints(_cellPaddingTop); var paddingRightPoints = MmToPoints(_settings.cellPaddingRightMm);
var paddingBottomPoints = MmToPoints(_cellPaddingBottom); var paddingTopPoints = MmToPoints(_settings.cellPaddingTopMm);
var paddingBottomPoints = MmToPoints(_settings.cellPaddingBottomMm);
var maxWidth = cellWidth - paddingLeftPoints * 2; var maxWidth = Math.Max(0, cellWidth - paddingLeftPoints - paddingRightPoints);
// Split text by newlines and remove empty trailing lines // Split text by newlines and remove empty trailing lines
var rawLines = text.Split(new[] { "\n" }, StringSplitOptions.None); var rawLines = text.Split(new[] { "\n" }, StringSplitOptions.None);
@@ -233,10 +224,8 @@ public class PdfBuilder
// Calculate total height of the text block // Calculate total height of the text block
var totalHeight = lines.Length * lineHeight; var totalHeight = lines.Length * lineHeight;
// Start drawing from the lower-left corner of the cell // Start drawing from the top of the cell (align addresses to top)
var startY = y + cellHeight - paddingBottomPoints - totalHeight; var startY = y + paddingTopPoints;
if (startY < y + paddingTopPoints)
startY = y + paddingTopPoints; // don't overflow top padding
var currentY = startY; var currentY = startY;
@@ -254,11 +243,45 @@ public class PdfBuilder
private void DrawLineWithMarkdown(XGraphics gfx, string line, double x, double y, double maxWidth) private void DrawLineWithMarkdown(XGraphics gfx, string line, double x, double y, double maxWidth)
{ {
if (string.IsNullOrWhiteSpace(line)) return;
var currentX = x; var currentX = x;
var i = 0; var i = 0;
while (i < line.Length) while (i < line.Length)
{ {
if (currentX - x >= maxWidth)
break;
var remainingWidth = maxWidth - (currentX - x);
// Check for small-font tag <font6> ... </font6>
if (i <= line.Length - 7 && line.Substring(i, 7) == "<font6>")
{
var endTag = line.IndexOf("</font6>", i + 7, StringComparison.Ordinal);
if (endTag != -1)
{
var inner = line.Substring(i + 7, endTag - (i + 7));
if (!string.IsNullOrEmpty(inner))
{
var measuredSmall = gfx.MeasureString(inner, _smallFont);
if (measuredSmall.Width > remainingWidth)
{
inner = TruncateTextToWidth(gfx, inner, _smallFont, remainingWidth);
measuredSmall = gfx.MeasureString(inner, _smallFont);
}
gfx.DrawString(inner, _smallFont, XBrushes.Black,
new XRect(currentX, y, remainingWidth, _smallFont.Size * 1.2),
XStringFormats.TopLeft);
currentX += measuredSmall.Width;
}
i = endTag + 8; // move past </font6>
continue;
}
}
// Check for bold marker (**) // Check for bold marker (**)
if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*') if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*')
{ {
@@ -267,11 +290,18 @@ public class PdfBuilder
if (endIndex != -1) if (endIndex != -1)
{ {
var boldText = line.Substring(i + 2, endIndex - (i + 2)); var boldText = line.Substring(i + 2, endIndex - (i + 2));
var measured = gfx.MeasureString(boldText, _boldFont);
if (measured.Width > remainingWidth)
{
boldText = TruncateTextToWidth(gfx, boldText, _boldFont, remainingWidth);
measured = gfx.MeasureString(boldText, _boldFont);
}
// Draw bold text and measure width accurately // Draw bold text and measure width accurately
gfx.DrawString(boldText, _boldFont, XBrushes.Black, gfx.DrawString(boldText, _boldFont, XBrushes.Black,
new XRect(currentX, y, maxWidth - (currentX - x), _boldFont.Size * 1.2), new XRect(currentX, y, remainingWidth, _boldFont.Size * 1.2),
XStringFormats.TopLeft); XStringFormats.TopLeft);
var measured = gfx.MeasureString(boldText, _boldFont);
currentX += measured.Width; currentX += measured.Width;
i = endIndex + 2; i = endIndex + 2;
continue; continue;
@@ -285,9 +315,16 @@ public class PdfBuilder
if (!string.IsNullOrEmpty(regularText)) if (!string.IsNullOrEmpty(regularText))
{ {
gfx.DrawString(regularText, _regularFont, XBrushes.Black,
new XRect(currentX, y, maxWidth - (currentX - x), _regularFont.Size * 1.2), XStringFormats.TopLeft);
var measured = gfx.MeasureString(regularText, _regularFont); var measured = gfx.MeasureString(regularText, _regularFont);
if (measured.Width > remainingWidth)
{
regularText = TruncateTextToWidth(gfx, regularText, _regularFont, remainingWidth);
measured = gfx.MeasureString(regularText, _regularFont);
}
gfx.DrawString(regularText, _regularFont, XBrushes.Black,
new XRect(currentX, y, remainingWidth, _regularFont.Size * 1.2), XStringFormats.TopLeft);
currentX += measured.Width; currentX += measured.Width;
} }
@@ -295,6 +332,22 @@ public class PdfBuilder
} }
} }
private string TruncateTextToWidth(XGraphics gfx, string text, XFont font, double maxWidth)
{
if (string.IsNullOrEmpty(text))
return text;
for (var len = text.Length; len > 0; len--)
{
var truncated = text.Substring(0, len);
var measured = gfx.MeasureString(truncated, font);
if (measured.Width <= maxWidth)
return truncated;
}
return string.Empty;
}
/// <summary> /// <summary>
/// Converts millimeters to points (1 mm = 2.834645669 points) /// Converts millimeters to points (1 mm = 2.834645669 points)
/// </summary> /// </summary>
@@ -303,6 +356,18 @@ public class PdfBuilder
return mm * 2.834645669; return mm * 2.834645669;
} }
private double GetCellWidthMm()
{
var availableWidthMm = 210d - _settings.pageMarginLeftMm - _settings.pageMarginRightMm;
return availableWidthMm / _settings.columnsPerPage;
}
private double GetCellHeightMm()
{
var availableHeightMm = 297d - _settings.pageMarginTopMm - _settings.pageMarginBottomMm;
return availableHeightMm / _settings.rowsPerPage;
}
// Configuration methods to allow customization // Configuration methods to allow customization
/// <summary> /// <summary>
+136
View File
@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using PdfSharp.Fonts;
using System.Globalization;
namespace Logof_Client;
public class StableFontResolver : IFontResolver
{
// family -> style -> bytes
private readonly Dictionary<string, Dictionary<string, byte[]>> _families = new(StringComparer.OrdinalIgnoreCase);
private readonly List<string> _orderedFamilies = new();
public StableFontResolver(string fontsDirectory)
{
try
{
if (string.IsNullOrEmpty(fontsDirectory)) return;
if (!Directory.Exists(fontsDirectory)) return;
var files = Directory.EnumerateFiles(fontsDirectory, "*.ttf");
foreach (var f in files)
{
try
{
var baseName = Path.GetFileNameWithoutExtension(f);
if (string.IsNullOrEmpty(baseName)) continue;
// Try to split family and style by hyphen or underscore
var family = baseName;
var style = "Regular";
var idx = baseName.IndexOf('-');
if (idx < 0) idx = baseName.IndexOf('_');
if (idx > 0)
{
family = baseName.Substring(0, idx);
style = baseName.Substring(idx + 1);
}
// Normalize common style names
style = NormalizeStyle(style);
var data = File.ReadAllBytes(f);
if (data == null || data.Length == 0) continue;
if (!_families.TryGetValue(family, out var dict))
{
dict = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
_families[family] = dict;
_orderedFamilies.Add(family);
}
if (!dict.ContainsKey(style))
dict[style] = data;
}
catch
{
// ignore individual font load errors
}
}
}
catch
{
// ignore resolver init errors
}
}
private static string NormalizeStyle(string s)
{
if (string.IsNullOrEmpty(s)) return "Regular";
s = s.ToLowerInvariant();
if (s.Contains("bold") && s.Contains("italic")) return "BoldItalic";
if (s.Contains("bold")) return "Bold";
if (s.Contains("italic") || s.Contains("oblique")) return "Italic";
if (s.Contains("regular") || s == "r") return "Regular";
return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(s);
}
public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)
{
// If requested family exists, pick corresponding style if available
string familyToUse = null;
if (!string.IsNullOrEmpty(familyName) && _families.ContainsKey(familyName))
familyToUse = familyName;
if (familyToUse == null && _orderedFamilies.Count > 0)
familyToUse = _orderedFamilies[0];
if (familyToUse == null)
return new FontResolverInfo("Arial");
var style = "Regular";
if (isBold && isItalic) style = "BoldItalic";
else if (isBold) style = "Bold";
else if (isItalic) style = "Italic";
// Face name format: Family#Style
return new FontResolverInfo($"{familyToUse}#{style}");
}
public byte[] GetFont(string faceName)
{
if (string.IsNullOrEmpty(faceName)) return null;
// faceName expected as "Family#Style" or just "Family"
var family = faceName;
var style = "Regular";
var idx = faceName.IndexOf('#');
if (idx >= 0)
{
family = faceName.Substring(0, idx);
style = faceName.Substring(idx + 1);
}
style = NormalizeStyle(style);
if (_families.TryGetValue(family, out var dict))
{
// Try exact style
if (dict.TryGetValue(style, out var data)) return data;
// Fallback order
if (style != "Regular" && dict.TryGetValue("Regular", out data)) return data;
if (dict.TryGetValue("Bold", out data)) return data;
if (dict.TryGetValue("Italic", out data)) return data;
// Any available
foreach (var kv in dict) return kv.Value;
}
return null;
}
}
+17
View File
@@ -0,0 +1,17 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Logof_Client.Wiki.EditorWindow"
Title="Wiki Editor" MinWidth="600" MinHeight="350" WindowState="Maximized">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Button x:Name="BtnSave" Content="Speichern" Click="BtnSave_OnClick" />
<Button x:Name="BtnSaveAs" Content="Speichern unter..." Click="BtnSaveAs_OnClick" />
<Button x:Name="BtnDelete" Content="Löschen" Click="BtnDelete_OnClick" />
</StackPanel>
<TextBox Grid.Row="1" x:Name="TbContent" />
</Grid>
</Window>
+59
View File
@@ -0,0 +1,59 @@
using System;
using System.IO;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace Logof_Client.Wiki;
public partial class EditorWindow : Window
{
public string filename = "";
public EditorWindow(string filename = "")
{
InitializeComponent();
this.filename = filename;
if (!string.IsNullOrWhiteSpace(filename) && File.Exists(filename))
{
var content = TbContent;
if (content != null) content.Text = File.ReadAllText(this.filename);
Title = "Wiki Editor - " + filename;
}
else if (!string.IsNullOrWhiteSpace(filename))
{
MessageBox.Show(null, "Die Datei existiert nicht", "Fehler");
Close();
}
}
private void BtnSave_OnClick(object? sender, RoutedEventArgs e)
{
try
{
File.WriteAllText(filename, TbContent.Text);
MainWindow._instance.PopulateNavTree();
}
catch (Exception ex)
{
MessageBox.Show(null,
"Es ist ein Fehler aufgetreten. Bitte senden Sie ihn über git.mypapercloud.de/fierke/logofclient ein:\n" +
ex.StackTrace, "Fehler");
}
}
private void BtnSaveAs_OnClick(object? sender, RoutedEventArgs e)
{
MessageBox.Show(null,
"Feature noch nicht implemetiert.\nErstelle neue Dateien unter " + Global._instance.wiki_storage_path,
"Fehler");
}
private async void BtnDelete_OnClick(object? sender, RoutedEventArgs e)
{
var result = await MessageBox.Show(null, "Sicher?", "Sicher?", MessageBoxButton.YesNo);
if (result == MessageBoxResult.No) return;
File.Delete(filename);
MainWindow._instance.PopulateNavTree();
Close();
}
}
+129
View File
@@ -0,0 +1,129 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Media;
using Markdig;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using System.Text;
namespace Logof_Client.Wiki;
public static class MarkdownRenderer
{
public static Control Render(string markdown)
{
var panel = new StackPanel { Spacing = 6 };
if (string.IsNullOrWhiteSpace(markdown)) return panel;
var doc = Markdown.Parse(markdown);
foreach (var block in doc)
{
switch (block)
{
case HeadingBlock hb:
{
var text = GetInlineText(hb.Inline);
var tb = new TextBlock
{
Text = text,
FontWeight = FontWeight.Bold,
Margin = new Avalonia.Thickness(0, hb.Level == 1 ? 6 : 2, 0, 2)
};
tb.FontSize = hb.Level switch { 1 => 22, 2 => 18, 3 => 16, _ => 14 };
panel.Children.Add(tb);
break;
}
case ParagraphBlock pb:
{
var text = GetInlineText(pb.Inline);
var tb = new TextBlock { Text = text, TextWrapping = Avalonia.Media.TextWrapping.Wrap };
panel.Children.Add(tb);
break;
}
case FencedCodeBlock cb:
{
var sb = new StringBuilder();
foreach (var line in cb.Lines.Lines)
{
sb.Append(line.ToString());
}
var codeBox = new TextBox
{
Text = sb.ToString(),
FontFamily = "Consolas, monospace",
IsReadOnly = true,
AcceptsReturn = true
};
panel.Children.Add(codeBox);
break;
}
case ListBlock lb:
{
var sp = new StackPanel { Spacing = 2 };
var number = 1;
foreach (var item in lb)
{
if (item is ListItemBlock lib)
{
var itemText = new StringBuilder();
foreach (var sub in lib)
{
if (sub is ParagraphBlock pp)
itemText.Append(GetInlineText(pp.Inline));
}
var tb = new TextBlock { Text = (lb.IsOrdered ? (number++ + ". ") : "• ") + itemText.ToString() };
sp.Children.Add(tb);
}
}
panel.Children.Add(sp);
break;
}
default:
{
// fallback: raw text
panel.Children.Add(new TextBlock { Text = block.ToString() });
break;
}
}
}
return panel;
}
private static string GetInlineText(ContainerInline? container)
{
if (container == null) return string.Empty;
var sb = new StringBuilder();
foreach (var inline in container)
{
switch (inline)
{
case LiteralInline li:
sb.Append(li.Content.ToString());
break;
case EmphasisInline ei:
sb.Append(GetInlineText(ei));
break;
case CodeInline ci:
sb.Append(ci.Content);
break;
case LinkInline li:
sb.Append(GetInlineText(li));
break;
case LineBreakInline:
sb.Append("\n");
break;
default:
sb.Append(inline.ToString());
break;
}
}
return sb.ToString();
}
}
+13
View File
@@ -0,0 +1,13 @@
using System.Collections.ObjectModel;
namespace Logof_Client.Wiki;
public class WikiItem
{
public string Name { get; set; }
public string Path { get; set; }
public bool IsFolder { get; set; }
public ObservableCollection<WikiItem> Children { get; } = new();
public override string ToString() => Name;
}
+71
View File
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Logof_Client.Wiki;
public class WikiService
{
public WikiService(string wikiRoot = null)
{
// prefer global wiki storage path if configured
if (Global._instance != null && !string.IsNullOrWhiteSpace(Global._instance.wiki_storage_path))
{
var cfg = Global._instance.wiki_storage_path;
WikiRootFullPath = Path.IsPathRooted(cfg)
? cfg
: Path.Combine(Directory.GetCurrentDirectory(), cfg);
WikiRoot = WikiRootFullPath;
}
else
{
if (wikiRoot == null)
wikiRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"logofclient",
"wiki");
WikiRoot = wikiRoot;
WikiRootFullPath = Path.Combine(Directory.GetCurrentDirectory(), wikiRoot);
}
}
public string WikiRoot { get; }
public string WikiRootFullPath { get; }
public List<WikiItem> GetRootItems()
{
var list = new List<WikiItem>();
if (!Directory.Exists(WikiRootFullPath)) return list;
var dirInfo = new DirectoryInfo(WikiRootFullPath);
// Add folders
foreach (var dir in dirInfo.GetDirectories()) list.Add(BuildFolderItem(dir));
// Add files in root
foreach (var file in dirInfo.GetFiles("*.md"))
list.Add(new WikiItem { Name = file.Name, Path = file.FullName, IsFolder = false });
return list.OrderBy(i => i.IsFolder ? 0 : 1).ToList();
}
private WikiItem BuildFolderItem(DirectoryInfo dir)
{
var node = new WikiItem { Name = dir.Name, Path = dir.FullName, IsFolder = true };
foreach (var subdir in dir.GetDirectories()) node.Children.Add(BuildFolderItem(subdir));
foreach (var file in dir.GetFiles("*.md"))
node.Children.Add(new WikiItem { Name = file.Name, Path = file.FullName, IsFolder = false });
return node;
}
public Task<string?> LoadFileContentAsync(string path)
{
if (!File.Exists(path)) return Task.FromResult<string?>(null);
return File.ReadAllTextAsync(path);
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.