1 Commits
Author SHA1 Message Date
PANDA Project 4bbdd85ae1 Add configurable default shift and confirmation dialog 2026-08-07 15:07:52 +02:00
8 changed files with 320 additions and 11 deletions
+1
View File
@@ -7,6 +7,7 @@ PANDA-Uninstall.Payload.exe
panda-preview.png panda-preview.png
panda-import-assistent.png panda-import-assistent.png
panda-setup.png panda-setup.png
panda-settings.png
# Lokale Exporte und temporäre Tests # Lokale Exporte und temporäre Tests
export.csv export.csv
+4 -4
View File
@@ -11,8 +11,8 @@ using Microsoft.Win32;
[assembly: AssemblyDescription("Installer für PANDA")] [assembly: AssemblyDescription("Installer für PANDA")]
[assembly: AssemblyProduct("PANDA")] [assembly: AssemblyProduct("PANDA")]
[assembly: AssemblyCompany("PANDA")] [assembly: AssemblyCompany("PANDA")]
[assembly: AssemblyVersion("1.3.0.0")] [assembly: AssemblyVersion("1.4.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyFileVersion("1.4.0.0")]
namespace PandaSetup namespace PandaSetup
{ {
@@ -143,7 +143,7 @@ namespace PandaSetup
}); });
heading.Controls.Add(new Label heading.Controls.Add(new Label
{ {
Text = "Version 1.3.0 • Installation für den aktuellen Windows-Benutzer", Text = "Version 1.4.0 • Installation für den aktuellen Windows-Benutzer",
ForeColor = Blue, ForeColor = Blue,
AutoSize = true, AutoSize = true,
Location = new Point(2, 73) Location = new Point(2, 73)
@@ -329,7 +329,7 @@ namespace PandaSetup
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\PANDA")) using (RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\PANDA"))
{ {
key.SetValue("DisplayName", "PANDA"); key.SetValue("DisplayName", "PANDA");
key.SetValue("DisplayVersion", "1.3.0"); key.SetValue("DisplayVersion", "1.4.0");
key.SetValue("DisplayIcon", applicationPath); key.SetValue("DisplayIcon", applicationPath);
key.SetValue("Publisher", "PANDA"); key.SetValue("Publisher", "PANDA");
key.SetValue("InstallLocation", target); key.SetValue("InstallLocation", target);
Binary file not shown.
BIN
View File
Binary file not shown.
+295 -5
View File
@@ -11,8 +11,8 @@ using System.Windows.Forms;
[assembly: AssemblyDescription("Pseudonymisierung alphanumerischer Nutzdaten durch Alphabetverschiebung")] [assembly: AssemblyDescription("Pseudonymisierung alphanumerischer Nutzdaten durch Alphabetverschiebung")]
[assembly: AssemblyProduct("PANDA")] [assembly: AssemblyProduct("PANDA")]
[assembly: AssemblyCompany("PANDA")] [assembly: AssemblyCompany("PANDA")]
[assembly: AssemblyVersion("1.3.0.0")] [assembly: AssemblyVersion("1.4.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyFileVersion("1.4.0.0")]
namespace Panda namespace Panda
{ {
@@ -57,6 +57,22 @@ namespace Panda
} }
return; return;
} }
if (args.Length == 2 && string.Equals(args[0], "--settings-screenshot", StringComparison.OrdinalIgnoreCase))
{
var previewSettings = new AppSettings { DefaultShift = 6, ConfirmBeforeShift = true };
using (var settingsForm = new SettingsForm(previewSettings))
{
settingsForm.Show();
Application.DoEvents();
using (var bitmap = new Bitmap(settingsForm.Width, settingsForm.Height))
{
settingsForm.DrawToBitmap(bitmap, new Rectangle(Point.Empty, settingsForm.Size));
bitmap.Save(args[1], System.Drawing.Imaging.ImageFormat.Png);
}
settingsForm.Close();
}
return;
}
Application.Run(new MainForm()); Application.Run(new MainForm());
} }
} }
@@ -314,6 +330,227 @@ namespace Panda
} }
} }
internal sealed class AppSettings
{
public int DefaultShift = 1;
public bool ConfirmBeforeShift = true;
private static string SettingsPath
{
get
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "PANDA", "settings.ini");
}
}
public static AppSettings Load()
{
try
{
if (File.Exists(SettingsPath))
return Parse(File.ReadAllLines(SettingsPath));
}
catch
{
}
return new AppSettings();
}
public void Save()
{
string directory = Path.GetDirectoryName(SettingsPath);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
File.WriteAllLines(SettingsPath, Serialize(), new UTF8Encoding(false));
}
internal static AppSettings Parse(IEnumerable<string> lines)
{
var settings = new AppSettings();
foreach (string line in lines)
{
if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith("#"))
continue;
int separator = line.IndexOf('=');
if (separator <= 0)
continue;
string key = line.Substring(0, separator).Trim();
string value = line.Substring(separator + 1).Trim();
if (string.Equals(key, "DefaultShift", StringComparison.OrdinalIgnoreCase))
{
int parsed;
if (int.TryParse(value, out parsed))
settings.DefaultShift = Math.Max(1, Math.Min(25, parsed));
}
else if (string.Equals(key, "ConfirmBeforeShift", StringComparison.OrdinalIgnoreCase))
{
bool parsed;
if (bool.TryParse(value, out parsed))
settings.ConfirmBeforeShift = parsed;
}
}
return settings;
}
internal string[] Serialize()
{
return new[]
{
"DefaultShift=" + DefaultShift,
"ConfirmBeforeShift=" + ConfirmBeforeShift
};
}
}
internal sealed class SettingsForm : Form
{
private readonly Color Navy = Color.FromArgb(24, 38, 58);
private readonly Color Blue = Color.FromArgb(41, 112, 255);
private readonly Color Background = Color.FromArgb(244, 247, 251);
private readonly Color Muted = Color.FromArgb(94, 108, 128);
private readonly NumericUpDown defaultShiftNumeric = new NumericUpDown();
private readonly CheckBox confirmationCheckBox = new CheckBox();
public int DefaultShift { get { return (int)defaultShiftNumeric.Value; } }
public bool ConfirmBeforeShift { get { return confirmationCheckBox.Checked; } }
public SettingsForm(AppSettings settings)
{
Text = "PANDA Einstellungen";
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
ClientSize = new Size(560, 330);
BackColor = Background;
Font = new Font("Segoe UI", 9F);
Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath) ?? SystemIcons.Application;
BuildLayout(settings);
}
private void BuildLayout(AppSettings settings)
{
var root = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 1,
RowCount = 3,
Padding = new Padding(24, 20, 24, 18),
BackColor = Background
};
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 78));
root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 58));
Controls.Add(root);
var heading = new Panel { Dock = DockStyle.Fill };
heading.Controls.Add(new Label
{
Text = "Einstellungen",
Font = new Font("Segoe UI Semibold", 18F),
ForeColor = Navy,
AutoSize = true,
Location = new Point(0, 0)
});
heading.Controls.Add(new Label
{
Text = "Lege die Standardwerte für zukünftige Umwandlungen fest.",
ForeColor = Muted,
AutoSize = true,
Location = new Point(2, 40)
});
root.Controls.Add(heading, 0, 0);
var card = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 2,
RowCount = 4,
BackColor = Color.White,
Padding = new Padding(18, 14, 18, 14),
Margin = new Padding(0, 0, 0, 12)
};
card.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
card.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 90));
card.RowStyles.Add(new RowStyle(SizeType.Absolute, 28));
card.RowStyles.Add(new RowStyle(SizeType.Absolute, 38));
card.RowStyles.Add(new RowStyle(SizeType.Absolute, 16));
card.RowStyles.Add(new RowStyle(SizeType.Absolute, 42));
var shiftLabel = new Label
{
Text = "Standard-Zählwert",
Font = new Font("Segoe UI Semibold", 10F),
ForeColor = Navy,
AutoSize = true,
Anchor = AnchorStyles.Left
};
card.Controls.Add(shiftLabel, 0, 0);
card.SetColumnSpan(shiftLabel, 2);
card.Controls.Add(new Label
{
Text = "Dieser Wert wird beim Programmstart und nach dem Speichern voreingestellt.",
ForeColor = Muted,
AutoSize = true,
Anchor = AnchorStyles.Left
}, 0, 1);
defaultShiftNumeric.Minimum = 1;
defaultShiftNumeric.Maximum = 25;
defaultShiftNumeric.Value = Math.Max(1, Math.Min(25, settings.DefaultShift));
defaultShiftNumeric.TextAlign = HorizontalAlignment.Center;
defaultShiftNumeric.Dock = DockStyle.Fill;
defaultShiftNumeric.Margin = new Padding(8, 5, 0, 5);
card.Controls.Add(defaultShiftNumeric, 1, 1);
confirmationCheckBox.Text = "Vor jeder Umwandlung eine Bestätigung mit dem gewählten Zählwert anzeigen";
confirmationCheckBox.Checked = settings.ConfirmBeforeShift;
confirmationCheckBox.AutoSize = true;
confirmationCheckBox.ForeColor = Navy;
confirmationCheckBox.Anchor = AnchorStyles.Left;
card.SetColumnSpan(confirmationCheckBox, 2);
card.Controls.Add(confirmationCheckBox, 0, 3);
root.Controls.Add(card, 0, 1);
var footer = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 4,
RowCount = 1,
Padding = new Padding(0, 10, 0, 0)
};
footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
footer.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 130));
footer.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 10));
footer.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 130));
footer.RowStyles.Add(new RowStyle(SizeType.Absolute, 36));
var cancelButton = new Button
{
Text = "Abbrechen",
DialogResult = DialogResult.Cancel,
Dock = DockStyle.Fill,
FlatStyle = FlatStyle.Flat,
BackColor = Color.White,
ForeColor = Navy,
Margin = new Padding(0)
};
cancelButton.FlatAppearance.BorderColor = Color.FromArgb(206, 216, 230);
var saveButton = new Button
{
Text = "Speichern",
DialogResult = DialogResult.OK,
Dock = DockStyle.Fill,
FlatStyle = FlatStyle.Flat,
BackColor = Blue,
ForeColor = Color.White,
Margin = new Padding(0)
};
saveButton.FlatAppearance.BorderSize = 0;
footer.Controls.Add(cancelButton, 1, 0);
footer.Controls.Add(saveButton, 3, 0);
root.Controls.Add(footer, 0, 2);
AcceptButton = saveButton;
CancelButton = cancelButton;
}
}
internal sealed class ImportWizardForm : Form internal sealed class ImportWizardForm : Form
{ {
private readonly string csvPath; private readonly string csvPath;
@@ -683,9 +920,11 @@ namespace Panda
private readonly Label fileLabel = new Label(); private readonly Label fileLabel = new Label();
private readonly Button exportButton = new Button(); private readonly Button exportButton = new Button();
private readonly Button resetButton = new Button(); private readonly Button resetButton = new Button();
private readonly Button settingsButton = new Button();
private CsvDocument document; private CsvDocument document;
private string importedPath; private string importedPath;
private AppSettings appSettings = AppSettings.Load();
public MainForm() public MainForm()
{ {
@@ -742,7 +981,7 @@ namespace Panda
var toolbar = new TableLayoutPanel var toolbar = new TableLayoutPanel
{ {
Dock = DockStyle.Fill, Dock = DockStyle.Fill,
ColumnCount = 8, ColumnCount = 9,
RowCount = 2, RowCount = 2,
Padding = new Padding(12, 10, 12, 8), Padding = new Padding(12, 10, 12, 8),
BackColor = Color.White, BackColor = Color.White,
@@ -755,6 +994,7 @@ namespace Panda
toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 135)); toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 135));
toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 108)); toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 108));
toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 122)); toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 122));
toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 116));
toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
toolbar.RowStyles.Add(new RowStyle(SizeType.Absolute, 34)); toolbar.RowStyles.Add(new RowStyle(SizeType.Absolute, 34));
toolbar.RowStyles.Add(new RowStyle(SizeType.Absolute, 28)); toolbar.RowStyles.Add(new RowStyle(SizeType.Absolute, 28));
@@ -773,7 +1013,7 @@ namespace Panda
stepNumeric.Minimum = 1; stepNumeric.Minimum = 1;
stepNumeric.Maximum = 25; stepNumeric.Maximum = 25;
stepNumeric.Value = 1; stepNumeric.Value = Math.Max(1, Math.Min(25, appSettings.DefaultShift));
stepNumeric.Dock = DockStyle.Fill; stepNumeric.Dock = DockStyle.Fill;
stepNumeric.TextAlign = HorizontalAlignment.Center; stepNumeric.TextAlign = HorizontalAlignment.Center;
stepNumeric.Margin = new Padding(7, 3, 7, 3); stepNumeric.Margin = new Padding(7, 3, 7, 3);
@@ -797,6 +1037,11 @@ namespace Panda
exportButton.Click += delegate { ExportCsv(); }; exportButton.Click += delegate { ExportCsv(); };
toolbar.Controls.Add(exportButton, 6, 0); toolbar.Controls.Add(exportButton, 6, 0);
settingsButton.Text = "Einstellungen";
StyleSecondaryButton(settingsButton);
settingsButton.Click += delegate { OpenSettings(); };
toolbar.Controls.Add(settingsButton, 7, 0);
fileLabel.Text = "Noch keine CSV geladen"; fileLabel.Text = "Noch keine CSV geladen";
fileLabel.ForeColor = Muted; fileLabel.ForeColor = Muted;
fileLabel.AutoEllipsis = true; fileLabel.AutoEllipsis = true;
@@ -813,7 +1058,7 @@ namespace Panda
Dock = DockStyle.Fill, Dock = DockStyle.Fill,
TextAlign = ContentAlignment.MiddleRight TextAlign = ContentAlignment.MiddleRight
}; };
toolbar.SetColumnSpan(hint, 4); toolbar.SetColumnSpan(hint, 5);
toolbar.Controls.Add(hint, 4, 1); toolbar.Controls.Add(hint, 4, 1);
var grids = new TableLayoutPanel var grids = new TableLayoutPanel
@@ -894,6 +1139,29 @@ namespace Panda
button.FlatAppearance.BorderColor = Color.FromArgb(206, 216, 230); button.FlatAppearance.BorderColor = Color.FromArgb(206, 216, 230);
} }
private void OpenSettings()
{
using (var dialog = new SettingsForm(appSettings))
{
if (dialog.ShowDialog(this) != DialogResult.OK)
return;
appSettings.DefaultShift = dialog.DefaultShift;
appSettings.ConfirmBeforeShift = dialog.ConfirmBeforeShift;
try
{
appSettings.Save();
stepNumeric.Value = appSettings.DefaultShift;
statusLabel.Text = "Einstellungen gespeichert. Standard-Zählwert: " + appSettings.DefaultShift + ".";
statusLabel.ForeColor = Color.FromArgb(29, 132, 88);
}
catch (Exception exception)
{
MessageBox.Show(this, "Die Einstellungen konnten nicht gespeichert werden.\r\n\r\n" + exception.Message, "Speichern fehlgeschlagen", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void ConfigureGrid(DataGridView grid, bool selectable) private void ConfigureGrid(DataGridView grid, bool selectable)
{ {
grid.Dock = DockStyle.Fill; grid.Dock = DockStyle.Fill;
@@ -1104,6 +1372,18 @@ namespace Panda
return; return;
} }
if (appSettings.ConfirmBeforeShift)
{
string message = BuildConfirmationMessage(amount, cells.Count, scopeComboBox.SelectedIndex == 2);
DialogResult confirmation = MessageBox.Show(this, message, "Umwandlung bestätigen", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
if (confirmation != DialogResult.Yes)
{
statusLabel.Text = "Umwandlung abgebrochen.";
statusLabel.ForeColor = Muted;
return;
}
}
int changed = 0; int changed = 0;
foreach (var coordinate in cells) foreach (var coordinate in cells)
{ {
@@ -1119,6 +1399,16 @@ namespace Panda
statusLabel.ForeColor = Color.FromArgb(29, 132, 88); statusLabel.ForeColor = Color.FromArgb(29, 132, 88);
} }
internal static string BuildConfirmationMessage(int amount, int cellCount, bool allValues)
{
string subject = allValues ? "Alle Werte" : "Die ausgewählten Werte";
string direction = amount > 0 ? "hochgezählt" : "runtergezählt";
return subject + " werden um " + Math.Abs(amount) + " " + direction + ".\r\n\r\n"
+ "Gewählter Zählwert: " + Math.Abs(amount) + "\r\n"
+ "Betroffene Zellen: " + cellCount + "\r\n\r\n"
+ "Möchtest du die Umwandlung durchführen?";
}
private List<Tuple<int, int>> GetTargetCells() private List<Tuple<int, int>> GetTargetCells()
{ {
var result = new List<Tuple<int, int>>(); var result = new List<Tuple<int, int>>();
+6
View File
@@ -29,6 +29,12 @@ Alternativ `PANDA-Portable.exe` doppelt anklicken. Diese Einzeldatei benötigt k
6. Schrittweite festlegen und **Hochzählen (+)** oder **Runterzählen (-)** verwenden. 6. Schrittweite festlegen und **Hochzählen (+)** oder **Runterzählen (-)** verwenden.
7. Das Ergebnis rechts kontrollieren und mit **CSV exportieren** speichern. 7. Das Ergebnis rechts kontrollieren und mit **CSV exportieren** speichern.
## Einstellungen
Über **Einstellungen** lässt sich der Standard-Zählwert von 1 bis 25 festlegen. Dieser Wert wird benutzerbezogen gespeichert und beim nächsten Programmstart wiederhergestellt.
Standardmäßig zeigt PANDA vor jeder Umwandlung eine Bestätigung mit Richtung, gewähltem Zählwert und Anzahl der betroffenen Zellen. Diese Abfrage kann im Einstellungsfenster deaktiviert werden.
Auswahlmarkierungen werden ausschließlich in der linken Originaltabelle dargestellt. Auswahlmarkierungen werden ausschließlich in der linken Originaltabelle dargestellt.
Die Buchstaben `A-Z` und `a-z` werden zyklisch verschoben. Beispiel: `Z + 1 = A` und `a - 1 = z`. Zahlen, Leerzeichen, Satzzeichen und Umlaute bleiben unverändert. Die Buchstaben `A-Z` und `a-z` werden zyklisch verschoben. Beispiel: `Z + 1 = A` und `a - 1 = z`. Zahlen, Leerzeichen, Satzzeichen und Umlaute bleiben unverändert.
+12
View File
@@ -32,6 +32,18 @@ namespace Panda
AssertEqual("C", filtered.Headers[0], "selected column order"); AssertEqual("C", filtered.Headers[0], "selected column order");
AssertEqual("a2", filtered.Rows[1][1], "selected column values"); AssertEqual("a2", filtered.Rows[1][1], "selected column values");
var settings = AppSettings.Parse(new[] { "DefaultShift=6", "ConfirmBeforeShift=False" });
AssertEqual(6, settings.DefaultShift, "settings default shift");
AssertEqual(false, settings.ConfirmBeforeShift, "settings confirmation flag");
var boundedSettings = AppSettings.Parse(new[] { "DefaultShift=99" });
AssertEqual(25, boundedSettings.DefaultShift, "settings upper bound");
AssertEqual("DefaultShift=6", settings.Serialize()[0], "settings serialization");
string confirmationUp = MainForm.BuildConfirmationMessage(6, 12, false);
AssertEqual(true, confirmationUp.Contains("Die ausgewählten Werte werden um 6 hochgezählt."), "confirmation count up text");
AssertEqual(true, confirmationUp.Contains("Betroffene Zellen: 12"), "confirmation affected cells");
string confirmationDown = MainForm.BuildConfirmationMessage(-4, 20, true);
AssertEqual(true, confirmationDown.Contains("Alle Werte werden um 4 runtergezählt."), "confirmation count down text");
string tempPath = Path.Combine(Path.GetTempPath(), "csv-buchstaben-test-" + Guid.NewGuid().ToString("N") + ".csv"); string tempPath = Path.Combine(Path.GetTempPath(), "csv-buchstaben-test-" + Guid.NewGuid().ToString("N") + ".csv");
try try
{ {
+2 -2
View File
@@ -10,8 +10,8 @@ using Microsoft.Win32;
[assembly: AssemblyDescription("Deinstallationsprogramm für PANDA")] [assembly: AssemblyDescription("Deinstallationsprogramm für PANDA")]
[assembly: AssemblyProduct("PANDA")] [assembly: AssemblyProduct("PANDA")]
[assembly: AssemblyCompany("PANDA")] [assembly: AssemblyCompany("PANDA")]
[assembly: AssemblyVersion("1.3.0.0")] [assembly: AssemblyVersion("1.4.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyFileVersion("1.4.0.0")]
namespace PandaUninstall namespace PandaUninstall
{ {