// // KMMainViewController.swift // PDF Master // // Created by wanjun on 2022/12/15. // import Cocoa @objcMembers class KMMainViewController: NSViewController,CPDFViewDelegate,CPDFListViewDelegate,NSTextFieldDelegate { @IBOutlet var PDFContendView: NSView! @IBOutlet var centerContentView: NSView! @IBOutlet var listView: CPDFListView! @IBOutlet var secondaryPdfView: CPDFView! @IBOutlet weak var readContentView: NSView! @IBOutlet weak var tipCurrentPageBox: KMBox! @IBOutlet weak var rightView: NSView! @IBOutlet weak var leftView: NSView! @IBOutlet weak var mianSplitView: KMSplitView! @IBOutlet weak var pdfSplitView: KMSplitView! @IBOutlet weak var pdfContentView: NSView! @IBOutlet weak var pdfSplitSecondView: NSBox! @IBOutlet weak var locationPageView: NSView! @IBOutlet weak var tipLabel: NSTextField! @IBOutlet weak var toplayoutConstraint: NSLayoutConstraint! @IBOutlet var childToolbarController: KMToolbarViewController! @IBOutlet var toolbarController: KMToolbarController! @IBOutlet weak var toolbarBox: NSBox! @IBOutlet weak var heightOffset: NSLayoutConstraint! //阅读模式界面 @IBOutlet weak var readModelView: KMReadModelView! @IBOutlet weak var bottomAreaView: KMBox! @IBOutlet weak var readModelViewWidthConstraint: NSLayoutConstraint! var isReadMode: Bool = false var readLeftMethodType: BotaType = .None var readLeftPanelOpen = false var readLastLeftPanWidth = 0.0 var readLeftViewShowPanel = false var readRightPanelOpen = false var readToolbarType: KMToolbarViewType = .None var readToolbarItemIdentifier: String = "" var readToolMode: CToolMode = .textToolMode var readAnnotationType: CAnnotationType = .unkown var readSubViewType: RightSubViewType = .None //页码显示器 @IBOutlet weak var pageNumberDisplayView: KMPageNumberDisplayView! @IBOutlet weak var tipCurrentPageBoxWidthConstraint: NSLayoutConstraint! private var _needSave = false var needSave: Bool { set { _needSave = newValue if (_needSave == false) { self.clearIsPDFDocumentEdited() self.hiddenSecureSuccessTip() } } get { return _needSave } } // 标识 pdf 已编辑 fileprivate var _isPDFDocumentEdited = false // 标识 pdf文字图片已编辑 internal var isPDFTextImageEdited = false var isPDFDocumentEdited: Bool { get { return _isPDFDocumentEdited } } var password: String? var leftSideViewController: KMLeftSideViewController = KMLeftSideViewController.init(type: KMLeftMethodMode()) var rightSideViewController: KMRightSideViewController! var searchResults: [KMSearchMode] = [] var mwcFlags: MwcFlags = MwcFlags() var isShowQuickTour: Bool = false var document: CPDFDocument? var myDocument: NSDocument? var browserWindowController: KMBrowserWindowController? var documentAIViewController: KMDocumentAIViewController? var cropSettingWindowController: KMCropSettingWindowController! var currentWindowController: NSWindowController! let CPDFOfficeLeftSidePaneWidthKey = "CPDFOfficeLeftSidePaneWidthKey" let CPDFOfficeRightSidePaneWidthKey = "CPDFOfficeRightSidePaneWidthKey" @IBOutlet weak var topTipBox: NSBox! @IBOutlet weak var exitFullButton: NSButton! var hasAddRedact: Bool = false var functionWidth: Double { get { if self.isReadMode { if !self.isShowBOTA { return 0 } } return 48 } } var isShowBOTA: Bool = false let panelWidth = 212.0 let defaultRightWidth = 260.0 var lastLeftPanWidth = 0.0 var lastRightPanWidth = 0.0 var leftPanelOpen: Bool = false var rightPanelIsOpen = false var pageNumber: UInt? var openSecondaryPdfView: KMSecondaryViewController? var secondaryPdfContentView: NSView? var lastSplitPDFHeight: Float? internal var isSaveKeyChain = true var rightMouseEventing = false var pdfEditController: KMPDFEditViewController? { get { return self.getPDFEditController() } } var autoSaveTimer: Timer? var progressController: SKProgressController? private var _documentFirstLoad: Bool = true var eventMonitor: Any? var keyEventMonitor: Any? var mouseRightMenuEvent: NSEvent? var aiTranslationWindow: KMAITranslationWindowController? var aiTranslationConfirWC: KMAITranslationConfirmWindowController? private var background_mask: NSView? fileprivate var _secureOptions: [CPDFDocumentWriteOption : Any]? var secureOptions: [CPDFDocumentWriteOption : Any]? { get { return self._secureOptions } } fileprivate var _removeSecureFlag = false var removeSecureFlag: Bool { get { return self._removeSecureFlag } } fileprivate var _saveWatermarkFlag = false var saveWatermarkFlag: Bool { get { return self._saveWatermarkFlag } } deinit { NotificationCenter.default.removeObserver(self) self.stopAutoSaveTimer() self.removeEventMonitor() self.removeKeyEventMonitor() } override func awakeFromNib() { super.awakeFromNib() self.addBackgroundMaskView() // pdfSplitView.frame = NSMakeRect(0, 0, NSWidth(centerContentView.bounds), NSHeight(centerContentView.bounds)-1) // centerContentView.addSubview(pdfSplitView) self.PDFContendView.backgroundColor(NSColor.km_init(hex: "FFFFFF")) listView.delegate = self listView.pdfListViewDelegate = self // listView.editingConfig().isSupportMultipleSelectEditingArea = true if (document != nil) { // if (self.document!.isLocked) { // // } else { listView.document = document // } // listView.document.delegate = self let autoScale = listView.autoScales if !autoScale { listView.scaleFactor = 1.0 } } self.initPDFLeftViewVC() self.initRightSideView() self.toolbarController.listView = self.listView } override func viewDidAppear() { super.viewDidAppear() //刷新向前向后按钮 self.updateBackAndForwardButtonState() KMLightMemberManager.manager.canShowAdvancedView = false Task { @MainActor in await KMLightMemberManager.manager.canUseAdvanced(needNetworking: true) } // self.addEventMonitor() self.view.window?.makeFirstResponder(self.listView) // 更新属性页面的信息 NotificationCenter.default.post(name: KMInfoWindowC.windowDidBecomeMainNotification, object: self.myDocument) if (self.document == nil) { return } if (self.document!.isLocked == false) { return } if (self.view.window == nil) { return } if (self.password != nil) { self.isSaveKeyChain = false self.listView.document.unlock(withPassword: self.password) return } KMPasswordInputWindow.openWindow(window: self.view.window!, url: self.document!.documentURL) { result , password in if (result == .cancel) { self.browserWindowController?.browser.closeTab() return } self.isSaveKeyChain = true self.listView.document = self.document self.document?.unlock(withPassword: password) } } override func viewWillAppear() { super.viewWillAppear() self.reStartAutoSaveTimer() //是否弹出登录窗口 // self.needShowRegisterView() } override func viewWillDisappear() { super.viewWillDisappear() // KMPreferenceManager.shared.setPageNumber(self.listView.currentPageIndex, forKey: self.listView.document.documentURL.path) self.pauseAutoSaveTimer() self.removeEventMonitor() } override func viewWillLayout() { super.viewWillLayout() if (KMTools.isFullScreen(self.view.window ?? NSWindow())) { // 全屏 self.exitFullButton.isHidden = false self.listView.backgroundColor = KMPreferenceManager.shared.displayBackgroundFullScreenColor } else { self.exitFullButton.isHidden = true self.listView.backgroundColor = KMPreferenceManager.shared.displayBackgroundNormalColor } } override func viewDidLoad() { super.viewDidLoad() leftSideViewController.mainVC = self mwcFlags.settingUpWindow = 1 toolbarController.delegate = self //TODO: 先让项目运行,看后面怎么调整这段逻辑,目前最外层是 KMBrowserWindowController toolbarBox.contentView = toolbarController.view // self.childToolbarController.updateType(newType: .Annatiton) // self.showChildToolbar(showToolbar: true) // self.toolbarController.editPDFButtonAction(item: NSMenuItem()) if (UserDefaults.standard.object(forKey: CPDFOfficeLeftSidePaneWidthKey) != nil) { UserDefaults.standard.set(256, forKey: CPDFOfficeLeftSidePaneWidthKey) UserDefaults.standard.synchronize() } if (UserDefaults.standard.object(forKey: CPDFOfficeRightSidePaneWidthKey) != nil) { UserDefaults.standard.set(256, forKey: CPDFOfficeRightSidePaneWidthKey) UserDefaults.standard.synchronize() } let position = mianSplitView.maxPossiblePositionOfDivider(at: 1) mianSplitView.setPosition(position, ofDividerAt: 0) mianSplitView.setPosition(mianSplitView.minPossiblePositionOfDivider(at: 0), ofDividerAt: 0) pdfSplitView.setPosition(mianSplitView.maxPossiblePositionOfDivider(at: 1), ofDividerAt: 0) self.locationPageView.wantsLayer = true; self.locationPageView.layer?.backgroundColor = NSColor(red: 189.0/255.0, green: 223.0/255.0, blue: 253.0/255.0, alpha: 1).cgColor self.tipLabel.stringValue = NSLocalizedString("Please use the scroll bar, thumbnail tool to locate the target page, click or box the area to select the target range.", comment: "") if (KMPreferenceManager.shared.openLastUnlockedDocumentWhenAppStart) { if (self.listView.document != nil) { let pageNumber = KMPreferenceManager.shared.getPageNumber(forKey: self.listView.document.documentURL.path) let pageScale = KMPreferenceManager.shared.getPageScale(forKey: self.listView.document.documentURL.path) if (pageNumber != nil && pageNumber! >= 0 && pageNumber! < self.listView.document.pageCount) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { if (pageScale != nil) { self.listView.scaleFactor = CGFloat(pageScale!) } self.listView.go(toPageIndex: pageNumber!, animated: false) } } else { self._goToFirstPageForFristAppear() } } } else { self._goToFirstPageForFristAppear() } //阅读页面 readModelView.delegate = self pageNumberDisplayView.delegate = self tipCurrentPageBox.moveCallback = { [unowned self] mouseEntered, mouseBox in if mouseEntered { self.pageNumberDisplayView.hover = true // self.updatePageIndicatoreType() } else { self.pageNumberDisplayView.hover = false // self.updatePageIndicatoreType() } } NotificationCenter.default.addObserver(self, selector: #selector(rename(_:)), name: NSNotification.Name.init(rawValue: "KMTabControllerRename"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(closeTab(_:)), name: NSNotification.Name.init(rawValue: "KMTabControllerCloseTabs"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(showInFinder(_:)), name: NSNotification.Name.init(rawValue: "KMTabControllerShowInFinder"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(preferenceDidChangeNotification), name: KMPreferenceManager.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(documentDidUnlockNotification), name: Notification.Name("CPDFDocumentDidUnlockNotification"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(applicationWillTerminateNotification), name: NSApplication.willTerminateNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(KMPDFViewCurrentPageDidChangedNotification), name: NSNotification.Name.init(rawValue: "KMPDFViewCurrentPageDidChanged"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(CPDFDocumentPageCountChangedNotification), name: NSNotification.Name.init(rawValue: "CPDFDocumentPageCountChangedNotification"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(CEditPDFToolModeChangeStateUnkownNotification), name: Notification.Name.init("CEditPDFToolModeChangeStateUnkown"), object: nil) self.autoSaveTimeStartOrStopIfNeed() self.toolbarController.selectItem(KMDocumentAnnotationToolbarItemIdentifier) self.closeRightPane() self.addKeyEventMonitor() //检测OCR包是否需要更新 #if VERSION_DMG KMResourceDownloadManager.manager.checkDocumentAIVersion() #endif } //MARK: - PDFListView func initPDFLeftViewVC() { leftSideViewController.listView = self.listView ?? CPDFListView() leftSideViewController.view.frame = CGRect(x: 0, y:0 , width: self.leftView.frame.size.width, height: self.leftView.frame.size.height) leftSideViewController.view.autoresizingMask = [.height,.width] leftSideViewController.delegate = self self.leftView.addSubview(leftSideViewController.view) } func initRightSideView() { self.rightSideViewController = KMRightSideViewController.init() self.rightSideViewController.view.frame = CGRect(x: 0, y: 0, width: self.rightView.frame.width, height: self.rightView.frame.size.height) self.rightSideViewController.view.autoresizingMask = [.height,.width] self.rightSideViewController.listView = self.listView // self.rightSideViewController.view.isHidden = true self.rightSideViewController.isHidden = true self.rightSideViewController.delegate = self self.rightView.addSubview(self.rightSideViewController.view) self.rightSideViewController.propertyDidChange = { [weak self] (model: AnyObject?) in let topBarView = self?.toolbarController.fetchTopBarView() if (topBarView == nil || ((topBarView?.isKind(of: KMWatermarkAdjectiveTopBarView.self)) == false)) { return } /// Bates码、页眉页脚、背景、水印 if (model == nil) { (topBarView as! KMWatermarkAdjectiveTopBarView).isCanApply(can: false) } else { if ((model?.isKind(of: KMBatesModel.self))!) { if ((model as! KMBatesModel).hasVaild) { (topBarView as! KMWatermarkAdjectiveTopBarView).isCanApply(can: true) } else { (topBarView as! KMWatermarkAdjectiveTopBarView).isCanApply(can: false) } } else if ((model?.isKind(of: KMHeaderFooterModel.self))!) { if ((model as! KMHeaderFooterModel).hasVaild) { (topBarView as! KMWatermarkAdjectiveTopBarView).isCanApply(can: true) } else { (topBarView as! KMWatermarkAdjectiveTopBarView).isCanApply(can: false) } } else if ((model?.isKind(of: KMBackgroundModel.self))!) { (topBarView as! KMWatermarkAdjectiveTopBarView).isCanApply(can: true) } else if ((model?.isKind(of: KMWatermarkModel.self))!) { (topBarView as! KMWatermarkAdjectiveTopBarView).isCanApply(can: true) } } } } // MARK: Private Methods internal func removeNotifications() { NotificationCenter.default.removeObserver(self) self.leftSideViewController.clearAnnotationFilterData() self.leftSideViewController.clearNotification() } func checkShouldAutoOpenLeftVC() { if (KMPreferenceManager.shared.leftSideNeedCloseWhenOpenFile()) { return } if (KMPreferenceManager.shared.leftSideDisplayType == .showOutlineIfHas) { let outlineRoot = self.listView.document.outlineRoot() let hasOutline = (outlineRoot != nil && outlineRoot!.numberOfChildren > 0) if (hasOutline == false) { return } DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { self.leftSideViewController.refreshMethodType(methodType: .Outline) } return } DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { let selectedIndex = UserDefaults.standard.integer(forKey: "KMBOTASelectedIndexKey") let thumai = KMLeftMethodMode() if selectedIndex == 0 { thumai.methodType = .Thumbnail } else if selectedIndex == 1 { thumai.methodType = .Outline } else if selectedIndex == 2 { thumai.methodType = .BookMark } else if selectedIndex == 3 { thumai.methodType = .Annotation } else if selectedIndex == 4 { thumai.methodType = .Search } self.leftSideViewController.refreshMethodType(methodType: thumai.methodType) } } func applyLeftSideWidth(_ leftSideWidth: CGFloat, rightSideWidth: CGFloat) -> Void { mianSplitView.setPosition(leftSideWidth, ofDividerAt: 0) mianSplitView.setPosition(mianSplitView.maxPossiblePositionOfDivider(at: 1) - mianSplitView.dividerThickness - rightSideWidth, ofDividerAt: 1) lastLeftPanWidth = leftSideWidth lastRightPanWidth = rightSideWidth } //MARK: 动画 暂未接入 func animateSplitView(to position: CGFloat) { let frame1 = NSRect(x: 0, y: 0, width: position, height: mianSplitView.frame.height) let frame2 = NSRect(x: position, y: 0, width: mianSplitView.frame.width - position, height: mianSplitView.frame.height) // 创建一个新的动画上下文 let animationContext = NSAnimationContext.current animationContext.duration = 0.5 animationContext.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) // 启用隐式动画 NSAnimationContext.current.allowsImplicitAnimation = true // 移动分隔条 mianSplitView.setPosition(position, ofDividerAt: 0) mianSplitView.subviews[0].frame = frame1 mianSplitView.subviews[1].frame = frame2 } internal var removeAllAnnotationsStore = KMPDFViewRemoveAllAnnotationsStore() internal func removeAllAnnotations() { let alert = NSAlert() alert.messageText = NSLocalizedString("This will permanently remove all annotations. Are you sure to continue?", comment: "") alert.addButton(withTitle: NSLocalizedString("Yes", comment:"")) alert.addButton(withTitle: NSLocalizedString("No", comment:"")) if (alert.runModal() != .alertFirstButtonReturn) { return } DispatchQueue.main.async { self.removeAllAnnotationsStore.store(t: self.listView) } } // MARK: Set Methods var setDocument: CPDFDocument? { get { return document } set { if document != newValue { document = newValue } listView.document = document // listView.document.delegate = self self.listView.layoutDocumentView() } } var setPageNumber: UInt { get { return pageNumber! } set { let pageCount = listView.document.pageCount var value = newValue if value > pageCount { value = pageCount } if value > 0 && listView.currentPage().pageIndex() != value-1 { listView.go(to: listView.document.page(at: value-1)) } if pageNumber != value { pageNumber = value } } } // MARK: - 页面编辑 open func enterPageEdit(_ pages: [Int] = []) { //选中page var tPages = pages if tPages.count == 0 { tPages = self.leftSideViewController.selectPages ?? [self.listView.currentPageIndex] } if (hasEnterPageEdit()) { exitPageEdit() return } if (self.toolbarController != nil && self.toolbarController.mainToolBarView != nil) { let toolBarView: KMToolbarViewController = self.toolbarController.mainToolBarView! for (key, value) in toolBarView.toolbarItems { if (key == KMRightControlToolbarItemIdentifier || key == KMLeftControlToolbarItemIdentifier) { (value as! KMToolBoxItem).unEnabled = true } } } #if VERSION_DMG let controller = KMPDFEditViewController_dmg(self.listView.document) #else let controller = KMPDFEditViewController(self.listView.document) #endif controller.selectedPages = tPages controller.listView = self.listView self.addChild(controller) self.PDFContendView.addSubview(controller.view) controller.view.frame = self.PDFContendView.bounds controller.view.autoresizingMask = [.width,.height] self.listView.isHidden = true controller.itemClick = { [weak self] index, params in if (index == 1) { /// 双击退出 self?.enterEditMode(self!.leftSideViewController, []) DispatchQueue.main.async { let pageIndex: Int = params.first as! Int self?.listView.go(toPageIndex: pageIndex, animated: true) } } else if (index == 2) { // 打印 self?.showPrintWindow(pageRange: KMPrintPageRange(type: .custom, selectPages: params.first as! [Int])) } } controller.documentEditedCallback = { [weak self] params in self?.recordIsPDFDocumentEdited() // self?.needSave = true if let data = params.first as? KMPageEditType { self?.recordSaveWatermarkFlag(type: data.toSubscribeWaterMarkType()) } else { self?.recordSaveWatermarkFlag() } } controller.selectionDidChange = { [weak self] selectedIndexs in var indexSet = IndexSet() for indexPath in selectedIndexs { indexSet.insert(indexPath.item) } if indexSet.count != 0 { self?.leftSideViewController.thumbnailViewController.selectPages(indexs: indexSet, needScroll: true) // self?.listView.go(toPageIndex: indexSet.first!, animated: false) } } } open func exitPageEdit() { if (self.toolbarController != nil && self.toolbarController.mainToolBarView != nil) { let toolBarView: KMToolbarViewController = self.toolbarController.mainToolBarView! for (key, value) in toolBarView.toolbarItems { if (key == KMRightControlToolbarItemIdentifier || key == KMLeftControlToolbarItemIdentifier) { (value as! KMToolBoxItem).unEnabled = false } } } let editController = getPDFEditController() if (editController == nil) { return } self.listView.annotationType = .highlight editController?.view.removeFromSuperview() editController?.removeFromParent() self.listView.isHidden = false self.listView.layoutDocumentView() self.view.window?.makeFirstResponder(self.listView) self.listView.annotationType = .unkown self.listView.go(toPageIndex: editController!.listViewCurrentIndex, animated: false) } open func hasEnterPageEdit() -> Bool { return self.getPDFEditController() != nil } // MARK: - Private Methods private func getPDFEditController() -> KMPDFEditViewController? { var editController: KMPDFEditViewController? for controller in self.children { if (controller.isKind(of: KMPDFEditViewController.self)) { editController = (controller as! KMPDFEditViewController) break } } return editController } private func addBackgroundMaskView() { self.removeBackgroundMaskView() if let superview = self.mianSplitView.superview { let view = NSView() superview.addSubview(view) view.frame = superview.bounds view.autoresizingMask = [.width, .height] view.wantsLayer = true view.layer?.backgroundColor = .white self.background_mask = view } } private func removeBackgroundMaskView() { self.background_mask?.removeFromSuperview() self.background_mask = nil } private func _goToFirstPageForFristAppear() { DispatchQueue.main.asyncAfter(wallDeadline: .now()+0.1) { self.listView.go(toPageIndex: 0, animated: false) } } func isFileGreaterThan10MB(atPath filePath: String) -> Bool { let fileManager = FileManager.default do { let fileAttributes = try fileManager.attributesOfItem(atPath: filePath) if let fileSize = fileAttributes[.size] as? UInt64 { let megabyteSize = fileSize / (1024 * 1024) return megabyteSize >= 10 } } catch { KMPrint("Error: \(error)") } return false } func isPDFPageCountExceedsLimit(filePath: String) -> Bool { let url = URL(fileURLWithPath: filePath) guard let document = PDFDocument(url: url) else { return false } let pageCount = document.pageCount return pageCount > 30 } // MARK: Redact 【标记密文】 func exeRedactConfirm(_ type: KMRedactConfirmType, callback: @escaping () -> ()?) { let windowController = KMRedactConfirmWindowController(type) self.currentWindowController = windowController self.view.window?.beginSheet(windowController.window!) windowController.itemClick = { [weak self] index in if (index == 2) { /// 取消 self?.view.window?.endSheet((self?.currentWindowController.window)!) self?.currentWindowController = nil callback() return } self?.view.window?.endSheet((self?.currentWindowController.window)!) self?.currentWindowController = nil let panel = NSSavePanel() panel.nameFieldStringValue = "[新文件]"+(self?.listView.document.documentURL.lastPathComponent)! let button = NSButton.init(checkboxWithTitle: "保存后打开文档", target: nil, action: nil) button.state = .on panel.accessoryView = button panel.isExtensionHidden = true panel.beginSheetModal(for: (self?.view.window!)!) { response in if response != .OK { callback() return } if (type == .redactOne) { let anno = self!.listView.activeAnnotation if (anno == nil || (anno?.isKind(of: CPDFRedactAnnotation.self)) == false) { callback() return } (anno as! CPDFRedactAnnotation).applyRedaction() } else if (type == .redactAll) { self?.listView.document.applyRedactions() } else if (type == .eraserOne) { let anno = self!.listView.activeAnnotation if (anno == nil || (anno?.isKind(of: CPDFRedactAnnotation.self)) == false) { callback() return } anno?.page.erasureRedact(from: anno!.bounds) } else if (type == .eraserAll) { KMRedactTools.eraserDocument((self?.listView.document)!) { result, errorAnno in if (result == false) { callback() return } } } self!.listView.document.write(to: panel.url) if (button.state == .on) { NSDocumentController.shared.openDocument(withContentsOf: panel.url!, display: true) { document, alreadyOpen, error in } } else { NSWorkspace.shared.activateFileViewerSelecting([panel.url!]) } callback() } } } // MARK: Secure 【安全】 public func showSecureSuccessTip() { let view: NSView = self.view let tip = KMSecureEncryptSuccessTipView() let size = NSSize(width: 379, height: 176) tip.frame = NSMakeRect(view.frame.size.width-size.width-16, view.frame.size.height-size.height-88, size.width, size.height) tip.autoresizingMask = [.minXMargin, .minYMargin] view.addSubview(tip) tip.itemClick = { [weak self] in self!.hiddenSecureSuccessTip() } } public func hiddenSecureSuccessTip() { let view: NSView = self.view var tip: KMSecureEncryptSuccessTipView? for subview in view.subviews { if (subview.isKind(of: KMSecureEncryptSuccessTipView.self)) { tip = (subview as! KMSecureEncryptSuccessTipView) break } } if (tip == nil) { return } tip?.removeFromSuperview() } public func showSecureLimitTip() { self.hiddenSecureLimitTip() let view: NSView = self.view let tip = KMSecureLimitAlertView() let size = NSSize(width: 379, height: 154) tip.frame = NSMakeRect(view.frame.size.width-size.width-16, view.frame.size.height-size.height-50, size.width, size.height) tip.autoresizingMask = [.minXMargin, .minYMargin] view.addSubview(tip) tip.itemClick = { [unowned self] index in if (index == 1) { self.hiddenSecureLimitTip() return } if (self.listView.document.allowsPrinting == false || self.listView.document.allowsCopying == false) { KMPasswordInputWindow.openWindow(window: self.view.window!, type: .owner, url: self.listView.document.documentURL) { [weak self] result, password in if result == .cancel { /// 关闭 return } /// 解密成功 self?.hiddenSecureLimitTip() self?.isSaveKeyChain = false self?.listView.document.unlock(withPassword: password) } return } } } public func hiddenSecureLimitTip() { let view: NSView = self.view var tip: KMSecureLimitAlertView? for subview in view.subviews { if (subview.isKind(of: KMSecureLimitAlertView.self)) { tip = (subview as! KMSecureLimitAlertView) break } } if (tip == nil) { return } tip?.removeFromSuperview() } override func mouseMoved(with event: NSEvent) { } func savePageNumberIfNeed() { if (KMPreferenceManager.shared.openLastUnlockedDocumentWhenAppStart) { if self.listView.document != nil { KMPreferenceManager.shared.setPageNumber(self.listView.currentPageIndex, forKey: self.listView.document.documentURL.path) KMPreferenceManager.shared.setPageScale(Float(self.listView.scaleFactor), forKey: self.listView.document.documentURL.path) } } } // MARK: - // MARK: 退出全屏 @IBAction func exitFullScreen(_ sender: Any) { if (self.view.window == nil) { return } if (KMTools.isFullScreen(self.view.window!)) { self.view.window?.toggleFullScreen(nil) } } // MARK: - // MARK: 显示合并窗口 public func showMergeWindow(url: URL? = nil, _ password: String?) { DispatchQueue.main.async { if let _url = url { let document = PDFDocument(url: _url) let windowController = KMPDFEditAppendWindow(pdfDocument: document, password: password) windowController?.oriDucumentUrl = self.listView.document.documentURL self.toolbarController.cancelSelected(KMToolbarToolMergeItemIdentifier) windowController?.beginSheetModal(for: self.view.window, completionHandler: { result, selectedIndexSet in }) } else { let document = PDFDocument(url: self.listView.document.documentURL) let windowController = KMPDFEditAppendWindow(pdfDocument: document, password: password) self.toolbarController.cancelSelected(KMToolbarToolMergeItemIdentifier) windowController?.beginSheetModal(for: self.view.window, completionHandler: { result, selectedIndexSet in }) } } } // MARK: - // MARR: 显示加密弹窗 public func showSecureWindow(_ url: URL) { let windowController = KMSecureEncryptWindowController(windowNibName: "KMSecureEncryptWindowController") windowController.documentURL = url windowController.myDocument = self.listView.document self.currentWindowController = windowController windowController.itemClick = { [weak self] index in self?.view.window?.endSheet((self?.currentWindowController.window)!) self?.currentWindowController = nil } windowController.resultCallback = { [weak self] result in let windowController_secure = self?.currentWindowController as! KMSecureEncryptWindowController self?.view.window?.endSheet((self?.currentWindowController.window)!) self?.currentWindowController = nil self?._secureOptions = windowController_secure.options self?.needSave = true self?.recordIsPDFDocumentEdited(type: .setPassword) if (result) { self?.showSecureSuccessTip() self?.recordSaveWatermarkFlag(type: .setPassword) } else { let alert = NSAlert() alert.messageText = NSLocalizedString("Failure", comment: "") alert.runModal() } } self.view.window?.beginSheet(windowController.window!) } // MARK: - // MARK: 保存文档 internal func needSaveDocument() -> Bool { if (self.isPDFDocumentEdited) { return self.isPDFDocumentEdited } if (self.needSave) { return self.needSave } let document: KMMainDocument? = self.myDocument as? KMMainDocument if (document?.isDocumentEdited == nil || document!.isDocumentEdited == false) { return false } return true } internal func saveDocument(overlookDocumentIfEdited overlook: Bool = false) { let document: KMMainDocument? = self.myDocument as? KMMainDocument if (overlook) { document?.save(nil) return } if (self.isPDFDocumentEdited) { self.clearIsPDFDocumentEdited() self.needSave = false document?.save(nil) return } if (document?.isDocumentEdited == nil || document!.isDocumentEdited == false) { return } document?.save(nil) } internal func asyncSaveDocument(overlookDocumentIfEdited overlook: Bool = false, callback:@escaping KMCommonBlock) { let document: KMMainDocument? = self.myDocument as? KMMainDocument if (overlook) { DispatchQueue.main.async { document?.save(nil) callback() } return } if (self.isPDFDocumentEdited) { self.clearIsPDFDocumentEdited() self.needSave = false DispatchQueue.main.async { document?.save(nil) callback() } return } if (document?.isDocumentEdited == nil || document!.isDocumentEdited == false) { callback() return } DispatchQueue.main.async { document?.save(nil) callback() } } internal func saveDocumentWithProgressAlert(callback:@escaping KMCommonBlock) { // 显示进度 self.showProgressWindow(message: NSLocalizedString("Save PDF", comment: "")) self.progressController?.maxValue = 3.0 self.progressController?.increment(by: 1.0) // 保存文档 self.asyncSaveDocument { [unowned self] params in // 执行进度 [假进度] self.progressController?.increment(by: 1.0) self.progressController?.increment(by: 1.0) // DispatchQueue.main.async { DispatchQueue.main.asyncAfter(deadline: .now()+0.1) { // 隐藏进度 self.hiddenProgressWindow() // 回调 callback() } } } // MARK: - // MARK: 选择 PDFDisplay 模式 @objc public func selectDisplay(display: KMPDFDisplayType, viewSettingIsReload: Bool = true) { let toolModel = self.listView.toolMode self.isReadMode = false switch display { case .singlePage: self.listView.setDisplay(.singlePage) break case .singlePageContinuous: self.listView.setDisplay(.singlePageContinuous) break case .twoUp: self.listView.setDisplay(.twoUp) break case .twoUpContinuous: self.listView.setDisplay(.twoUpContinuous) break case .bookMode: self.listView.displaysAsBook = true self.listView.displayTwoUp = true self.listView.displayDirection = .horizontal break case .bookContinuous: self.listView.displaysAsBook = true self.listView.displayTwoUp = true self.listView.displayDirection = .vertical break case .readModel: self.openReadModel() break case .readContinuous: self.openReadModel() break } self.listView.layoutDocumentView() if (viewSettingIsReload && self.leftSideViewController.panelSetViewController.isViewLoaded) { self.leftSideViewController.panelSetViewController.reloadListViewModel() } if (toolModel == .editPDFToolMode) { if self.rightSideViewController.eidtPDFImageProperty != nil { self.rightSideViewController.eidtPDFImageProperty?.cancelCutImageAction("") self.rightSideViewController.isHidden = true self.closeRightPane() } } } // Mark: - // MARK: - 选择缩放模式 @objc public func selectZoom(_ type: KMPDFZoomType) { switch type { case .width: self.listView.autoScales = true // self.listView.autoScales = false break case .fit: // self.listView.autoScales = !self.listView.autoScales if let pageHeight = self.listView.currentPage()?.size.height, pageHeight > 0 { let pdfviewHeight = self.listView.bounds.size.height self.listView.scaleFactor = pdfviewHeight/pageHeight self.listView.autoScales = false } break case .actualSize: if self.listView.scaleFactor != 1.0 { self.listView.scaleFactor = 1.0 self.listView.autoScales = false } break } } // MARK: - 自动保存 internal func autoSaveTimeStartOrStopIfNeed() { if (KMPreferenceManager.shared.autoSave == false) { self.stopAutoSaveTimer() return } self.startAutoSaveTimer(KMPreferenceManager.shared.autoSaveTimeInterval) if (self.myDocument == nil) { self.stopAutoSaveTimer() return } let browser = self.browserWindowController?.browser guard let activeDocument = browser?.activeTabContents() else { return } if (self.myDocument!.isEqual(to: activeDocument) == false) { self.pauseAutoSaveTimer() return } } private func startAutoSaveTimer(_ interval: TimeInterval) { if (self.autoSaveTimer != nil) { self.autoSaveTimer?.invalidate() self.autoSaveTimer = nil } self.autoSaveTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] timer in DispatchQueue.main.async { #if DEBUG Swift.debugPrint("文档已自动保存") if let _document = self?.myDocument { Swift.debugPrint(_document.fileURL as Any) } #endif self?.saveDocument() } } RunLoop.current.add(self.autoSaveTimer!, forMode: .common) } private func pauseAutoSaveTimer() { self.autoSaveTimer?.fireDate = Date.distantFuture } private func reStartAutoSaveTimer() { self.autoSaveTimer?.fireDate = Date()+KMPreferenceManager.shared.autoSaveTimeInterval } private func needShowRegisterView() { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { if KMLightMemberManager.manager.checkPopupRegister() { // Login & Logout let window = NSApp.mainWindow ?? self.view.window if KMLightMemberManager.manager.isLogin() { } else if window != nil { var email: String = UserDefaults.standard.value(forKey: "kLoginEmail") as? String ?? "" if email.count == 0 { KMLoginWindowController.show(window: window!, .Batch, .register) } else { KMLoginWindowController.show(window: window!, .Batch, .login) } } } } } private func stopAutoSaveTimer() { self.autoSaveTimer?.invalidate() self.autoSaveTimer = nil } // MARK: - // MARK: Progress func showProgressWindow(message: String = "") { if (self.progressController != nil) { self.hiddenProgressWindow() } let progress = SKProgressController() progress.window?.backgroundColor = NSColor.km_init(hex: "#36383B") progress.window?.contentView?.wantsLayer = true progress.window?.contentView?.layer?.backgroundColor = NSColor.km_init(hex: "#36383B").cgColor progress.progressField.textColor = NSColor.white progress.showClose = false progress.message = message self.progressController = progress self.view.window?.beginSheet(progress.window!) } func hiddenProgressWindow() { if (self.progressController != nil) { self.view.window?.endSheet((self.progressController?.window)!) self.progressController = nil } } // MARK - // MARK - Event 监听 private func addEventMonitor() { if (self.eventMonitor != nil) { self.removeEventMonitor() } KMPrint("已添加事件监听") self.eventMonitor = NSEvent.addLocalMonitorForEvents(matching: .scrollWheel) { [weak self] event in if (event.type == .scrollWheel && event.modifierFlags.contains(.option)) { // Alt + 鼠标滚轮 self?.listView.magnifyWheel(event) return nil } return event } } func addKeyEventMonitor() { if (self.keyEventMonitor != nil) { self.removeKeyEventMonitor() } keyEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in // print(event.keyCode) if event.keyCode == 53 { if self?.listView.toolMode == .editPDFToolMode { if self != nil { //使用editingSelectionString获取内容文字 if self!.listView.editingAreas() != nil { if self!.listView.editingAreas().count > 0 && self!.listView.isEditable() { self!.listView.clearEditingSelectCharItem() } else if self!.listView.editingAreas().count > 0 { if self?.listView.annotationType == .addImage || self?.listView.annotationType == .addText { let textItem = self?.toolbarController.findItem(KMToolbarAddTextEditPDFItemIdentifier) let imageItem = self?.toolbarController.findItem(KMToolbarAddImageEditPDFItemIdentifier) textItem?.isSelected = false imageItem?.isSelected = false } self?.rightSideViewController.isHidden = true self?.listView.endEditIsRemoveBlock(with: self!.listView.editingAreas().first as? CPDFEditArea) self?.listView.updateEditing([]) self?.listView.isEditImage = false self?.listView.setNeedsDisplayPageViewFor(self!.listView.currentPage()) self?.closeRightPane() } else if(self?.listView.annotationType == .addImage || self!.listView.annotationType == .addText) { if self?.listView.annotationType == .addImage || self?.listView.annotationType == .addText { let textItem = self?.toolbarController.findItem(KMToolbarAddTextEditPDFItemIdentifier) let imageItem = self?.toolbarController.findItem(KMToolbarAddImageEditPDFItemIdentifier) textItem?.isSelected = false imageItem?.isSelected = false } self?.rightSideViewController.isHidden = true self?.listView.setShouAddEdit([]) self?.listView.change([.text, .image]) self?.listView.annotationType = .editTextImage self?.closeRightPane() } } else { if self?.listView.annotationType == .addImage || self?.listView.annotationType == .addText { let textItem = self?.toolbarController.findItem(KMToolbarAddTextEditPDFItemIdentifier) let imageItem = self?.toolbarController.findItem(KMToolbarAddImageEditPDFItemIdentifier) textItem?.isSelected = false imageItem?.isSelected = false } } } } } return event } } func removeKeyEventMonitor() { if (self.keyEventMonitor != nil) { KMPrint("removeKeyEventMonitor 已移除事件监听") NSEvent.removeMonitor(self.keyEventMonitor as Any) self.keyEventMonitor = nil } } private func removeEventMonitor() { if (self.eventMonitor != nil) { KMPrint("已移除事件监听") NSEvent.removeMonitor(self.eventMonitor as Any) self.eventMonitor = nil } } // MARK: - // MARK: Tools func pdfViewCanHorizontalScroll() -> Bool { let scroll = self.listView.scroll() if (scroll == nil) { return false } return scroll?.horizontalScroller?.isHidden == nil ? false : !(scroll!.horizontalScroller!.isHidden) } func pdfViewCanVerticalScroll() -> Bool { let scroll = self.listView.scroll() if (scroll == nil) { return false } return scroll?.verticalScroller?.isHidden == nil ? false : !(scroll!.verticalScroller!.isHidden) } // MARK: - Public Methods // 清理数据 [eg. 通知] public func clearData() { self.removeNotifications() if (self.listView.spellingTag() > 0) { NSSpellChecker.shared.closeSpellDocument(withTag: self.listView.spellingTag()) } } public func clearSecureOptions() { self._secureOptions = nil } public func recordRemoveSecureFlag() { self._removeSecureFlag = true self.clearSecureOptions() self.recordIsPDFDocumentEdited(type: .removePassword) self._needSave = true } public func clearRemoveSecureFlag() { self._removeSecureFlag = false } public func recordSaveWatermarkFlag(type: KMSubscribeWaterMarkType = .none) { km_synchronized(self) { self._saveWatermarkFlag = true } if let _document = self.myDocument as? KMMainDocument { _document.recordTrackEvent(type: type) } } public func clearSaveWatermarkFlag() { km_synchronized(self) { self._saveWatermarkFlag = false } } public func recordIsPDFDocumentEdited(type: KMSubscribeWaterMarkType = .none) { km_synchronized(self) { self._isPDFDocumentEdited = true if let _document = self.myDocument { KMTools.setDocumentEditedState(document: _document) } } } public func clearIsPDFDocumentEdited() { km_synchronized(self) { self._isPDFDocumentEdited = false } } // MARK: - Noti Actions internal func documentDidUnlockNotification(_ sender: Notification) { if (self.listView.document != nil && self.listView.document.isEqual(to: sender.object)) { if (self.myDocument == nil) { return } if (self.listView.document.allowsPrinting && self.listView.document.allowsCopying) { self.hiddenSecureLimitTip() } if ((self.myDocument as! KMMainDocument).isUnlockFromKeychain || self.isSaveKeyChain == false) { return } let type = KMPreferenceManager.shared.savePasswordType if (type == .never) { return } if (type == .always) { self.myDocument?.savePasswordInKeychain(self.listView.document.password, self.listView.document) return } // 保存到钥匙串 let alert = NSAlert() alert.messageText = NSLocalizedString("Remember Password?", comment: "") alert.informativeText = NSLocalizedString("Do you want to save this password in your Keychain?", comment: "") alert.addButton(withTitle: NSLocalizedString("Yes", comment: "")) alert.addButton(withTitle: NSLocalizedString("No", comment: "")) if (alert.runModal() == .alertFirstButtonReturn) { // 保存密码 self.myDocument?.savePasswordInKeychain(self.listView.document.password, self.listView.document) return } } } internal func applicationWillTerminateNotification(_ sender: Notification) { self.savePageNumberIfNeed() self.saveDocument() } func KMPDFViewCurrentPageDidChangedNotification(_ sender: Notification) { if self.isReadMode { self.readModelView.currentPageIndex = self.listView.currentPageIndex } //刷新向前向后按钮 self.updateBackAndForwardButtonState() } func CPDFDocumentPageCountChangedNotification(_ sender: Notification) { if self.isReadMode { self.readModelView.totalPagesCount = Int(self.listView.document.pageCount) } //刷新向前向后按钮 self.updateBackAndForwardButtonState() } func CEditPDFToolModeChangeStateUnkownNotification(_ sender: Notification) { var editSelectd = false if (self.listView.annotationType == .addText || self.listView.annotationType == .addImage) && self.listView.toolMode == .editPDFToolMode { editSelectd = true } if self.listView.toolMode == .editPDFToolMode { if editSelectd { self.toolbarController.cancelSelected(KMToolbarAddTextEditPDFItemIdentifier) } } } //MARK: - PDFListViewDelegate func pdfViewDocumentDidLoaded(_ pdfView: CPDFView!) { // KMPrint("pdfViewDocumentDidLoaded") self.removeBackgroundMaskView() if (!self.listView.document!.allowsCopying || !self.listView.document!.allowsPrinting) { self.showSecureLimitTip() } if (self._documentFirstLoad) { self.checkShouldAutoOpenLeftVC() if (KMPreferenceManager.shared.openLastUnlockedDocumentWhenAppStart) { let pageNumber = KMPreferenceManager.shared.getPageNumber(forKey: self.listView.document.documentURL.path) let pageScale = KMPreferenceManager.shared.getPageScale(forKey: self.listView.document.documentURL.path) if (pageScale != nil) { self.listView.scaleFactor = CGFloat(pageScale!) } if (pageNumber != nil && pageNumber! >= 0 && pageNumber! < self.listView.document.pageCount) { self.listView.go(toPageIndex: pageNumber!, animated: false) } else { self._goToFirstPageForFristAppear() } } else { self._goToFirstPageForFristAppear() } self._documentFirstLoad = false } let notification = Notification(name: Notification.Name(rawValue: "pdfViewDocumentDidLoaded")) self.preferenceDidChangeNotification(notification:notification) let leftWidthNumber = UserDefaults.standard.object(forKey: CPDFOfficeLeftSidePaneWidthKey) as? NSNumber ?? NSNumber(value: panelWidth + functionWidth) let rightWidthNumber = UserDefaults.standard.object(forKey: CPDFOfficeRightSidePaneWidthKey) as? NSNumber ?? NSNumber(value: defaultRightWidth) applyLeftSideWidth(leftWidthNumber.doubleValue, rightSideWidth: rightWidthNumber.doubleValue) self.updatePageIndicatoreType() } func pdfViewCurrentPageDidChanged(_ pdfView: CPDFView!) { self.updatePageIndicatoreType() NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "KMPDFViewCurrentPageDidChanged"), object: self.document) // KMPrint("KMPDFViewCurrentPageDidChanged") } func pdfViewScaleDidChanged(_ pdfView: CPDFView!) { self.toolbarController.mainToolBarView?.zoomTextField.stringValue = "\(Int(self.listView.scaleFactor * 100))%" self.updateZoomInOutButtonState() // KMPrint("pdfViewScaleDidChanged") } func pdfViewDidClick(onLink pdfView: CPDFView!, withURL url: String!) { if let urlString = url, urlString == kKMPurchaseProductURLString { //跳转订阅比较表 let _ = KMComparativeTableViewController.show(window: NSApp.mainWindow ?? NSWindow()) return } KMTools.openURL(urlString: url) } func pdfViewPerformURL(_ pdfView: CPDFView!, withContent content: String!) { KMPrint("pdfViewPerformURL") } func pdfViewPerformPrint(_ pdfView: CPDFView!) { KMPrint("pdfViewPerformPrint") } func pdfViewPerformGo(toPage pdfView: CPDFView!) { KMPrint("pdfViewPerformGo") } func pdfViewOpenPDF(_ pdfView: CPDFView!, forRemoteGoTo action: CPDFAction!) { KMPrint("pdfViewOpenPDF") } func pdfViewPerformReset(_ pdfView: CPDFView!) { KMPrint("pdfViewPerformReset") } func pdfViewEditingBlockDidChanged(_ pdfView: CPDFView!) { KMPrint("pdfViewEditingBlockDidChanged") } func pdfViewAsBookBookmark() -> NSImage! { return NSImage(named: "KMImageNameUXIconPDFViewBookMark")! } func pdfViewEditingSelectionDidChanged(_ pdfView: CPDFView!) { self.recordSaveWatermarkFlag() if self.rightSideViewController != nil && self.rightSideViewController.subViewType == .EditPDFAddText { self.rightSideViewController.eidtPDFTextProperty.reloadData() self.rightSideViewController.eidtPDFTextProperty.updateTextTextPresuppositionState() } } func pdfViewEditingAreaDidChanged(_ pdfView: CPDFView!) { let areas = self.listView.editingAreas() if areas == nil || areas?.count ?? 0 == 0 { if self.listView.toolMode == .editPDFToolMode { if self.listView.annotationType == .addImage || self.listView.annotationType == .addText { if self.listView.isEditImage { self.menuItemEditingClick_CropImage(sender: NSMenuItem()) } else { // if self.listView.annotationType == .addImage { // self.closeRightPane() // } // if self.rightSideViewController.eidtPDFImageProperty != nil { // self.rightSideViewController.eidtPDFImageProperty.reloadData() // } // self.openRightPane() } } else { self.closeRightPane() } } else { self.rightSideViewController.isHidden = true self.closeRightPane() if self.rightSideViewController != nil && self.rightSideViewController.subViewType == .EditPDFAddText && self.listView.annotationType == .addText { self.rightSideViewController.eidtPDFTextProperty.initData() } } if self.listView.isEdited() { self.recordIsPDFDocumentEdited(type: .editText) } if self.listView.annotationType != .addText { NotificationCenter.default.post(name: NSNotification.Name(rawValue: "kPDFViewEditingAreaDidChanged"), object: self.listView.document) } return } self.isPDFTextImageEdited = true if (self.listView.annotationType == .addImage) && areas!.count > 0 { var isImageArea = false for i in 0 ... areas!.count-1 { if areas![i] is CPDFEditImageArea { isImageArea = true } } if isImageArea { // self.rightSideViewController.view.isHidden = false self.rightSideViewController.isHidden = false if self.rightSideViewController != nil && self.rightSideViewController.subViewType == .EditPDFAddImage { self.rightSideViewController.subViewType = .EditPDFAddImage self.rightSideViewController.eidtPDFImageProperty.reloadData() } self.openRightPane() } else { // self.rightSideViewController.view.isHidden = true self.rightSideViewController.isHidden = true self.closeRightPane() } } else if self.rightSideViewController != nil && self.rightSideViewController.subViewType == .EditPDFAddText && self.listView.annotationType == .addText { // self.rightSideViewController.view.isHidden = false self.rightSideViewController.isHidden = false if self.listView.editingSelectionString().count != 0 { self.rightSideViewController.eidtPDFTextProperty.reloadData() } else { self.rightSideViewController.eidtPDFTextProperty.refreshSelectAreaProperty(needDefaultData: true) } self.openRightPane() } else { var textsAreas : [CPDFEditTextArea] = [] var imagesAreas : [CPDFEditImageArea] = [] if self.listView.editingAreas()?.count ?? 0 < 1 { return } for i in 0 ... areas!.count-1 { if areas![i] is CPDFEditTextArea { textsAreas.append(areas![i] as! CPDFEditTextArea) } if areas![i] is CPDFEditImageArea { imagesAreas.append(areas![i] as! CPDFEditImageArea) } } if textsAreas.count > 0 && textsAreas.count == areas!.count { // self.rightSideViewController.view.isHidden = false self.rightSideViewController.isHidden = false self.rightSideViewController.subViewType = .EditPDFAddText self.rightSideViewController.eidtPDFTextProperty?.reloadData() self.openRightPane() } else if imagesAreas.count > 0 { // self.rightSideViewController.view.isHidden = false self.rightSideViewController.isHidden = false self.rightSideViewController.subViewType = .EditPDFAddImage self.rightSideViewController.eidtPDFImageProperty?.reloadData() self.openRightPane() } } if self.listView.isEdited() { self.recordIsPDFDocumentEdited(type: .editText) if self.listView.annotationType != .addText { NotificationCenter.default.post(name: NSNotification.Name(rawValue: "kPDFViewEditingAreaDidChanged"), object: self.listView.document) } } } func pdfViewEditingCropBoundsDidChanged(_ pdfView: CPDFView!, editing editArea: CPDFEditArea!) { self.recordSaveWatermarkFlag() if editArea != nil && (editArea is CPDFEditImageArea){ self.listView.cropAreas = editArea as? CPDFEditImageArea } } //编辑PDF 创建图片区域回调 func pdfViewEditingAddImageArea(_ pdfView: CPDFView!, add page: CPDFPage!, add rect: CGRect) { self.recordSaveWatermarkFlag(type: .editImage) if self.listView.isEditImage { self.menuItemEditingClick_CropImage(sender: NSMenuItem()) } else { let panel = NSOpenPanel() panel.allowsMultipleSelection = false panel.allowedFileTypes = ["png","jpg"] panel.beginSheetModal(for: NSApp.mainWindow!) { response in if response == .OK { var filePath = panel.url?.path var image = NSImage.init(contentsOf: panel.url!) //图片自适应范围 if image != nil { var imageRect = rect let imageSize = image!.size var previewSize = rect.size var isChangeSize = false if previewSize.width == 0 && previewSize.height == 0 { previewSize = CGSize(width: 500, height: 500) isChangeSize = true } let scale = min(previewSize.width / imageSize.width, previewSize.height / imageSize.height) let newSize = CGSize(width: imageSize.width * scale, height: imageSize.height * scale) if isChangeSize { imageRect.origin.x = imageRect.origin.x - newSize.width / 2 imageRect.origin.y = imageRect.origin.y - newSize.height / 2 } else { imageRect.origin.x = imageRect.origin.x + imageRect.width / 2 - newSize.width / 2 imageRect.origin.y = imageRect.origin.y + imageRect.height / 2 - newSize.height / 2 } imageRect.size = newSize let limitWidth = 1920.0 if imageSize.width > limitWidth || imageSize.height > limitWidth { filePath = KMImageOptimization.needCompressImageLosslessly(image: image!, targetSize: CGSize(width: limitWidth, height: limitWidth), maxSizeInBytes: 1024 * 1024 * 5, targetCompression: 1.0) } //自适应page let pageRect = self.listView.currentPage().bounds if imageRect.width > pageRect.width || imageRect.height > pageRect.height { let pageScale = min(pageRect.width / imageSize.width, pageRect.height / imageSize.height) imageRect = CGRect(x: imageRect.origin.x, y: imageRect.origin.y, width: imageRect.width * pageScale, height: imageRect.height * pageScale) } if imageRect.origin.x < 0 { imageRect.origin.x = 5 } if imageRect.origin.y < 0 { imageRect.origin.y = 5 } if imageRect.origin.x + imageRect.width > pageRect.width || imageRect.origin.y + imageRect.height > pageRect.height { let offsetX = imageRect.origin.x + imageRect.width - pageRect.width let offsetY = imageRect.origin.y + imageRect.height - pageRect.height imageRect.origin.x = imageRect.origin.x - offsetX - 5 imageRect.origin.y = imageRect.origin.y - offsetY - 5 } DispatchQueue.main.async { self.listView.createImagePath(filePath, rect: imageRect, page: pdfView.currentPage()) self.isPDFTextImageEdited = true self.recordIsPDFDocumentEdited(type: .editImage) // self.asyncSaveDocument { params in // // } } } } } } } func pdfViewEditingAddTextArea(_ pdfView: CPDFView!, add page: CPDFPage!, add rect: CGRect) { // print(rect) var newrect = CGRect(x: rect.origin.x, y: rect.origin.y, width: rect.size.width, height: rect.size.height) if __CGSizeEqualToSize(rect.size, CGSize.zero) { newrect = CGRect(x: rect.origin.x, y: rect.origin.y - 12, width: 20, height: 12) } else { newrect = CGRect(x: rect.origin.x, y: rect.origin.y + rect.size.height - 12, width: rect.size.width, height: 12) } let model = KMEditPDFTextManager.manager.fetchUserDefaultData(type: .commonly) let fontName = KMEditPDFTextManager.manager.fetchFontName(fontName: model.fontName) let fontSize = model.fontSize let fontColor = model.color let fontAlign = model.alignment let fontStyle = KMEditPDFTextManager.manager.fetchFontStyle(fontName: model.fontName) NSColorPanel.shared.color = fontColor let font = KMEditPDFTextManager.manager.fetchFont(fontName: fontName, style: fontStyle, size: fontSize) let style = NSMutableParagraphStyle() style.alignment = fontAlign let attributes = [NSAttributedString.Key.font:font, NSAttributedString.Key.foregroundColor:fontColor,NSAttributedString.Key.paragraphStyle:style] as [NSAttributedString.Key : Any] self.listView.createEmptyStringBounds(newrect,withAttributes: attributes as [NSAttributedString.Key : Any], page: pdfView.currentPage()) if self.rightSideViewController != nil && self.rightSideViewController.subViewType == .EditPDFAddText && self.listView.annotationType == .addText { self.rightSideViewController.eidtPDFTextProperty.refreshSelectAreaProperty(needDefaultData: true) } // self.asyncSaveDocument { params in // // } self.recordSaveWatermarkFlag(type: .editText) } // func pdfViewEditingDoubleClick(_ pdfView: CPDFView!, imageArea editArea: CPDFEditArea!) { // debugPrint("pdfViewEditingDoubleClick") // } func pdfViewEditingOperationDidChanged(_ pdfView: CPDFView!) { debugPrint("pdfViewEditingOperationDidChanged") if self.listView.isEdited() { self.recordSaveWatermarkFlag() } } // func pdfListViewEditAnnotation(_ pdfListView: CPDFListView!, for anotation: CPDFAnnotation!) { // debugPrint("pdfListViewEditAnnotation") // } func pdfListViewKeyDownIsContinue(_ pdfListView: CPDFListView!, theEvent: NSEvent!) -> Bool { let command = theEvent.modifierFlags.contains(.command) let control = theEvent.modifierFlags.contains(.control) KMPrint(theEvent.keyCode) if (theEvent.keyCode == 11 && command) { // command + B [添加书签] self.menuItemBookMarkClick_add(sender: NSMenuItem()) return false } else if (command && control && theEvent.keyCode == 14) { // command + control + E [注释 橡皮擦] return false } else if (theEvent.keyCode == 123) { // 向左 if(self.listView.isEditing() && !self.listView.isSelecteditAreaNotEdit()) { return false } else { if (self.pdfViewCanHorizontalScroll() == false && self.listView.canGoToPreviousPage()) { self.listView.goToPreviousPage(nil) return false } } } else if (theEvent.keyCode == 126) { // 向上 if(self.listView.isEditing() && !self.listView.isSelecteditAreaNotEdit()) { return false } else { if (self.listView.isContinousScroll()) { return true } if (self.pdfViewCanVerticalScroll() == false && self.listView.canGoToPreviousPage()) { self.listView.goToPreviousPage(nil) return false } } } else if (theEvent.keyCode == 124) { // 向右 if(self.listView.isEditing() && !self.listView.isSelecteditAreaNotEdit()) { return false } else { if (self.pdfViewCanHorizontalScroll() == false && self.listView.canGoToNextPage()) { self.listView.goToNextPage(nil) return false } } } else if (theEvent.keyCode == 125) { // 向下 if(self.listView.isEditing() && !self.listView.isSelecteditAreaNotEdit()) { return false } else { if (self.listView.isContinousScroll()) { return true } if (self.pdfViewCanVerticalScroll() == false && self.listView.canGoToNextPage()) { self.listView.goToNextPage(nil) return false } } } else if (theEvent.keyCode == 36) { if self.listView.annotationType == .addImage || self.listView.annotationType == .addText { if self.listView.isEditImage { self.menuItemEditingClick_CropImage(sender: NSMenuItem()) } } } if theEvent.keyCode == 53 { if self.isReadMode { self.closeReadModel() } self.leftSideViewCancelSelect() if (self.toolbarController.toolbarType.isToolMode()) { self.toolbarController.selectItem(self.toolbarController.toolbarType.itemIdentifier()) } } return true } func pdfListViewMenuValidate(_ pdfListView: CPDFListView!, menuItem: NSMenuItem!, isTakesEffect: UnsafeMutablePointer!) -> Bool { guard let action = menuItem.action else { isTakesEffect.pointee = false return false } if (KMSystemMenu.isEditSelector(sel: action)) { if (KMSystemMenu.Edit.deleteSelector == action) { isTakesEffect.pointee = true return self.listView.activeAnnotations.count > 0 } else if (KMSystemMenu.Edit.copySelector == action) { isTakesEffect.pointee = true return true//self.listView.canCopy() } else if (KMSystemMenu.Edit.cutSelector == action) { isTakesEffect.pointee = true return self.listView.canCopy() } else if (KMSystemMenu.Edit.pasteSelector == action) { isTakesEffect.pointee = true return self.listView.canPaste() } } isTakesEffect.pointee = false return false } //MARK: -CPDFListViewDelegate func cPDFListView(_ pdfListView: CPDFListView, didDelete annotation: CPDFAnnotation, in pdfPage: CPDFPage) { } func pdfListViewChangeatioActiveAnnotations(_ pdfListView: CPDFListView!, forActiveAnnotations annotations: [CPDFAnnotation]!, isRightMenu: Bool) { self.view.window?.makeFirstResponder(self.listView) if isRightMenu { } else if annotations.count > 0 { if annotations.count > 1 { let fristAnnotation = annotations.first var isSameAnnotation = true let className = NSStringFromClass(fristAnnotation!.classForCoder) for annotation in annotations { let cunrrentClassName = NSStringFromClass(annotation.classForCoder) if (className == "CPDFSquareAnnotation") || (className == "CPDFCircleAnnotation") || (className == "CPDFLineAnnotation") { if (cunrrentClassName != "CPDFSquareAnnotation") && (cunrrentClassName != "CPDFCircleAnnotation") && (cunrrentClassName != "CPDFLineAnnotation") { isSameAnnotation = false } } else { if className != cunrrentClassName { isSameAnnotation = false } } } if isSameAnnotation == false { self.rightSideViewController?.reloadDataWithPDFView(pdfView: pdfListView, isShow: false) // self.closeRightPane() } else { self.rightSideViewController?.reloadDataWithPDFView(pdfView: pdfListView, isShow: true) self.openRightPane() } } else { let fristAnnotation = annotations.first let className = NSStringFromClass(fristAnnotation!.classForCoder) if self.isReadMode { self.rightSideViewController?.reloadDataWithPDFView(pdfView: pdfListView, isShow: false) self.closeRightPane() } else { self.rightSideViewController?.reloadDataWithPDFView(pdfView: pdfListView, isShow: true) if className != "CPDFStampAnnotation" && className != "CPDFSignatureAnnotation" && className != "CPDFListStampAnnotation" { self.openRightPane() } } } } else if (annotations.count == 0){ if pdfListView.annotationType == .unkown { self.rightSideViewController?.reloadDataWithPDFView(pdfView: pdfListView, isShow: false) self.closeRightPane() } else { if self.isReadMode { self.rightSideViewController?.reloadDataWithPDFView(pdfView: pdfListView, isShow: false) self.closeRightPane() } else { self.rightSideViewController?.reloadDataWithPDFView(pdfView: pdfListView, isShow: true) self.openRightPane() } } } } func pdfListViewChangedAnnotationType(_ pdfListView: CPDFListView!, for annotationType: CAnnotationType) { if(annotationType == .unkown) { // self.rightSideViewController.view.isHidden = true self.rightSideViewController.isHidden = true self.closeRightPane() } } ///开始定位链接注释 func pdfListViewLinkDestinationStart(_ pdfListView: CPDFListView!, withActiveAnnotation annotation: CPDFAnnotation!) { if self.locationPageView.superview == nil { self.locationPageView.frame = CGRect(x: 0, y: pdfListView.frame.maxY-32, width: pdfListView.frame.width, height: 32) pdfListView.addSubview(self.locationPageView) } } ///刷新链接注释 func pdfListViewLinkDestinationEnd(_ pdfListView: CPDFListView!, withActiveAnnotation annotation: CPDFAnnotation!) { if self.locationPageView.superview != nil { self.locationPageView.removeFromSuperview() } if self.rightSideViewController.subViewType == .AnnotationProperts && pdfListView.annotationType == .link { self.rightSideViewController.reloadDataWithPDFView(pdfView: pdfListView, isShow: true) } } func pdfListViewMenuItemsEditing(at point: CGPoint, for page: CPDFPage!, menuItems: [NSMenuItem]!) -> [NSMenuItem]! { if (listView.toolMode != CToolMode.editPDFToolMode) { return menuItems } var tMenuItems = menuItems; if(listView.isSelectEditCharRange() || listView.isSelecteditArea(with: point)) { tMenuItems?.append(NSMenuItem.separator()) // tMenuItems?.append(self.fontColorMenuItem()) // tMenuItems?.append(self.fontSizeMenuItem()) } let areas = self.listView.editingAreas() ?? [] if areas.count == 1 { let fristAreas = areas.first if fristAreas is CPDFEditImageArea { self.listView.selectImageAreas = fristAreas as! CPDFEditImageArea if self.listView.isEditImage { tMenuItems?.removeAll() tMenuItems?.append(self.corpImageMenuItem()) tMenuItems?.append(self.cancelCorpImageMenuItem()) tMenuItems?.append(self.restoreCorpImageMenuItem()) } else { tMenuItems?.append(NSMenuItem.separator()) tMenuItems?.append(self.cutImageArea()) tMenuItems?.append(self.replaceImageArea()) tMenuItems?.append(self.exportImageArea()) } } else { tMenuItems?.swapAt(0, 1) } } else if areas.count == 0 { tMenuItems?.append(NSMenuItem.separator()) tMenuItems?.append(self.addText()) tMenuItems?.append(self.addImage()) } return tMenuItems } func pdfListViewMenu(forEvent pdfListView: CPDFListView!, for theEvent: NSEvent!, click menu: AutoreleasingUnsafeMutablePointer!) { self.mouseRightMenuEvent = theEvent let currentMenu : NSMenu = menu.pointee! if (listView.toolMode == .redactToolMode) { if (listView.activeAnnotation == nil) { } else { let currentPoint: NSPoint = pdfListView.convert(theEvent.locationInWindow, from: pdfListView.superview) let currentPage = pdfListView.page(for: currentPoint, nearest: true) // let currentPage = pdfListView.activeAnnotation.page let pagePoint = pdfListView.convert(currentPoint, to: currentPage) currentMenu.removeAllItems() if (pdfListView.activeAnnotation.bounds.contains(pagePoint)) { currentMenu.insertItem(withTitle: NSLocalizedString("Delete", comment: ""), action: #selector(redact_menuItemClick_delete), target: self, at: currentMenu.items.count) currentMenu.insertItem(NSMenuItem.separator(), at: currentMenu.items.count) currentMenu.insertItem(withTitle: NSLocalizedString("Properties...", comment: ""), action: #selector(redact_menuItemClick_setProperty), target: self, at: currentMenu.items.count) currentMenu.insertItem(withTitle: NSLocalizedString("Use Current Properties as New Default", comment: ""), action: #selector(redact_menuItemClick_setCurrentPropertyToDefaultValue), target: self, at: currentMenu.items.count) currentMenu.insertItem(NSMenuItem.separator(), at: currentMenu.items.count) currentMenu.insertItem(withTitle: NSLocalizedString("Repeat mark across pages", comment: ""), action: #selector(redact_menuItemClick_MultiPageFlag), target: self, at: currentMenu.items.count) currentMenu.insertItem(withTitle: NSLocalizedString("Apply Redactions", comment: ""), action: #selector(redact_menuItemClick_apply), target: self, at: currentMenu.items.count) currentMenu.insertItem(withTitle: NSLocalizedString("Erase Redactions", comment: ""), action: #selector(redact_menuItemClick_clear), target: self, at: currentMenu.items.count) } else { currentMenu.insertItem(withTitle: NSLocalizedString("Paste", comment: ""), action: #selector(redact_menuItemClick_paste), target: self, at: currentMenu.items.count) currentMenu.insertItem(NSMenuItem.separator(), at: currentMenu.items.count) currentMenu.insertItem(self.findStringMenu(), at: currentMenu.items.count) currentMenu.insertItem(self.printingMenu(), at: currentMenu.items.count) } } return } if(listView.toolMode == .selectToolMode) { if currentMenu.items.count > 0 { let export = NSMenuItem(title: NSLocalizedString("Export", comment: ""), action: nil, target: self) export.submenu = self.exportMenu() currentMenu.insertItem(export, at: currentMenu.items.count) let corp = NSMenuItem(title: NSLocalizedString("Crop", comment: ""), action:#selector(cropCurrentPage), target: self) // corp?.submenu = self.cropMenu() currentMenu.insertItem(corp, at: currentMenu.items.count) // MARK: 暂时拿掉 2023.04.26 // currentMenu.insertItem(self.zoomSelectionMenuItem(), at: currentMenu.items.count) currentMenu.insertItem(self.printingMenu(), at: currentMenu.items.count) } } else { if listView.currentSelection != nil { if (listView.currentSelection.string() != nil || listView.currentSelection.selectionType() == .image) { if listView.activeAnnotation != nil && ((listView.activeAnnotation is CPDFStampAnnotation) || (listView.activeAnnotation is CPDFSignatureAnnotation)) { currentMenu.insertItem(self.exportImageStampItem(), at: currentMenu.items.count-2) } else { var isMarkup = false for item in self.listView.activeAnnotations { if (item is CPDFMarkupAnnotation) { isMarkup = true break } } if(!isMarkup) { self.addAnnotationForStyleMenu(menu: currentMenu) } } } } else if (listView.activeAnnotation == nil) { if(currentMenu.items.count > 2) { currentMenu.insertItem(self.enterAnnotationStype(), at: 2) } if(currentMenu.items.count > 3) { currentMenu.insertItem(self.addBookmarkMenu(), at: 4) } if(currentMenu.items.count > 4) { currentMenu.insertItem(NSMenuItem.separator(), at: 5) } if(currentMenu.items.count > 5) { currentMenu.insertItem(self.setAnnotationToolStype(), at: 6) } if (currentMenu.items.count > 6) { currentMenu.insertItem(self.addReadModelStype(), at: 7) } if (currentMenu.items.count > 7 && self.isReadMode) { currentMenu.removeItem(currentMenu.item(withTitle: "Zoom")!) } // if(currentMenu.items.count > 6) { // let menuItem = NSMenuItem(title: NSLocalizedString("Automatic rolling", comment: ""), action:nil, target: self)! // currentMenu.insertItem(menuItem, at: 6) // } // if(currentMenu.items.count > 7) { // let menuItem = NSMenuItem(title: NSLocalizedString("TTS", comment: ""), action:nil, target: self)! // currentMenu.insertItem(menuItem, at: 7) // } // if(currentMenu.items.count > 8) { // let menuItem = NSMenuItem(title: NSLocalizedString("menuItem", comment: ""), action:nil, target: self)! // currentMenu.insertItem(menuItem, at: 8) // } if !self.isReadMode { if (currentMenu.items.count > 10) { currentMenu.insertItem(self.addHighlightLinksStype(), at: 11) } } if self.isReadMode { } else { currentMenu.insertItem(NSMenuItem.separator(), at: currentMenu.items.count) currentMenu.insertItem(self.findStringMenu(), at: currentMenu.items.count) currentMenu.insertItem(self.printingMenu(), at: currentMenu.items.count) } } else if listView.activeAnnotation != nil { var type : CAnnotationType = .unkown if listView.activeAnnotation is CPDFMarkupAnnotation { if (listView.activeAnnotation as! CPDFMarkupAnnotation).markupType() == .highlight { type = .highlight } else if (listView.activeAnnotation as! CPDFMarkupAnnotation).markupType() == .underline { type = .underline } else { type = .strikeOut } } else if listView.activeAnnotation is CPDFInkAnnotation { type = .ink } else if listView.activeAnnotation is CPDFFreeTextAnnotation { type = .freeText } else if listView.activeAnnotation is CPDFTextAnnotation { type = .anchored } else if listView.activeAnnotation is CPDFCircleAnnotation { type = .circle } else if listView.activeAnnotation is CPDFSquareAnnotation { type = .square } if ((listView.activeAnnotation is CPDFStampAnnotation) || (listView.activeAnnotation is CPDFSignatureAnnotation)) { if listView.activeAnnotations.count == 1 { currentMenu.insertItem(self.exportImageStampItem(), at: currentMenu.items.count-2) } } if type != .unkown && listView.activeAnnotations.count == 1{ currentMenu.insertItem(self.setDefaultAnnotationPorpert(type: type), at: currentMenu.items.count) } // closeRightPane() } } for item in currentMenu.items { if (item.action == NSSelectorFromString("menuItemClick_HidenorShowNote:")) { // 显示与隐藏注释 item action 截取 item.action = #selector(menuItemClick_HidenorShowNote) item.target = self break } } } func pdfListViewAddAnnotations(_ pdfListView: CPDFListView!, forAdd annotations: [CPDFAnnotation]!, in pdfPage: CPDFPage!) { var addRedact = false var saveWatermark = false var saveWatermarkType: KMSubscribeWaterMarkType = .none for anno in annotations { if (anno.isKind(of: CPDFRedactAnnotation.self)) { addRedact = true // break } else if (anno.isKind(of: CPDFLinkAnnotation.self)) { // link 注释 saveWatermark = true saveWatermarkType = .link } else if (anno.isKind(of: CPDFListStampAnnotation.self)) { // 图章注释 saveWatermark = true saveWatermarkType = .stamp } else if (anno.isKind(of: CPDFListSignatureAnnotation.self)) { // 签名注释 saveWatermark = true saveWatermarkType = .sign } } if (saveWatermark) { self.recordSaveWatermarkFlag(type: saveWatermarkType) } self.hasAddRedact = addRedact if self.isReadMode || self.listView.toolMode == .moveToolMode { self.listView.toolMode = .textToolMode self.listView.annotationType = .unkown self.toolbarController.toolbarType = .Annatiton } if (self.rightMouseEventing) { self.rightMouseEventing = false if (self.toolbarController.ignoreCurrentAnnotationTypeChange && self.listView.annotationType == .ink) { self.listView.toolMode = .textToolMode self.listView.annotationType = .unkown } } self.toolbarController.ignoreCurrentAnnotationTypeChange = false } func pdfListViewDidSelectionEnd(_ pdfListView: CPDFListView!) { if (!self.listView.isEqual(to: pdfListView)) { return } if (self.listView.toolMode != .selectToolMode) { return } if (self.topTipBox.isHidden || self.topTipBox.contentView?.subviews.count == 0) { return } let tipView = self.topTipBox.contentView?.subviews.first if (tipView?.isKind(of: KMCropTipView.self) == false) { return } (tipView as! KMCropTipView).setString(string: "请按 Enter 键确定裁剪区域") } func pdfListViewKeyDowClosePanel(_ speedy: CPDFViewSidebarSpeedMode, event theEvent: NSEvent!) { if(speedy == .right) { self.toggleRightPane() } else if (speedy == .left) { self.menuItemAction_hiddenLeftSide(speedy) } } func pdfListViewEventMarkupColor(with annotation: CPDFAnnotation!) -> [NSColor]! { if (annotation.isKind(of: CPDFMarkupAnnotation.self)) { if (annotation as! CPDFMarkupAnnotation).markupType() == .highlight { return KMAnnotationPropertiesColorManager.manager.markHighlightColors } else { return KMAnnotationPropertiesColorManager.manager.markOtherColors } } else { return KMAnnotationPropertiesColorManager.manager.markOtherColors } } func pdfListViewHaveDocumentAttribute() -> Bool { if(!self.listView.document.allowsCopying) { KMPasswordInputWindow.openWindow(window: self.view.window!, type: .owner, url: self.listView.document.documentURL) { [weak self] result, password in if (result == .cancel) { return } self?.isSaveKeyChain = false self?.listView.document.unlock(withPassword: password) self?.hiddenSecureLimitTip() } return false } return true } func addTopTip(_ view: NSView?) { if (Thread.isMainThread) { if (view == nil) { let contentView: NSView = self.topTipBox.contentView! for subview in contentView.subviews { subview.removeFromSuperview() } self.topTipBox.isHidden = true return } let contentView: NSView = self.topTipBox.contentView! for subview in contentView.subviews { subview.removeFromSuperview() } self.topTipBox.isHidden = false self.topTipBox.contentView?.addSubview(view!) } else { DispatchQueue.main.async { if (view == nil) { let contentView: NSView = self.topTipBox.contentView! for subview in contentView.subviews { subview.removeFromSuperview() } self.topTipBox.isHidden = true return } let contentView: NSView = self.topTipBox.contentView! for subview in contentView.subviews { subview.removeFromSuperview() } self.topTipBox.isHidden = false self.topTipBox.contentView?.addSubview(view!) } } } } extension KMMainViewController: KMEditImagePropertyViewControllerDelegate { func editImagePropertyViewControllerDidChanged(controller: KMEditImagePropertyViewController, type: KMEditImagePropertyViewControllerChangeType) { self.isPDFTextImageEdited = true } }