// // KMBackgroundManager.swift // PDF Reader Pro // // Created by tangchao on 2022/12/23. // import Cocoa /* Pro Mac -> New New !-> Pro Mac 1.Pro Mac New 更新 Pro Mac 的模板,会新建新的 New 模板(原数据保存不变) 会出现两个 2.Pro Mac 删除 */ class KMBackgroundManager: NSObject, NSCoding { let kBackgroundFolderPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("background") let kBackgroundPlistPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("background").stringByAppendingPathComponent("background.plist") var datas: Array = [] var backgroundObjects: [KMBackgroundObject] = [] static let defaultManager = KMBackgroundManager() static let kRemovedKey = "KMBackgroundRemovedKey" override init() { super.init() // 查找原数据 if let storedData = UserDefaults.standard.value(forKey: "kBackgroundInfoSaveKey") as? Data { NSKeyedUnarchiver.setClass(KMBackgroundObject.self, forClassName: "KMBackgroundObject") NSKeyedUnarchiver.setClass(KMBackgroundManager.self, forClassName: "KMBackgroundManager") if let man = NSKeyedUnarchiver.unarchiveObject(with: storedData) as? KMBackgroundManager { for object in man.backgroundObjects { self.backgroundObjects.append(object) } } } // 原数据转换 let removedKeys = KMDataManager.ud_array(forKey: Self.kRemovedKey) as? [String] ?? [] for obj in self.backgroundObjects { let id = obj.backgroundID ?? "" if removedKeys.contains(id) { continue } let model = self.parseObject(object: obj) self.datas.append(model) } if (FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) { let dataDict = NSDictionary(contentsOfFile: kBackgroundPlistPath!) if (dataDict == nil) { return } var deleteKeys: Array = [] for keyIndex in 0 ..< (dataDict?.allKeys.count)! { let key: String = dataDict?.allKeys[keyIndex] as! String let backgroundDict: NSDictionary = dataDict?.object(forKey: key) as! NSDictionary let model = parseDictionary(dict: backgroundDict) /// 赋值id model.backgroundID = key if (model.type == .file) { if (model.image == nil) { deleteKeys.append(key) } else { self.datas.append(model) } } else { self.datas.append(model) } } if (deleteKeys.count > 0) { let newDict: NSMutableDictionary = NSMutableDictionary(dictionary: dataDict!) for key in deleteKeys { newDict.removeObject(forKey: key) } newDict.write(toFile: kBackgroundPlistPath!, atomically: true) } } } required init?(coder: NSCoder) { if let data = coder.decodeObject(forKey: "backgroundObjects") { self.backgroundObjects = data as? [KMBackgroundObject] ?? [] } } func encode(with coder: NSCoder) { // coder.encode(self.backgroundObjects, forKey: "backgroundObjects") } func addTemplate(model: KMBackgroundModel) -> Bool { if (!FileManager.default.fileExists(atPath: kBackgroundFolderPath!)) { let create: ()? = try?FileManager.default.createDirectory(atPath: kBackgroundFolderPath!, withIntermediateDirectories: false) if (create == nil) { return false } } if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) { let create = try?FileManager.default.createFile(atPath: kBackgroundPlistPath!, contents: nil) if (create == nil) { return false } } let dict = NSDictionary(contentsOfFile: kBackgroundPlistPath!) var newDict:NSMutableDictionary! if (dict != nil) { newDict = NSMutableDictionary(dictionary: dict!) } else { newDict = NSMutableDictionary() } model.modificationDate = Date().timeIntervalSince1970 let backgroundDict = self.parseModel(model: model) if (backgroundDict.isEmpty) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("文件\(model.imagePath.lastPathComponent)已损坏", comment: "") alert.runModal() return false } let tag = fetchAvailableName()//tagString() newDict.addEntries(from: [tag : backgroundDict]) if model.backgroundID.count == 0 { model.backgroundID = tag } let result = newDict.write(toFile: kBackgroundPlistPath!, atomically: true) if (result) { if (self.datas.count < 1) { self.datas.append(model) } else { self.datas.insert(model, at: 0) } } return result } func deleteTemplate(model: KMBackgroundModel) -> Bool { if model.isMigrate && model.isUpdated == false { // 原数据删除 // if (self._addRemovedTemplate(model: model)) { var removedKeys = KMDataManager.ud_array(forKey: Self.kRemovedKey) ?? [] removedKeys.append(model.backgroundID) KMDataManager.ud_set(removedKeys, forKey: Self.kRemovedKey) if (self.datas.contains(model)) { // 数据源删除 self.datas.removeObject(model) } // } return true } if (model.backgroundID.isEmpty) { return false } if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) { return false } let key: String = model.backgroundID let dictionary = NSDictionary(contentsOfFile: kBackgroundPlistPath!) var newDictionary: NSMutableDictionary! if (dictionary != nil) { newDictionary = NSMutableDictionary(dictionary: dictionary!) } else { newDictionary = NSMutableDictionary() } newDictionary.removeObject(forKey: key) let result = newDictionary.write(toFile: kBackgroundPlistPath!, atomically: true) if (result) { if (model.type == .file) { let filePath: String = model.imagePath try?FileManager.default.removeItem(atPath: filePath) } if (self.datas.contains(model)) { self.datas.removeObject(model) } } return result } func deleteAllTemplates() -> Bool { if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) { return false } let dictionary = NSDictionary(contentsOfFile: kBackgroundPlistPath!) var newDictionary: NSMutableDictionary! if (dictionary != nil) { newDictionary = NSMutableDictionary(dictionary: dictionary!) } else { newDictionary = NSMutableDictionary() } newDictionary.removeAllObjects() let result = newDictionary.write(toFile: kBackgroundPlistPath!, atomically: true) if (result) { self.datas.removeAll() } KMDataManager.ud_set(nil, forKey: Self.kRemovedKey) self._clearStored() return result } func deleteAllColorTemplates() -> Bool { if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) { return false } let dictionary = NSDictionary(contentsOfFile: kBackgroundPlistPath!) var newDictionary: NSMutableDictionary! if (dictionary != nil) { newDictionary = NSMutableDictionary(dictionary: dictionary!) } else { newDictionary = NSMutableDictionary() } let count = self.datas.count-1 var deleteArray: Array = [] for i in 0 ... count { let model = self.datas[i] if (model.type == .color) { newDictionary.removeObject(forKey: model.backgroundID as Any) deleteArray.append(model) } } let result = newDictionary.write(toFile: kBackgroundPlistPath!, atomically: true) if (result) { for model in deleteArray { self.datas.removeObject(model) } } self._clearStored() return result } func deleteAllFileTemplates() -> Bool { if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) { return false } let dictionary = NSDictionary(contentsOfFile: kBackgroundPlistPath!) var newDictionary: NSMutableDictionary! if (dictionary != nil) { newDictionary = NSMutableDictionary(dictionary: dictionary!) } else { newDictionary = NSMutableDictionary() } let count = self.datas.count-1 var deleteArray: Array = [] for i in 0 ... count { let model = self.datas[i] if (model.type == .file) { newDictionary.removeObject(forKey: model.backgroundID as Any) deleteArray.append(model) } } let result = newDictionary.write(toFile: kBackgroundPlistPath!, atomically: true) if (result) { for model in deleteArray { self.datas.removeObject(model) } } self._clearStored() return result } func updateTemplate(model: KMBackgroundModel) -> Bool { if model.isMigrate && model.isUpdated == false { // 原数据 // 移除旧数据 _ = self.deleteTemplate(model: model) // 新增新数据 model.isUpdated = true model.modificationDate = Date().timeIntervalSince1970 return self.addTemplate(model: model) } if (!FileManager.default.fileExists(atPath: kBackgroundFolderPath!)) { let create = try?FileManager.default.createDirectory(atPath: kBackgroundFolderPath!, withIntermediateDirectories: false) if (create == nil) { return false } } if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) { let create = try?FileManager.default.createFile(atPath: kBackgroundPlistPath!, contents: nil) if (create == nil) { return false } } var flagModel: KMBackgroundModel! for model_ in self.datas { if (model_.backgroundID == model.backgroundID) { flagModel = model_ break } } if (flagModel == nil) { return false } let dict = NSDictionary(contentsOfFile: kBackgroundPlistPath!) var newDict:NSMutableDictionary! if (dict != nil) { newDict = NSMutableDictionary(dictionary: dict!) } else { newDict = NSMutableDictionary() } model.isUpdated = true model.modificationDate = Date().timeIntervalSince1970 let modelDict = self.parseModel(model: model) if (modelDict.isEmpty) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("文件\(model.imagePath.lastPathComponent)已损坏", comment: "") alert.runModal() return false } newDict.setObject(modelDict, forKey: flagModel.backgroundID as NSCopying) let result = newDict.write(toFile: kBackgroundPlistPath!, atomically: true) if (result) { let index = self.datas.index(of: flagModel) self.datas[index!] = model } return result } /** `Private Methods` */ private func _addRemovedTemplate(model: KMBackgroundModel) -> Bool { if (!FileManager.default.fileExists(atPath: kBackgroundFolderPath!)) { let create: ()? = try?FileManager.default.createDirectory(atPath: kBackgroundFolderPath!, withIntermediateDirectories: false) if (create == nil) { return false } } if (!FileManager.default.fileExists(atPath: kBackgroundPlistPath!)) { let create = try?FileManager.default.createFile(atPath: kBackgroundPlistPath!, contents: nil) if (create == nil) { return false } } let dict = NSDictionary(contentsOfFile: kBackgroundPlistPath!) var newDict:NSMutableDictionary! if (dict != nil) { newDict = NSMutableDictionary(dictionary: dict!) } else { newDict = NSMutableDictionary() } model.isRemoved = true model.modificationDate = Date().timeIntervalSince1970 let backgroundDict = self.parseModel(model: model) if (backgroundDict.isEmpty) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("文件\(model.imagePath.lastPathComponent)已损坏", comment: "") alert.runModal() return false } let tag = model.backgroundID newDict.addEntries(from: [tag : backgroundDict]) model.backgroundID = tag let result = newDict.write(toFile: kBackgroundPlistPath!, atomically: true) return result } private func parseModel(model: KMBackgroundModel) -> Dictionary { let tag = tagString() var dict: [String : Any] = [:] if (model.type == .color) { var red: CGFloat = 0.0 var green: CGFloat = 0.0 var blue: CGFloat = 0.0 model.color!.usingColorSpaceName(NSColorSpaceName.calibratedRGB)?.getRed(&red, green: &green, blue: &blue, alpha: nil) dict["red"] = red dict["green"] = green dict["blue"] = blue } else { if (!FileManager.default.fileExists(atPath: kBackgroundFolderPath!)) { try?FileManager.default.createDirectory(atPath: kBackgroundFolderPath!, withIntermediateDirectories: false) } let path = kBackgroundFolderPath?.stringByAppendingPathComponent("\(tag).png") let image = model.image let data = image?.tiffRepresentation let imageRep = NSBitmapImageRep(data: data!) imageRep?.size = image!.size var imageData: Data! let pathExtension = model.imagePath.components(separatedBy: ".").last if (pathExtension?.lowercased() == "png") { imageData = imageRep?.representation(using: NSBitmapImageRep.FileType.png, properties: [:]) } else { imageData = imageRep?.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [:]) } do { try imageData.write(to: URL(fileURLWithPath: path!)) dict["imagePath"] = path?.lastPathComponent } catch { KMPrint("Failed to write to disk.") return [:] } } dict["type"] = model.type.rawValue dict["scale"] = model.scale dict["opacity"] = model.opacity dict["rotation"] = model.rotation dict["horizontalMode"] = model.horizontalMode dict["horizontalSpace"] = model.horizontalSpace dict["verticalMode"] = model.verticalMode dict["verticalSpace"] = model.verticalSpace dict["pageRangeType"] = model.pageRangeType.rawValue dict["pagesString"] = model.pagesString dict["isMigrate"] = model.isMigrate dict["isUpdated"] = model.isUpdated if let data = model.modificationDate { dict["modificationDate"] = data } dict["isRemoved"] = model.isRemoved return dict } private func parseDictionary(dict: NSDictionary) -> KMBackgroundModel { let model = KMBackgroundModel() model.type = KMBackgroundType(rawValue: dict.object(forKey: "type") as! Int)! if (model.type == .file) { let path = kBackgroundFolderPath?.stringByAppendingPathComponent(dict.object(forKey: "imagePath") as! String) if (FileManager.default.fileExists(atPath: path!)) { model.image = NSImage(contentsOfFile: path!) model.imagePath = path! } else { model.image = nil } } else { let red: CGFloat = dict.object(forKey: "red") as! CGFloat let green: CGFloat = dict.object(forKey: "green") as! CGFloat let blue: CGFloat = dict.object(forKey: "blue") as! CGFloat model.color = NSColor(red: red, green: green, blue: blue, alpha: 1.0) } model.scale = dict.object(forKey: "scale") as! CGFloat model.rotation = CGFloat(Int(dict.object(forKey: "rotation") as! CGFloat)) model.opacity = (dict.object(forKey: "opacity") as! CGFloat) model.verticalMode = (dict.object(forKey: "verticalMode") as! Int) model.verticalSpace = (dict.object(forKey: "verticalSpace") as! CGFloat) model.horizontalMode = (dict.object(forKey: "horizontalMode") as! Int) model.horizontalSpace = (dict.object(forKey: "horizontalSpace") as! CGFloat) model.pageRangeType = KMWatermarkeModelPageRangeType.init(rawValue: (dict.object(forKey: "pageRangeType") as! Int))! model.pagesString = (dict.object(forKey: "pagesString") as! String) model.isMigrate = (dict.object(forKey: "isMigrate") as? Bool) ?? false model.isUpdated = (dict.object(forKey: "isUpdated") as? Bool) ?? false model.modificationDate = (dict.object(forKey: "modificationDate") as? TimeInterval) model.isRemoved = (dict.object(forKey: "isRemoved") as? Bool) ?? false return model } private func parseObject(object: KMBackgroundObject) -> KMBackgroundModel { let model = KMBackgroundModel() model.type = object.type if (model.type == .file) { model.image = object.image model.imagePath = object.imagePath ?? "" } else { model.color = object.color } model.scale = object.scale model.rotation = object.rotation.cgFloat model.opacity = object.opacity model.verticalMode = object.verticalMode model.verticalSpace = object.verticalSpace.cgFloat model.horizontalMode = object.horizontalMode model.horizontalSpace = object.horizontalSpace.cgFloat model.pageRangeType = KMWatermarkeModelPageRangeType.init(rawValue: object.pageRangeType.rawValue) ?? .all model.pagesString = object.pagesString model.backgroundID = object.backgroundID ?? "background0" model.isMigrate = true return model } private func parseModeForObject(_ mode: KMBackgroundModel) -> KMBackgroundObject { let obj = KMBackgroundObject() obj.type = mode.type if (obj.type == .file) { obj.image = mode.image obj.imagePath = mode.imagePath } else { obj.color = mode.color ?? .red } obj.scale = mode.scale obj.rotation = Int(mode.rotation) obj.opacity = mode.opacity obj.verticalMode = mode.verticalMode obj.verticalSpace = Int(mode.verticalSpace) obj.horizontalMode = mode.horizontalMode obj.horizontalSpace = Int(mode.horizontalSpace) obj.pageRangeType = KMBatchOperatePageChoice.init(rawValue: mode.pageRangeType.rawValue) ?? .All obj.pagesString = mode.pagesString obj.backgroundID = mode.backgroundID return obj } private func _addBackground(_ obj: KMBackgroundObject) { // self.backgroundObjects.insert(obj, at: 0) // self._store() } private func _removeBackground(_ obj: KMBackgroundObject) { // self.backgroundObjects.removeObject(obj) // self._store() } private func _store() { // let encodedObject = NSKeyedArchiver.archivedData(withRootObject: self) // let defaults = UserDefaults.standard // defaults.set(encodedObject, forKey: "kBackgroundInfoSaveKey") // defaults.synchronize() } private func _clearStored() { let defaults = UserDefaults.standard defaults.set(nil, forKey: "kBackgroundInfoSaveKey") defaults.synchronize() } private func tagString() -> String { var result: String = "" let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyMMddHHmmss" result.append(dateFormatter.string(from: Date())) result = result.appendingFormat("%04d", arc4random()%10000) return result } func fetchAvailableName() -> String { var availableIndex = 0 for item in datas { if item.backgroundID.hasPrefix("Background") { if let index = Int(item.backgroundID.dropFirst("Background".count)), index >= availableIndex { availableIndex = index + 1 } } } return "Background\(availableIndex)" } }