// // KMHeaderPropertyController.swift // PDF Reader Pro // // Created by Niehaoyu on 2024/11/8. // import Cocoa import KMComponentLibrary @objc protocol KMHeaderPropertyControllerDelegate: AnyObject { //数据有更新 @objc optional func headerFooterPropertyControllerDidUpdate(_ controller: KMHeaderPropertyController) //切换到模板界面 @objc optional func headerFooterPropertyControllerDidChangetoTemplate(_ controller: KMHeaderPropertyController) //成功保存到模板 @objc optional func headerFooterPropertyControllerSaveTemplateSuccess(_ controller: KMHeaderPropertyController, _ data: KMHeaderFooterObject) //取消修改模板信息 @objc optional func headerFooterPropertyControllerCancelTemplateEdit(_ controller: KMHeaderPropertyController) @objc optional func headerFooterPropertyControllerFinishTemplateEdit(_ controller: KMHeaderPropertyController) } class KMHeaderPropertyController: NSViewController { @IBOutlet var contendView: NSView! @IBOutlet var titleLabel: NSTextField! @IBOutlet var templateButton: ComponentButton! @IBOutlet var leftTopButton: ComponentButton! //Font @IBOutlet var fontBGView: NSView! @IBOutlet var fontLabel: NSTextField! @IBOutlet var fontNameSelect: ComponentSelect! @IBOutlet var fontSizeSelect: ComponentSelect! @IBOutlet var fontColorView: ComponentCColorCustom! //Margin @IBOutlet var marginBGView: NSView! @IBOutlet var marginLabel: NSTextField! @IBOutlet var marginLeftInput: ComponentInputNumber! @IBOutlet var marginTopInput: ComponentInputNumber! @IBOutlet var marginRightInput: ComponentInputNumber! @IBOutlet var marginBottomInput: ComponentInputNumber! //Display @IBOutlet var displayBGView: NSView! @IBOutlet var displayLabel: NSTextField! @IBOutlet var dateLabel: NSTextField! @IBOutlet var dateSelect: ComponentSelect! @IBOutlet var pageLabel: NSTextField! @IBOutlet var pageSelect: ComponentSelect! @IBOutlet var startPageLabel: NSTextField! @IBOutlet var startPageSelect: ComponentSelect! //Layout&Content @IBOutlet var layoutBGView: NSView! @IBOutlet var layoutLabel: NSTextField! @IBOutlet var layoutInsertButton: ComponentButton! @IBOutlet var layoutPosition: ComponentCPosition! @IBOutlet var layoutTextarea: ComponentTextarea! //Save @IBOutlet var saveTemplateBGView: NSView! @IBOutlet var saveButton: ComponentButton! var groupView: ComponentGroup! private var familyNames = CPDFFont.familyNames private var positionSelRow: Int = 1 private var positionSelColumn: Int = 0 var totalPDFCount: Int = 1 var originalDataDict: Dictionary? var headerFooterModel: KMHeaderFooterObject = KMHeaderFooterObject() weak open var delegate: KMHeaderPropertyControllerDelegate? var editSubType: KMPDFEditSubModeType = .none override func viewDidLoad() { super.viewDidLoad() // Do view setup here. setupProperty() reloadData() } func setupProperty() { contendView.wantsLayer = true contendView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle").cgColor titleLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") titleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-bold") templateButton.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "icon_wm_template"), keepPressState: false) templateButton.setTarget(self, action: #selector(componentButtonClicked(_:))) leftTopButton.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "watermark_arrowLeft"), keepPressState: false) leftTopButton.setTarget(self, action: #selector(leftTopButtonClicked(_:))) //Font fontLabel.stringValue = KMLocalizedString("Font") fontLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") fontLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") fontNameSelect.properties = ComponentSelectProperties(size: .s, state: .normal, text: "") var menuItemArr: [ComponentMenuitemProperty] = [] for string in familyNames { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) menuItemArr.append(item) } fontNameSelect.updateMenuItemsArr(menuItemArr) fontNameSelect.delegate = self fontSizeSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: true, text: "12 pt", regexString: "0123456789 pt") if true { var sizeItemArr: [ComponentMenuitemProperty] = [] for string in KMHeaderFooterManager.getFontSize() { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) sizeItemArr.append(item) } fontSizeSelect.updateMenuItemsArr(sizeItemArr) } fontSizeSelect.delegate = self fontColorView.properties = ComponentCColorProperty(colorType: .colorPanel, state: .normal, isCustom: true, color: NSColor.black) fontColorView.delegate = self //Margin marginLabel.stringValue = KMLocalizedString("Margin (mm)") marginLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") marginLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") marginLeftInput.properties = ComponentInputNumberProperty(alignment: .left, size: .s, minSize: 0, maxSize: 1000, text: "0") marginLeftInput.delegate = self marginTopInput.properties = ComponentInputNumberProperty(alignment: .left, size: .s, minSize: 0, maxSize: 1000, text: "0") marginTopInput.delegate = self marginRightInput.properties = ComponentInputNumberProperty(alignment: .left, size: .s, minSize: 0, maxSize: 1000, text: "0") marginRightInput.delegate = self marginBottomInput.properties = ComponentInputNumberProperty(alignment: .left, size: .s, minSize: 0, maxSize: 1000, text: "0") marginBottomInput.delegate = self //Display displayLabel.stringValue = KMLocalizedString("Display") displayLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") displayLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") dateSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: false, text: "m/d") if true { var sizeItemArr: [ComponentMenuitemProperty] = [] for string in KMHeaderFooterManager.getdateFormatArray() { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) sizeItemArr.append(item) } dateSelect.updateMenuItemsArr(sizeItemArr) } dateSelect.selectItemAtIndex(0) dateSelect.delegate = self pageSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: false, text: "1") if true { var sizeItemArr: [ComponentMenuitemProperty] = [] for string in KMHeaderFooterManager.getPageFormats() { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) sizeItemArr.append(item) } pageSelect.updateMenuItemsArr(sizeItemArr) } pageSelect.selectItemAtIndex(0) pageSelect.delegate = self startPageSelect.properties = ComponentSelectProperties(size: .s, state: .normal, creatable: false, text: "1") startPageSelect.delegate = self //Layout&Content layoutLabel.stringValue = KMLocalizedString("Layout & Content") layoutLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") layoutLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") layoutInsertButton.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "watermark_template_add"), keepPressState: false) layoutInsertButton.setTarget(self, action: #selector(componentButtonClicked(_:))) layoutPosition.properties = ComponentCPositionProperty(rowCount: 2, columnCount: 3, dash: true) layoutPosition.delegate = self layoutTextarea.properties = ComponentTextareaProperty(totalCount: -1) layoutTextarea.delegate = self //Save saveTemplateBGView.wantsLayer = true saveTemplateBGView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle").cgColor saveButton.properties = ComponentButtonProperty(type: .default_tertiary, size: .m, showLeftIcon: true, buttonText: KMLocalizedString("Save as Template"), icon: NSImage(named: "icon_wm_template_save"), keepPressState: false) saveButton.setTarget(self, action: #selector(componentButtonClicked(_:))) } func clearData() { positionSelRow = 1 positionSelColumn = 0 totalPDFCount = 1 } func reloadData() { titleLabel.stringValue = KMLocalizedString("Add Header & Footer") leftTopButton.isHidden = true saveTemplateBGView.isHidden = false templateButton.properties.icon = NSImage(named: "icon_wm_template") if editSubType == .edit { titleLabel.stringValue = KMLocalizedString("Edit Header & Footer") leftTopButton.isHidden = false saveTemplateBGView.isHidden = true templateButton.properties.icon = NSImage(named: "edit_save") } templateButton.reloadData() //Font if let index = familyNames.firstIndex(of: headerFooterModel.fontName) { fontNameSelect.selectItemAtIndex(index) } fontNameSelect.properties.text = headerFooterModel.fontName fontNameSelect.reloadData() let fontSizeStr = String(format: "%.0f", headerFooterModel.fontsize) if let index = KMHeaderFooterManager.getFontSize().firstIndex(of: fontSizeStr) { fontSizeSelect.selectItemAtIndex(index) } fontSizeSelect.properties.text = String(format: "%.0f", headerFooterModel.fontsize) + " pt" fontSizeSelect.reloadData() fontColorView.properties?.color = headerFooterModel.color fontColorView.reloadData() //Margin marginLeftInput.properties.text = String(format: "%d", headerFooterModel.leftMargin) marginLeftInput.reloadData() marginTopInput.properties.text = String(format: "%d", headerFooterModel.topMargin) marginTopInput.reloadData() marginRightInput.properties.text = String(format: "%d", headerFooterModel.rightMargin) marginRightInput.reloadData() marginBottomInput.properties.text = String(format: "%d", headerFooterModel.bottomMargin) marginBottomInput.reloadData() //Display if let index = KMHeaderFooterManager.getdateFormatArray().firstIndex(of: headerFooterModel.dateFormatString) { dateSelect.selectItemAtIndex(index) } else { dateSelect.properties.text = headerFooterModel.dateFormatString } dateSelect.reloadData() if let index = KMHeaderFooterManager.getPageFormats().firstIndex(of: headerFooterModel.pageFormatString) { pageSelect.selectItemAtIndex(index) } else { pageSelect.properties.text = headerFooterModel.pageFormatString } pageSelect.reloadData() if true { var sizeItemArr: [ComponentMenuitemProperty] = [] var indexArr: [String] = [] for index in 1...totalPDFCount { indexArr.append(String(format: "%ld", index)) } for string in indexArr { let item = ComponentMenuitemProperty(type: .normal, text: string, identifier: string) sizeItemArr.append(item) } startPageSelect.updateMenuItemsArr(sizeItemArr) } startPageSelect.properties.text = headerFooterModel.startString startPageSelect.reloadData() //Layout layoutPosition.properties.selRow = positionSelRow layoutPosition.properties.selColumn = positionSelColumn layoutPosition.reloadData() layoutTextarea.properties.text = "" layoutTextarea.reloadData() updateTextAreaText() //Save if headerFooterModel.topLeftString.count == 0 && headerFooterModel.topCenterString.count == 0 && headerFooterModel.topRightString.count == 0 && headerFooterModel.bottomLeftString.count == 0 && headerFooterModel.bottomCenterString.count == 0 && headerFooterModel.bottomRightString.count == 0 { saveButton.properties.isDisabled = true } else { saveButton.properties.isDisabled = false } saveButton.reloadData() } func updateTextAreaText() { if positionSelRow == 0 && positionSelColumn == 0 { //左下角 layoutTextarea.properties.text = headerFooterModel.bottomLeftString } else if positionSelRow == 0 && positionSelColumn == 1 { layoutTextarea.properties.text = headerFooterModel.bottomCenterString } else if positionSelRow == 0 && positionSelColumn == 2 { layoutTextarea.properties.text = headerFooterModel.bottomRightString } else if positionSelRow == 1 && positionSelColumn == 0 { layoutTextarea.properties.text = headerFooterModel.topLeftString } else if positionSelRow == 1 && positionSelColumn == 1 { layoutTextarea.properties.text = headerFooterModel.topCenterString } else if positionSelRow == 1 && positionSelColumn == 2 { layoutTextarea.properties.text = headerFooterModel.topRightString } layoutTextarea.reloadData() layoutPosition.setString(headerFooterModel.bottomLeftString, to: 0, 0) layoutPosition.setString(headerFooterModel.bottomCenterString, to: 0, 1) layoutPosition.setString(headerFooterModel.bottomRightString, to: 0, 2) layoutPosition.setString(headerFooterModel.topLeftString, to: 1, 0) layoutPosition.setString(headerFooterModel.topCenterString, to: 1, 1) layoutPosition.setString(headerFooterModel.topRightString, to: 1, 2) } //MARK: - Action @objc func leftTopButtonClicked(_ sender: ComponentButton) { if sender == leftTopButton { var isChanged = false if let dict = self.originalDataDict { isChanged = KMHeaderFooterManager.compareIsChangedModel(headerFooterModel, withDict: dict as NSDictionary) } if isChanged == true { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = KMLocalizedString("Save template changes?") alert.informativeText = KMLocalizedString("Cancel and they will not be saved.") alert.addButton(withTitle: KMLocalizedString("Save")) alert.addButton(withTitle: KMLocalizedString("Cancel")) alert.beginSheetModal(for: NSApp.mainWindow!) { (response) in if response == NSApplication.ModalResponse.alertFirstButtonReturn { let _ = KMHeaderFooterManager.defaultManager.updateHeaderFooter(theModel: self.headerFooterModel) self.delegate?.headerFooterPropertyControllerFinishTemplateEdit?(self) } else { if let dict = self.originalDataDict { KMHeaderFooterManager.defaultManager.updateModel(self.headerFooterModel, with: dict as NSDictionary) let _ = KMHeaderFooterManager.defaultManager.updateHeaderFooter(theModel: self.headerFooterModel) } self.delegate?.headerFooterPropertyControllerCancelTemplateEdit?(self) } } } else { delegate?.headerFooterPropertyControllerCancelTemplateEdit?(self) } } } @objc func componentButtonClicked(_ sender: ComponentButton) { if sender == templateButton { delegate?.headerFooterPropertyControllerDidChangetoTemplate?(self) } else if sender == layoutInsertButton { var menuItemArr: [ComponentMenuitemProperty] = [] let items: [(String, String)] = [("Insert Page Number", "InsertPageNumberKey"), ("Insert Date", "InsertDateKey")] for (i, value) in items { let properties_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false, itemSelected: false, isDisabled: false, keyEquivalent: nil, text: KMLocalizedString(i), identifier: value) menuItemArr.append(properties_Menuitem) } if groupView == nil { groupView = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle()) } groupView.groupDelegate = self groupView?.frame = CGRectMake(0, 0, 160, 80) groupView.updateGroupInfo(menuItemArr) if let point: CGPoint = sender.superview?.convert(sender.frame.origin, to: self.view.window?.contentView) { groupView.showWithPoint(CGPoint(x: point.x, y: point.y - CGRectGetHeight(groupView.frame)-4), relativeTo: self.view) } } else if sender == saveButton { let saveWindow: KMWatermarkSaveWindow = KMWatermarkSaveWindow(windowNibName: "KMWatermarkSaveWindow") saveWindow.nameValue = KMHeaderFooterManager.defaultManager.fetchHeaderFooterAvailableName() saveWindow.saveHandler = {[weak self] string in guard let weakSelf = self else { return } DispatchQueue.main.async { weakSelf.headerFooterModel.name = string ?? "" if KMHeaderFooterManager.defaultManager.addHeaderFooter(weakSelf.headerFooterModel) { weakSelf.delegate?.headerFooterPropertyControllerSaveTemplateSuccess?(weakSelf, weakSelf.headerFooterModel) } } } saveWindow.own_beginSheetModal(for: view.window) { string in } } } //MARK: - Mouse override func mouseDown(with event: NSEvent) { super.mouseDown(with: event) view.window?.makeFirstResponder(nil) } } //MARK: - ComponentCColorDelegate extension KMHeaderPropertyController: ComponentCColorDelegate { func componentCColorDidChooseColor(_ view: NSView, _ color: NSColor?) { if view == fontColorView { headerFooterModel.color = color ?? NSColor.clear } reloadData() delegate?.headerFooterPropertyControllerDidUpdate?(self) } } //MARK: - ComponentInputNumberDelegate extension KMHeaderPropertyController: ComponentInputNumberDelegate { func componentInputNumberDidValueChanged(inputNumber: ComponentInputNumber?) { if inputNumber == marginLeftInput { if let text = inputNumber?.properties.text { headerFooterModel.leftMargin = Int(text) ?? 30 } } else if inputNumber == marginTopInput { if let text = inputNumber?.properties.text { headerFooterModel.topMargin = Int(text) ?? 30 } } else if inputNumber == marginRightInput { if let text = inputNumber?.properties.text { headerFooterModel.rightMargin = Int(text) ?? 30 } } else if inputNumber == marginBottomInput { if let text = inputNumber?.properties.text { headerFooterModel.bottomMargin = Int(text) ?? 30 } } reloadData() delegate?.headerFooterPropertyControllerDidUpdate?(self) } } //MARK: - ComponentSelectDelegate extension KMHeaderPropertyController: ComponentSelectDelegate { func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) { if view == fontNameSelect { headerFooterModel.fontName = menuItemProperty?.text ?? "" } else if view == fontSizeSelect { if let text = menuItemProperty?.text { let result = text.stringByDeleteCharString(" pt") headerFooterModel.fontsize = result.stringToCGFloat() } } else if view == dateSelect { if let text = menuItemProperty?.text { headerFooterModel.dateFormatString = text } } else if view == pageSelect { if let text = menuItemProperty?.text { headerFooterModel.pageFormatString = text } } else if view == startPageSelect { if let text = menuItemProperty?.text { headerFooterModel.startString = text } } reloadData() delegate?.headerFooterPropertyControllerDidUpdate?(self) } } //MARK: - ComponentGroupDelegate extension KMHeaderPropertyController: ComponentGroupDelegate { func componentGroupDidSelect(group: ComponentGroup?, menuItemProperty: ComponentMenuitemProperty?) { if menuItemProperty?.identifier == "InsertPageNumberKey" { if let text = pageSelect.properties.text { let tString = "<<\(text)>>" if positionSelRow == 0 && positionSelColumn == 0 { //左下角 headerFooterModel.bottomLeftString = headerFooterModel.bottomLeftString + tString } else if positionSelRow == 0 && positionSelColumn == 1 { headerFooterModel.bottomCenterString = headerFooterModel.bottomCenterString + tString } else if positionSelRow == 0 && positionSelColumn == 2 { headerFooterModel.bottomRightString = headerFooterModel.bottomRightString + tString } else if positionSelRow == 1 && positionSelColumn == 0 { headerFooterModel.topLeftString = headerFooterModel.topLeftString + tString } else if positionSelRow == 1 && positionSelColumn == 1 { headerFooterModel.topCenterString = headerFooterModel.topCenterString + tString } else if positionSelRow == 1 && positionSelColumn == 2 { headerFooterModel.topRightString = headerFooterModel.topRightString + tString } } } else if menuItemProperty?.identifier == "InsertDateKey" { if let text = dateSelect.properties.text { let tString = "<<\(text)>>" if positionSelRow == 0 && positionSelColumn == 0 { //左下角 headerFooterModel.bottomLeftString = headerFooterModel.bottomLeftString + tString } else if positionSelRow == 0 && positionSelColumn == 1 { headerFooterModel.bottomCenterString = headerFooterModel.bottomCenterString + tString } else if positionSelRow == 0 && positionSelColumn == 2 { headerFooterModel.bottomRightString = headerFooterModel.bottomRightString + tString } else if positionSelRow == 1 && positionSelColumn == 0 { headerFooterModel.topLeftString = headerFooterModel.topLeftString + tString } else if positionSelRow == 1 && positionSelColumn == 1 { headerFooterModel.topCenterString = headerFooterModel.topCenterString + tString } else if positionSelRow == 1 && positionSelColumn == 2 { headerFooterModel.topRightString = headerFooterModel.topRightString + tString } } } reloadData() updateTextAreaText() delegate?.headerFooterPropertyControllerDidUpdate?(self) } } //MARK: - ComponentCPositionDelegate extension KMHeaderPropertyController: ComponentCPositionDelegate { func componentCPositionDidChoose(_ view: NSView, _ row: Int, _ column: Int) { if view == layoutPosition { view.window?.makeFirstResponder(nil) positionSelRow = row positionSelColumn = column reloadData() delegate?.headerFooterPropertyControllerDidUpdate?(self) } } } //MARK: - ComponentTextareaDelegate extension KMHeaderPropertyController: ComponentTextareaDelegate { func componentTextareaTextDidChange(_ view: ComponentTextarea) { if positionSelRow == 0 && positionSelColumn == 0 { //左下角 headerFooterModel.bottomLeftString = layoutTextarea.properties.text } else if positionSelRow == 0 && positionSelColumn == 1 { headerFooterModel.bottomCenterString = layoutTextarea.properties.text } else if positionSelRow == 0 && positionSelColumn == 2 { headerFooterModel.bottomRightString = layoutTextarea.properties.text } else if positionSelRow == 1 && positionSelColumn == 0 { headerFooterModel.topLeftString = layoutTextarea.properties.text } else if positionSelRow == 1 && positionSelColumn == 1 { headerFooterModel.topCenterString = layoutTextarea.properties.text } else if positionSelRow == 1 && positionSelColumn == 2 { headerFooterModel.topRightString = layoutTextarea.properties.text } reloadData() delegate?.headerFooterPropertyControllerDidUpdate?(self) } }