// // KMLinkViewController.swift // PDF Reader Pro // // Created by Niehaoyu on 2024/10/18. // import Cocoa import KMComponentLibrary @objc public enum PDFLinkType: Int, CaseIterable{ case Page = 0 // case Web // case Email // } @objc public enum PDFLinkPopupType: Int, CaseIterable{ case None = 0 //默认类型 case Page // case Web // case Email // case MultiSelect //Popup弹窗 } @objcMembers class KMLinkViewController: NSViewController { @IBOutlet var contendBox: NSBox! @IBOutlet var infoContendView: NSView! @IBOutlet var tabsBGView: NSView! @IBOutlet var tabsView: ComponentTabs! @IBOutlet var typeContendView: NSView! @IBOutlet var linkPageView: KMLinkPageView! @IBOutlet var linkEmailView: KMLinkEmailView! @IBOutlet var linkWebView: KMLinkWebView! private var _annotations: [CPDFLinkAnnotation] = [] private weak var _pdfView: CPDFListView? = nil private var annotation: CPDFLinkAnnotation? = nil var multiController: KMNAlignmentController = KMNAlignmentController.init() //注释多选界面 let pageProperty = ComponentTabsProperty(tabsType: .underline_Fill, state: .normal, showIcon: false, title: NSLocalizedString("Page", comment: "")) let webProperty = ComponentTabsProperty(tabsType: .underline_Fill, state: .normal, showIcon: false, title: NSLocalizedString("Web", comment: "")) let emailProperty = ComponentTabsProperty(tabsType: .underline_Fill, state: .normal, showIcon: false, title: NSLocalizedString("Email", comment: "")) public override func viewDidLoad() { super.viewDidLoad() // Do view setup here. pdfLinkType = .Page setUpUI() } func setUpUI() { contendBox.fillColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-middle") if pdfLinkType == .Page { pageProperty.state = .pressed } else if pdfLinkType == .Web { webProperty.state = .pressed } else if pdfLinkType == .Email { emailProperty.state = .pressed } tabsView.updateItemProperty([pageProperty, webProperty, emailProperty]) tabsView.delegate = self linkPageView.delegate = self linkWebView.delegate = self linkEmailView.delegate = self multiController.delegate = self } //MARK: - Setter var pdfLinkType: PDFLinkType = .Page { didSet { view.window?.makeFirstResponder(nil) reloadData() } } weak var pdfView: CPDFListView? { get { return _pdfView } set { _pdfView = newValue } } var annotations: [CPDFLinkAnnotation] { get { return _annotations } set { _annotations = newValue if _annotations.isEmpty == false { annotation = _annotations.first } else { annotation = nil } } } //MARK: - func public func reloadData() { infoContendView.isHidden = false if annotations.count == 0 { infoContendView.isHidden = true } else if annotations.count > 1 { multiController.view.frame = CGRectMake((CGRectGetWidth(infoContendView.frame) - 232)/2, CGRectGetHeight(infoContendView.frame)-112, 232, 112) multiController.view.autoresizingMask = [.width, .maxYMargin] infoContendView.addSubview(multiController.view) multiController.view.isHidden = false tabsBGView.isHidden = true typeContendView.isHidden = true if annotations.count > 2 { multiController.aligHorizontalXButton.properties.isDisabled = false multiController.aligVerticalYButton.properties.isDisabled = false } else { multiController.aligHorizontalXButton.properties.isDisabled = true multiController.aligVerticalYButton.properties.isDisabled = true } multiController.aligHorizontalXButton.reloadData() multiController.aligVerticalYButton.reloadData() } else { multiController.view.isHidden = true tabsBGView.isHidden = false typeContendView.isHidden = false linkPageView.isHidden = true linkEmailView.isHidden = true linkWebView.isHidden = true if pdfLinkType == .Page { linkPageView.isHidden = false linkPageView.pdfView = pdfView linkPageView.annotation = annotation linkPageView.reloadData() } else if pdfLinkType == .Web { linkWebView.isHidden = false } else if pdfLinkType == .Email { linkEmailView.isHidden = false } } } //MARK: - private func judgeWebURL(_ urlString: String) -> String { var modifiedURLString = urlString modifiedURLString = modifiedURLString.replacingOccurrences(of: "\n", with: "") modifiedURLString = modifiedURLString.replacingOccurrences(of: " ", with: "") if !modifiedURLString.hasPrefix("http://") && !modifiedURLString.hasPrefix("https://") { if modifiedURLString.hasPrefix("smb://") { } else { modifiedURLString = "https://" + modifiedURLString } } if modifiedURLString.hasSuffix(".com") == false { modifiedURLString = modifiedURLString + ".com" } if modifiedURLString == "https://" { modifiedURLString = "" } return modifiedURLString } func judgeEmailURL(_ urlString: String) -> String { var modifiedURLString = urlString if !modifiedURLString.hasPrefix("mailto:") { modifiedURLString = "mailto:" + modifiedURLString } if modifiedURLString == "mailto:" { modifiedURLString = "" } return modifiedURLString } func isValidateEmail(_ email: String) -> Bool { let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}" let emailTest = NSPredicate(format: "SELF MATCHES %@", emailRegex) return emailTest.evaluate(with: email) } //MARK: - MouseEvent public override func mouseUp(with event: NSEvent) { super.mouseUp(with: event) } } //MARK: - ComponentTabsDelegate extension KMLinkViewController: ComponentTabsDelegate { public func componentTabsDidSelected(_ view: ComponentTabs, _ property: ComponentTabsProperty) { if property == pageProperty { pdfLinkType = .Page } else if property == webProperty { pdfLinkType = .Web } else if property == emailProperty { pdfLinkType = .Email } DispatchQueue.main.async { self.reloadData() } } } //MARK: - KMLinkPageViewDelegate extension KMLinkViewController: KMLinkPageViewDelegate { func kmLinkPageViewDidGoToPage(_ view: KMLinkPageView, _ pageIndex: Int) { if let page = pdfView?.document.page(at: UInt(pageIndex-1)) { pdfView?.go(toPageIndex: pageIndex-1, animated: false) } } func kmLinkPageViewDidChangeDestination(_ view: KMLinkPageView, _ pageIndex: Int) { guard let activiteAnno = annotation else { return } activiteAnno.setURL(nil) if let page = pdfView?.document.page(at: UInt(pageIndex-1)) { let bounds = page.bounds(for: .cropBox) let destination = CPDFDestination(page: page, at: NSPoint(x: 0, y: bounds.size.height)) activiteAnno.setDestination(destination) pdfView?.needsDisplay = true } if let page = pdfView?.document.page(at: UInt(pageIndex-1)) { pdfView?.go(toPageIndex: pageIndex-1, animated: false) } } } //MARK: - KMLinkPageViewDelegate extension KMLinkViewController: KMLinkWebViewDelegate { func kmLinkWebViewDidGo(_ view: KMLinkWebView, _ webString: String) { guard let activeAnnotation = annotation else { return } activeAnnotation.setDestination(nil) let linkUrlPath = judgeWebURL(webString) activeAnnotation.setURL(linkUrlPath) pdfView!.needsDisplay = true if let url = activeAnnotation.url() { if let data = URL(string: url) { NSWorkspace.shared.open(data) } } } } //MARK: - KMLinkPageViewDelegate extension KMLinkViewController: KMLinkEmailViewDelegate { func kmLinkEmailViewDidGo(_ view: KMLinkEmailView, _ emailString: String) { guard let activeAnnotation = annotation else { return } activeAnnotation.setDestination(nil) if !isValidateEmail(emailString) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Invalid Email. Please try again.", comment: "") alert.runModal() return } let linkUrlPath = judgeEmailURL(emailString) activeAnnotation.setURL(linkUrlPath) pdfView!.needsDisplay = true if let url = activeAnnotation.url() { NSWorkspace.shared.open(URL(string: url)!) } } } //MARK: - KMNAlignmentControllerDelegate extension KMLinkViewController: KMNAlignmentControllerDelegate { func alignmentControllerDidClick(_ controller: KMNAlignmentController, _ alignmentType: KMPDFActiveFormsAlignType) { pdfView?.change(annotations, alignmentType: alignmentType) pdfView?.setNeedsDisplayForVisiblePages() } }