Files
Rozpisky/Rozpisky/Cad/CadEnginesStore.cs
T
marekandClaude Opus 5 3c0e30bf35 Uživatelská data do %APPDATA%\Rozpisky místo vedle .exe
Vše, co píše uživatel, míří přes novou třídu UzivatelskaData do profilu
uživatele: cad.json, layout.json, error.log, tisk_diag.log. Dosud se
zapisovalo vedle .exe, takže .exe v Program Files (složka jen pro čtení)
nebo přeinstalace novou verzí uživatele o data připravily.

Dodávané podklady (šablona, mustr, loga, mapovani.json) se dál čtou vedle
.exe – ty se naopak při každé instalaci obnovují. Hranice mezi tím, co se
instaluje, a tím, co patří uživateli, vede přesně tudy.

Portable režim zůstává: soubor portable.txt vedle .exe vrátí staré chování
(běh z USB, vývoj s jednou složkou).

Migrace: Migrace.PrenesStaraData() při startu přenese cad.json a layout.json
ze staré polohy, pokud v cíli ještě nejsou; originál označí příponou
.migrovano. Nic se nemaže a nic novějšího se nepřepíše.

Číselníky přijdou na řadu samostatně (rozpad na dodávanou a uživatelskou
vrstvu).

Build 0 chyb / 0 varování, 36 testů zelených.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 10:39:25 +02:00

102 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.IO;
using System.Text.Encodings.Web;
using System.Text.Json;
using Rozpisky.Data;
namespace Rozpisky.Cad;
/// <summary>
/// Načítání/ukládání seznamu podporovaných CAD systémů + naposledy vybraného enginu do JSON souboru
/// v <b>uživatelské složce</b> (<see cref="Data.UzivatelskaData"/>). CAD je vlastnost stroje
/// (ne zakázky), takže výběr žije mimo projekt a je společný všem projektům a mimo instalační
/// složku, aby o něj uživatel při přeinstalaci nepřišel.
/// </summary>
public static class CadEnginesStore
{
public const string SouborNazev = "cad.json";
private static readonly JsonSerializerOptions Opts = new()
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
/// <summary>Výchozí umístění: uživatelská složka (viz <see cref="UzivatelskaData"/>).</summary>
public static string DefaultPath => UzivatelskaData.Cesta(SouborNazev);
/// <summary>Uložený výběr CADu: buď předdefinovaný engine (<see cref="VybranyProgId"/>), nebo
/// ruční režim „Manuálně" (<see cref="RucniRezim"/> + <see cref="RucniProgId"/>). Režim a ruční
/// text se drží zvlášť, aby se po startu obnovil přesně stav UI včetně (i prázdného) inputboxu
/// a aby ruční hodnota shodná s předdefinovaným ProgID nepřeklopila UI zpět do comboboxu.</summary>
public readonly record struct Vyber(string? VybranyProgId, bool RucniRezim, string? RucniProgId);
/// <summary>Načte seznam enginů a uložený výběr. Když soubor chybí/je poškozený, vrátí vestavěný
/// <see cref="Seed"/> (a rovnou ho uloží, aby šel soubor dál ručně upravovat).</summary>
public static (List<CadEngine> Engines, Vyber Vyber) Load(string? path = null)
{
path ??= DefaultPath;
try
{
if (File.Exists(path))
{
var data = JsonSerializer.Deserialize<Dto>(File.ReadAllText(path), Opts);
if (data?.Engines is { Count: > 0 })
{
// „Mrtvý“ ProgId (ručně upravený soubor) → žádný výběr comboboxu, ať nedrží
// engine, který v seznamu není. Ruční režim tím netknutý.
var vybrany = data.Engines.Any(e => e.ProgId == data.VybranyProgId)
? data.VybranyProgId : null;
return (data.Engines, new Vyber(vybrany, data.RucniRezim, data.RucniProgId));
}
// Soubor existuje, ale nenese žádný engine („null" / prázdný seznam) → stejné
// zacházení jako s rozbitým JSON, viz catch níže.
JsonConfigFile.ZalohujAOhlasPoskozeny(path);
}
}
catch (Exception ex) when (ex is IOException or JsonException or UnauthorizedAccessException)
{
// Poškozený / nečitelný soubor → fallback na seed. Soubor se hned dole přepíše seedem,
// proto se originál (třeba ručně dopsané ProgID) nejdřív zazálohuje a uživatel dozví.
JsonConfigFile.ZalohujAOhlasPoskozeny(path, ex);
}
var seed = Seed();
Save(seed, default, path);
return (seed, default);
}
/// <summary>Uloží seznam enginů + výběr (režim comboboxu vs. Manuálně a ruční text) atomicky
/// přes .tmp, aby pád uprostřed zápisu soubor nepoškodil. Chyby zápisu (např. read-only složka
/// u .exe v Program Files) propustí volajícímu.</summary>
public static void Save(List<CadEngine> engines, Vyber vyber, string? path = null)
=> JsonConfigFile.ZapisAtomicky(path ?? DefaultPath, JsonSerializer.Serialize(
new Dto
{
Engines = engines,
VybranyProgId = vyber.VybranyProgId,
RucniRezim = vyber.RucniRezim,
RucniProgId = vyber.RucniProgId,
}, Opts));
/// <summary>Vestavěný výchozí seznam CADů (databáze od programátora, PODKLAD_CAD_Layout_Tisk.md §1:
/// pořadí AutoCAD → ZWCAD → BricsCAD). Vedle verzově nezávislých ProgID („poslední nainstalovaná")
/// jsou i verzově konkrétní položky verzově nezávislé „ZWCAD.Application" se u některých instalací
/// na běžící instanci nenapojí, „ZWCAD.Application.2023" ano. Kdo potřebuje jinou verzi, dopíše ji
/// ručně přes režim „Manuálně" v UI (nebo do cad.json). Soubor jde kdykoli ručně upravit.</summary>
public static List<CadEngine> Seed() =>
[
new() { ProgId = "AutoCAD.Application", Nazev = "AutoCAD (poslední)" },
new() { ProgId = "ZWCAD.Application", Nazev = "ZWCAD (poslední)" },
new() { ProgId = "ZWCAD.Application.2023", Nazev = "ZWCAD 2023" },
new() { ProgId = "ZWCAD.Application.2022", Nazev = "ZWCAD 2022" },
new() { ProgId = "BricscadApp.AcadApplication", Nazev = "BricsCAD" },
];
private sealed class Dto
{
public List<CadEngine> Engines { get; set; } = new();
public string? VybranyProgId { get; set; }
public bool RucniRezim { get; set; }
public string? RucniProgId { get; set; }
}
}