Add configurable default shift and confirmation dialog
This commit is contained in:
@@ -7,6 +7,7 @@ PANDA-Uninstall.Payload.exe
|
||||
panda-preview.png
|
||||
panda-import-assistent.png
|
||||
panda-setup.png
|
||||
panda-settings.png
|
||||
|
||||
# Lokale Exporte und temporäre Tests
|
||||
export.csv
|
||||
|
||||
+4
-4
@@ -11,8 +11,8 @@ using Microsoft.Win32;
|
||||
[assembly: AssemblyDescription("Installer für PANDA")]
|
||||
[assembly: AssemblyProduct("PANDA")]
|
||||
[assembly: AssemblyCompany("PANDA")]
|
||||
[assembly: AssemblyVersion("1.3.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.3.0.0")]
|
||||
[assembly: AssemblyVersion("1.4.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.4.0.0")]
|
||||
|
||||
namespace PandaSetup
|
||||
{
|
||||
@@ -143,7 +143,7 @@ namespace PandaSetup
|
||||
});
|
||||
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,
|
||||
AutoSize = true,
|
||||
Location = new Point(2, 73)
|
||||
@@ -329,7 +329,7 @@ namespace PandaSetup
|
||||
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\PANDA"))
|
||||
{
|
||||
key.SetValue("DisplayName", "PANDA");
|
||||
key.SetValue("DisplayVersion", "1.3.0");
|
||||
key.SetValue("DisplayVersion", "1.4.0");
|
||||
key.SetValue("DisplayIcon", applicationPath);
|
||||
key.SetValue("Publisher", "PANDA");
|
||||
key.SetValue("InstallLocation", target);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+295
-5
@@ -11,8 +11,8 @@ using System.Windows.Forms;
|
||||
[assembly: AssemblyDescription("Pseudonymisierung alphanumerischer Nutzdaten durch Alphabetverschiebung")]
|
||||
[assembly: AssemblyProduct("PANDA")]
|
||||
[assembly: AssemblyCompany("PANDA")]
|
||||
[assembly: AssemblyVersion("1.3.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.3.0.0")]
|
||||
[assembly: AssemblyVersion("1.4.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.4.0.0")]
|
||||
|
||||
namespace Panda
|
||||
{
|
||||
@@ -57,6 +57,22 @@ namespace Panda
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
private readonly string csvPath;
|
||||
@@ -683,9 +920,11 @@ namespace Panda
|
||||
private readonly Label fileLabel = new Label();
|
||||
private readonly Button exportButton = new Button();
|
||||
private readonly Button resetButton = new Button();
|
||||
private readonly Button settingsButton = new Button();
|
||||
|
||||
private CsvDocument document;
|
||||
private string importedPath;
|
||||
private AppSettings appSettings = AppSettings.Load();
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
@@ -742,7 +981,7 @@ namespace Panda
|
||||
var toolbar = new TableLayoutPanel
|
||||
{
|
||||
Dock = DockStyle.Fill,
|
||||
ColumnCount = 8,
|
||||
ColumnCount = 9,
|
||||
RowCount = 2,
|
||||
Padding = new Padding(12, 10, 12, 8),
|
||||
BackColor = Color.White,
|
||||
@@ -755,6 +994,7 @@ namespace Panda
|
||||
toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 135));
|
||||
toolbar.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 108));
|
||||
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.RowStyles.Add(new RowStyle(SizeType.Absolute, 34));
|
||||
toolbar.RowStyles.Add(new RowStyle(SizeType.Absolute, 28));
|
||||
@@ -773,7 +1013,7 @@ namespace Panda
|
||||
|
||||
stepNumeric.Minimum = 1;
|
||||
stepNumeric.Maximum = 25;
|
||||
stepNumeric.Value = 1;
|
||||
stepNumeric.Value = Math.Max(1, Math.Min(25, appSettings.DefaultShift));
|
||||
stepNumeric.Dock = DockStyle.Fill;
|
||||
stepNumeric.TextAlign = HorizontalAlignment.Center;
|
||||
stepNumeric.Margin = new Padding(7, 3, 7, 3);
|
||||
@@ -797,6 +1037,11 @@ namespace Panda
|
||||
exportButton.Click += delegate { ExportCsv(); };
|
||||
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.ForeColor = Muted;
|
||||
fileLabel.AutoEllipsis = true;
|
||||
@@ -813,7 +1058,7 @@ namespace Panda
|
||||
Dock = DockStyle.Fill,
|
||||
TextAlign = ContentAlignment.MiddleRight
|
||||
};
|
||||
toolbar.SetColumnSpan(hint, 4);
|
||||
toolbar.SetColumnSpan(hint, 5);
|
||||
toolbar.Controls.Add(hint, 4, 1);
|
||||
|
||||
var grids = new TableLayoutPanel
|
||||
@@ -894,6 +1139,29 @@ namespace Panda
|
||||
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)
|
||||
{
|
||||
grid.Dock = DockStyle.Fill;
|
||||
@@ -1104,6 +1372,18 @@ namespace Panda
|
||||
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;
|
||||
foreach (var coordinate in cells)
|
||||
{
|
||||
@@ -1119,6 +1399,16 @@ namespace Panda
|
||||
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()
|
||||
{
|
||||
var result = new List<Tuple<int, int>>();
|
||||
|
||||
@@ -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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -32,6 +32,18 @@ namespace Panda
|
||||
AssertEqual("C", filtered.Headers[0], "selected column order");
|
||||
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");
|
||||
try
|
||||
{
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ using Microsoft.Win32;
|
||||
[assembly: AssemblyDescription("Deinstallationsprogramm für PANDA")]
|
||||
[assembly: AssemblyProduct("PANDA")]
|
||||
[assembly: AssemblyCompany("PANDA")]
|
||||
[assembly: AssemblyVersion("1.3.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.3.0.0")]
|
||||
[assembly: AssemblyVersion("1.4.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.4.0.0")]
|
||||
|
||||
namespace PandaUninstall
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user