// // KMMainViewController.swift // PDF Reader Pro // // Created by wanjun on 2022/12/15. // import Cocoa @objcMembers class KMMainViewController: KMBaseViewController, NSTextFieldDelegate { @IBOutlet var PDFContendView: NSView! @IBOutlet var centerContentView: NSView! @IBOutlet var listView: CPDFListView! @IBOutlet var secondaryPdfView: KMSecondaryPDFView? @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 newPDFSplitView: 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! //页码显示器 @IBOutlet weak var pageNumberDisplayView: KMPageNumberDisplayView! @IBOutlet weak var tipCurrentPageBoxWidthConstraint: NSLayoutConstraint! @IBOutlet weak var topTipBox: NSBox! @IBOutlet weak var exitFullButton: NSButton! var model = KMMainModel() var isReadMode: Bool = false var readAlertView: CustomAlertView? 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 //自动滚动 var autoFlowOptionsSheetController: KMAutoFlowOptionsSheetController? //AI相关 var aiTipView: AITipIconView! var aiTypeChooseView: AITypeChooseView! //Search var searchIndex: Int = 0 //Form var formAlertView: KMFormAlertView? //Secure var secureAlertView: KMSecureAlertView? //对比 var isCompareModel: Bool = false { didSet { self.toolbarController.updataItemVisible() } } //密码弹窗 var passwordWindow: KMPasswordInputWindow? //春季活动 var recommondPopWindowVC: KMRecommondPopWindow? private var _needSave = false var needSave: Bool { set { _needSave = newValue if (_needSave == false) { self.clearIsPDFDocumentEdited() } } get { return _needSave } } var isPDFDocumentEdited: Bool { get { return self.model.isPDFDocumentEdited } } var leftSideViewController: KMLeftSideViewController = KMLeftSideViewController.init(type: KMLeftMethodMode()) var rightSideViewController: KMRightSideViewController! var searchResults: [KMSearchMode] = [] var mwcFlags: MwcFlags = MwcFlags() var document: CPDFDocument? var myDocument: NSDocument? weak var browserWindowController: KMBrowserWindowController? var cropSettingWindowController: KMCropSettingWindowController! var currentWindowController: NSWindowController! var savedNormalSetup: NSMutableDictionary = NSMutableDictionary() //数字签名 var digitalSignController: KMPDFDigitalSignViewController? let CPDFOfficeLeftSidePaneWidthKey = "CPDFOfficeLeftSidePaneWidthKey" let CPDFOfficeRightSidePaneWidthKey = "CPDFOfficeRightSidePaneWidthKey" var functionWidth: Double { get { if self.isReadMode { if !self.model.isShowBOTA { return 0 } } return 48-4 } } var pageNumber: UInt? var openSecondaryPdfView: KMSecondaryViewController? var secondaryPdfContentView: NSView? var lastSplitPDFHeight: Float = 0.0 var pdfEditController: KMPDFEditViewController? { get { return self.getPDFEditController() } } var autoSaveTimer: Timer? private var _documentFirstLoad: Bool = true var eventMonitor: Any? var keyEventMonitor: Any? var mouseRightMenuEvent: NSEvent? var aiTranslationWindow: KMAITranslationWindowController? var aiTranslationConfirWC: KMAITranslationConfirmWindowController? lazy private var homeVC: KMHomeViewController? = { let vc = KMHomeViewController() return vc }() private var background_mask: NSView? fileprivate var _secureOptions: [CPDFDocumentWriteOption : Any]? var secureOptions: [CPDFDocumentWriteOption : Any]? { get { return self._secureOptions } } var documentAttribute: [CPDFDocumentAttribute : Any]? fileprivate var _removeSecureFlag = false var removeSecureFlag: Bool { get { return self._removeSecureFlag } } fileprivate var _saveWatermarkFlag = false var saveWatermarkFlag: Bool { get { return self._saveWatermarkFlag } } private var mainWindow_: NSWindow? var mainWindow: NSWindow? { get { return self.mainWindow_ } set { self.mainWindow_ = newValue } } deinit { NotificationCenter.default.removeObserver(self) self.removeEventMonitor() self.removeKeyEventMonitor() } override func awakeFromNib() { super.awakeFromNib() self.addBackgroundMaskView() 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 self.toolbarController.mainViewController = self self.leftSideViewController.mainViewController = self self.newPDFSplitView.delegate = self } override func viewDidAppear() { super.viewDidAppear() //春季活动 if ((KMAdvertisementManager.manager.info.popWindowContent) != nil) { if KMAdvertisementManager.manager.info.popWindowContent!.content!.count > 0 { let info = KMAdvertisementManager.manager.info.popWindowContent!.content?.first if KMAdvertisementManager.checkAdvertisementValid(info!) { self.loadRecommondPopWindow() } } } //刷新前一页后一页按钮 self.updateNextAndPreViousButtonState() 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) self.interfaceThemeDidChanged(self.view.window?.appearance?.name ?? (NSApp.appearance?.name ?? .aqua)) if (self.document == nil) { return } if (self.document == nil || self.document!.isLocked == false) { self.loadFunctionGuide() // self.loadAIIconView() } if (self.document?.isLocked == false) { return } if (self.view.window == nil) { return } if (self.model.password != nil) { if self.listView.document.unlock(withPassword: self.model.password) { self.model.isSaveKeyChain = false return } } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { if self.passwordWindow == nil && self.view.window != nil { self.passwordWindow = KMPasswordInputWindow.openWindow(window: self.view.window!, url: self.document!.documentURL) { [unowned self] result , password in self.passwordWindow = nil if (result == .cancel) { self.browserWindowController?.browser.closeTab() return } self.model.isSaveKeyChain = true self.listView.document = self.document self.document?.unlock(withPassword: password) } } else { self.passwordWindow = nil } } } override func viewWillDisappear() { super.viewWillDisappear() 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() mwcFlags.settingUpWindow = 1 toolbarController.delegate = self //TODO: 先让项目运行,看后面怎么调整这段逻辑,目前最外层是 KMBrowserWindowController toolbarBox.contentView = toolbarController.view 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 } else { self.pageNumberDisplayView.hover = false } } 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(annotationsAttributeHasChange), name: NSNotification.Name.CPDFListViewAnnotationsAttributeHasChange, 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) NotificationCenter.default.addObserver(self, selector: #selector(handlePageChangedNotification), name: NSNotification.Name.CPDFViewPageChanged, object: self.listView) NotificationCenter.default.addObserver(self, selector: #selector(handleDisplayBoxChangedNotification), name: NSNotification.Name.CPDFViewDisplayBoxChanged, object: self.listView) // 互动模式 NotificationCenter.default.addObserver(self, selector: #selector(willEnterInteractionModeNotification), name: NSWindow.willEnterInteractionModeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didEnterInteractionModeNotification), name: NSWindow.didEnterInteractionModeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(willShowFullScreenNotification), name: NSWindow.willShowFullScreenNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didShowFullScreenNotification), name: NSWindow.didShowFullScreenNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didAddContentViewNotification), name: NSWindow.didAddContentViewNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(addAutoSaveEvent), name: AutoSaveManager.kTimeValueChangedNotificationName, object: nil) Task { self.addAutoSaveEvent() } self.toolbarController.selectItem(KMDocumentAnnotationToolbarItemIdentifier) self.closeRightPane() self.addKeyEventMonitor() self.addAdsBannerView() //检测OCR包是否需要更新 #if VERSION_DMG // KMResourceDownloadManager.manager.checkDocumentAIVersion() #endif // Open snapshots? var snapshotSetups: NSArray? // if (hasWindowSetup) // snapshotSetups = [savedNormalSetup objectForKey:SNAPSHOTS_KEY]; // else if ([sud boolForKey:SKRememberSnapshotsKey]) if KMPreferenceManager.shared.rememberSnapshot { // snapshotSetups = [[SKBookmarkController sharedBookmarkController] snapshotsForRecentDocumentAtURL:[(NSDocument *)[self document] fileURL]]; if let fileUrl = (self.myDocument as? KMMainDocument)?.fileURL { snapshotSetups = SKBookmarkController.shared().snapshotsForRecentDocument(at: fileUrl) as NSArray? } } if let cnt = snapshotSetups?.count, cnt > 0 { if let data = self.listView?.document?.isLocked, data { self.savedNormalSetup.setObject(snapshotSetups as Any, forKey: "snapshots" as NSCopying) } else { self.showSnapshots(setups: snapshotSetups) } } let readModel = UserDefaults.standard.bool(forKey: "kKMPDFViewIsReadMode") if readModel == true { self.openReadModel() } // [self applyPDFSettings:hasWindowSetup ? savedNormalSetup : [sud dictionaryForKey:SKDefaultPDFDisplaySettingsKey]]; // self.applyPDFSettings((KMDataManager.ud_dictionary(forKey: SKDefaultPDFDisplaySettingsKey) as? NSDictionary) ?? [:]) // self.interfaceThemeDidChanged(self.view.window?.appearance?.name ?? (NSApp.appearance?.name ?? .aqua)) } //MARK: - PDFListView func initPDFLeftViewVC() { var frame = self.leftView.frame frame.size.width += 44 self.leftView.frame = frame 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 in if let anno = model as? CSelfSignAnnotation { self?.leftSideViewController.refreshUIForAnnoAttributeDidChange(anno, attributes: nil) } } } func addAdsBannerView() { #if VERSION_FREE if !IAPProductsManager.default().isAvailableAllFunction(){ guard let document = self.listView.document else { return } if !document.isLocked { KMAdsManager.defaultManager.beginSheetModalForView(self.readContentView, directions: .down, adPosY: 30, animated: false) { pageIndex in } } NotificationCenter.default.addObserver(self, selector: #selector(purchaseStateUpdateNoti), name: NSNotification.Name(rawValue: "KMIAPProductPurchasedNotification"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(purchaseStateUpdateNoti), name: NSNotification.Name(rawValue: "kDeviceActivateNotification"), object: nil) } #endif //加载底部banner // - (void)loadingAdsManager { // #if VERSION_FREE // if(![self.pdfDocument isLocked]) { // if (![IAPProductsManager defaultManager].isAvailableAllFunction) { // [[KMAdsManager defaultManager] beginSheetModalForView:self.pdfView // directions:KMADViewDirectionsDown // animated:NO // completionHandler:nil]; // } // [[NSNotificationCenter defaultCenter] addObserverForName:KMIAPProductPurchasedNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note){ // if ([IAPProductsManager defaultManager].isAvailableAllFunction) { // [[KMAdsManager defaultManager] dismissSheetModalForView:self.pdfView]; // } // }]; // [[NSNotificationCenter defaultCenter] addObserverForName:kDeviceActivateStatusChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note){ // if ([IAPProductsManager defaultManager].isAvailableAllFunction) { // [[KMAdsManager defaultManager] dismissSheetModalForView:self.pdfView]; // } // }]; // } // #endif // } } // MARK: Private Methods internal func removeNotifications() { NotificationCenter.default.removeObserver(self) self.leftSideViewController.clearAnnotationFilterData() self.leftSideViewController.clearNotification() } func checkShouldAutoOpenLeftVC() { if KMPreference.shared.showLeftSideBar == false { return } if self.model.leftPanelOpen { return } Task { @MainActor in self.leftSideViewController.showThumbnail() self.toolbarController.findItem(KMLeftControlToolbarItemIdentifier)?.isSelected = true } } func applyLeftSideWidth(_ leftSideWidth: CGFloat, rightSideWidth: CGFloat) -> Void { mianSplitView.setPosition(leftSideWidth, ofDividerAt: 0) mianSplitView.setPosition(mianSplitView.maxPossiblePositionOfDivider(at: 1) - mianSplitView.dividerThickness - rightSideWidth, ofDividerAt: 1) self.model.lastLeftPanWidth = leftSideWidth self.model.lastRightPanWidth = rightSideWidth } 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: - 标记密文 func enterRedact() { if !IAPProductsManager.default().isAvailableAllFunction(){ KMPurchaseCompareWindowController.sharedInstance().showWindow(nil) return } if self.listView.document.allowsPrinting == false || self.listView.document.allowsCopying == false { Task { _ = await KMAlertTool.runModel(message: KMLocalizedString("This is a secured document. Editing is not permitted.", nil)) } return } if self.hasEnterRedact() { self.exitRedact() return } let ttsWindowC = KMTTSWindowController.share if ttsWindowC.pdfView?.document?.documentURL.path == self.listView.document.documentURL.path { if let data = ttsWindowC.window?.isVisible, data { ttsWindowC.stopSpeaking() ttsWindowC.close() } } NSColorPanel.shared.showsAlpha = false let controller = KMPDFRedactViewController(url: self.listView.document!.documentURL, password: self.listView.document.password) self.addChild(controller) self.PDFContendView.addSubview(controller.view) controller.view.frame = self.PDFContendView.bounds controller.view.autoresizingMask = [.width, .height] self.listView.isHidden = true controller.scaleFactor = self.listView.scaleFactor controller.titleBack = { [weak self] title in self?.view.window?.title = title } controller.callback = { [weak self] result, currentPageIndex, saveResult, saveUrl in self?.listView.go(toPageIndex: controller.redactPdfView.currentPageIndex, animated: false) if result == false { // 退出 self?.exitRedact() return } let controller = self?._getPDFRedactController() controller?.redactPdfView.newAddAnnotation.removeAll() self?.exitRedact() if saveResult { let newDocument = CPDFDocument(url: saveUrl) if let data = newDocument?.isLocked, data { newDocument?.unlock(withPassword: self?.listView.document.password ?? "") } self?.document = newDocument self?.listView.document = newDocument self?.listView.layoutDocumentView() } } controller.setCurrentPageIndex(self.listView.currentPageIndex) } func exitRedact() { let controller = self._getPDFRedactController() if let data = controller { if data.redactPdfView.newAddAnnotation.count > 0 { KMAlertTool.runModel(message: "", informative: KMLocalizedString("There are unapplied redactions in this file. Exit will not save redaction.", nil), buttons: [KMLocalizedString("Exit", nil), KMLocalizedString("Cancel", nil)]) { response in if response == .alertFirstButtonReturn { data.redactPdfView.newAddAnnotation.removeAll() self.exitRedact() } } return } } NSColorPanel.shared.showsAlpha = true self.toolbarController.findItem(KMDocumentRedactToolbarItemIdentifier)?.isSelected = false // self.toolbarController.toolbarType = .None // self.listView.toolMode = .moveToolMode controller?.redactPdfView.resignMonitor() controller?.view.removeFromSuperview() controller?.removeFromParent() self.listView.isHidden = false // self.listView.layoutDocumentView() // self.view.window?.makeFirstResponder(self.listView) self.listView.annotationType = .unkown } func hasEnterRedact() -> Bool { return self._getPDFRedactController() != nil } //MARK: - AI func loadAIIconView() -> Void { NotificationCenter.default.addObserver(self, selector: #selector(aiTipIconViewShowStateChangeNoti), name: NSNotification.Name(rawValue: "kAIIconShowStateChangeNotification"), object: nil) if self.aiTipView == nil { self.aiTipView = AITipIconView.createFromNib() self.aiTipView.clickHandle = { [weak self] view in // self?.showAITypeChooseView() } self.aiTipView.rightClickHandle = {[unowned self] view in AIInfoManager.default().showAIIcon = false NotificationCenter.default.post(name: NSNotification.Name(rawValue: "kAIIconShowStateChangeNotification"), object: nil) } } self.aiTipView.frame = CGRectMake(CGRectGetWidth(self.readContentView.frame)-84, CGRectGetHeight(self.readContentView.frame)-64-40, 72, 72) self.aiTipView.autoresizingMask = [.minXMargin, .minYMargin] self.readContentView.addSubview(self.aiTipView) self.updateAITipViewShowState() } func updateAITipViewShowState() { if AIInfoManager.default().showAIIcon { if self.view.window != nil { if self.isReadMode || KMTools.isFullScreen(self.view.window!) { self.aiTipView.isHidden = true } else { self.aiTipView.isHidden = false } } else { self.aiTipView.isHidden = false } } else { self.aiTipView.isHidden = true } } func showAITypeChooseView(aiConfigType: AIConfigType) -> Void { if (self.document != nil) { AIChatInfoManager.defaultManager.currentFilePath = (self.document?.documentURL.path)! } else { AIChatInfoManager.defaultManager.currentFilePath = "" } let windowVC: AINewConfigWindowController = AINewConfigWindowController.currentWC() windowVC.chooseCurFileHandle = {[unowned self] windowVC in if AIChatInfoManager.defaultManager.currentFilePath.isEmpty == false { let documentArray = NSDocumentController.shared.documents var didFileEdit: Bool = false var curDoc: KMMainDocument! for document in documentArray { if document.fileURL?.path == AIChatInfoManager.defaultManager.currentFilePath { didFileEdit = document.isDocumentEdited curDoc = document as! KMMainDocument break } } if didFileEdit { let tempFileURL = FileManager.default.temporaryDirectory.appendingPathComponent(AIChatInfoManager.defaultManager.currentFilePath.lastPathComponent) if FileManager.default.fileExists(atPath: tempFileURL.path) { do { try FileManager.default.removeItem(at: tempFileURL) } catch { } } if curDoc != nil { curDoc.mainViewController?.SaveTempPDFDocumentToURLPath(tempPath: tempFileURL.path) } } windowVC.window?.becomeMain() } } windowVC.window?.center() if windowVC.window?.isVisible == true && windowVC.didSetOriginFrame == true { } else { var windowRect = windowVC.window?.frame windowRect!.origin.x = CGRectGetMaxX(self.view.window!.frame) - (windowRect?.size.width)! windowRect!.origin.y = CGRectGetMaxY(self.view.window!.frame) - (windowRect?.size.height)! - 64 windowVC.window?.setFrame(windowRect!, display: true) windowVC.didSetOriginFrame = true } windowVC.eventLabel = "AITools_Tbr" windowVC.showWindow(nil) if (aiConfigType != .none) { windowVC.eventLabel = "AITools_Start" if self.listView.currentSelection.string().isEmpty == false { windowVC.setCurrentPDFSelection(self.listView.currentSelection.string()) } windowVC.chooseAIFunctionWithType(aiConfigType) } } @objc func aiTipIconViewShowStateChangeNoti() { self.updateAITipViewShowState() } //MARK: - 引导 func loadFunctionGuide() -> Void { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { if self.view.window != nil { self.loadOpenFileFunctionGuide(.openFileNormal) } } } func loadOpenFileFunctionGuide(_ showType: KMGuideInfoType) -> Void { if showType == .openFileNormal && KMGuideInfoWindowController.availableShow(.openFileNormal) { let leftPanelItem:KMToolbarItemView = self.toolbarController.findItem("KMLeftControlToolbarItemIdentifier")! let guideWC = KMGuideInfoWindowController.currentWC() guideWC.type = .openFileNormal guideWC.openPanelRect = (self.view.window?.contentView?.convert(leftPanelItem.frame, from: leftPanelItem.superview)) ?? CGRectZero guideWC.window?.collectionBehavior = [.canJoinAllSpaces] guideWC.normalGuideFinishHandle = { [weak self] windowVC in let rightPanelItem = self?.toolbarController.findItem(KMRightControlToolbarItemIdentifier) let digitalPanelItem = self?.toolbarController.findItem(KMDocumentDigitalSignToolbarItemIdentifier) windowVC.rightPanelRect = (self!.view.window?.contentView?.convert(rightPanelItem?.frame ?? .zero, from: rightPanelItem?.superview)) ?? .zero guideWC.digitalBoxRect = (self!.view.window?.contentView?.convert(digitalPanelItem?.frame ?? .zero, from: digitalPanelItem?.superview)) ?? .zero } guideWC.finishHandle = { [weak self] windowVC, type in if type == .windowNewFinish || type == . windowDigitalFinish { self?.checkFirstTrialController() } } guideWC.openFileToggleHandle = { [weak self] windowVC, type in self?.checkFirstTrialController() } var rect = self.view.window!.frame rect.size.height -= 20 guideWC.window?.setFrame(rect, display: false) guideWC.window?.minSize = rect.size guideWC.window?.maxSize = rect.size self.view.window?.addChildWindow(guideWC.window!, ordered: .above) guideWC.show() } else if showType == .digitalSignGuide && KMGuideInfoWindowController.availableShow(.digitalSignGuide) { let guideWC = KMGuideInfoWindowController.currentWC() guideWC.type = .digitalSignGuide let digitalPanelItem:KMToolbarItemView = self.toolbarController.findItem(KMDocumentDigitalSignToolbarItemIdentifier)! guideWC.digitalBoxRect = (self.view.window?.contentView?.convert(digitalPanelItem.frame, from: digitalPanelItem.superview))! guideWC.window?.collectionBehavior = [.canJoinAllSpaces] guideWC.finishHandle = { [weak self] windowVC, type in self?.checkFirstTrialController() } var rect = self.view.window!.frame rect.size.height -= 20 guideWC.window?.setFrame(rect, display: false) guideWC.window?.minSize = rect.size guideWC.window?.maxSize = rect.size self.view.window?.addChildWindow(guideWC.window!, ordered: .above) guideWC.show() } else if showType == .pdfCompareGuide && KMGuideInfoWindowController.availableShow(.pdfCompareGuide) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { let guideWC = KMGuideInfoWindowController.currentWC() guideWC.type = .pdfCompareGuide let digitalPanelItem:KMToolbarItemView = self.toolbarController.findItem(KMDocumentDigitalSignToolbarItemIdentifier)! guideWC.digitalBoxRect = (self.view.window?.contentView?.convert(digitalPanelItem.frame, from: digitalPanelItem.superview))! let compareItem:KMToolbarItemView = self.toolbarController.findItem(KMToolbarComparisonItemIdentifier)! guideWC.compareItemRect = (self.view.window?.contentView?.convert(compareItem.frame, from: compareItem.superview))! guideWC.window?.collectionBehavior = [.canJoinAllSpaces] var rect = self.view.window!.frame rect.size.height -= 20 guideWC.window?.setFrame(rect, display: false) guideWC.window?.minSize = rect.size guideWC.window?.maxSize = rect.size self.view.window?.addChildWindow(guideWC.window!, ordered: .above) guideWC.show() } } else if showType == .convertGuide && KMGuideInfoWindowController.availableShow(.convertGuide) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { let guideWC = KMGuideInfoWindowController.currentWC() guideWC.type = .convertGuide let digitalPanelItem:KMToolbarItemView = self.toolbarController.findItem(KMDocumentDigitalSignToolbarItemIdentifier)! guideWC.digitalBoxRect = (self.view.window?.contentView?.convert(digitalPanelItem.frame, from: digitalPanelItem.superview))! guideWC.purchaseHandle = { [weak self] windowVC in #if VERSION_DMG if IAPProductsManager.default().isAvailableAllFunction() { if IAPProductsManager.default().isAvailableAdvancedPDFToOffice() { //Convert: self?.showAllConvertWindow(convertT: .Word) } else { let limitWC = KMPurchaseLimitWindowController.currentLimitWC() limitWC.continueBlock = { windowController in } limitWC.window?.center() limitWC.showWindow(nil) } } else { KMPurchaseCompareWindowController.sharedInstance().showWindow(nil) } #else if IAPProductsManager.default().isAvailableAllFunction() { if IAPProductsManager.default().isAvailableAdvancedPDFToOffice() { //Convert: } else { var vc = KMToolCompareWindowController(toolType: .Convert, selectNum: 1) vc.showWindow(nil) } } else { KMPurchaseCompareWindowController.sharedInstance().showWindow(nil) } #endif } guideWC.window?.collectionBehavior = [.canJoinAllSpaces] var rect = self.view.window!.frame rect.size.height -= 20 guideWC.window?.setFrame(rect, display: false) guideWC.window?.minSize = rect.size guideWC.window?.maxSize = rect.size self.view.window?.addChildWindow(guideWC.window!, ordered: .above) guideWC.show() } } else { } } func checkFirstTrialController() -> Void { #if VERSION_DMG //打开文档后引导相关 if VerificationManager.default().status == .none { let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" let lastVersion = UserDefaults.standard.object(forKey: "SKLastTrialVersionMainDocumentLaunchedKey") as? String ?? "" if lastVersion.isEmpty || lastVersion != appVersion { UserDefaults.standard.setValue(appVersion, forKey: "SKLastTrialVersionMainDocumentLaunchedKey") UserDefaults.standard.synchronize() KMPurchaseCompareWindowController.sharedInstance().showWindow(nil) } } #endif } // MARK: - 页面编辑 open func enterPageEdit(_ pages: [Int] = []) { if let doc = self.listView?.document { if doc.allowsCopying == false || doc.allowsPrinting == false { KMBaseWindowController.checkPassword(url: doc.documentURL, type: .owner) { result, pwd in if result && pwd.isEmpty == false { self.listView?.document?.unlock(withPassword: pwd) Task { @MainActor in self.enterPageEdit(pages) } } else { self.exitPageEdit() } } return } } //选中page var tPages = pages if tPages.count == 0 { tPages = self.leftSideViewController.thumb_fetchSelectedRows() ?? [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! KMToolbarItemView).unEnabled = true } } } let controller = KMPDFEditViewController(self.listView.document) 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() } controller.selectionDidChange = { 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! KMToolbarItemView).unEnabled = false } } } self.toolbarController.findItem(KMDocumentPageToolbarItemIdentifier)?.isSelected = 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) if let data = editController?.isEdited, data { self.leftSideViewController.reloadThumbnailDataIfNeed() } } 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 _getPDFRedactController() -> KMPDFRedactViewController? { var controller: KMPDFRedactViewController? for childC in self.children { if (childC.isKind(of: KMPDFRedactViewController.self)) { controller = (childC as! KMPDFRedactViewController) break } } return controller } 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 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 showSecureLimitTip() { self.hiddenSecureLimitTip() if self.secureAlertView == nil { self.secureAlertView = KMSecureAlertView() self.secureAlertView?.show(in: self.listView) self.secureAlertView?.closeAction = { [unowned self] view in self.hiddenSecureLimitTip() self.removeFromAlertView() self.showFormAlertView() } self.secureAlertView?.passwordAction = { [unowned self] view in self.removeOwnerPassword() self.removeFromAlertView() self.showFormAlertView() } } } func removeOwnerPassword() { guard let doc = self.listView?.document else { NSSound.beep() return } if doc.allowsCopying && doc.allowsPrinting { NSSound.beep() return } _ = KMPasswordInputWindow.openWindow(window: self.view.window!, type: .owner, url: doc.documentURL) { [weak self] result, password in if result == .cancel { /// 关闭 return } /// 解密成功 self?.hiddenSecureLimitTip() self?.model.isSaveKeyChain = false self?.listView.document.unlock(withPassword: password) } } public func hiddenSecureLimitTip() { self.secureAlertView?.removeFromSuperview() self.secureAlertView = nil } //MARK: - Form func showFormAlertView() { if (formAlertView == nil) { formAlertView = KMFormAlertView() formAlertView?.isCloseSecureView = self.secureAlertView != nil ? false : true formAlertView?.showInView(self.listView) } else { self.removeFromAlertView() } } func removeFromAlertView() { formAlertView?.removeFromSuperview() formAlertView = nil } override func mouseMoved(with event: NSEvent) { self.view.window?.mouseMoved(with: event) } func savePageNumberIfNeed() { if (KMPreferenceManager.shared.openLastUnlockedDocumentWhenAppStart) { let scaleFactor = self.listView?.scaleFactor ?? 0 if scaleFactor <= 0 { return } 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: - 显示合并窗口 public func showMergeWindow(url: URL? = nil, _ password: String?) { DispatchQueue.main.async { var documentURL = url if documentURL == nil { documentURL = self.listView.document.documentURL } guard let _url = documentURL else { return } guard let document = PDFDocument(url: _url) else { return } let windowController = KMMergeWindowController(document: document, password: password ?? "") windowController.oriDucumentUrl = self.listView.document.documentURL windowController.pageIndex = self.listView.currentPageIndex self.currentWindowController = windowController windowController.cancelAction = { [unowned self] controller in self.view.window?.endSheet((self.currentWindowController.window)!) self.currentWindowController = nil } windowController.mergeAction = { [unowned self] controller, filePath in self.view.window?.endSheet((self.currentWindowController.window)!) self.currentWindowController = nil // let newDocument = CPDFDocument(url: NSURL(fileURLWithPath: filePath) as URL) // if let data = newDocument?.isLocked, data { // newDocument?.unlock(withPassword: self.listView.document.password ?? "") // } // // self.setDocument = newDocument // self.leftSideViewController.refreshUIForDocumentChanged() } self.toolbarController.cancelSelected(KMToolbarToolMergeItemIdentifier) self.view.window?.beginSheet(windowController.window!) } } // MARK: - 显示加密弹窗 public func showSecureWindow(_ url: URL) { let controller = KMSecurityWindowController(windowNibName: "KMSecurityWindowController") controller.documentURL = self.listView.document.documentURL self.currentWindowController = controller controller.batchAction = { [unowned self] controller, files in self.view.window?.endSheet((self.currentWindowController.window)!) self.currentWindowController = nil self.toolbarController.cancelSelected(KMToolbarToolCompressItemIdentifier) let batchWindowController = KMBatchOperateWindowController.sharedWindowController // batchWindowController.window?.makeKeyAndOrderFront("") let batchOperateFile = KMBatchOperateFile(filePath: self.document?.documentURL.path ?? "", type: .AddPassword) batchWindowController.switchToOperateType(.AddPassword, files: [batchOperateFile]) batchWindowController.window?.makeKeyAndOrderFront("") } controller.doneAction = { [unowned self] controller, options, attribute in // let windowController_secure = self.currentWindowController as! KMSecureEncryptWindowController let openPanel = NSOpenPanel() openPanel.canChooseFiles = false openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.beginSheetModal(for: NSWindow.currentWindow()) { (result) in if result == NSApplication.ModalResponse.OK { for fileURL in openPanel.urls { let document = CPDFDocument(url: self.document?.documentURL) if document != nil { document!.setDocumentAttributes(attribute) let path = fileURL.path.stringByAppendingPathComponent(url.deletingPathExtension().lastPathComponent) + "_SetPassword" + "." + url.pathExtension let success = document!.write(to: NSURL(fileURLWithPath: path) as URL, withOptions: options) if success { self.view.window?.endSheet((self.currentWindowController.window)!) self.currentWindowController = nil NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: path)]) } } } } } } controller.cancelAction = { [unowned self] controller in self.view.window?.endSheet((self.currentWindowController.window)!) self.currentWindowController = nil } NSWindow.currentWindow().beginSheet(controller.window!) } // 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", comment: "") + "PDF") self.progressC?.maxValue = 3.0 self.progressC?.increment(by: 1.0) // 保存文档 self.asyncSaveDocument { [unowned self] params in // 执行进度 [假进度] self.progressC?.increment(by: 1.0) self.progressC?.increment(by: 1.0) // DispatchQueue.main.async { DispatchQueue.main.asyncAfter(deadline: .now()+0.1) { // 隐藏进度 self.hiddenProgressWindow() // 回调 callback() } } } func SaveTempPDFDocumentToURLPath(tempPath: String) { self.document?.write(toFile: tempPath) } // MARK: - 定时保存 func addAutoSaveEvent() { if (self.autoSaveTimer != nil) { self.autoSaveTimer?.invalidate() self.autoSaveTimer = nil } if self.document != nil { self.autoSaveTimer = Timer.scheduledTimer(withTimeInterval: AutoSaveManager.manager.timeInterval * 60, repeats: true, block: { [weak self] timer in self?.autoSaveTimerAction(timer) }) } self.checkAutoSaveInfo() } func checkAutoSaveInfo() { guard let cnt = AutoSaveManager.manager.autoSavePaths?.count, cnt > 0 else { return } if AutoSaveManager.manager.autoSaveAlertShow { return } AutoSaveManager.manager.autoSaveDidEndAction = false AutoSaveManager.manager.autoSaveAlertShow = true let blockSaveWindow = AutoSavePopController() blockSaveWindow.cancelHandle = { [weak self] windowController in AutoSaveManager.manager.autoSaveDidEndAction = true AutoSaveManager.manager.clearCache() self?.km_quick_endSheet() } blockSaveWindow.confirmHandle = { [weak self] windowController in self?.km_quick_endSheet() self?.saveAutoSaveInfo() } self.km_beginSheet(windowC: blockSaveWindow) } func saveAutoSaveInfo() { let openPanel = NSOpenPanel() openPanel.canChooseDirectories = true openPanel.canChooseFiles = false openPanel.allowsMultipleSelection = false let win = NSApp.keyWindow != nil ? NSApp.keyWindow : self.view.window openPanel.beginSheetModal(for: win!) { result in if (result == .OK) { let folderPath = openPanel.url?.path ?? openPanel.url?.absoluteString for path in AutoSaveManager.manager.opendPaths ?? [] { let _path = path as? String var newPath = "\(folderPath ?? "")/\(_path?.lastPathComponent ?? "")" newPath = self.getValidFilePath(newPath) do { try FileManager.default.moveItem(atPath: _path ?? "", toPath: newPath) } catch { NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: newPath)]) } } AutoSaveManager.manager.clearCache() } AutoSaveManager.manager.autoSaveDidEndAction = true } } func autoSaveTimerAction(_ timer: Timer) { if (self.document == nil || self.listView?.document?.documentURL.path == nil) { return } if AutoSaveManager.manager.autoSaveDidEndAction == false { //防止提示弹窗出现后,未进行任何操作又进入自动保存的机制 return } if let data = self.document?.isLocked, data { return } if AutoSaveManager.manager.autoSaveEnabled == false { return } let documentArray = NSDocumentController.shared.documents var didFileEdit = false for doc in documentArray { if doc.fileURL?.path == self.document?.documentURL.path { didFileEdit = doc.isDocumentEdited break } } if (didFileEdit == false) { return } AutoSaveManager.manager.isSaving = true let savePath = AutoSaveManager.manager.autoSaveWithPath(self.listView?.document?.documentURL.path ?? "") if (!self.document!.isLocked) { self.document?.write(to: URL(fileURLWithPath: savePath)) } DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { AutoSaveManager.manager.isSaving = false } } func removeAutoSaveInfo() { if self.autoSaveTimer != nil { self.autoSaveTimer?.invalidate() self.autoSaveTimer = nil } if AutoSaveManager.manager.autoSaveDidEndAction == false { //防止提示弹窗出现后,未进行任何操作又进入自动保存的机制 return } if AutoSaveManager.manager.autoSaveEnabled == false { return } if self.document == nil || self.listView?.document?.documentURL.path == nil { return } AutoSaveManager.manager.removeAutoSavePath(self.listView?.document?.documentURL.path ?? "") } // 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: - 选择缩放模式 @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 } } internal func createPdf(index:Int) { if index == 1 { self.homeVC?.openBlankPage() } else if index == 4 { self.homeVC?.importFromCamera() } else if index == 5 { self.homeVC?.importFromScanner() } else if index == 3 { self.homeVC?.importFromWebPage() } else if index == 2 { self.homeVC?.newFromImages() } } // 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 let data = self?.interactionMode, data == .presentation { // 幻灯片模式下 if let data = self?.canExitPresentation(), data { self?.browserWindowController?.exitFullscreen() } return nil } 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()) if self?.listView.annotationType == .addImage { self?.listView.change([.text, .image]) } self?.listView.annotationType = .editTextImage 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 } } } } } else { if let data = self?.interactionMode, data == .presentation { // 幻灯片模式下 self?.listView.keyDown(with: event) return nil } } 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: - 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()) } self.removeAutoSaveInfo() KMAdsManager.defaultManager.dismissSheetModal(for: self.readContentView) } public func clearSecureOptions() { self._secureOptions = nil self.documentAttribute = nil } public func recordRemoveSecureFlag() { self._removeSecureFlag = true self.clearSecureOptions() self.recordIsPDFDocumentEdited(type: .removePassword) self._needSave = true } public func clearRemoveSecureFlag() { self._removeSecureFlag = false } public func clearSaveWatermarkFlag() { km_synchronized(self) { self._saveWatermarkFlag = false } } public func recordIsPDFDocumentEdited(type: KMSubscribeWaterMarkType = .none) { km_synchronized(self) { self.model.isPDFDocumentEdited = true if let _document = self.myDocument { KMTools.setDocumentEditedState(document: _document) } } } public func clearIsPDFDocumentEdited() { km_synchronized(self) { self.model.isPDFDocumentEdited = false } } func showSnapshots(setups: NSArray?) { for setup in setups ?? [] { let swc = KMSnapshotWindowController() swc.delegate = self swc.setPdfDocument(self.listView.document, setup: setup as? NSDictionary) swc.setForceOnTop(self.interactionMode != .normal) self.myDocument?.addWindowController(swc) } } func dealDocumentDidLoaded() { self.removeBackgroundMaskView() if (!self.listView.document!.allowsCopying || !self.listView.document!.allowsPrinting) { self.showSecureLimitTip() } if self.document != nil { self.convertNotesUsingPDFDocument(self.document!) } 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 } } // MARK: - Noti Actions internal func documentDidUnlockNotification(_ sender: Notification) { if (self.listView.document != nil && self.listView.document.isEqual(to: sender.object)) { // self.loadAIIconView() if (self.myDocument == nil) { return } if (self.listView.document.allowsPrinting && self.listView.document.allowsCopying) { self.hiddenSecureLimitTip() } if ((self.myDocument as! KMMainDocument).isUnlockFromKeychain || self.model.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 } } } func annotationsAttributeHasChange(_ sender: Notification) { guard let dict = sender.object as? [String : Any] else { return } if let anno = dict["object"] as? CPDFAnnotation { let value = dict["keyPath"] as? String ?? "" let didEnd = dict["didEnd"] as? Bool ?? false if didEnd { if value == CPDFAnnotationBoundsKey { if anno is CPDFSquareAnnotation || anno is CPDFCircleAnnotation { anno.contents = anno.page?.string(for: anno.bounds) ?? "" } } self.leftSideViewController.refreshUIForAnnoAttributeDidChange(anno, attributes: ["keyPath" : value]) } else { if value != CPDFAnnotationBoundsKey && value != CPDFAnnotationStartPointKey && value != CPDFAnnotationEndPointKey && value != CPDFAnnotationPathsKey { // 改变bounds(箭头、直线注释 开始点和结束点, 手绘注释的paths)的操作会卡顿,比如移动 self.leftSideViewController.refreshUIForAnnoAttributeDidChange(anno, attributes: ["keyPath" : value]) } } } } internal func applicationWillTerminateNotification(_ sender: Notification) { self.savePageNumberIfNeed() self.saveDocument() } func KMPDFViewCurrentPageDidChangedNotification(_ sender: Notification) { if self.isReadMode { self.readModelView.currentPageIndex = self.listView.currentPageIndex } //刷新前一页后一页按钮 self.updateNextAndPreViousButtonState() } func CPDFDocumentPageCountChangedNotification(_ sender: Notification) { if self.isReadMode { self.readModelView.totalPagesCount = Int(self.listView.document.pageCount) } //刷新前一页后一页按钮 self.updateNextAndPreViousButtonState() } 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) } } } @objc func handlePageChangedNotification(_ sender: Notification) { // When the PDFView is changing scale, or when view settings change when switching fullscreen modes, // a lot of wrong page change notifications may be send, which we better ignore. // Full screen switching and zooming should not change the current page anyway. if self.mwcFlags.isSwitchingFullScreen > 0 { // if ([pdfView isZooming] || mwcFlags.isSwitchingFullScreen) { // [self updatePageNumber]; // [self updateLeftStatus]; return } // let page = self.listView.currentPage() let pageIndex = page?.pageIndex() ?? 0 // // if ([lastViewedPages count] == 0) { // [lastViewedPages addPointer:(void *)pageIndex]; // } else if ((NSUInteger)[lastViewedPages pointerAtIndex:0] != pageIndex) { // [lastViewedPages insertPointer:(void *)pageIndex atIndex:0]; // if ([lastViewedPages count] > 5) // [lastViewedPages setCount:5]; // } self.leftSideViewController.thumb_selectRowIndexsIfNeed(IndexSet(integer: IndexSet.Element(pageIndex))) self.leftSideViewController.thumbnailTableView.needsDisplay = true self.leftSideViewController.tocOutlineView.needsDisplay = true // // [self updatePageNumber]; // [self updatePageLabel]; // // [self updateOutlineSelection]; // [self updateNoteSelection]; // [self updateThumbnailSelection]; // // if (beforeMarkedPageIndex != NSNotFound && [[pdfView currentPage] pageIndex] != markedPageIndex) // beforeMarkedPageIndex = NSNotFound; // // [self synchronizeWindowTitleWithDocumentName]; // [self updateLeftStatus]; // if ([[NSUserDefaults standardUserDefaults] boolForKey:SKDisplayPageBoundsKey]) // [self updateRightStatus]; // if ([self interactionMode] == SKPresentationMode) // [[self presentationNotesDocument] setCurrentPage:[[[self presentationNotesDocument] pdfDocument] pageAtIndex:[page pageIndex]]]; } @objc func handleDisplayBoxChangedNotification(_ sender: Notification) { self.leftSideViewController.reloadThumbnailDataIfNeed() // if ([[NSUserDefaults standardUserDefaults] boolForKey:SKDisplayPageBoundsKey]) // [self updateRightStatus]; } @objc func willEnterInteractionModeNotification(_ sender: Notification) { guard let win = sender.object as? NSWindow, win.isEqual(to: self.view.window) else { return } let interactionMode = sender.userInfo?[NSWindow.UserInfo.interactionModeKey] as? KMInteractionMode if interactionMode == .presentation { let backgroundColor = NSColor.black let level = UserDefaults.standard.bool(forKey: "SKUseNormalLevelForPresentationKey") ? NSWindow.Level.normal : NSWindow.Level.popUpMenu let wasInteractionMode = self.interactionMode if wasInteractionMode == .normal { self.savedNormalSetup.setDictionary(self.currentPDFSettings() as! [AnyHashable : Any]) } // if findController.view().window() != nil { // findController.toggleAboveView(nil, animate: false) // } if wasInteractionMode == .legacyFullScreen { self.enterPresentationMode() // updatePresentationOptions(for: self.view.window!) self.pdfSplitView.frame = CGRect(x: 0, y: 0, width: CGRectGetWidth(centerContentView.bounds), height: CGRectGetHeight(centerContentView.bounds)-1) self.centerContentView.addSubview(pdfSplitView) self.listView.frame = (self.view.window?.contentView?.bounds)! self.view.window?.contentView?.addSubview(listView) self.view.window?.backgroundColor = backgroundColor self.view.window?.level = level self.listView.layoutDocumentView() self.listView.requiresDisplay() self.forceSubwindowsOnTop(false) self.hideLeftSideWindow() self.hideRightSideWindow() self.removeBlankingWindows() } } else { KMPrint("2") } } @objc func didEnterInteractionModeNotification(_ sender: Notification) { guard let win = sender.object as? NSWindow, win.isEqual(to: self.view.window) else { return } if self.interactionMode == .presentation { // if self.pdfView().currentPage()?.isEqual(page) == false { // self.pdfView().go(to: page) // } // pdfView().setInteractionMode(SKPresentationMode) self.listView?.layoutDocumentView() self.listView?.requiresDisplay() } } @objc func willShowFullScreenNotification(_ sender: Notification) { guard let win = sender.object as? NSWindow, win.isEqual(to: self.view.window) else { return } if self.interactionMode == .presentation { let view = self.view.window?.firstResponder as? NSView if let data = view?.isDescendant(of: self.pdfSplitView), data { self.view.window?.makeFirstResponder(nil) } } } @objc func didShowFullScreenNotification(_ sender: Notification) { guard let win = sender.object as? NSWindow, win.isEqual(to: self.view.window) else { return } if self.interactionMode == .presentation { self.enterPresentationMode() } } @objc func didAddContentViewNotification(_ sender: Notification) { guard let win = sender.object as? NSWindow, win.isEqual(to: self.view.window) else { return } if self.interactionMode == .presentation { } } @objc func purchaseStateUpdateNoti() { if IAPProductsManager.default().isAvailableAllFunction() { KMAdsManager.defaultManager.dismissSheetModal(for: self.readContentView) } } // MARK: Split View func changePDFDocument(isChange: Bool, replaceBlock: @escaping (String) -> Void) { let openPanel = NSOpenPanel() openPanel.allowedFileTypes = ["pdf", "PDF"] openPanel.allowsMultipleSelection = false guard let mainWindow = NSApp.mainWindow else { return } openPanel.beginSheetModal(for: mainWindow) { [weak self] response in if response == NSApplication.ModalResponse.OK { guard let url = openPanel.url else { return } if let document = CPDFDocument(url: url) { self?.secondaryPdfView?.document = nil self?.secondaryPdfView?.document = document if isChange { self!.openSecondaryPdfView!.view.removeFromSuperview() } replaceBlock(document.documentURL?.path ?? "") } else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "") alert.runModal() } } } } } extension KMMainViewController { func currentSetup() -> [String: Any] { var setup: [String: Any] = [:] var point = NSZeroPoint // var rotated = listView.currentPage().rotation let pageIndex = listView.currentPageIndexAndPoint(&point, rotated: nil) setup[kWindowFrameKey] = NSStringFromRect(mainWindow?.frame ?? NSZeroRect) setup[KMMainModel.Key.kLeftSidePaneWidth] = self.model.lastLeftPanWidth setup[KMMainModel.Key.kRightSidePaneWidth] = self.model.lastRightPanWidth setup[KMMainModel.Key.pageIndex] = pageIndex // if rotated != 0 { // setup[SCROLLPOINT_KEY] = NSStringFromPoint(point) // } // if !snapshots.isEmpty { // setup[SNAPSHOTS_KEY] = snapshots.map { $0[SKSnapshotCurrentSetupKey] } // } // if interactionMode == SKNormalMode { // setup.merge(currentPDFSettings(), uniquingKeysWith: { $1 }) // } else { // setup.merge(savedNormalSetup, uniquingKeysWith: { $1 }) // ["HASHORIZONTALSCROLLER_KEY", "HASVERTICALSCROLLER_KEY", "AUTOHIDESSCROLLERS_KEY", "LOCKED_KEY"].forEach { setup.removeValue(forKey: $0) } // } return setup } // MARK: - Recommond活动 func loadRecommondPopWindow() { if IAPProductsManager.default().isAvailableAllFunction() { return } if let info = KMAdvertisementManager.manager.info.popWindowContent?.content?.first { if recommondPopWindowVC == nil { recommondPopWindowVC = KMRecommondPopWindow() } recommondPopWindowVC?.recommondInfo = info guard let windowFrame = self.view.window?.frame, let popWindowFrame = recommondPopWindowVC?.window?.frame else { return } let x = windowFrame.minX + (windowFrame.size.width - popWindowFrame.size.width) / 2.0 let y = windowFrame.minY + (windowFrame.size.height - popWindowFrame.size.height) / 2.0 recommondPopWindowVC?.window?.setFrame(NSRect(x: x, y: y, width: popWindowFrame.size.width, height: popWindowFrame.size.height), display: true) recommondPopWindowVC?.window?.orderFront((Any).self) recommondPopWindowVC?.window?.becomeMain() UserDefaults.standard.set("Show", forKey: info.version ?? "") UserDefaults.standard.synchronize() } } }