// // KMCloudDocumentsViewController.swift // PDF Master // // Created by wanjun on 2022/11/23. // import Cocoa enum KMCloudDocumentState : Int { case Box = 0 case DropBox case GoogleDrive case OneDrive public func string() -> String { switch self { case .Box: return "Box" case .DropBox: return "DropBox" case .GoogleDrive: return "GoogleDrive" case .OneDrive: return "OneDrive" } } } class KMCloudAccountListTableviewCell: NSTableCellView { @IBOutlet weak var accountLabel: NSTextField! } class KMCloudFileNameListTableviewCell: NSTableCellView { @IBOutlet weak var fileImageView: NSImageView! @IBOutlet weak var titleLabel: NSTextField! @IBOutlet weak var subtitleLabel: NSTextField! } class KMCloudFileModifyTimeListTableviewCell: NSTableCellView { @IBOutlet weak var modifyTimeLabel: NSTextField! } class KMCloudFileSizeListTableviewCell: NSTableCellView { @IBOutlet weak var sizeLabel: NSTextField! } class KMCloudDocumentsViewController: NSViewController { @IBOutlet weak var topBox: NSBox! @IBOutlet weak var cloudDocumentsLabel: NSTextField! @IBOutlet weak var addCloudsBox: KMBox! @IBOutlet weak var addCloudsLabel: NSTextField! @IBOutlet weak var addCloudsImageView: NSImageView! @IBOutlet weak var mainBox: NSBox! @IBOutlet weak var emptyScrollView: NSScrollView! @IBOutlet weak var emptyCollectionView: KMCollectionView! @IBOutlet weak var cloudSplitView: NSSplitView! @IBOutlet weak var cloudLeftBox: NSBox! @IBOutlet weak var cloudRightBox: NSBox! @IBOutlet weak var accountListTableView: NSTableView! @IBOutlet weak var fileListTableView: KMMenuTableView! @IBOutlet weak var fileNameTableColumn: NSTableColumn! @IBOutlet weak var fileModifyTimeTableColumn: NSTableColumn! @IBOutlet weak var fileSizeTableColumn: NSTableColumn! @IBOutlet weak var pathControl: NSPathControl! @IBOutlet weak var pathBox: NSBox! var myPathControl = KMCloudPathControl() var pathString: String = "" var cloudPlates: [KMCloudDocumentState] = [.DropBox, .GoogleDrive] var accounts: [Any] = [] var cloudFiles: [Any] = [] var currentAccount: KMCloudServer? var filepaths: [KMServicesCloudFile] = [] override func viewDidLoad() { super.viewDidLoad() // Do view setup here. let serverArr = KMCloudServer.getAllLoginServer() for server in serverArr! { self.accounts.append(server) } emptyCollectionView.register(KMCloudEmptyCollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMCloudEmptyCollectionViewItem")) emptyCollectionView.isSelectable = true self.fileListTableView.menuDelegate = self initializeUI() initLocalization() self.pathControl.url = URL(fileURLWithPath: "Cloud Documents\\Test\\Test2") self.pathControl.pathStyle = .popUp addCloudsBox.downCallback = { [weak self](downEntered: Bool, mouseBox: KMBox, event) -> Void in if self != nil { if downEntered { self!.addCloudsAction() } } } } override func viewDidAppear() { super.viewDidAppear() reloadData() } override func viewWillLayout() { super.viewWillLayout() if (self.emptyCollectionView.isHidden == false) { self.emptyCollectionView.reloadData() } } // MARK: Init func initializeUI() -> Void { let menu = NSMenu() let item = NSMenuItem(title: NSLocalizedString("Delete", comment: ""), action: #selector(accountListTableView_itemClick), target: self) menu.addItem(item!) self.accountListTableView.menu = menu self.accountListTableView.allowsEmptySelection = false self.fileListTableView.doubleAction = #selector(fileListTableViewDoubleAction) let pathControl = self.myPathControl self.pathBox.contentView?.addSubview(pathControl) pathControl.frame = self.pathBox.contentView!.bounds pathControl.autoresizingMask = [.width, .height] self.pathString = NSLocalizedString("Cloud Documents", comment: "") pathControl.urlString = self.pathString pathControl.itemClick = { [weak self] index in if (index == 0) { self!.updateFilesList((self?.currentAccount)!, nil) } else { self!.updateFilesList((self?.currentAccount)!, self?.filepaths[index-1]) } } } func initLocalization() -> Void { cloudDocumentsLabel.stringValue = NSLocalizedString("Cloud Documents", comment: "") addCloudsLabel.stringValue = NSLocalizedString("Add Account", comment: "") addCloudsBox.toolTip = NSLocalizedString("Add Account", comment: "") fileNameTableColumn.title = NSLocalizedString("File Name", comment: "") fileModifyTimeTableColumn.title = NSLocalizedString("Change time", comment: "") fileSizeTableColumn.title = NSLocalizedString("Size", comment: "") } // MARK: private func reloadData() -> Void { DispatchQueue.main.async { if (KMCloudServer.isConnectionAvailable()) { self.hiddenNotNetworkView() self.emptyCollectionView.isHidden = true self.cloudSplitView.isHidden = true if self.accounts.count > 0 { self.cloudSplitView.isHidden = false self.accountListTableView.reloadData() self.fileListTableView.reloadData() } else { self.emptyCollectionView.isHidden = false self.emptyCollectionView.reloadData() } } else { self.showNotNetworkView() } } } // MARK: Action func loginCloud(state: KMCloudDocumentState) -> Void { switch state { case .Box: break case .DropBox: let serverArray = KMCloudServer.getAllLoginServer() var dropboxIsLogin: Bool = false for server in serverArray! { if (server.serverType == KMDropbox) { dropboxIsLogin = true break } } if (dropboxIsLogin) { self.logoutAccount(self.currentAccount!) } else { let server = KMCloudServer(cloudWith: KMDropbox) server?.authorizedLoginCompletion({ cloudFiles, finished in if (!finished) { /// 登录失败 return } let serverArr = KMCloudServer.getAllLoginServer() var currentServer: KMCloudServer? for server in serverArr! { if ((server as! KMCloudServer).serverType == KMDropbox) { currentServer = server break } } // let userData: KMDropboxUserMetadata = cloudFiles as! KMDropboxUserMetadata if (currentServer != nil) { self.accounts.append(currentServer) self.reloadData() } }) } break case .GoogleDrive: let serverArray = KMCloudServer.getAllLoginServer() var isLogin: Bool = false for server in serverArray! { if (server.serverType == KMGoogleDrive) { isLogin = true break } } if (isLogin) { self.logoutAccount(self.currentAccount!) } else { let server = KMCloudServer(cloudWith: KMGoogleDrive) server?.authorizedLoginCompletion({ cloudFiles, finished in if (!finished) { /// 登录失败 return } let serverArr = KMCloudServer.getAllLoginServer() var currentServer: KMCloudServer? for server in serverArr! { if ((server as! KMCloudServer).serverType == KMGoogleDrive) { currentServer = server break } } // let userData: KMDropboxUserMetadata = cloudFiles as! KMDropboxUserMetadata if (currentServer != nil) { self.accounts.append(currentServer) self.reloadData() } }) } break case .OneDrive: break } } func addCloudsAction() -> Void { var popViewDataArr: [String] = [] for plate in self.cloudPlates { popViewDataArr.append(plate.string()) } let vc: KMHomePopViewController = KMHomePopViewController().initWithPopViewDataArr(popViewDataArr) let createFilePopover: NSPopover = NSPopover.init() createFilePopover.contentViewController = vc createFilePopover.animates = true createFilePopover.behavior = .transient createFilePopover.setValue(true, forKey: "shouldHideAnchor") createFilePopover.show(relativeTo: CGRect(x: addCloudsBox.bounds.origin.x, y: 10, width: addCloudsBox.bounds.size.width, height: addCloudsBox.bounds.size.height), of: addCloudsBox, preferredEdge: .minY) vc.downCallback = { [weak self](downEntered: Bool, count: String) -> Void in if self != nil { if downEntered { var state: KMCloudDocumentState = .Box if count == NSLocalizedString("Box", comment: "") { state = .Box } else if count == NSLocalizedString("DropBox", comment: "") { state = .DropBox } else if count == NSLocalizedString("GoogleDrive", comment: "") { state = .GoogleDrive } else if count == NSLocalizedString("OneDrive", comment: "") { state = .OneDrive } self!.loginCloud(state: state) } } } } private func showNotNetworkView() { self.hiddenNotNetworkView() self.emptyCollectionView.isHidden = true self.cloudSplitView.isHidden = true let superview = self.emptyCollectionView.superview let view = KMCloudNoNetworkView() let viewH: CGFloat = 220 view.frame = NSMakeRect(0, (NSHeight(superview!.bounds)-viewH)*0.5, (superview?.bounds.size.width)!, viewH) view.autoresizingMask = [.width, .minYMargin] superview?.addSubview(view) view.itemClick = { [weak self] index in /// 刷新 self?.hiddenNotNetworkView() self?.reloadData() } } private func hiddenNotNetworkView() { let superview = self.emptyCollectionView.superview var flagView: KMCloudNoNetworkView? for view in superview!.subviews { if (view is KMCloudNoNetworkView) { flagView = (view as! KMCloudNoNetworkView) break } } if (flagView == nil) { return } flagView?.removeFromSuperview() } private func showCloudFileEmptyView() { self.hiddenCloudFileEmptyView() let superview = self.fileListTableView.enclosingScrollView?.superview self.fileListTableView.enclosingScrollView!.isHidden = true let view = KMCloudFileEmptyView() let viewH: CGFloat = 220 view.frame = NSMakeRect(0, (NSHeight(superview!.bounds)-viewH)*0.5, (superview?.bounds.size.width)!, viewH) view.autoresizingMask = [.width, .minYMargin] superview?.addSubview(view) } private func hiddenCloudFileEmptyView() { let superview = self.fileListTableView.enclosingScrollView?.superview self.fileListTableView.enclosingScrollView!.isHidden = false var flagView: KMCloudFileEmptyView? for view in superview!.subviews { if (view is KMCloudFileEmptyView) { flagView = (view as! KMCloudFileEmptyView) break } } if (flagView == nil) { return } flagView?.removeFromSuperview() } @objc func accountListTableView_itemClick(sender: NSMenuItem) { if (self.accountListTableView.selectedRow < self.accounts.count) { self.logoutAccount(self.accounts[self.accountListTableView.selectedRow] as! KMCloudServer) } } @objc func openFileMenuClick(sender: NSMenuItem) { if (self.currentAccount == nil) { return } let file: KMServicesCloudFile = self.cloudFiles[sender.tag] as! KMServicesCloudFile self.fileListBeginLoading() KMCloudDocumentTools.downloadDocument(self.currentAccount!, file) { finished, fileUrl in self.fileListEndLoading() if (finished) { if !fileUrl.path.isPDFValid() { 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() return } NSDocumentController.shared.openDocument(withContentsOf: fileUrl, display: true) { document, result, error in (document as! KMMainDocument).cloud = true (document as! KMMainDocument).cloudUploadHanddler = { callback in KMCloudDocumentTools.uploadDocument(self.currentAccount!, file) { finished, fileUrl in callback(finished, "") } } } } } } @objc func openFolderMenuClick(sender: NSMenuItem) { if (self.currentAccount == nil) { return } let file: KMServicesCloudFile = self.cloudFiles[sender.tag] as! KMServicesCloudFile self.updateFilesList(self.currentAccount!, file) } @objc func fileListTableViewDoubleAction(sender: Any) { let indexs = self.fileListTableView.selectedRowIndexes for index in indexs { let file: KMServicesCloudFile = self.cloudFiles[index] as! KMServicesCloudFile if (file.filetype == .file) { if (self.currentAccount == nil) { continue } if (!KMCloudDocumentTools.canOpenDocument(file)) { continue } self.fileListBeginLoading() KMCloudDocumentTools.downloadDocument(self.currentAccount!, file) { finished, fileUrl in self.fileListEndLoading() if (finished) { NSDocumentController.shared.openDocument(withContentsOf: fileUrl, display: true) { document, result, error in (document as! KMMainDocument).cloud = true (document as! KMMainDocument).cloudUploadHanddler = { callback in KMCloudDocumentTools.uploadDocument(self.currentAccount!, file) { finished, fileUrl in callback(finished, "") } } } } } } else { if (self.currentAccount == nil) { continue } self.updateFilesList(self.currentAccount!, file) } } } private func logoutAccount(_ account: KMCloudServer) { DispatchQueue.main.async { let alert = NSAlert() alert.messageText = NSLocalizedString("Are you Sure Delete“\(account.userEmail!)”?", comment: "") alert.addButton(withTitle: NSLocalizedString("Delete", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) let response = alert.runModal() if (response == .alertSecondButtonReturn) { /// 取消 return } account.authorizedLogoutCompletion({ [weak self] finished in if (!finished) { return } var i: Int = 0 for account_ in self!.accounts { if ((account_ as! KMCloudServer).serverType == account.serverType) { break } i += 1 } self!.accounts.remove(at: i) self!.cloudFiles.removeAll() self!.reloadData() }) } } private func updateFilesList(_ server: KMCloudServer, _ file: KMServicesCloudFile?) { self.myPathControl.enabled = false self.fileListBeginLoading() server.getListWithFilePath(file) { [weak self] fileList, serviceType, error in if (error != nil) { self!.myPathControl.enabled = true return } if (fileList == nil) { self!.myPathControl.enabled = true return } if (file == nil) { /// 切换账号 self?.filepaths.removeAll() self?.currentAccount = server } else { let index = self?.filepaths.firstIndex(of: file!) if (index == nil) { self?.filepaths.append(file!) } else { let count = self?.filepaths.count for i in 0 ..< count! { if (i <= index!) { continue } self?.filepaths.remove(at: i) } } } self!.pathString = NSLocalizedString("Cloud Documents", comment: "") for filepath in self!.filepaths { self!.pathString = self!.pathString.appending("\\\(filepath.fileName!)") } self!.myPathControl.urlString = self?.pathString self?.cloudFiles.removeAll() for file in fileList! { self?.cloudFiles.append(file) } /// 排序 self?.cloudFiles.sort(by: { file1, file2 in if ((file1 as! KMServicesCloudFile).filetype == .file) { if ((file2 as! KMServicesCloudFile).filetype == .file) { return (file1 as! KMServicesCloudFile).fileName.caseInsensitiveCompare((file2 as! KMServicesCloudFile).fileName) == .orderedAscending } else { return false } } else { if ((file2 as! KMServicesCloudFile).filetype == .file) { return true } else { return (file1 as! KMServicesCloudFile).fileName.caseInsensitiveCompare((file2 as! KMServicesCloudFile).fileName) == .orderedAscending } } }) DispatchQueue.main.async { self?.fileListTableView.reloadData() if (self?.cloudFiles.count == 0) { self?.showCloudFileEmptyView() } else { self?.hiddenCloudFileEmptyView() } self!.myPathControl.enabled = true self?.fileListEndLoading() } } } private func fileListBeginLoading() { self.fileListTableView.enclosingScrollView?.superview!.beginLoading() } private func fileListEndLoading() { self.fileListTableView.enclosingScrollView?.superview?.endLoading() } } // MARK: - NSSplitViewDelegate extension KMCloudDocumentsViewController: NSSplitViewDelegate { func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { return 170.0 } func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { return 170.0 } func splitView(_ splitView: NSSplitView, shouldAdjustSizeOfSubview view: NSView) -> Bool { return false } func splitViewDidResizeSubviews(_ notification: Notification) { let rect = self.cloudSplitView.frame self.cloudLeftBox.setFrameSize(CGSize(width: 170, height: rect.size.height)) self.cloudRightBox.frame = CGRect(origin: CGPoint(x: 171.0, y: 0), size: CGSize(width: rect.width - 171.0, height: rect.height)) } func splitView(_ splitView: NSSplitView, constrainSplitPosition proposedPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { return proposedPosition } func splitView(_ splitView: NSSplitView, canCollapseSubview subview: NSView) -> Bool { return false } func splitView(_ splitView: NSSplitView, shouldCollapseSubview subview: NSView, forDoubleClickOnDividerAt dividerIndex: Int) -> Bool { return false } func splitView(_ splitView: NSSplitView, shouldHideDividerAt dividerIndex: Int) -> Bool { return splitView.isEqual(to: self.cloudSplitView) } func splitView(_ splitView: NSSplitView, effectiveRect proposedEffectiveRect: NSRect, forDrawnRect drawnRect: NSRect, ofDividerAt dividerIndex: Int) -> NSRect { return proposedEffectiveRect } func splitView(_ splitView: NSSplitView, additionalEffectiveRectOfDividerAt dividerIndex: Int) -> NSRect { return NSMakeRect(0, 0, 0, 0) } } // MARK: - NSCollectionViewDataSource extension KMCloudDocumentsViewController: NSCollectionViewDataSource { func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { if collectionView.isEqual(to: emptyCollectionView) { return self.cloudPlates.count } return 0 } func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { if collectionView.isEqual(to: emptyCollectionView) { let item: KMCloudEmptyCollectionViewItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMCloudEmptyCollectionViewItem"), for: indexPath) as! KMCloudEmptyCollectionViewItem let state = self.cloudPlates[indexPath.item] item.refreshUI(item: state.rawValue) return item } return NSCollectionViewItem.init() } } // MARK: - NSCollectionViewDelegate extension KMCloudDocumentsViewController: NSCollectionViewDelegate { func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set) { if collectionView.isEqual(to: emptyCollectionView) { let state = self.cloudPlates[indexPaths.first!.item] loginCloud(state: state) } } } // MARK: - NSCollectionViewDelegateFlowLayout extension KMCloudDocumentsViewController: NSCollectionViewDelegateFlowLayout { func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize { if collectionView.isEqual(to: emptyCollectionView) { return self.emptyCollectionView.itemSize } return CGSize(width: 218, height: 224) } func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { if collectionView.isEqual(to: emptyCollectionView) { return self.emptyCollectionView.minimumLineSpacing } return 14 } func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { if collectionView.isEqual(to: emptyCollectionView) { return self.emptyCollectionView.minimumInteritemSpacing } return 14 } func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, insetForSectionAt section: Int) -> NSEdgeInsets { if (collectionView.isEqual(to: self.emptyCollectionView)) { let width: CGFloat = NSWidth(collectionView.superview!.frame) let height: CGFloat = NSHeight(collectionView.superview!.frame) let itemSize = self.emptyCollectionView.itemSize let itemCount: Int = self.emptyCollectionView.numberOfItems(inSection: 0) let itemHSpace = self.emptyCollectionView.minimumLineSpacing let itemVSpace = self.emptyCollectionView.minimumInteritemSpacing var left: CGFloat = (width - itemSize.width * CGFloat(itemCount) - itemHSpace * CGFloat(itemCount-1)) * 0.5 if (left < 0) { left = 0 } var top: CGFloat = (height - itemSize.height * CGFloat(itemCount) - itemVSpace * CGFloat(itemCount-1)) * 0.5 if (top < 0) { top = 0 } return NSEdgeInsetsMake(top, left, top, left) } return NSEdgeInsetsZero } } // MARK: NSTableViewDelegate extension KMCloudDocumentsViewController: NSTableViewDelegate { func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { if tableView.isEqual(to: accountListTableView) { return 57 } else if tableView.isEqual(to: fileListTableView) { return 75 } return 0; } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let identifier = tableColumn?.identifier if tableView.isEqual(to: accountListTableView) { let server: KMCloudServer = self.accounts[row] as! KMCloudServer let cellView: KMCloudAccountListTableviewCell = tableView.makeView(withIdentifier:identifier!, owner: self) as! KMCloudAccountListTableviewCell cellView.accountLabel.stringValue = server.userEmail ?? "" cellView.toolTip = server.userEmail return cellView } else if tableView.isEqual(to: fileListTableView) { let file: KMServicesCloudFile = self.cloudFiles[row] as! KMServicesCloudFile if identifier == NSUserInterfaceItemIdentifier("name") { let cellView: KMCloudFileNameListTableviewCell = tableView.makeView(withIdentifier:identifier!, owner: self) as! KMCloudFileNameListTableviewCell if (file.filetype == .file) { let fileType = KMCloudDocumentTools.getFileType(self.currentAccount!, file) let image = NSWorkspace.shared.icon(forFileType: fileType) cellView.fileImageView.image = image cellView.subtitleLabel.stringValue = fileType } else { cellView.fileImageView.image = NSImage(named: NSImage.folderName) cellView.subtitleLabel.stringValue = "Folders" } cellView.titleLabel.stringValue = file.fileName return cellView } else if identifier == NSUserInterfaceItemIdentifier("modifyTime") { let cellView: KMCloudFileModifyTimeListTableviewCell = tableView.makeView(withIdentifier:identifier!, owner: self) as! KMCloudFileModifyTimeListTableviewCell if (file.fileModiDateString != nil) { cellView.modifyTimeLabel.stringValue = KMCloudDocumentTools.parseDate(file.fileModiDate) } else { cellView.modifyTimeLabel.stringValue = "" } return cellView } else if identifier == NSUserInterfaceItemIdentifier("size") { let cellView: KMCloudFileSizeListTableviewCell = tableView.makeView(withIdentifier:identifier!, owner: self) as! KMCloudFileSizeListTableviewCell if (file.filetype == .file) { cellView.sizeLabel.stringValue = file.fileSizeString } else { cellView.sizeLabel.stringValue = "" } return cellView } else { let cellView: KMCloudFileNameListTableviewCell = tableView.makeView(withIdentifier:identifier!, owner: self) as! KMCloudFileNameListTableviewCell return cellView } } else { let cellView: KMCloudAccountListTableviewCell = tableView.makeView(withIdentifier:identifier!, owner: self) as! KMCloudAccountListTableviewCell return cellView } } func tableViewSelectionDidChange(_ notification: Notification) { let tableView = notification.object as! NSTableView if tableView.isEqual(to: accountListTableView) { let server: KMCloudServer = self.accounts[self.accountListTableView.selectedRow] as! KMCloudServer self.updateFilesList(server, nil) self.pathString = NSLocalizedString("Cloud Documents", comment: "") self.myPathControl.urlString = self.pathString } else if tableView.isEqual(to: fileListTableView) {} } } // MARK: NSTableViewDataSource extension KMCloudDocumentsViewController: NSTableViewDataSource { func numberOfRows(in tableView: NSTableView) -> Int { if tableView.isEqual(to: accountListTableView) { return self.accounts.count } else if tableView.isEqual(to: fileListTableView) { return self.cloudFiles.count } return 0 } } extension KMCloudDocumentsViewController: KMLoadingProtocol { func beginLoading() { self.view.beginLoading() } func endLoading() { self.view.endLoading() } } extension KMCloudDocumentsViewController: KMMenuTableViewDelegate { func km_menu(for event: NSEvent) -> NSMenu? { let point = self.fileListTableView.convert(event.locationInWindow, from: nil) let index = self.fileListTableView.row(at: point) if (index < 0) { /// 空白处 return nil } let file: KMServicesCloudFile = self.cloudFiles[index] as! KMServicesCloudFile if (file.filetype == .folder) { let menu = NSMenu(title: "") let editItem = NSMenuItem(title: NSLocalizedString("Open Folder", comment: ""), action: #selector(openFolderMenuClick), keyEquivalent: "") editItem.target = self editItem.tag = index menu.addItem(editItem) return menu } if (file.filetype == .file) { let fileExtension: String = file.fileName.components(separatedBy: ".").last! if (fileExtension.lowercased() != "pdf") { return nil } let menu = NSMenu(title: "") let editItem = NSMenuItem(title: NSLocalizedString("Open File", comment: ""), action: #selector(openFileMenuClick), keyEquivalent: "") editItem.target = self editItem.tag = index menu.addItem(editItem) return menu } return nil } }