diff options
Diffstat (limited to 'Map/Data')
| -rw-r--r-- | Map/Data/UserPreferences.swift | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/Map/Data/UserPreferences.swift b/Map/Data/UserPreferences.swift new file mode 100644 index 0000000..cf9dd7e --- /dev/null +++ b/Map/Data/UserPreferences.swift @@ -0,0 +1,322 @@ +// Copyright (C) 2024 Rubén Beltrán del Río + +// 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 3 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://map.tranquil.systems. + +import Foundation + +// MARK: - JSON Export/Import Structures +struct UserPreferencesJSON: Codable { + // MARK: - Map Preferences + var showMapBackground: Bool? + var useCustomFont: Bool? + var customFontName: String? + var defaultExportFormat: String? + var useSmartLabelPositioning: Bool? + + // MARK: - Editor Preferences + var useSmartEditor: Bool? + var useCustomEditorFont: Bool? + var customEditorFontName: String? + var editorFontSize: Double? + + // MARK: - Templates (as actual objects) + var mapTemplates: [Template]? + + // MARK: - Stages (as actual objects) + var customStages: [CustomStage]? + + // MARK: - UI Preferences + var viewStyle: String? + var zoom: Double? +} + +struct UserPreferences: Codable { + + // MARK: - Map Preferences + var showMapBackground: Bool + var useCustomFont: Bool + var customFontName: String + var defaultExportFormat: String + var useSmartLabelPositioning: Bool + + // MARK: - Editor Preferences + var useSmartEditor: Bool + var useCustomEditorFont: Bool + var customEditorFontName: String + var editorFontSize: Double + + // MARK: - Templates + var mapTemplates: Data + + // MARK: - Stages + var customStages: Data + + // MARK: - UI Preferences + var viewStyle: String + var zoom: Double + + // MARK: - Default Values + struct Defaults { + static let showMapBackground = true + static let useCustomFont = false + static let customFontName = "Helvetica" + static let defaultExportFormat = "png" + static let useSmartLabelPositioning = true + + static let useSmartEditor = false + static let useCustomEditorFont = false + static let customEditorFontName = "Menlo" + static let editorFontSize = 14.0 + + static let viewStyle = "horizontal" + static let zoom = 1.0 + + // Computed properties for complex defaults + static var mapTemplates: Data { + let defaultTemplate = Template( + name: String(localized: "map_template.default.title"), + content: String(localized: "map_template.default.content"), + isDefault: true + ) + + let emptyTemplate = Template( + name: String(localized: "map_template.empty.title"), + content: "", + isDefault: false + ) + + let templates = [defaultTemplate, emptyTemplate] + return (try? JSONEncoder().encode(templates)) ?? Data() + } + + static var customStages: Data { + let defaultStage = CustomStage.cynefinDefault() + let stages = [defaultStage] + return (try? JSONEncoder().encode(stages)) ?? Data() + } + } + + // MARK: - Initialization + init() { + self.showMapBackground = Defaults.showMapBackground + self.useCustomFont = Defaults.useCustomFont + self.customFontName = Defaults.customFontName + self.defaultExportFormat = Defaults.defaultExportFormat + self.useSmartLabelPositioning = Defaults.useSmartLabelPositioning + + self.useSmartEditor = Defaults.useSmartEditor + self.useCustomEditorFont = Defaults.useCustomEditorFont + self.customEditorFontName = Defaults.customEditorFontName + self.editorFontSize = Defaults.editorFontSize + + self.mapTemplates = Defaults.mapTemplates + self.customStages = Defaults.customStages + + self.viewStyle = Defaults.viewStyle + self.zoom = Defaults.zoom + } + + // MARK: - JSON Methods + func toJSON() -> String? { + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + do { + // Convert to JSON-friendly format + let jsonPrefs = UserPreferencesJSON( + showMapBackground: showMapBackground, + useCustomFont: useCustomFont, + customFontName: customFontName, + defaultExportFormat: defaultExportFormat, + useSmartLabelPositioning: useSmartLabelPositioning, + useSmartEditor: useSmartEditor, + useCustomEditorFont: useCustomEditorFont, + customEditorFontName: customEditorFontName, + editorFontSize: editorFontSize, + mapTemplates: templatesFromData(mapTemplates), + customStages: stagesFromData(customStages), + viewStyle: viewStyle, + zoom: zoom + ) + + let data = try encoder.encode(jsonPrefs) + return String(data: data, encoding: .utf8) + } catch { + return nil + } + } + + mutating func updateFromJSON(_ json: String) -> Bool { + guard let data = json.data(using: .utf8) else { return false } + do { + let decoder = JSONDecoder() + let jsonPrefs = try decoder.decode(UserPreferencesJSON.self, from: data) + + // Convert from JSON format back to UserPreferences + // Only update values that are present in the JSON (partial updates) + if let value = jsonPrefs.showMapBackground { + self.showMapBackground = value + } + if let value = jsonPrefs.useCustomFont { + self.useCustomFont = value + } + if let value = jsonPrefs.customFontName { + self.customFontName = value + } + if let value = jsonPrefs.defaultExportFormat { + self.defaultExportFormat = value + } + if let value = jsonPrefs.useSmartLabelPositioning { + self.useSmartLabelPositioning = value + } + if let value = jsonPrefs.useSmartEditor { + self.useSmartEditor = value + } + if let value = jsonPrefs.useCustomEditorFont { + self.useCustomEditorFont = value + } + if let value = jsonPrefs.customEditorFontName { + self.customEditorFontName = value + } + if let value = jsonPrefs.editorFontSize { + self.editorFontSize = value + } + if let value = jsonPrefs.mapTemplates { + self.mapTemplates = dataFromTemplates(value) + } + if let value = jsonPrefs.customStages { + self.customStages = dataFromStages(value) + } + if let value = jsonPrefs.viewStyle { + self.viewStyle = value + } + if let value = jsonPrefs.zoom { + self.zoom = value + } + + apply() + return true + } catch { + print("JSON import error: \(error)") + return false + } + } + + // MARK: - Helper Methods for Data/Object Conversion + private func templatesFromData(_ data: Data) -> [Template] { + guard let templates = try? JSONDecoder().decode([Template].self, from: data) else { + return [] + } + return templates + } + + private func dataFromTemplates(_ templates: [Template]) -> Data { + guard let data = try? JSONEncoder().encode(templates) else { + return Data() + } + return data + } + + private func stagesFromData(_ data: Data) -> [CustomStage] { + guard let stages = try? JSONDecoder().decode([CustomStage].self, from: data) else { + return [] + } + return stages + } + + private func dataFromStages(_ stages: [CustomStage]) -> Data { + guard let data = try? JSONEncoder().encode(stages) else { + return Data() + } + return data + } + + // MARK: - Reset to Defaults + mutating func resetToDefaults() { + self = UserPreferences() + apply() + } + + // MARK: - Apply to UserDefaults + private func apply() { + let defaults = UserDefaults.standard + + // Map Preferences + defaults.set(showMapBackground, forKey: "showMapBackground") + defaults.set(useCustomFont, forKey: "useCustomFont") + defaults.set(customFontName, forKey: "customFontName") + defaults.set(defaultExportFormat, forKey: "defaultExportFormat") + defaults.set(useSmartLabelPositioning, forKey: "useSmartLabelPositioning") + + // Editor Preferences + defaults.set(useSmartEditor, forKey: "useSmartEditor") + defaults.set(useCustomEditorFont, forKey: "useCustomEditorFont") + defaults.set(customEditorFontName, forKey: "customEditorFontName") + defaults.set(editorFontSize, forKey: "editorFontSize") + + // Templates + defaults.set(mapTemplates, forKey: "mapTemplates") + + // Stages + defaults.set(customStages, forKey: "customStages") + + // UI Preferences + defaults.set(viewStyle, forKey: "viewStyle") + defaults.set(zoom, forKey: "zoom") + } + + // MARK: - Load from UserDefaults + static func loadFromUserDefaults() -> UserPreferences { + let defaults = UserDefaults.standard + + var preferences = UserPreferences() + + // Map Preferences + preferences.showMapBackground = + defaults.object(forKey: "showMapBackground") as? Bool ?? Defaults.showMapBackground + preferences.useCustomFont = + defaults.object(forKey: "useCustomFont") as? Bool ?? Defaults.useCustomFont + preferences.customFontName = + defaults.object(forKey: "customFontName") as? String ?? Defaults.customFontName + preferences.defaultExportFormat = + defaults.object(forKey: "defaultExportFormat") as? String ?? Defaults.defaultExportFormat + preferences.useSmartLabelPositioning = + defaults.object(forKey: "useSmartLabelPositioning") as? Bool + ?? Defaults.useSmartLabelPositioning + + // Editor Preferences + preferences.useSmartEditor = + defaults.object(forKey: "useSmartEditor") as? Bool ?? Defaults.useSmartEditor + preferences.useCustomEditorFont = + defaults.object(forKey: "useCustomEditorFont") as? Bool ?? Defaults.useCustomEditorFont + preferences.customEditorFontName = + defaults.object(forKey: "customEditorFontName") as? String ?? Defaults.customEditorFontName + preferences.editorFontSize = + defaults.object(forKey: "editorFontSize") as? Double ?? Defaults.editorFontSize + + // Templates + preferences.mapTemplates = + defaults.object(forKey: "mapTemplates") as? Data ?? Defaults.mapTemplates + + // Stages + preferences.customStages = + defaults.object(forKey: "customStages") as? Data ?? Defaults.customStages + + // UI Preferences + preferences.viewStyle = defaults.object(forKey: "viewStyle") as? String ?? Defaults.viewStyle + preferences.zoom = defaults.object(forKey: "zoom") as? Double ?? Defaults.zoom + + return preferences + } +} |