94 lines
3.4 KiB
Python
94 lines
3.4 KiB
Python
"""Persistent application settings (recent files, defaults, window state)."""
|
|
|
|
# This file is part of ISO Scalpel.
|
|
# Copyright (C) 2025 Jeremy Anderson <info@dcos.net>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, see <https://www.gnu.org/licenses/>
|
|
# or write to the Free Software Foundation, Inc., 51 Franklin Street,
|
|
# Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from dataclasses import asdict, dataclass, field
|
|
|
|
|
|
def _config_dir() -> str:
|
|
base = os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser("~/.config")
|
|
return os.path.join(base, "iso-scalpel")
|
|
|
|
|
|
def _config_path() -> str:
|
|
return os.path.join(_config_dir(), "settings.json")
|
|
|
|
|
|
@dataclass
|
|
class Settings:
|
|
recent_files: list[str] = field(default_factory=list)
|
|
last_dir: str = os.path.expanduser("~")
|
|
show_hidden: bool = False
|
|
confirm_delete: bool = True
|
|
default_volume_label: str = "CDROM"
|
|
default_interchange_level: int = 1
|
|
default_joliet: int = 3
|
|
default_rock_ridge: str = "1.09"
|
|
default_udf: str = "2.60"
|
|
default_block_size: int = 2048
|
|
window_width: int = 1100
|
|
window_height: int = 720
|
|
splitter_sizes: list[int] = field(default_factory=lambda: [500, 500])
|
|
|
|
# ------------------------------------------------------------------
|
|
@classmethod
|
|
def load(cls) -> Settings:
|
|
"""Load settings from disk, merging onto a fresh instance.
|
|
|
|
A missing file, a permission error, or a corrupt JSON payload yields
|
|
the default settings rather than aborting startup.
|
|
"""
|
|
try:
|
|
with open(_config_path(), encoding="utf-8") as fh:
|
|
data = json.load(fh)
|
|
except (OSError, json.JSONDecodeError):
|
|
return cls()
|
|
if not isinstance(data, dict):
|
|
return cls()
|
|
# Merge onto defaults so new fields added in a later release do not
|
|
# break a settings file written by an older one.
|
|
merged = cls()
|
|
for key, value in data.items():
|
|
if hasattr(merged, key):
|
|
setattr(merged, key, value)
|
|
return merged
|
|
|
|
def save(self) -> None:
|
|
"""Persist settings to disk. Failures are reported on stderr but
|
|
never raised: a read-only home directory must not crash the app."""
|
|
try:
|
|
os.makedirs(_config_dir(), exist_ok=True)
|
|
with open(_config_path(), "w", encoding="utf-8") as fh:
|
|
json.dump(asdict(self), fh, indent=2)
|
|
except OSError as exc:
|
|
sys.stderr.write(f"warning: could not save settings: {exc}\n")
|
|
|
|
# ------------------------------------------------------------------ helpers
|
|
def add_recent(self, path: str) -> None:
|
|
path = os.path.abspath(path)
|
|
if path in self.recent_files:
|
|
self.recent_files.remove(path)
|
|
self.recent_files.insert(0, path)
|
|
self.recent_files = self.recent_files[:10]
|