// // KMNPopAnnotationWindowController.swift // PDF Reader Pro // // Created by 丁林圭 on 2024/12/2. // import Cocoa import KMComponentLibrary let popOffSet:CGFloat = 8.0 typealias PaneCallback = (_ isOpenPane: Bool) -> () typealias UpdatePDFViewCallback = () -> () class KMNPopOperationWindow: NSWindow { override var canBecomeKey: Bool { return true } override var canBecomeMain: Bool { return true } } class KMNPopAnnotationWindowController: KMNBaseWindowController { public var popType:ListViewPopType = .popTypeNone { didSet { rightOffsetConstraint.constant = 45.0 operationHeightConstraint.constant = 32.0 paneBox.isHidden = false switch popType { case .generaAnnotation : generaPopView.layoutSubtreeIfNeeded() operationWidthConstraint.constant = generaPopView.bounds.width self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = generaPopView break case .freeTextAnnotation: fontPopView.layoutSubtreeIfNeeded() operationWidthConstraint.constant = fontPopView.bounds.width self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = fontPopView break case .shapeAnnotation: shapeView.layoutSubtreeIfNeeded() operationWidthConstraint.constant = shapeView.bounds.width self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = shapeView break case .linkAnnotation: if let newLinkAnnotation = annotationPopMode.annotation as? CPDFLinkAnnotation { linkAnnotation = newLinkAnnotation } break case .formAnnotation: formRightConstraint.constant = 0 gropNameInput.isHidden = true formView.layoutSubtreeIfNeeded() operationWidthConstraint.constant = formView.bounds.width self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = formView break case .formRadioAnnotation: formRightConstraint.constant = 110 gropNameInput.isHidden = false formView.layoutSubtreeIfNeeded() operationWidthConstraint.constant = formView.bounds.width self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = formView break case .multpleAnnotation: alightView.layoutSubtreeIfNeeded() operationWidthConstraint.constant = alightView.bounds.width self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = alightView break case .popTypeNone: break } } } public var isOpenPane = false { didSet { if isOpenPane == true { paneSelectorItem.properties.state = .pressed } else { paneSelectorItem.properties.state = .normal } paneSelectorItem.reloadData() } } public var annotationPopMode:KMNAnnotationPopMode = KMNAnnotationPopMode(pdfAnnotations: []) { didSet { let annotationPopType = annotationPopMode.popType popType = annotationPopType if annotationPopType == .generaAnnotation { let newColor = annotationPopMode.annotationColor() let oldColor = annotationColor if !CPDFListView.isTheSameColor(newColor, anotherColor: oldColor) { annotationColor = newColor } } else if(annotationPopType == .shapeAnnotation) { let newColor = annotationPopMode.annotationColor() let oldColor = annotationColor if !CPDFListView.isTheSameColor(newColor, anotherColor: oldColor) { annotationColor = newColor } } else if(annotationPopType == .freeTextAnnotation) { let newColor = annotationPopMode.annotationFontColor() let oldColor = fontColor if !CPDFListView.isTheSameColor(newColor, anotherColor: oldColor) { fontColor = newColor } let alignment = annotationPopMode.annotationAlignment() if(alignment != self.fontAlight) { fontAlight = alignment } let font = annotationPopMode.annotationFontName() if(font?.familyName != compdfFont?.familyName || font?.styleName != compdfFont?.styleName) { compdfFont = font } } else if annotationPopType == .linkAnnotation { if annotationPopMode.annotation != nil { linkAnnotation = annotationPopMode.annotation as! CPDFLinkAnnotation } } else if annotationPopType == .formAnnotation { if annotationPopMode.annotationFieldName() != fildNameString { fildNameString = annotationPopMode.annotationFieldName() ?? "" } } else if annotationPopType == .formRadioAnnotation { if annotationPopMode.annotationFieldName() != fildNameString { fildNameString = annotationPopMode.annotationFieldName() ?? "" } if annotationPopMode.annotationGropName() != gropNameString { gropNameString = annotationPopMode.annotationGropName() ?? "" } } } } private var annotationColor:NSColor?{ didSet { if popType == .generaAnnotation { generaColorGroup.currentColor = annotationColor generaColorGroup.refreshUI() } else if popType == .shapeAnnotation { shapeColorGroup.currentColor = annotationColor shapeColorGroup.refreshUI() } } } private var fontColor:NSColor?{ didSet { fontColorItem.properties?.color = fontColor fontColorItem.reloadData() } } private var linkAnnotation:CPDFLinkAnnotation = CPDFLinkAnnotation(){ didSet { if let destination = linkAnnotation.destination() { linkType = .page } else if let annotationURL = linkAnnotation.url(){ var urlString = annotationURL.fileURL.absoluteString if annotationURL.hasPrefix("mailto:") { linkType = .email } else { linkType = .url } } else { linkType = .linkSetting } } } private var fildNameString:String = ""{ didSet { fildNameInput.properties.text = fildNameString fildNameInput.reloadData() } } private var gropNameString:String = ""{ didSet { gropNameInput.properties.text = gropNameString gropNameInput.reloadData() } } private var compdfFont:CPDFFont?{ didSet { if(compdfFont != nil) { let styleName = compdfFont!.styleName if(styleName?.isEmpty == true) { fontNameSelect.properties.text = compdfFont!.familyName } else { fontNameSelect.properties.text = compdfFont!.familyName + "-" + (compdfFont!.styleName ?? "") } } else { fontNameSelect.properties.text = "-" } fontNameSelect.selectItemAtIndex(0) fontNameSelect.reloadData() } } private var fontAlight:NSTextAlignment = .left { didSet { if fontAlight == .center { fontAlightButton.properties.icon = NSImage(named: "KMNImageNameListViewPopAlightCenter") } else if fontAlight == .right { fontAlightButton.properties.icon = NSImage(named: "KMNImageNameListViewPopAlightRight") } else { fontAlightButton.properties.icon = NSImage(named: "KMNImageNameListViewPopAlightLeft") } fontAlightButton.reloadData() } } private var objectAlignType:ObjectAlignType = .alightLeft { didSet { var zeroRect = NSRect.null var highestRect = NSZeroRect var widthestRect = NSZeroRect let objects = annotationPopMode.annotations let fristObject : CPDFAnnotation = objects.first ?? CPDFAnnotation() var leftestRect = fristObject.bounds var rightestRect = fristObject.bounds var topestRect = fristObject.bounds var bottomestRect = fristObject.bounds var leftestObject : CPDFAnnotation = fristObject var rightestObject : CPDFAnnotation = fristObject var topestObject : CPDFAnnotation = fristObject var bottomestObject: CPDFAnnotation = fristObject var totalWidth = 0.0 var totalHeight = 0.0 for i in 0 ..< objects.count { let annotation : CPDFAnnotation = objects[i] zeroRect = zeroRect.union(annotation.bounds) totalWidth = totalWidth + annotation.bounds.width totalHeight = totalHeight + annotation.bounds.height if annotation.bounds.height > highestRect.height { highestRect = annotation.bounds } if annotation.bounds.width > widthestRect.width { widthestRect = annotation.bounds } if leftestRect.minX > annotation.bounds.minX { leftestRect = annotation.bounds leftestObject = annotation } if annotation.bounds.maxX > rightestRect.maxX { rightestRect = annotation.bounds rightestObject = annotation } if annotation.bounds.maxY > topestRect.maxY { topestRect = annotation.bounds topestObject = annotation } if bottomestRect.minY > annotation.bounds.minY { bottomestRect = annotation.bounds bottomestObject = annotation } } var resultAreasArray: [CPDFAnnotation] = [] var newBoundsArray: [String] = [] switch objectAlignType { case .alightLeft : for i in 0 ..< objects.count { let object = objects[i] var bounds = object.bounds bounds.origin.x = zeroRect.origin.x newBoundsArray.append(NSStringFromRect(bounds)) } resultAreasArray = objects alightButton.properties.icon = NSImage(named: "KMNImageNameObjectPopAlightLeft") break case .alightRight: for i in 0 ..< objects.count { let object = objects[i] var bounds = object.bounds bounds.origin.x = zeroRect.maxX - bounds.size.width newBoundsArray.append(NSStringFromRect(bounds)) } resultAreasArray = objects alightButton.properties.icon = NSImage(named: "KMNImageNameObjectPopAlightRight") break case .alightTop: for i in 0 ..< objects.count { let object = objects[i] var bounds = object.bounds bounds.origin.y = zeroRect.maxY - bounds.size.height newBoundsArray.append(NSStringFromRect(bounds)) } resultAreasArray = objects alightButton.properties.icon = NSImage(named: "KMNImageNameObjectPopAlightTop") break case .alightVer: for i in 0 ..< objects.count { let object = objects[i] var bounds = object.bounds bounds.origin.x = widthestRect.midX - bounds.width/2 newBoundsArray.append(NSStringFromRect(bounds)) } resultAreasArray = objects alightButton.properties.icon = NSImage(named: "KMNImageNameObjectPopAlightVer") break case .alightHor: for i in 0 ..< objects.count { let object = objects[i] var bounds = object.bounds bounds.origin.y = highestRect.midY - bounds.height/2 newBoundsArray.append(NSStringFromRect(bounds)) } resultAreasArray = objects alightButton.properties.icon = NSImage(named: "KMNImageNameObjectPopAlightHor") break case .alightBottom: for i in 0 ..< objects.count { let object = objects[i] var bounds = object.bounds bounds.origin.y = zeroRect.minY newBoundsArray.append(NSStringFromRect(bounds)) } resultAreasArray = objects alightButton.properties.icon = NSImage(named: "KMNImageNameObjectPopAlightBottom") break case .averageVer: let middleGap = zeroRect.height - topestRect.height - bottomestRect.height let otherAreasTotalHeight = totalHeight - topestRect.height - bottomestRect.height let gap = (middleGap - otherAreasTotalHeight)/CGFloat(objects.count - 1) var areasCopyArray : [CPDFAnnotation] = objects areasCopyArray.sorted(by: { obj1, obj2 in let area1 = obj1 let area2 = obj2 if area1.bounds.origin.x < area2.bounds.origin.x { return true } else { return false } }) if let index = areasCopyArray.firstIndex(of: topestObject) { areasCopyArray.remove(at: index) } if let index = areasCopyArray.firstIndex(of: bottomestObject) { areasCopyArray.remove(at: index) } var bottomStartY = bottomestRect.maxY + gap for i in 0 ..< areasCopyArray.count { let areas = areasCopyArray[i] var bounds = areas.bounds bounds.origin.y = bottomStartY newBoundsArray.append(NSStringFromRect(bounds)) bottomStartY = bottomStartY + bounds.height + gap } resultAreasArray = areasCopyArray alightButton.properties.icon = NSImage(named: "KMNImageNameObjecAlignAverageVer") break case .averageHor: let middleGap = zeroRect.width - leftestRect.width - rightestRect.width let otherAreasTotalWidth = totalWidth - leftestRect.width - rightestRect.width let gap = (middleGap - otherAreasTotalWidth)/CGFloat(objects.count - 1) var areasCopyArray : [CPDFAnnotation] = objects areasCopyArray.sorted(by: { obj1, obj2 in let area1 = obj1 let area2 = obj2 if area1.bounds.origin.x < area2.bounds.origin.x { return true } else { return false } }) if let index = areasCopyArray.firstIndex(of: leftestObject) { areasCopyArray.remove(at: index) } if let index = areasCopyArray.firstIndex(of: rightestObject) { areasCopyArray.remove(at: index) } var leftStartX = leftestRect.maxX + gap for i in 0 ..< areasCopyArray.count { let areas = areasCopyArray[i] var bounds = areas.bounds bounds.origin.x = leftStartX newBoundsArray.append(NSStringFromRect(bounds)) leftStartX = leftStartX + bounds.width + gap } resultAreasArray = areasCopyArray alightButton.properties.icon = NSImage(named: "KMNImageNameObjecAlignAverageHor") break } alightButton.reloadData() for i in 0 ..< resultAreasArray.count { let annotation : CPDFAnnotation = resultAreasArray[i] annotation.bounds = NSRectFromString(newBoundsArray[i]) } } } var paneCallback: PaneCallback? var updatePDFViewCallback: UpdatePDFViewCallback? static let shared: KMNPopAnnotationWindowController = { let windowC = KMNPopAnnotationWindowController(windowNibName: "KMNPopAnnotationWindowController") return windowC }() var linkType:linkDetailType = .linkSetting { didSet { if linkType == .linkSetting { linkView.layoutSubtreeIfNeeded() rightOffsetConstraint.constant = 45.0 operationHeightConstraint.constant = 32.0 operationWidthConstraint.constant = linkView.bounds.width self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = linkView paneBox.isHidden = false } else if linkType == .page { linkPageDetailView.layoutSubtreeIfNeeded() rightOffsetConstraint.constant = 8 operationWidthConstraint.constant = linkPageDetailView.bounds.width operationHeightConstraint.constant = 224 self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = linkPageDetailView paneBox.isHidden = true if let destination = linkAnnotation.destination() { reloadPageUrlData() refreshPageThum() } else { paginationView.properties.currentIndex = Int(((listView?.currentPageIndex ?? 0) + 1)) paginationView.reloadData() refreshPageThum() } } else if linkType == .url { linkUrlDetailView.layoutSubtreeIfNeeded() rightOffsetConstraint.constant = 8 operationWidthConstraint.constant = linkUrlDetailView.bounds.width operationHeightConstraint.constant = linkUrlDetailView.bounds.height self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = linkUrlDetailView paneBox.isHidden = true urlInput.properties.placeholder = KMNCompanyWebsite if let annotationURL = linkAnnotation.url(){ if annotationURL.hasPrefix("mailto:") { urlInput.properties.text = "" } else { urlInput.properties.text = annotationURL } } else { urlInput.properties.text = "" } urlInput.properties.state = .pressed urlInput.beginEditing() urlInput.reloadData() } else if linkType == .email { linkUrlDetailView.layoutSubtreeIfNeeded() rightOffsetConstraint.constant = 8 operationWidthConstraint.constant = linkUrlDetailView.bounds.width operationHeightConstraint.constant = linkUrlDetailView.bounds.height self.window?.display() //需刷新约束才会有值,不然会变化 operationBox.contentView = linkUrlDetailView paneBox.isHidden = true urlInput.properties.placeholder = KMNCompanyEamil if let annotationURL = linkAnnotation.url(){ if annotationURL.hasPrefix("mailto:") { if let range = annotationURL.range(of: "mailto:") { let trimmedString = String(annotationURL[range.upperBound...]) urlInput.properties.text = trimmedString } } else { urlInput.properties.text = "" } } else { urlInput.properties.text = "" } urlInput.properties.state = .pressed urlInput.beginEditing() urlInput.reloadData() } updateFrame(listView: listView ?? CPDFListView()) } } @IBOutlet weak var contentBox: NSBox! @IBOutlet weak var operationBox: NSBox! @IBOutlet weak var paneBox: NSBox! @IBOutlet weak var lineBox: NSBox! @IBOutlet var paneSelectorItem: ComponentCSelector! @IBOutlet weak var operationWidthConstraint: NSLayoutConstraint! @IBOutlet weak var rightOffsetConstraint: NSLayoutConstraint! @IBOutlet weak var operationHeightConstraint: NSLayoutConstraint! @IBOutlet var generaPopView: NSView! @IBOutlet var generaColorGroup: ComponentCColorGroup! @IBOutlet var fontPopView: NSView! @IBOutlet var fontColorItem: ComponentCColorItem! @IBOutlet var fontNameSelect: ComponentSelect! @IBOutlet var fontSizeZoomOutButton: ComponentButton! @IBOutlet var fontSizeZoomInButton: ComponentButton! @IBOutlet var fontAlightButton: ComponentButton! @IBOutlet var shapeView: NSView! @IBOutlet var shapeColorGroup: ComponentCColorGroup! @IBOutlet var widthZoomOutButton: ComponentButton! @IBOutlet var widthZoomInButton: ComponentButton! @IBOutlet var linkView: NSView! @IBOutlet var pageLinkButton: ComponentButton! @IBOutlet var urlLinkButton: ComponentButton! @IBOutlet var emailLinkButton: ComponentButton! @IBOutlet weak var pageLinkWidthConstraint: NSLayoutConstraint! @IBOutlet weak var urlLinkWidthConstraint: NSLayoutConstraint! @IBOutlet weak var emailLinkWidthConstraint: NSLayoutConstraint! @IBOutlet var formView: NSView! @IBOutlet var fildNameInput: ComponentInput! @IBOutlet var gropNameInput: ComponentInput! @IBOutlet var formRightConstraint: NSLayoutConstraint! @IBOutlet var alightView: NSView! @IBOutlet var alightButton: ComponentButton! @IBOutlet var linkPageDetailView: NSView! @IBOutlet var backPageLinkButton: ComponentButton! @IBOutlet var paginationView: ComponentPagination! @IBOutlet var paginationDesBox: NSBox! @IBOutlet var pageThumImageView: NSImageView! @IBOutlet var goPageLinkButton: ComponentButton! @IBOutlet weak var backPageWidthConstraint: NSLayoutConstraint! @IBOutlet var linkUrlDetailView: NSView! @IBOutlet var backUrlLinkButton: ComponentButton! @IBOutlet var goUrlLinkButton: ComponentButton! @IBOutlet var urlInput: ComponentInput! @IBOutlet weak var backUrlWidthConstraint: NSLayoutConstraint! private var fontPopover:NSPopover? private var ObjectPopover:NSPopover? weak var listView:CPDFListView? { didSet { paginationView.properties.totalCount = Int(listView?.document.pageCount ?? 1) paginationView.reloadData() } } override func windowDidLoad() { super.windowDidLoad() } override func initContentView() { super.initContentView() configFormPopUI() congfigFontPopUI() congfigGeneraPopUI() congfigShapePopUI() paneSelectorItem.properties = ComponentCSelectorProperty(iconImage:NSImage(named: "KMNImageNameListViewPopPane")) paneSelectorItem.setTarget(self, action: #selector(paneButtonClicked(_ :))) fontSizeZoomOutButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: true, icon:NSImage(named: "KMNImageNameListViewPopFontSizeZoomOut"), keepPressState: false) fontSizeZoomOutButton.setTarget(self, action: #selector(fontSizeZoomOutButtonClicked(_ :))) fontSizeZoomInButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: true, icon:NSImage(named: "KMNImageNameListViewPopFontSizeZoomIn"), keepPressState: false) fontSizeZoomInButton.setTarget(self, action: #selector(fontSizeZoomInButtonClicked(_ :))) fontAlightButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: true, icon:NSImage(named: "KMNImageNameListViewPopAlightLeft")) fontAlightButton.setTarget(self, action: #selector(fontAlightButtonClicked(_ :))) widthZoomOutButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: true, icon:NSImage(named: "KMNImageNameListViewPopWidthZoomOut"), keepPressState: false) widthZoomOutButton.setTarget(self, action: #selector(widthZoomOutButtonClicked(_ :))) widthZoomInButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: true, icon:NSImage(named: "KMNImageNameListViewPopWidthZoomIn"), keepPressState: false) widthZoomInButton.setTarget(self, action: #selector(widthZoomInButtonClicked(_ :))) pageLinkButton.setTarget(self, action: #selector(pageLinkButtonClicked(_ :))) urlLinkButton.setTarget(self, action: #selector(urlLinkButtonClicked(_ :))) emailLinkButton.setTarget(self, action: #selector(emailLinkButtonClicked(_ :))) alightButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: true, icon:NSImage(named: "KMNImageNameObjectPopAlightLeft")) alightButton.setTarget(self, action: #selector(alightButtonClicked(_ :))) goUrlLinkButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: true, icon:NSImage(named: "KMNImageNamePopLinkGO"),keepPressState: false) goUrlLinkButton.setTarget(self, action: #selector(linkGoButtonClicked(_ :))) goPageLinkButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: true, icon:NSImage(named: "KMNImageNamePopLinkGO"),keepPressState: false) goPageLinkButton.setTarget(self, action: #selector(linkGoButtonClicked(_ :))) paginationView.properties = ComponentPaginationProperty(doubleArrow_show: false, currentIndex: 0, totalCount: 1) paginationView.delegate = self let inputFiedProperty: ComponentInputProperty = ComponentInputProperty(size: .s, state:.pressed , isError: false, showPrefix: false, showSuffix: false, showClear: false, isDisabled: false, placeholder: KMNCompanyWebsite, text: "") urlInput.properties = inputFiedProperty urlInput.delegate = self } override func updateUIThemeColor() { super.updateUIThemeColor() lineBox.fillColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/divider") contentBox.fillColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/popup") contentBox.borderColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/3-default") paginationDesBox.fillColor = ComponentLibrary.shared.getComponentColorFromKey("colorFill/4") paginationDesBox.borderColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/4") pageThumImageView.wantsLayer = true paginationDesBox.layer?.borderColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/divider").cgColor } override func updateUILanguage() { super.updateUILanguage() pageLinkButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: false, showLeftIcon:true, buttonText:KMLocalizedString("Page"), icon:NSImage(named: "KMNImageNameListViewLinkPage"),keepPressState: false) urlLinkButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: false, showLeftIcon:true, buttonText:KMLocalizedString("Web"), icon:NSImage(named: "KMNImageNameListViewLinkUrl"),keepPressState: false) emailLinkButton.properties = ComponentButtonProperty(type: .text_gray, size: .s, state: .normal, onlyIcon: false, showLeftIcon:true, buttonText:KMLocalizedString("Email"), icon:NSImage(named: "KMNImageNameListViewLinkEmail"),keepPressState: false) pageLinkWidthConstraint.constant = pageLinkButton.properties.propertyInfo.viewWidth urlLinkWidthConstraint.constant = urlLinkButton.properties.propertyInfo.viewWidth emailLinkWidthConstraint.constant = emailLinkButton.properties.propertyInfo.viewWidth backUrlLinkButton.properties = ComponentButtonProperty(type: .text_primary, size: .s, state: .normal, onlyIcon: false, showLeftIcon: true, buttonText: KMLocalizedString("Back"), icon:NSImage(named: "KMNImageNamePopLinkBack"),keepPressState: false) backUrlLinkButton.setTarget(self, action: #selector(linkBackButtonClicked(_ :))) backUrlWidthConstraint.constant = backUrlLinkButton.properties.propertyInfo.viewWidth backUrlLinkButton.reloadData() backPageLinkButton.properties = ComponentButtonProperty(type: .text_primary, size: .s, onlyIcon: false, showLeftIcon:true, buttonText: KMLocalizedString("Back"), icon:NSImage(named: "KMNImageNamePopLinkBack"),keepPressState: false) backPageLinkButton.setTarget(self, action: #selector(linkBackButtonClicked(_ :))) backPageWidthConstraint.constant = backPageLinkButton.properties.propertyInfo.viewWidth } //MARK: - Public public func updateFrame(listView:CPDFListView) { let activeAnnotations = annotationPopMode.annotations let page = activeAnnotations.first?.page let windowFram = listView.window?.frame ?? CGRectZero if(page != nil) { let pageRect = listView.selectionMultipleBounds(with: activeAnnotations) let positioningRect = listView.convert(pageRect, from: page!) if (CGRectIntersectsRect(positioningRect, listView.frame)) { let view: NSView? = nil let position = listView.convert(positioningRect, to: view) var positionNew = position.origin positionNew.x += windowFram.origin.x + position.width/2 positionNew.y += windowFram.origin.y + position.height var positionRect = self.window?.frame ?? CGRectZero positionRect.origin.x = positionNew.x - positionRect.width/2 positionRect.origin.y = positionNew.y + popOffSet var listViewWindRect = listView.convert(listView.frame, to: view) listViewWindRect.origin.x += windowFram.origin.x listViewWindRect.origin.y += windowFram.origin.y if CGRectGetMaxY(positionRect) > CGRectGetMaxY(listViewWindRect) { positionRect.origin.y = positionNew.y - popOffSet - position.height - positionRect.size.height } if CGRectGetMinX(positionRect) < CGRectGetMinX(listViewWindRect) { positionRect.origin.x = CGRectGetMinX(listViewWindRect) } if CGRectGetMaxX(positionRect) > CGRectGetMaxX(listViewWindRect) { positionRect.origin.x = CGRectGetMaxX(listViewWindRect) - positionRect.width } self.window?.setFrame(positionRect, display: true) updateUILanguage() } else { } } } public func closeWindow(listView:CPDFListView?) { if self.window?.isVisible == true { listView?.window?.removeChildWindow(self.window ?? NSWindow()) self.window?.close() } if ObjectPopover?.isShown == true { ObjectPopover?.close() } if fontPopover?.isShown == true { fontPopover?.close() } } //MARK: - private private func congfigGeneraPopUI() { var colors = KMAnnotationPropertiesColorManager.manager.markHighlightColors if(annotationPopMode.annotation is CPDFMarkupAnnotation) { let markupAnnotation:CPDFMarkupAnnotation = annotationPopMode.annotation as! CPDFMarkupAnnotation if markupAnnotation.markupType() == .highlight { colors = KMAnnotationPropertiesColorManager.manager.markHighlightColors } else { colors = KMAnnotationPropertiesColorManager.manager.markOtherColors } } let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: colors[4]) generaColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) generaColorGroup.delegate = self } private func congfigFontPopUI() { fontNameSelect.properties = ComponentSelectProperties(size: .s, state: .normal, isDisabled: false, isError: false, leftIcon: false, placeholder: nil, errorText: nil, creatable: false, text: NSLocalizedString("", comment: "")) fontNameSelect.delegate = self var menuItemArr: [ComponentMenuitemProperty] = [] for familyName in CPDFFont.familyNames { let fontNames = CPDFFont.fontNames(forFamilyName: familyName) let itemProperty: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false, itemSelected: false, isDisabled: false, keyEquivalent: nil, text: familyName,identifier: "1") var subMenuItemArr: [ComponentMenuitemProperty] = [] for fontName in fontNames { let subItemProperty: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false, itemSelected: false, isDisabled: false, keyEquivalent: nil, text: fontName,identifier: "2") subMenuItemArr.append(subItemProperty) } itemProperty.subPropertys = subMenuItemArr menuItemArr.append(itemProperty) } fontNameSelect.updateMenuItemsArr(menuItemArr) fontColorItem.properties = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: NSColor.red) fontColorItem.delegate = self fontColorItem.reloadData() } private func congfigShapePopUI() { var colors = KMAnnotationPropertiesColorManager.manager.inkColors let annotation = annotationPopMode.annotation ?? CPDFAnnotation() if annotation.isKind(of: CPDFInkAnnotation.self) { colors = KMAnnotationPropertiesColorManager.manager.inkColors } else if annotation.isKind(of: CPDFCircleAnnotation.self) || annotation.isKind(of: CPDFSquareAnnotation.self) { colors = KMAnnotationPropertiesColorManager.manager.inkColors } else if annotation.isKind(of: CPDFLineAnnotation.self) { if let lineAnnotation:CPDFLineAnnotation = annotation as? CPDFLineAnnotation { if lineAnnotation.isMeasure { colors = KMAnnotationPropertiesColorManager.manager.measureColors } else { colors = KMAnnotationPropertiesColorManager.manager.inkColors } } } else if (annotation.isKind(of: CPDFPolygonAnnotation.self) || annotation.isKind(of: CPDFPolylineAnnotation.self)) { colors = KMAnnotationPropertiesColorManager.manager.measureColors } else if annotation.isKind(of: CPDFTextAnnotation.self) { colors = KMAnnotationPropertiesColorManager.manager.anchoredNoteColors } else if annotation.isKind(of: CSelfSignAnnotation.self) { colors = KMAnnotationPropertiesColorManager.manager.selfSignColors } let colorAProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[0]) let colorBProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[1]) let colorCProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[2]) let colorDProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: false, color: colors[3]) let colorEProperty = ComponentCColorProperty(colorType: .color, state: .normal, isCustom: true, color: colors[4]) shapeColorGroup.setUpWithColorPropertys([colorAProperty, colorBProperty, colorCProperty, colorDProperty], customItemProperty: colorEProperty) shapeColorGroup.delegate = self } private func configFormPopUI() { let inputFiedProperty: ComponentInputProperty = ComponentInputProperty(size: .s, state:.pressed , isError: false, showPrefix: false, showSuffix: false, showClear: false, isDisabled: false, placeholder: "File Name", text: "") fildNameInput.properties = inputFiedProperty fildNameInput.delegate = self let inputGropProperty: ComponentInputProperty = ComponentInputProperty(size: .s, state:.pressed , isError: false, showPrefix: false, showSuffix: false, showClear: false, isDisabled: false, placeholder: "Grop Name", text: "") gropNameInput.properties = inputGropProperty gropNameInput.delegate = self } private func reloadPageUrlData() { if let destination = linkAnnotation.destination() { if let page = destination.page() { paginationView.properties.currentIndex = Int(page.pageIndex() + 1) paginationView.reloadData() return } } paginationView.properties.currentIndex = Int(((listView?.currentPageIndex ?? 0) + 1)) paginationView.reloadData() } private func refreshPageThum() { guard let pdfView = listView else { return } let thumbnail = KMNThumbnail.init(document: pdfView.document, currentPageIndex:Int(paginationView.properties.currentIndex - 1)) thumbnail.generateThumImage {[weak self] image in self?.pageThumImageView.image = image } } //MARK: - Action @objc func paneButtonClicked(_ sender: NSView) { isOpenPane = !isOpenPane paneCallback?(isOpenPane) } @objc func fontSizeZoomOutButtonClicked(_ sender: NSView) { if let freeText = annotationPopMode.annotation as? CPDFFreeTextAnnotation { if listView?.isEdit(withCurrentFreeText: freeText) == true { listView?.commitEditAnnotationFreeText(freeText) } if annotationPopMode.zoomOutFontSize() { updatePDFViewCallback?() } } } @objc func fontSizeZoomInButtonClicked(_ sender: NSView) { if let freeText = annotationPopMode.annotation as? CPDFFreeTextAnnotation { if listView?.isEdit(withCurrentFreeText: freeText) == true { listView?.commitEditAnnotationFreeText(freeText) } if annotationPopMode.zoomInFontSize() { updatePDFViewCallback?() } } } @objc func fontAlightButtonClicked(_ sender: NSView) { if fontPopover?.isShown == true { fontPopover?.close() } else { let vc = KMNPopDetailsViewController(nibName: "KMNPopDetailsViewController", bundle: nil) let createFilePopover: NSPopover = NSPopover.init() createFilePopover.contentViewController = vc createFilePopover.delegate = self createFilePopover.animates = true createFilePopover.behavior = .semitransient createFilePopover.setValue(true, forKey: "shouldHideAnchor") createFilePopover.show(relativeTo: CGRect(x: sender.bounds.origin.x, y: 20, width: sender.bounds.size.width, height: sender.bounds.size.height), of: sender, preferredEdge: .maxY) vc.detailPopType = .freeTextAlight vc.fontChangeAlighCallback = { [weak self] alight in if let freeText = self?.annotationPopMode.annotation as? CPDFFreeTextAnnotation { if self?.listView?.isEdit(withCurrentFreeText: freeText) == true { self?.listView?.commitEditAnnotationFreeText(freeText) } } self?.annotationPopMode.setAnnotationAlignment(annotationAlignment: alight) self?.updatePDFViewCallback?() self?.fontAlight = alight } fontPopover = createFilePopover vc.fontAlight = fontAlight } } @objc func widthZoomOutButtonClicked(_ sender: NSView) { if annotationPopMode.zoomOutShapeWidth() { updatePDFViewCallback?() } } @objc func widthZoomInButtonClicked(_ sender: NSView) { if annotationPopMode.zoomInShapeWidth() { updatePDFViewCallback?() } } @objc func pageLinkButtonClicked(_ sender: NSView) { linkType = .page } @objc func urlLinkButtonClicked(_ sender: NSView) { linkType = .url } @objc func emailLinkButtonClicked(_ sender: NSView) { linkType = .email } @objc func alightButtonClicked(_ sender: NSView) { if ObjectPopover?.isShown == true { ObjectPopover?.close() } else { let vc = KMNPopDetailsViewController(nibName: "KMNPopDetailsViewController", bundle: nil) let createFilePopover: NSPopover = NSPopover.init() createFilePopover.contentViewController = vc createFilePopover.delegate = self createFilePopover.animates = true createFilePopover.behavior = .semitransient createFilePopover.setValue(true, forKey: "shouldHideAnchor") createFilePopover.show(relativeTo: CGRect(x: sender.bounds.origin.x, y: 20, width: sender.bounds.size.width, height: sender.bounds.size.height), of: sender, preferredEdge: .maxY) if (annotationPopMode.annotations.count == 2) { vc.detailPopType = .freeTextDoubleAlight } else if (annotationPopMode.annotations.count > 2) { vc.detailPopType = .freeTextMultpleAlight } vc.objectChangeAlighCallback = { [weak self] alight in self?.objectAlignType = alight self?.updatePDFViewCallback?() } ObjectPopover = createFilePopover vc.objectAlignType = objectAlignType } } @objc func linkGoButtonClicked(_ sender: NSView) { if linkType == .url { guard let webString = linkAnnotation.url() else { return } if let data = URL(string: webString) { NSWorkspace.shared.open(data) } } else if linkType == .email { guard let emailString = linkAnnotation.url() else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Invalid Email. Please try again.", comment: "") alert.runModal() return } if KMNTools.isValidateEmail(emailString) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Invalid Email. Please try again.", comment: "") alert.runModal() } else { NSWorkspace.shared.open(URL(string: emailString)!) } } else if linkType == .page { if let destination = linkAnnotation.destination() { listView?.go(to: destination) closeWindow(listView: listView) } } } @objc func linkBackButtonClicked(_ sender: NSView) { linkType = .linkSetting } @objc func fontColorChange(_ sender: Any) { if let color = (sender as? NSColorPanel)?.color { if let freeText = annotationPopMode.annotation as? CPDFFreeTextAnnotation { if listView?.isEdit(withCurrentFreeText: freeText) == true { listView?.commitEditAnnotationFreeText(freeText) } } annotationPopMode.setAnnotationFontColor(annotationFontColor: color) fontColor = color updatePDFViewCallback?() } } } //MARK: - ComponentCColorDelegate extension KMNPopAnnotationWindowController: ComponentCColorDelegate { func componentCColorDidChooseColor(_ view: NSView, _ color: NSColor?) { if(view == generaColorGroup) { self.annotationPopMode.setAnnotationColor(annotationColor: color) updatePDFViewCallback?() } else if (view == shapeColorGroup) { self.annotationPopMode.setAnnotationColor(annotationColor: color) updatePDFViewCallback?() } else if (view == fontColorItem) { fontColorItem.properties?.state = .normal fontColorItem.reloadData() let colorPanel = NSColorPanel.shared colorPanel.setTarget(self) colorPanel.showsAlpha = false colorPanel.setAction(#selector(self.fontColorChange(_:))) colorPanel.orderFront(nil) } } } //MARK: - ComponentInputDelegate extension KMNPopAnnotationWindowController: ComponentInputDelegate { func componentInputDidChanged(inputView: ComponentInput) { if (inputView == fildNameInput) { for i in 0 ..< annotationPopMode.annotations.count { let annotation = annotationPopMode.annotations[i] if let widgt = annotation as? CPDFWidgetAnnotation { widgt.setFieldName(inputView.properties.text) } } updatePDFViewCallback?() } else if (inputView == gropNameInput) { for i in 0 ..< annotationPopMode.annotations.count { let annotation = annotationPopMode.annotations[i] if let button = annotation as? CPDFButtonWidgetAnnotation { button.setButtonWidgetStateString(inputView.properties.text) } } updatePDFViewCallback?() } else if (inputView == urlInput) { } } func componentInputDidEndEditing(inputView: ComponentInput) { if (inputView == fildNameInput) { } else if (inputView == gropNameInput) { } else if (inputView == urlInput) { if linkType == .email { let emailString = urlInput.properties.text if KMNTools.isValidateEmail(emailString) { linkAnnotation.setDestination(nil) let linkUrlPath = KMNTools.judgeEmailURL(emailString) linkAnnotation.setURL(linkUrlPath) updatePDFViewCallback?() } } else if linkType == .url { let webString = urlInput.properties.text linkAnnotation.setDestination(nil) let linkUrlPath = KMNTools.judgeWebURL(webString) linkAnnotation.setURL(linkUrlPath) updatePDFViewCallback?() } } } } //MARK: - ComponentSelectDelegate extension KMNPopAnnotationWindowController: ComponentSelectDelegate { func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) { if(view == fontNameSelect) { if menuItemProperty?.identifier == "1" { let familyName = fontNameSelect.properties.text ?? "Helvetica" let styles = CPDFFont.fontNames(forFamilyName: familyName) compdfFont = CPDFFont(familyName: familyName, fontStyle: styles.first ?? "") annotationPopMode.setAnnotationFont(font: compdfFont ?? CPDFFont.init(familyName: "Helvetica", fontStyle: "")) } else if menuItemProperty?.identifier == "2"{ //字体样式 compdfFont = CPDFFont(familyName: compdfFont?.familyName ?? "Helvetica", fontStyle: menuItemProperty?.text ?? "") annotationPopMode.setAnnotationFont(font: compdfFont ?? CPDFFont.init(familyName: "Helvetica", fontStyle: "")) } } } } //MARK: - ComponentSelectDelegate extension KMNPopAnnotationWindowController: NSPopoverDelegate { func popoverWillClose(_ notification: Notification) { if fontPopover == (notification.object as? NSPopover) { if(fontAlightButton.properties.state == .pressed) { fontAlightButton.properties.state = .normal fontAlightButton.reloadData() } } else if ObjectPopover == (notification.object as? NSPopover) { if(alightButton.properties.state == .pressed) { alightButton.properties.state = .normal alightButton.reloadData() } } } } //MARK: - ComponentPaginationDelegate extension KMNPopAnnotationWindowController: ComponentPaginationDelegate { public func componentPaginationDidValueChanged(pagination: ComponentPagination) { let pageIndex = pagination.properties.currentIndex if let page = listView?.document.page(at: UInt(pageIndex)) { linkAnnotation.setURL(nil) let destination = CPDFDestination(document: listView?.document ?? CPDFDocument(), pageIndex: Int(paginationView.properties.currentIndex - 1)) linkAnnotation.setDestination(destination) refreshPageThum() reloadPageUrlData() updatePDFViewCallback?() } } }