// // KMLeftSideViewController.swift // PDF Reader Pro // // Created by lxy on 2022/10/10. // import Cocoa @objc protocol KMLeftSideViewControllerDelegate { @objc optional func controlStateChange(_ obj: KMLeftSideViewController,show:Bool) @objc optional func enterEditMode(_ obj: KMLeftSideViewController, _ pages: [Int]) @objc optional func searchAction(searchString:String, isCase:Bool) @objc optional func controller(_ controller: KMLeftSideViewController, dispayDidChange dispay: KMPDFDisplayType) @objc optional func controller(controller: KMLeftSideViewController, itemClick item: Any?, itemKey: KMItemKey, params: Any?) @objc optional func controller(controller: KMLeftSideViewController, bookMarkDidChange bookMarks: [KMBookMarkItem]) @objc optional func controller(controller: KMLeftSideViewController, rotateType: KMRotateType) } extension KMLeftSideViewController.Key { static let disableTableToolTipsKey = "SKDisableTableToolTips" } class KMLeftSideViewController: KMSideViewController { var dataSource : [KMLeftMethodMode] = [KMLeftMethodMode]() var type : KMLeftMethodMode = KMLeftMethodMode() var isShowPanel : Bool = false var norImage : [String] = [] var selectImage : [String] = [] var mainVC: KMMainViewController? var selectPages: [Int]? open weak var delegate: KMLeftSideViewControllerDelegate? let noteColumnId = NSUserInterfaceItemIdentifier(rawValue: "note") let authorColumnId = NSUserInterfaceItemIdentifier(rawValue: "author") struct Key {} let scalingIncrement: Float = 0.1 deinit { KMPrint("KMLeftSideViewController deinit.") NotificationCenter.default.removeObserver(self) DistributedNotificationCenter.default().removeObserver(self) } override var nibName: NSNib.Name? { return "LeftSideView" } convenience init(type : KMLeftMethodMode) { self.init() self.type = type } override func viewDidLoad() { super.viewDidLoad() self.reloadThumbnailSize() self.reloadSnapshotSize() // DistributedNotificationCenter.default().addObserver(self, selector: #selector(interfaceThemeDidChanged), name: NSApplication.interfaceThemeChangedNotification, object: nil) } func showPanelView(show: Bool) { self.isShowPanel = show if show { self.leftView.segmentedControl.selectedSegment = 0 } else { self.leftView.segmentedControl.selectedSegment = UInt8.max self.type.methodType = .None self.delegate?.controlStateChange?(self, show: false) } } func refreshMethodType(methodType: BotaType) { let newType = KMLeftMethodMode() var show = true if self.type.methodType != methodType { newType.methodType = methodType } if self.type.methodType == methodType { show = false } else if methodType == .None { show = false } self.type = newType; self.delegate?.controlStateChange?(self,show:show) } // MARK: - New /* NSArrayController *findArrayController; NSArrayController *groupedFindArrayController; } */ @IBOutlet var segmentedControl: KMSegmentedControl! @IBOutlet var thumbnailTableView: KMThumbnailTableView! @IBOutlet var tocOutlineView: KMTocOutlineView! @IBOutlet var noteOutlineView: KMNoteOutlineView! @IBOutlet var findTableView: KMBotaTableView! @IBOutlet var groupedFindTableView: KMBotaTableView! @IBOutlet var snapshotTableView: KMBotaTableView! @IBOutlet weak var leftListView: NSView! @IBOutlet var searchViewController: KMBotaSearchViewController! @IBOutlet weak var toolButtonBox: NSBox! @IBOutlet weak var toolButtonBoxLayoutConstraint: NSLayoutConstraint! @IBOutlet weak var snapshotNormalView: NSView! @IBOutlet weak var snapshotLabel: NSTextField! @IBOutlet weak var snapshotNormalMoreButton: NSButton! @IBOutlet weak var snapshotNormalSearchButton: NSButton! @IBOutlet weak var snapshotNormalZoomOutButton: NSButton! @IBOutlet weak var snapshotNormalZoomInButton: NSButton! @IBOutlet weak var snapshotSearchZoomOutButton: NSButton! @IBOutlet weak var snapshotSearchZoomInButton: NSButton! @IBOutlet weak var snapshotSearchField: KMLeftSideViewSearchField! @IBOutlet weak var snapshotDoneButton: NSButton! @IBOutlet weak var outlineView: NSView! @IBOutlet weak var outlineMoreButton: NSButton! @IBOutlet weak var outlineAddButton: NSButton! @IBOutlet weak var outlineSearchButton: NSButton! @IBOutlet weak var outlineLabel: NSTextField! @IBOutlet weak var outlineSearchField: KMLeftSideViewSearchField! @IBOutlet weak var outlineDoneButton: NSButton! @IBOutlet weak var noteView: NSView! @IBOutlet weak var noteMoreButton: NSButton! @IBOutlet weak var noteFilterButton: NSButton! @IBOutlet weak var noteSearchButton: NSButton! @IBOutlet weak var noteSearchField: KMLeftSideViewSearchField! @IBOutlet weak var noteTitleLabel: NSTextField! @IBOutlet weak var noteHeaderView: NSView! @IBOutlet weak var sortTypeBox: KMBox! @IBOutlet weak var sortTypeLabel: NSTextField! @IBOutlet weak var noteSortButton: NSButton! @IBOutlet weak var noteDoneButton: NSButton! @IBOutlet weak var thumbnailView: NSView! @IBOutlet weak var thumbnailZoomOutButton: NSButton! @IBOutlet weak var thumbnailZoomInButton: NSButton! @IBOutlet weak var thumbnailTitleLabel: NSTextField! @IBOutlet weak var emptySearchBox: NSBox! @IBOutlet weak var emptySearchLabel: NSTextField! var filterButtonLayer: NSView? var moreButtonLayer: KMButtonLayer? var thumbnails: [KMThumbnail] = [] var isDisplayPageSize = false var thumbnailCacheSize: CGFloat = 32 * 3 var snapshotCacheSize: CGFloat = 32 * 3 private var _findState: KMFindState = .none var findState: KMFindState { get { return self._findState } set { if self._findState != newValue { self._findState = newValue self.displayFindState() if self._findState == .content { self.search(self.searchField) } else if self.findState == .note { // self.searchNotes(self.searchField) self.searchNotes(nil) } else if self.findState == .snapshot { self.searchNotes(self.searchField) } } } } var searchResults : [KMSearchMode] = [] { didSet { self.updataLeftSideFindView() } } var groupSearchResults: [KMSearchMode] = [] var findPaneState: KMFindPaneState = .singular var isSearchOutlineMode = false var isSearchSnapshotMode = false var outlineIgnoreCaseFlag = false var noteTypeDict: [String : Any] = [:] private let MIN_SIDE_PANE_WIDTH: CGFloat = 270 private let LABEL_COLUMNID = "label" var foldType: KMFoldAllAnnotationType = .none var tocType: KMFoldType = .none var snapshots: [KMSnapshotModel] = [] var dirtySnapshots: [KMSnapshotWindowController] = [] var searchSnapshots: [KMSnapshotModel] = [] private var _noteSortType: KMNoteSortType = .none var noteSortType: KMNoteSortType { get { return self._noteSortType } set { self._noteSortType = newValue NotificationCenter.default.post(name: NSNotification.Name(rawValue: "KMAnnotationSortTypeKeyNotification"), object: self) } } var isAscendSort = false { didSet { NotificationCenter.default.post(name: NSNotification.Name(rawValue: "KMAnnotationSortTypeKeyNotification"), object: self) } } lazy var leftSideEmptyVC: KMLeftSideEmptyFileViewController = { let vc = KMLeftSideEmptyFileViewController(nibName: "KMLeftSideEmptyFileViewController", bundle: nil) vc.view.wantsLayer = true return vc }() var leftMargin: CGFloat = 0 lazy var leftView: KMBotaLeftView = { let view = KMBotaLeftView() return view }() private var _copysPages: [CPDFPage] = [] var copyPages: [CPDFPage] { get { return self._copysPages } } var annotations: [KMBOTAAnnotationSection] = [] var allAnnotations: [CPDFAnnotation] = [] private let kKMPDFViewOutlineDragDataType = NSPasteboard.PasteboardType("kKMPDFViewOutlineDragDataType") private let KPDFThumbnailDoucumentURLForDraggedTypes = NSPasteboard.PasteboardType("KPDFThumbnailDoucumentURLForDraggedTypes") var renamePDFOutline: CPDFOutline? var renamePDFOutlineTextField: NSTextField? var allFoldNotes: [CPDFAnnotation] = [] var notes: [CPDFAnnotation] = [] var canFoldNotes: [CPDFAnnotation] = [] var isRenameNoteOutline = false var caseInsensitiveNoteSearch = false var noteSearchArray: [CPDFAnnotation] = [] var noteSearchMode = false var mwcFlags: MwcFlags = MwcFlags() private var _dragPDFOutline: CPDFOutline? var updatingOutlineSelection = false fileprivate var dragFilePath: String? var dragFlag = false var filePromiseQueue: OperationQueue = { let queue = OperationQueue() return queue }() var dragIn = false var dragedIndexPaths: [Int] = [] override func loadView() { super.loadView() self.initSubView() self.initDefalutValue() self.searchViewController.loadView() self.searchViewController.contentView = self.findTableView.enclosingScrollView self.searchField = self.searchViewController.searchField self.searchViewController.segmentedControl.setSegmentCount(2, with: 25) self.searchViewController.segmentedControl.setImage(NSImage(named: KMImageNameUXIconBtnSidebarListNor)!, for: 0) self.searchViewController.segmentedControl.setImage(NSImage(named: KMImageNameUXIconBtnSidebarPageNor)!, for: 1) self.searchViewController.segmentedControl.setToolTip(KMLocalizedString("Separate search results", nil), for: 0) self.searchViewController.segmentedControl.setToolTip(KMLocalizedString("Group search results by page", nil), for: 1) self.searchViewController.segmentedControl.isBackgroundHighlighted = true self.searchViewController.segmentedControl.selectedSegment = 0 self.searchViewController.segmentedControl.block = { [unowned self] segIndex in if segIndex == 0 { self.findPaneState = .singular self.displayFindViewAnimating(false) } else { self.findPaneState = .grouped self.displayGroupedFindViewAnimating(false) } } self.leftView.segmentedControl.block = { [unowned self] segIndex in self.toolButtonBox.isHidden = false self.toolButtonBoxLayoutConstraint.constant = 40.0 if (segIndex == 0) { if self.type.methodType == .Thumbnail { self.leftView.segmentedControl.selectedSegment = UInt8.max self.refreshMethodType(methodType: .None) return } self.refreshMethodType(methodType: .Thumbnail) DispatchQueue.main.async { self.toolButtonBox.contentView = self.thumbnailView self.displayThumbnailViewAnimating(false) } } else if (segIndex == 1) { if self.type.methodType == .Outline { self.leftView.segmentedControl.selectedSegment = UInt8.max self.refreshMethodType(methodType: .None) return } self.refreshMethodType(methodType: .Outline) DispatchQueue.main.async { self.toolButtonBox.contentView = self.outlineView self.displayTocViewAnimating(false) } } else if (segIndex == 2) { if self.type.methodType == .Annotation { self.leftView.segmentedControl.selectedSegment = UInt8.max self.refreshMethodType(methodType: .None) return } self.refreshMethodType(methodType: .Annotation) DispatchQueue.main.async { self.toolButtonBox.contentView = self.noteView self.displayNoteViewAnimating(false) } } else if (segIndex == 3) { if self.type.methodType == .snapshot { self.leftView.segmentedControl.selectedSegment = UInt8.max self.refreshMethodType(methodType: .None) return } self.refreshMethodType(methodType: .snapshot) self.toolButtonBox.contentView = self.snapshotNormalView self.updateSnapshotFilterPredicate() self.displaySnapshotViewAnimating(false) self.updataLeftSideSnapView() } else if (segIndex == 4) { if self.type.methodType == .Search { self.leftView.segmentedControl.selectedSegment = UInt8.max self.refreshMethodType(methodType: .None) return } self.refreshMethodType(methodType: .Search) DispatchQueue.main.async { self.toolButtonBox.isHidden = true self.toolButtonBoxLayoutConstraint.constant = 0 self.displayFindViewAnimating(false) } } } // self.button.setHelp(KMLocalizedString("View Thumbnails", "Tool tip message"), for: KMLeftSidePaneState.thumbnail.rawValue) // self.button.setHelp(KMLocalizedString("View Outline", "Tool tip message"), for: KMLeftSidePaneState.outline.rawValue) // self.alternateButton.setHelp(KMLocalizedString("Separate search results", "Tool tip message"), for: KMFindPaneState.singular.rawValue) // self.alternateButton.setHelp(KMLocalizedString("Group search results by page", "Tool tip message"), for: KMFindPaneState.grouped.rawValue) let menu = NSMenu() _ = menu.addItem(title: KMLocalizedString("Whole Words Only", "Menu item title"), action: #selector(toggleWholeWordSearch), target: self) _ = menu.addItem(title: KMLocalizedString("Ignore Case", "Menu item title"), action: #selector(toggleCaseInsensitiveSearch), target: self) (self.searchField.cell as? NSSearchFieldCell)?.searchMenuTemplate = menu (self.searchField.cell as? NSSearchFieldCell)?.placeholderString = KMLocalizedString("Search PDF", "placeholder") self.searchField.action = #selector(search) self.searchField.target = self // [thumbnailTableView setTypeSelectHelper:[SKTypeSelectHelper typeSelectHelperWithMatchOption:SKFullStringMatch]]; // //支持拖拽的文字类型 // [tocOutlineView setTypeSelectHelper:[SKTypeSelectHelper typeSelectHelperWithMatchOption:SKSubstringMatch]]; // NSSortDescriptor *countDescriptor = [[[NSSortDescriptor alloc] initWithKey:SKGroupedSearchResultCountKey ascending:NO] autorelease]; // [groupedFindArrayController setSortDescriptors:[NSArray arrayWithObjects:countDescriptor, nil]]; if KMDataManager.ud_bool(forKey: Self.Key.disableTableToolTipsKey) == false { self.tocOutlineView.hasImageToolTips = true self.findTableView.hasImageToolTips = true self.groupedFindTableView.hasImageToolTips = true } self._updateViewColor() } func initSubView() { self.view.addSubview(self.leftView) self.leftView.frame = NSMakeRect(0, 0, 44, NSHeight(self.view.frame)) self.leftView.autoresizingMask = [.height] self.thumb_initSubViews() self.outline_initSubViews() self.note_initSubViews() self.snapshot_initSubViews() self.search_initSubViews() } func initDefalutValue() { self.view.wantsLayer = true self.view.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.leftView.wantsLayer = true self.leftView.layer?.backgroundColor = .white self.leftListView.wantsLayer = true self.leftListView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.emptySearchLabel.stringValue = KMLocalizedString("No Results",nil) self.emptySearchLabel.textColor = KMAppearance.Layout.h0Color() self.emptySearchBox.isHidden = true self.thumb_initDefalutValue() self.outline_initDefalutValue() self.note_initDefalutValue() self.snapshot_initDefalutValue() self.search_initDefalutValue() } func displayThumbnailViewAnimating(_ animate: Bool) { self.replaceSideView(self.thumbnailTableView.enclosingScrollView!, animate: animate) var frame = self.thumbnailTableView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.origin.x = self.leftMargin frame.size.height = self.thumbnailTableView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.thumbnailTableView.enclosingScrollView?.frame = frame self.resetThumbnails() frame = self.noteOutlineView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.origin.x = self.leftMargin frame.size.height = self.noteOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.noteOutlineView.enclosingScrollView?.frame = frame frame = self.snapshotTableView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.origin.x = self.leftMargin frame.size.height = self.snapshotTableView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.snapshotTableView.enclosingScrollView?.frame = frame // [self updateThumbnailSelection]; } func displayFindViewAnimating(_ animate: Bool) { self.replaceSideView(self.searchViewController.view, animate: animate) if (self.findState != .content) { self.findState = .content } else { self.displayFindState() } var frame = self.searchViewController.view.frame frame.origin.y = 0 frame.size.height = self.searchViewController.view.superview?.frame.size.height ?? .zero self.searchViewController.view.frame = frame frame = self.noteOutlineView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.noteOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.noteOutlineView.enclosingScrollView?.frame = frame frame = self.snapshotTableView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.snapshotTableView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.snapshotTableView.enclosingScrollView?.frame = frame self.leftSideEmptyVC.emptySnapView.removeFromSuperview() self.updataLeftSideSnapView() } func displayFindState() { if (self.findState == .content) { self.displayFind() } else if (self.findState == .note) { self.displayNoteFind() } else if (self.findState == .snapshot) { self.displaySnapshotFind() } } func updataLeftSideFindView() { if (self.findState != .content) { return } if (self.searchResults.count > 0) { self.searchViewController.emptyBox.isHidden = true self.searchViewController.searchResultsView.isHidden = false self.searchViewController.searchResultsLabel.stringValue = String(format: KMLocalizedString("%ld Results", "Message in search table header"), self.searchResults.count) } else { self.searchViewController.emptyBox.isHidden = false self.searchViewController.searchResultsView.isHidden = true } } func displayGroupedFindViewAnimating(_ animate: Bool) { self.replaceSideView(self.searchViewController.view , animate: animate) if (self.findState != .content) { self.findState = .content } else { self.displayFindState() } var frame = self.searchViewController.view.frame frame.origin.y = 0 frame.size.height = self.searchViewController.view.superview?.frame.size.height ?? 0 self.searchViewController.view.frame = frame frame = self.noteOutlineView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.noteOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.noteOutlineView.enclosingScrollView?.frame = frame frame = self.snapshotTableView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.snapshotTableView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.snapshotTableView.enclosingScrollView?.frame = frame self.updataLeftSideSnapView() } func displayNoteViewAnimating(_ animate: Bool) { self.reloadAnnotation() self.annotationSort(sortArray: []) self.searchViewController.contentView = nil self.replaceSideView(self.noteOutlineView.enclosingScrollView!, animate: animate) if (self.findState != .note) { self.findState = .note } else { self.displayFindState() } var frame = self.noteOutlineView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.noteOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.noteOutlineView.enclosingScrollView?.frame = frame frame = self.noteOutlineView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.noteOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.noteOutlineView.enclosingScrollView?.frame = frame frame = self.snapshotTableView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.snapshotTableView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.snapshotTableView.enclosingScrollView?.frame = frame let view = self.noteOutlineView.enclosingScrollView! let emptyVcSize = self.leftSideEmptyVC.emptyAnnotationView.frame.size self.leftSideEmptyVC.emptyAnnotationView.frame = NSMakeRect((view.frame.size.width-emptyVcSize.width)/2.0,(view.frame.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height) DispatchQueue.main.async { self.noteOutlineView.reloadData() } } func displaySnapshotViewAnimating(_ animate: Bool) { self.searchViewController.contentView = nil self.replaceSideView(self.snapshotTableView.enclosingScrollView!, animate: animate) if (self.findState != .snapshot) { self.findState = .snapshot } else { self.displayFindState() } var frame = self.snapshotTableView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.snapshotTableView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.snapshotTableView.enclosingScrollView?.frame = frame frame = self.noteOutlineView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.noteOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.noteOutlineView.enclosingScrollView?.frame = frame frame = self.tocOutlineView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.tocOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.tocOutlineView.enclosingScrollView?.frame = frame // [self updateSnapshotsIfNeeded]; Task { @MainActor in self.snapshotTableView.reloadData() } } func displayFind() { self.searchField = self.searchViewController.searchField let menu = NSMenu() _ = menu.addItem(title: KMLocalizedString("Whole Words Only", "Menu item title"), action: #selector(toggleWholeWordSearch), target: self) _ = menu.addItem(title: KMLocalizedString("Ignore Case", "Menu item title"), action: #selector(toggleCaseInsensitiveSearch), target: self) (self.searchViewController.searchField.cell as? NSSearchFieldCell)?.searchMenuTemplate = menu (self.searchViewController.searchField.cell as? NSSearchFieldCell)?.placeholderString = KMLocalizedString("Search PDF", "placeholder") self.searchViewController.searchField.action = #selector(search) self.searchViewController.searchField.target = self if (self.findPaneState == .singular) { self.searchViewController.contentView = self.findTableView.enclosingScrollView self.findTableView.wantsLayer = true self.findTableView.layer?.backgroundColor = NSColor.red.cgColor DispatchQueue.main.async { self.findTableView.reloadData() } } else if (self.findPaneState == .grouped) { self.searchViewController.contentView = self.groupedFindTableView.enclosingScrollView var array = KMSearchMode.sortSearchResult(results: self.searchResults) array.sort(){$0.datas.count > $1.datas.count} self.groupSearchResults = array self.groupedFindTableView.reloadData() } } func displayTocViewAnimating(_ animate: Bool) { self.replaceSideView(self.tocOutlineView.enclosingScrollView!, animate: animate) var frame = self.tocOutlineView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.tocOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.tocOutlineView.enclosingScrollView?.frame = frame frame = self.noteOutlineView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.noteOutlineView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.noteOutlineView.enclosingScrollView?.frame = frame frame = self.snapshotTableView.enclosingScrollView?.frame ?? .zero frame.origin.y = 0 frame.size.height = self.snapshotTableView.enclosingScrollView?.superview?.frame.size.height ?? 0 self.snapshotTableView.enclosingScrollView?.frame = frame let view = self.tocOutlineView.enclosingScrollView! let emptyVcSize = self.leftSideEmptyVC.emptyOutlineView.frame.size self.leftSideEmptyVC.emptyOutlineView.frame = NSMakeRect((view.frame.size.width-emptyVcSize.width)/2.0,(view.frame.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height); DispatchQueue.main.async { self.tocOutlineView.reloadData() self.updateOutlineSelection() } } override func requiresAlternateButton(forView aview: NSView?) -> Bool { return false } func displayNoteFind() { self.searchField = self.noteSearchField let menu = NSMenu() _ = menu.addItem(title: KMLocalizedString("Ignore Case", "Menu item title"), action: #selector(toggleCaseInsensitiveNoteSearch), target: self) (self.noteSearchField.cell as? NSSearchFieldCell)?.searchMenuTemplate = menu (self.noteSearchField.cell as? NSSearchFieldCell)?.placeholderString = KMLocalizedString("Search Notes", "placeholder") self.noteSearchField.action = #selector(searchNotes) self.noteSearchField.target = self } func displaySnapshotFind() { self.searchField = self.snapshotSearchField; let menu = NSMenu() _ = menu.addItem(title: KMLocalizedString("Ignore Case", "Menu item title"), action: #selector(toggleCaseInsensitiveNoteSearch), target: self) (self.snapshotSearchField.cell as? NSSearchFieldCell)?.searchMenuTemplate = menu (self.snapshotSearchField.cell as? NSSearchFieldCell)?.placeholderString = KMLocalizedString("Search Snapshots", "placeholder") self.snapshotSearchField.action = #selector(searchNotes) self.snapshotSearchField.target = self self.snapshotSearchField.delegate = self } func resetThumbnails() { // [self willChangeValueForKey:THUMBNAILS_KEY]; // self.thumbnailCacheSize = 160 self.thumbnails.removeAll() let pageLabels = self.listView.document.pageLabels() if (pageLabels.isEmpty == false) { let isLocked = self.listView.document.isLocked let firstPage = self.listView.document.page(at: 0) let emptyPage = CPDFPage() let firstFrame = firstPage?.bounds(for: .cropBox) ?? .zero let firstFrame2 = firstPage?.bounds(for: .mediaBox) ?? .zero emptyPage.setBounds(firstFrame, for: .cropBox) emptyPage.setBounds(firstFrame2, for: .mediaBox) emptyPage.rotation = firstPage?.rotation ?? 0 let pageImage = firstPage!.thumbnail(of: NSMakeSize(self.thumbnailCacheSize, self.thumbnailCacheSize)) // NSImage * = [emptyPage thumbnailWithSize:thumbnailCacheSize forBox:[pdfView displayBox]]; var rect: NSRect = .zero rect.size = pageImage?.size ?? .zero let width = 1.2 * fmin(NSWidth(rect), NSHeight(rect)) rect = NSInsetRect(rect, 0.5 * (NSWidth(rect) - width), 0.5 * (NSHeight(rect) - width)); pageImage?.lockFocus() // NSImage(named: NSImage.applicationIconName)?.draw(in: rect, from: .zero, operation: .sourceOver, fraction: 0.5) if (isLocked) { NSWorkspace.shared.icon(forFileType: NSFileTypeForHFSTypeCode(OSType(kLockedBadgeIcon))).draw(in: rect, from: .zero, operation: .sourceOver, fraction: 0.5) // [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kLockedBadgeIcon)] drawInRect:rect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:0.5]; } pageImage?.unlockFocus() for (i, label) in pageLabels.enumerated() { let firstPage = self.listView.document.page(at: UInt(i)) let size = NSMakeSize(self.thumbnailCacheSize, self.thumbnailCacheSize) // let pageImage = firstPage!.thumbnail(of: size) let thumbnail = KMThumbnail(image: nil, label: label, pageIndex: i) // thumbnail.delegate = self thumbnail.dirty = true self.thumbnails.append(thumbnail) } } // [self didChangeValueForKey:THUMBNAILS_KEY]; // [self allThumbnailsNeedUpdate]; DispatchQueue.main.async { let ris = self.thumbnailTableView.selectedRowIndexes self.thumbnailTableView.reloadData() self.thumbnailTableView.km_safe_selectRowIndexes(ris, byExtendingSelection: false) } } @IBAction func leftSideViewDoneButtonAction(_ sender: AnyObject?) { let button = sender as? NSButton let tag = button?.tag ?? 0 if (tag == 310) { self.outlineSearchField.isHidden = true self.outlineDoneButton.isHidden = true self.outlineLabel.isHidden = false self.outlineSearchButton.isHidden = false self.outlineMoreButton.isHidden = false self.outlineAddButton.isHidden = false } else if (tag == 311) { self.noteSearchField.isHidden = true self.noteTitleLabel.isHidden = false self.noteSearchButton.isHidden = false self.noteDoneButton.isHidden = true self.noteFilterButton.isHidden = false self.noteMoreButton.isHidden = false } else if (tag == 312) { self.snapshotSearchField.isHidden = true // leftSideController.snapshotSearchZoomInButton.hidden = YES; self.snapshotNormalSearchButton.isHidden = false self.snapshotDoneButton.isHidden = true self.snapshotLabel.isHidden = false self.snapshotNormalZoomOutButton.isHidden = false self.snapshotNormalZoomInButton.isHidden = false } } @IBAction func thumbnailSizeScaling(_ sender: NSButton) { let tag = sender.tag if (tag == 0 || tag == 1) { var scaling = KMDataManager.ud_float(forKey: Self.Key.thumbSizeScaling) if (scaling <= 0) { scaling = 1 } if (tag == 0) { // Zoom In scaling += self.scalingIncrement // if scaling >= 2.2 { // return // } } else if (tag == 1) { // Zoom Out scaling -= self.scalingIncrement if scaling <= 0.4 { return } } KMDataManager.ud_set(scaling, forKey: Self.Key.thumbSizeScaling) let selectRow = self.thumbnailTableView.selectedRow self.thumbnailTableView.reloadData() self.thumbnailTableView.selectRowIndexes(IndexSet(integer: selectRow), byExtendingSelection: false) } else if (tag == 2 || tag == 3) { var scaling = KMDataManager.ud_float(forKey: Self.Key.snapshotSizeScaling) if (scaling <= 0) { scaling = 1 } if (tag == 2) { // Zoom In scaling += self.scalingIncrement } else if (tag == 3) { // Zoom Out scaling -= self.scalingIncrement } KMDataManager.ud_set(scaling, forKey: Self.Key.snapshotSizeScaling) let selectRow = self.snapshotTableView.selectedRow self.snapshotTableView.reloadData() self.snapshotTableView.selectRowIndexes(IndexSet(integer: selectRow), byExtendingSelection: false) } } func tableView(_ tv: NSTableView, cutRowsWithIndexes rowIndexes: IndexSet) { if tv.isEqual(to: self.thumbnailTableView) { self._copysPages.removeAll() for idx in rowIndexes { if (idx != NSNotFound) { if let page = self.listView.document.page(at: UInt(idx))?.copy() as? CPDFPage { self._copysPages.append(page) } } } self.tableView(tv, deleteRowsWithIndexes: rowIndexes) } } } // MARK: - KMBotaTableViewDelegate extension KMLeftSideViewController: KMBotaTableViewDelegate { func tableView(_ aTableView: NSTableView, canCopyRowsWithIndexes rowIndexes: IndexSet) -> Bool { if aTableView.isEqual(to: self.thumbnailTableView) || aTableView.isEqual(to: self.findTableView) || aTableView.isEqual(to: self.groupedFindTableView) { return rowIndexes.count > 0 } return false } func tableView(_ aTableView: NSTableView, canPasteFromPasteboard pboard: NSPasteboard) -> Bool { if aTableView.isEqual(to: self.thumbnailTableView) { if self._copysPages.count > 0 { return true } } return false } func tableView(_ aTableView: NSTableView, canDeleteRowsWithIndexes rowIndexes: IndexSet) -> Bool { if aTableView.isEqual(to: self.snapshotTableView) { return rowIndexes.count > 0 } else if aTableView.isEqual(to: self.thumbnailTableView) { if self.listView.document.pageCount <= 1 { return false } return true } return false } func tableView(_ aTableView: NSTableView, pasteFromPasteboard pboard: NSPasteboard?) { if IAPProductsManager.default().isAvailableAllFunction() == false { KMPurchaseCompareWindowController.sharedInstance().showWindow(nil) return } if aTableView.isEqual(to: self.thumbnailTableView) { if self._copysPages.count > 0 { let index = self.listView.document.index(for: self.listView.currentPage()) + 1 if (index == NSNotFound) { return } for page in self._copysPages.reversed() { // if let newPage = page.copy() as? CPDFPage { self.listView.document.insertPageObject(page, at: index) // } self.listView.layoutDocumentView() // [pageLabels setArray:[[pdfView document] pageLabels]]; self.resetThumbnails() let pageIndex = min(index, self.listView.document.pageCount-1) self.listView.go(toPageIndex: Int(pageIndex), animated: true) (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).tableView(self.thumbnailTableView, deleteRowsWithIndexes: IndexSet(integer: IndexSet.Element(index))) } } } } func tableViewMoveLeft(_ aTableView: NSTableView) { if aTableView.isEqual(to: self.findTableView) || aTableView.isEqual(to: self.groupedFindTableView) { // [self updateFindResultHighlightsForDirection:NSSelectingPrevious]; } } func tableViewMoveRight(_ aTableView: NSTableView) { if aTableView.isEqual(to: self.findTableView) || aTableView.isEqual(to: self.groupedFindTableView) { // [self updateFindResultHighlightsForDirection:NSSelectingNext]; } } func tableView(_ aTableView: NSTableView, imageContextForRow rowIndex: Int) -> AnyObject? { if aTableView.isEqual(to: self.findTableView) { if rowIndex >= self.searchResults.count { return nil } let model = self.searchResults[rowIndex] let selection = model.selection let point = NSPoint(x: NSWidth(selection.bounds) * 0.5, y: NSHeight(selection.bounds) * 0.5) return CPDFDestination(document: self.listView.document, pageIndex: Int(selection.page.pageIndex()), at: point, zoom: self.listView.scaleFactor) } else if aTableView.isEqual(to: self.groupedFindTableView) { if rowIndex >= self.groupSearchResults.count { return nil } // let model = self.groupSearchResults[rowIndex] // let selection = model.selection // let point = NSPoint(x: NSWidth(selection.bounds) * 0.5, y: NSHeight(selection.bounds) * 0.5) // return CPDFDestination(document: self.listView.document, pageIndex: Int(selection.page.pageIndex()), at: point, zoom: self.listView.scaleFactor) } return nil } /* - (NSArray *)tableView:(NSTableView *)tv typeSelectHelperSelectionStrings:(SKTypeSelectHelper *)typeSelectHelper { if ([tv isEqual:leftSideController.thumbnailTableView]) { return pageLabels; } return nil; } - (void)tableView:(NSTableView *)tv typeSelectHelper:(SKTypeSelectHelper *)typeSelectHelper didFailToFindMatchForSearchString:(NSString *)searchString { if ([tv isEqual:leftSideController.thumbnailTableView]) { [statusBar setLeftStringValue:[NSString stringWithFormat:NSLocalizedString(@"No match: \"%@\"", @"Status message"), searchString]]; } } - (void)tableView:(NSTableView *)tv typeSelectHelper:(SKTypeSelectHelper *)typeSelectHelper updateSearchString:(NSString *)searchString { if ([tv isEqual:leftSideController.thumbnailTableView]) { if (searchString.length > 0) [statusBar setLeftStringValue:[NSString stringWithFormat:NSLocalizedString(@"Go to page: %@", @"Status message"), searchString]]; else [self updateLeftStatus]; } } */ } // MARK: - 扩展 extension KMLeftSideViewController { public func selectType(_ type: BotaType) { // 更新 type var show = true if (self.type.methodType == .None) { show = true } else { if (self.type.methodType == type) { show = false } else { show = true } } var index: Int = NSNotFound switch type { case .None: index = NSNotFound break case .Thumbnail: index = 0 break case .Outline: index = 1 break case .BookMark: index = 2 break case .Annotation: index = 3 break case .Search: index = 4 break case .From: index = 5 break case .Signature: index = 6 break case .snapshot: index = 7 break } self.type = self.getMethodMode(show ? type : .None) // 更新按钮状态 // self.updateViewButtonState() // 将事件传递出去 self.delegate?.controlStateChange?(self,show:show) } private func getMethodMode(_ type: BotaType) -> KMLeftMethodMode { let mode = KMLeftMethodMode() mode.methodType = type switch type { case .None: mode.methodName = "" break case .Thumbnail: mode.methodName = thumbnailMethodKey break case .Outline: mode.methodName = outlineMethodKey break case .BookMark: mode.methodName = bookMarkMethodKey break case .Annotation: mode.methodName = anntationMethodKey break case .Search: mode.methodName = searchMethodKey break case .From: mode.methodName = formMethodKey break case .Signature: mode.methodName = signatureMethodKey break case .snapshot: mode.methodName = snapshotMethodKey } return mode } } //MARK: Cache extension KMLeftSideViewController { func clearNotification() { } } // MARK: - Private Methods extension KMLeftSideViewController { func updateViewColor() { self._updateViewColor() } private func _updateViewColor() { if(KMAppearance.isDarkMode()){ self.leftListView.layer?.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1).cgColor self.snapshotNormalView.layer?.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1).cgColor self.thumbnailView.layer?.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1).cgColor self.noteView.layer?.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1).cgColor self.outlineView.layer?.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1).cgColor self.view.layer?.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1).cgColor self.thumbnailTableView.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1) self.groupedFindTableView.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1) self.tocOutlineView.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1) self.findTableView.backgroundColor = NSColor(red: 0.149, green: 0.157, blue: 0.169, alpha: 1) self.snapshotSearchField.layer?.backgroundColor = NSColor(red: 0.224, green: 0.235, blue: 0.243, alpha: 1).cgColor self.outlineSearchField.layer?.backgroundColor = NSColor(red: 0.224, green: 0.235, blue: 0.243, alpha: 1).cgColor self.noteSearchField.layer?.backgroundColor = NSColor(red: 0.224, green: 0.235, blue: 0.243, alpha: 1).cgColor self.segmentedControl.layer?.backgroundColor = NSColor(red: 0.224, green: 0.235, blue: 0.243, alpha: 1).cgColor self.snapshotSearchField.backgroundColor = NSColor(red: 0.224, green: 0.235, blue: 0.243, alpha: 1) self.outlineSearchField.backgroundColor = NSColor(red: 0.224, green: 0.235, blue: 0.243, alpha: 1) self.noteSearchField.backgroundColor = NSColor(red: 0.224, green: 0.235, blue: 0.243, alpha: 1) } else { self.leftListView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.snapshotNormalView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.thumbnailView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.noteView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.outlineView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.view.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.thumbnailTableView.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1.000, alpha: 1) self.groupedFindTableView.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1.000, alpha: 1) self.tocOutlineView.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1.000, alpha: 1) self.findTableView.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1.000, alpha: 1) self.snapshotSearchField.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor self.outlineSearchField.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor self.noteSearchField.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor self.segmentedControl.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor self.snapshotSearchField.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1) self.outlineSearchField.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1) self.noteSearchField.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1) } } private func _hasContainString(_ searchString: String, rootOutline outline: CPDFOutline) -> Bool { var label = outline.label ?? "" var searchLabel = searchString if self.outlineIgnoreCaseFlag { label = label.lowercased() searchLabel = searchLabel.lowercased() } if label.contains(searchLabel) { // if ([outline.label rangeOfString:searchString options:self.outlineIgnoreCaseFlag?NSCaseInsensitiveSearch:0].location != NSNotFound){ return true } else { var subHas = false for i in 0 ..< outline.numberOfChildren { if let subOutline = outline.child(at: i) { subHas = self._hasContainString(searchString, rootOutline: subOutline) } else { continue } if (subHas) { break } } return subHas } } } // MARK: - NSTableViewDelegate, NSTableViewDataSource extension KMLeftSideViewController: NSTableViewDelegate, NSTableViewDataSource { func numberOfRows(in tableView: NSTableView) -> Int { if tableView.isEqual(to: self.thumbnailTableView) { return self.thumbnails.count } else if tableView.isEqual(to: self.findTableView) { return self.searchResults.count } else if tableView.isEqual(to: self.groupedFindTableView) { return self.groupSearchResults.count } else if tableView.isEqual(to: self.snapshotTableView) { if self.isSearchSnapshotMode { return self.searchSnapshots.count } return self.snapshots.count } return 0 } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { if tableView.isEqual(to: self.thumbnailTableView) { let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMThumbnailTableviewCell"), owner: self) as! KMThumbnailTableviewCell let thumbnail = self.thumbnails[row] cell.pageNumLabel.stringValue = thumbnail.label // cell.thumImage.image = thumbnail.image cell.pageView.page = self.listView.document.page(at: UInt(row)) if let _image = thumbnail.image { let multiplierHToW = _image.size.height / (_image.size.width == 0 ? 1 : _image.size.width) let multiplierWToH = _image.size.width / (_image.size.height == 0 ? 1 : _image.size.height) if (_image.size.height > _image.size.width) { NSLayoutConstraint.deactivate([cell.imageAspectRatioLayout]) // cell.imageAspectRatioLayout = NSLayoutConstraint(item: cell.thumImage, attribute: .height, relatedBy: .equal, toItem: cell.thumImage, attribute: .width, multiplier: multiplierHToW, constant: 0) // NSLayoutConstraint.activate([cell.imageAspectRatioLayout]) } else { NSLayoutConstraint.deactivate([cell.imageAspectRatioLayout]) // cell.imageAspectRatioLayout = NSLayoutConstraint(item: cell.thumImage, attribute: .width, relatedBy: .equal, toItem: cell.thumImage, attribute: .height, multiplier: multiplierWToH, constant: 0) // NSLayoutConstraint.activate([cell.imageAspectRatioLayout]) } } if (self.isDisplayPageSize) { cell.sizeLabel.isHidden = false //获取Page的真实尺寸 let page = self.listView.document.page(at: UInt(row)) let rect = page?.bounds(for: .cropBox) ?? .zero let w = KMPageSizeTool.conversion(withUnit: "mm", value: (CGRectGetWidth(rect)/595 * 210)) let h = KMPageSizeTool.conversion(withUnit: "mm", value: (CGRectGetHeight(rect)/842 * 297)) if let data = page?.rotation, data == 90 || data == 270 { cell.sizeLabel.stringValue = String(format: "%.f × %.f %@", h.stringToCGFloat(), w.stringToCGFloat(), KMLocalizedString("mm", nil)) } else { cell.sizeLabel.stringValue = String(format: "%.f × %.f %@", w.stringToCGFloat(), h.stringToCGFloat(), KMLocalizedString("mm", nil)) } } else { cell.sizeLabel.isHidden = true } cell.sizeTopConstant.constant = cell.sizeLabel.isHidden ? -cell.sizeLabel.frame.size.height : 0 if(self.thumbnailTableView.selectedRowIndexes.contains(row)) { cell.isSelectCell = true } else { cell.isSelectCell = false } return cell } else if (tableView.isEqual(to: self.findTableView)) { let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMFindTableviewCell"), owner: self) as! KMFindTableviewCell let selection = searchResults[row] if let data = tableColumn?.identifier.rawValue, data == kResultsColumnId.rawValue { cell.resultLabel.attributedStringValue = selection.attributedString cell.resultLabel.textColor = KMAppearance.Layout.h0Color() } else if let data = tableColumn?.identifier.rawValue, data == kPageColumnId.rawValue { cell.resultLabel.stringValue = "\(Int(selection.selection.page?.pageIndex() ?? 0) + 1)" cell.resultLabel.textColor = KMAppearance.Layout.h2Color() } return cell } else if tableView.isEqual(to: self.snapshotTableView) { let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMSnapshotTableViewCell"), owner: self) as! KMSnapshotTableViewCell var snapshot: KMSnapshotModel? if self.isSearchSnapshotMode { snapshot = self.searchSnapshots[row] } else { snapshot = self.snapshots[row] } cell.snapshotImage.image = snapshot?.windowC?.thumbnail cell.snapshotLabel.stringValue = snapshot?.windowC?.pageLabel ?? "" if let data = snapshot?.windowC?.hasWindow, data { cell.snapshotImageView.isHidden = false } else { cell.snapshotImageView.isHidden = true } cell.isSelectCell = snapshot?.isSelected ?? false if (row == tableView.selectedRow) { cell.snapshotImageView.image = NSImage(named: KMImageNameUXIconSidebarSnapshotWindowSel) } else { cell.snapshotImageView.image = NSImage(named: KMImageNameUXIconSidebarSnapshotWindowNor) } return cell } return nil } func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { if tableView.isEqual(to: self.thumbnailTableView) { let scaling = KMDataManager.ud_float(forKey: Self.Key.thumbSizeScaling) // let thumbnailSize = self.thumbnails[row].size let thumbnailSize = NSMakeSize(self.thumbnailCacheSize, self.thumbnailCacheSize) let newScaling: CGFloat = scaling.cgFloat + 0.1 let newThumbnailHeight = thumbnailSize.width * newScaling if (newThumbnailHeight > MIN_SIDE_PANE_WIDTH) { self.thumbnailZoomOutButton.isEnabled = false } else { self.thumbnailZoomOutButton.isEnabled = true } if ((scaling - 0.1) < 0.3) { self.thumbnailZoomInButton.isEnabled = false } else { self.thumbnailZoomInButton.isEnabled = true } var labelHeight = 0.0 if (self.isDisplayPageSize) { labelHeight = 56.0 } else { labelHeight = 41.5 } let cellHeight = thumbnailSize.height + labelHeight var thumbSize: NSSize = .zero if (scaling > 0) { thumbSize = NSMakeSize(thumbnailSize.width * scaling.cgFloat, cellHeight * scaling.cgFloat) } else { thumbSize = NSMakeSize(thumbnailSize.width, cellHeight) } return thumbSize.height } else if tableView.isEqual(to: self.snapshotTableView) { let scaling = KMDataManager.ud_float(forKey: Self.Key.snapshotSizeScaling) let snapshotSize = (self.snapshots.safe_element(for: row) as? KMSnapshotModel)?.windowC?.thumbnail?.size ?? CGSizeMake(120, 63) var newScaling = scaling + 0.1 let newSnapshotHeight = snapshotSize.width * newScaling.cgFloat; if (newSnapshotHeight > MIN_SIDE_PANE_WIDTH) { self.snapshotNormalZoomInButton.isEnabled = false } else { self.snapshotNormalZoomInButton.isEnabled = true } if ((scaling - 0.1) < 0.3 || (newSnapshotHeight < 150.0)) { self.snapshotNormalZoomOutButton.isEnabled = false } else { self.snapshotNormalZoomOutButton.isEnabled = true } let cellHeight = snapshotSize.height + 24.0 var thumbSize: NSSize = .zero if (scaling > 0) { thumbSize = NSMakeSize(snapshotSize.width * scaling.cgFloat, cellHeight * scaling.cgFloat) } else { thumbSize = NSMakeSize(snapshotSize.width, cellHeight) } return thumbSize.height } else if (tableView.isEqual(to: self.findTableView)) { return 40.0 } else if tableView.isEqual(to: self.groupedFindTableView) { return 16 } return tableView.rowHeight } func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? { if (tableView.isEqual(to: self.findTableView)) { let rowView = KMBotaTableRowView() return rowView } else if tableView.isEqual(to: self.groupedFindTableView) { let rowView = KMGroupFindTableRowView() rowView.totalNumber = self.groupSearchResults.first?.datas.count ?? 0 let model = self.groupSearchResults[row] rowView.number = model.datas.count rowView.pageIndex = Int(model.selectionPageIndex) + 1 return rowView } return nil } func tableViewSelectionIsChanging(_ notification: Notification) { } func tableViewSelectionDidChange(_ notification: Notification) { if self.findTableView.isEqual(to: notification.object) { // [self updateFindResultHighlightsForDirection:NSDirectSelection]; let row = self.findTableView.selectedRow if row >= 0 { let model = self.searchResults[row] if model.selection != nil { self.listView.go(to: model.selection, animated: true) self.listView.setHighlightedSelection(model.selection, animated: true) self.listView.setNeedsDisplayAnnotationViewForVisiblePages() } } } else if self.groupedFindTableView.isEqual(to: notification.object) { // [self updateFindResultHighlightsForDirection:NSDirectSelection]; } else if self.thumbnailTableView.isEqual(to: notification.object) { // if (mwcFlags.updatingThumbnailSelection == 0) { let row = self.thumbnailTableView.selectedRow let curPage = self.listView.document.index(for: self.listView.currentPage()) if (row != -1 && row != curPage) { self.listView.go(toPageIndex: row, animated: true) } self.thumbnailTableView.ks_reloadData() } else if self.snapshotTableView.isEqual(to: notification.object) { let row = self.snapshotTableView.selectedRow // 更新选中数据 for (i, model) in self.snapshots.enumerated() { model.isSelected = i == row } if (row != -1) { let controller = (self.snapshots.safe_element(for: row) as? KMSnapshotModel)?.windowC if let data = controller?.window?.isVisible, data { controller?.window?.orderFront(self) } } // self.snapshotTableView.reloadData() self.snapshotTableView.ks_reloadData() } } func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation { if dropOperation == .on || tableView.isEqual(to: self.thumbnailTableView) == false { return NSDragOperation(rawValue: 0) } let pboard = info.draggingPasteboard if (pboard.availableType(from: [.localDraggedTypes]) != nil) { return .move } else if (pboard.availableType(from: [.fileURL]) != nil) && tableView.isEqual(to: self.thumbnailTableView) { guard let pbItems = pboard.pasteboardItems else { return NSDragOperation(rawValue: 0) } var hasValidFile = false for item in pbItems { guard let data = item.string(forType: .fileURL), let _url = URL(string: data) else { continue } let type = _url.pathExtension.lowercased() if type == "pdf" || KMImageToPDFMethod.supportedImageTypes().contains(type) { hasValidFile = true } } if (!hasValidFile) { return NSDragOperation(rawValue: 0) } else { return .move } } return NSDragOperation(rawValue: 0) } func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool { var result = false if tableView.isEqual(to: self.thumbnailTableView) == false { return result } let pasteboard = info.draggingPasteboard if (pasteboard.availableType(from: [.localDraggedTypes]) != nil) { result = true let rowData = pasteboard.data(forType: .localDraggedTypes) let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: rowData!) as? IndexSet ?? IndexSet() let urlData = pasteboard.data(forType: KPDFThumbnailDoucumentURLForDraggedTypes) let url = NSKeyedUnarchiver.unarchiveObject(with: urlData!) as? String if url != nil && url != self.listView.document.documentURL.absoluteString { if (pasteboard.availableType(from: [.fileURL]) != nil) { // if ([pasteboard availableTypeFromArray:[NSArray arrayWithObject:NSFilenamesPboardType]]){ // NSArray *fileNames = [pasteboard propertyListForType:NSFilenamesPboardType]; var fileNames = pasteboard.propertyList(forType: .fileURL) as? [String] ?? [] if (fileNames.count == 1) { let path = fileNames.first ?? "" let pathExtension = path.pathExtension.lowercased() if pathExtension == "pdf" { var index = row var insertIndexSet = IndexSet() var pdf = CPDFDocument(url: URL(fileURLWithPath: path)) if let data = pdf?.isEncrypted, data { KMBaseWindowController.checkPassword(url: URL(fileURLWithPath: path)) { success, resultPassword in if success && resultPassword.isEmpty == false { for i in 0 ..< (pdf?.pageCount ?? 0) { let page = pdf?.page(at: i).copy() as? CPDFPage self.listView.document.insertPageObject(page!, at: UInt(index)) insertIndexSet.insert(index) index += 1 } self.insertPages(insertIndexSet, pageAt: index-1) } } } else { for i in 0 ..< (pdf?.pageCount ?? 0) { // PDFPage *page = [[pdf pageAtIndex:i] copy]; var page = pdf?.page(at: i) self.listView.document.insertPageObject(page, at: UInt(index)) insertIndexSet.insert(index) index += 1 } self.insertPages(insertIndexSet, pageAt: index-1) } } return true } } } var pageIndex = 0 var pages: [CPDFPage] = [] for idx in rowIndexes { let page = self.listView.document.page(at: UInt(idx))?.copy() as? CPDFPage pages.append(page!) } var toPage: CPDFPage? if row < self.listView.document.pageCount { toPage = self.listView.document.page(at: UInt(row)) } var toPageIndex = 0 if (toPage != nil) { toPageIndex = Int(self.listView.document.index(for: toPage)) } else { toPageIndex = Int(self.listView.document.pageCount) } for i in 0 ..< pages.count { var page = pages[i] self.listView.document.insertPageObject(page, at: UInt(toPageIndex+i)) pageIndex += 1 self.listView.go(toPageIndex: toPageIndex+i, animated: false) } self.listView.layoutDocumentView() var deletepages: [CPDFPage] = [] for idx in rowIndexes { if idx < toPageIndex { if((idx+pageIndex) < self.listView.document.pageCount) { var page = self.listView.document.page(at: UInt(idx)) deletepages.append(page!) self.listView.document.removePage(at: IndexSet(integer: idx)) } } else { if((idx+pageIndex) < self.listView.document.pageCount) { let page = self.listView.document.page(at: UInt(idx+pageIndex)) deletepages.append(page!) self.listView.document.removePage(at: IndexSet(integer: idx+pageIndex)) } } } // [[[[self document] undoManager] prepareWithInvocationTarget:self] undoClick]; // [self.pageLabels setArray:[[self.pdfView document] pageLabels]]; self.listView.layoutDocumentView() self.resetThumbnails() } else if pasteboard.availableType(from: [.fileURL]) != nil && tableView.isEqual(to: self.thumbnailTableView) { if IAPProductsManager.default().isAvailableAllFunction() == false { KMPurchaseCompareWindowController.sharedInstance().showWindow(nil) return false } var fileNames = pasteboard.propertyList(forType: .fileURL) var insertIndexSet = IndexSet() var index = row // if (fileNames.count == 1) { if fileNames != nil { // var path = fileNames.first as? String ?? "" var path = fileNames as? String ?? "" let url = URL(string: path) let pathExtension = url?.pathExtension.lowercased() ?? "" if pathExtension == "pdf" { let pdf = CPDFDocument(url: url!) if let data = pdf?.isEncrypted, data { // KMDecryptWindowController *vc = [[KMDecryptWindowController alloc] init]; KMBaseWindowController.checkPassword(url: url!) { success, resultPassword in if success && resultPassword.isEmpty == false { pdf?.unlock(withPassword: resultPassword) for i in 0 ..< (pdf?.pageCount ?? 0) { // let page = pdf?.page(at: i).copy() as? CPDFPage let page = pdf?.page(at: i) self.listView.document.insertPageObject(page!, at: UInt(index)) insertIndexSet.insert(index) index += 1 } self.insertPages(insertIndexSet, pageAt: index-1) } } } else { for i in 0 ..< (pdf?.pageCount ?? 0) { // let page = pdf?.page(at: i).copy() as? CPDFPage let page = pdf?.page(at: i) self.listView.document.insertPageObject(page!, at: UInt(index)) insertIndexSet.insert(index) index += 1 } self.insertPages(insertIndexSet, pageAt: index-1) } } else if KMImageToPDFMethod.supportedImageTypes().contains(pathExtension) { let image = NSImage(contentsOfFile: url?.path ?? "") // PDFPage * page = [[[PDFPage alloc] initWithImage:image] autorelease]; // [pdfView.document insertPage:page atIndex:index]; _ = self.listView.document.km_insert(image: image!, at: UInt(index)) insertIndexSet.insert(index) self.insertPages(insertIndexSet, pageAt: index-1) } result = true } else { result = false } } return result == false } func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool { if tableView.isEqual(to: self.thumbnailTableView) { let idx = rowIndexes.first ?? NSNotFound if (idx != NSNotFound && self.listView.document.isLocked == false) { let page = self.listView.document.page(at: UInt(idx)) var fileExt: String? let tiffData = page?.PDFListViewTIFFData(for: page?.bounds(for: self.listView.displayBox) ?? .zero) if self.listView.document.allowsPrinting { // NSData *pdfData = [page dataRepresentation]; fileExt = "pdf" // filenames pboard.declareTypes([.pdf, .tiff, .fileURL, .filePromise, .localDraggedTypes, KPDFThumbnailDoucumentURLForDraggedTypes], owner: self) // let newDoc = CPDFDocument() // newDoc?.insertPageObject(page, at: 0) // let data = newDoc?.dataRepresentation() // [pboard setData:pdfData forType:NSPasteboardTypePDF]; pboard.setData(tiffData, forType: .pdf) // NSData *zNSIndexSetData = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes]; let zNSIndexSetData = NSKeyedArchiver.archivedData(withRootObject: rowIndexes) pboard.setData(zNSIndexSetData, forType: .localDraggedTypes) let documentURL = NSKeyedArchiver.archivedData(withRootObject: self.listView.document.documentURL?.absoluteString) // NSString *docmentName = [[[pdfView.document.documentURL path] lastPathComponent] stringByDeletingPathExtension]; var docmentName = self.listView.document.documentURL.deletingPathExtension().lastPathComponent // __block NSMutableString *pagesName = nil; var pagesName = "" if (rowIndexes.count > 1) { pagesName = " pages" } else { pagesName = " page" } let tFileName = String(format: "%@ %@", pagesName, self.fileNameWithSelectedPages(rowIndexes)) let pdf = CPDFDocument() for idx in rowIndexes { // var copyPage = self.listView.document.page(at: UInt(idx)).copy() as? CPDFPage var copyPage = self.listView.document.page(at: UInt(idx)) pdf?.insertPageObject(copyPage, at: pdf?.pageCount ?? 0) } let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true) var cachesDir = paths.first ?? "" docmentName = String(format: "%@%@", docmentName, tFileName) if (docmentName.count > 50) { docmentName = docmentName.substring(to: 50) } // cachesDir = [[cachesDir stringByAppendingPathComponent:docmentName] stringByAppendingPathExtension:@"pdf"]; cachesDir = "\(cachesDir)/\(docmentName).pdf" let success = pdf?.write(toFile: cachesDir) ?? false if (success) { // [pboard setPropertyList:@[cachesDir] forType:NSFilenamesPboardType]; pboard.setPropertyList([cachesDir], forType: .fileURL) } else{ // [pboard setPropertyList:@[@""] forType:NSFilenamesPboardType]; pboard.setPropertyList([], forType: .fileURL) } pboard.setData(documentURL, forType: KPDFThumbnailDoucumentURLForDraggedTypes) } else { fileExt = "tiff" pboard.declareTypes([.tiff, .filePromise], owner: self) } pboard.setData(tiffData, forType: .tiff) // kPasteboardTypeFileURLPromise pboard.setPropertyList([fileExt], forType: .filePromise) return true } } else if self.snapshotTableView.isEqual(to: tableView) { let idx = rowIndexes.first ?? NSNotFound if (idx != NSNotFound) { let snapshot = (self.snapshots.safe_element(for: idx) as? KMSnapshotModel)?.windowC if let data = snapshot?.thumbnailWithSize(0)?.tiffRepresentation { pboard.declareTypes([.tiff, .filePromise], owner: self) pboard.setData(data, forType: .tiff) pboard.setPropertyList(["tiff"], forType: .filePromise) return true } } } return false } // func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? { // var provider: NSFilePromiseProvider? // // 创建数据提供者 // let fileExtension = "pdf" // if #available(macOS 11.0, *) { // let typeIdentifier = UTType(filenameExtension: fileExtension) // provider = KMFilePromiseProvider(fileType: typeIdentifier!.identifier, delegate: self) // } else { // let typeIdentifier = // UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension as CFString, nil) // provider = KMFilePromiseProvider(fileType: typeIdentifier!.takeRetainedValue() as String, delegate: self) // } // // // 记录拖拽索引 // self.dragedIndexPaths.append(row) // do { // if let _url = self.listView.document?.documentURL { // let data = try NSKeyedArchiver.archivedData(withRootObject: row, requiringSecureCoding: false) // provider!.userInfo = [KMFilePromiseProvider.UserInfoKeys.urlKey: _url, // KMFilePromiseProvider.UserInfoKeys.indexPathKey: data] // } else { // let data = try NSKeyedArchiver.archivedData(withRootObject: row, requiringSecureCoding: false) // provider!.userInfo = [KMFilePromiseProvider.UserInfoKeys.indexPathKey: data] // } // } catch { // fatalError("failed to archive indexPath to pasteboard") // } // return provider // } func tableView(_ tableView: NSTableView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forRowIndexes rowIndexes: IndexSet) { // if self.isNeedMarkerLine { // self.markBeginIndexes = collectionView.selectionIndexes // } self.dragIn = true // 将拖拽的page插入临时路径(文档) var indexs = IndexSet() for indexpath in self.dragedIndexPaths { indexs.insert(indexpath) } // 清空临时数据 if let _path = self.dragTempFilePath, FileManager.default.fileExists(atPath: _path) { try?FileManager.default.removeItem(atPath: _path) } // 重置拖拽标识 self.dragFlag = false if (indexs.count > 0) { let document = CPDFDocument() document?.importPages(indexs, from: self.listView.document, at: 0) if let data = self.dragTempFloderPath, !FileManager.default.fileExists(atPath: data) { try?FileManager.default.createDirectory(atPath: data, withIntermediateDirectories: false) } if let data = self.dragTempFilePath, !FileManager.default.fileExists(atPath: data) { FileManager.default.createFile(atPath: data, contents: nil) } if let data = self.dragTempFilePath { document?.write(to: URL(fileURLWithPath: data)) } self.dragFilePath = self.dragTempFilePath } } func tableView(_ tableView: NSTableView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, operation: NSDragOperation) { if (!self.dragIn) { // var indexpaths = Set() // for indexpath in self.dragedIndexPaths { // indexpaths.insert(indexpath) // } // 清空数据 self.dragedIndexPaths.removeAll() // 刷新数据 // self.reloadData() // 重新选中数据 // self.selectionIndexPaths = indexpaths } else { Swift.debugPrint("拖入文件 或 本地拖拽") } self.dragIn = false } @objc dynamic func tableView(_ aTableView: NSTableView, deleteRowsWithIndexes rowIndexes: IndexSet) { if IAPProductsManager.default().isAvailableAllFunction() == false { KMPurchaseCompareWindowController.sharedInstance().showWindow(nil) return } if aTableView.isEqual(to: self.snapshotTableView) { var controllers: [KMSnapshotWindowController] = [] for i in rowIndexes { let model: KMSnapshotModel? = self.snapshots.safe_element(for: i) as? KMSnapshotModel if let winC = model?.windowC { controllers.append(winC) } } for c in controllers { c.close() } } else if aTableView.isEqual(to: self.thumbnailTableView) { for idx in rowIndexes { if idx >= self.listView.document.pageCount { return } if let page = self.listView.document.page(at: UInt(idx)) { for anno in page.annotations { page.removeAnnotation(anno) } (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).insertPage(page, pageAt: idx) self.listView.document.removePage(at: IndexSet(integer: idx)) } } self.listView.layoutDocumentView() // [pageLabels setArray:[[pdfView document] pageLabels]]; self.resetThumbnails() let idx = rowIndexes.first ?? -1 let index = min(idx, Int(self.listView.document.pageCount)-1) self.listView.go(toPageIndex: index, animated: false) } } func tableView(_ aTableView: NSTableView, copyRowsWithIndexes rowIndexes: IndexSet) { if IAPProductsManager.default().isAvailableAllFunction() == false { KMPurchaseCompareWindowController.sharedInstance().showWindow(nil) return } if aTableView.isEqual(to: self.thumbnailTableView) { self._copysPages.removeAll() if self.listView.document.isLocked == false { for idx in rowIndexes { if let page = self.listView.document.page(at: UInt(idx))?.copy() as? CPDFPage { self._copysPages.append(page) } } } } else if aTableView.isEqual(to: self.findTableView) { var string = "" for idx in rowIndexes { let match = self.searchResults[idx].selection string.append("* ") // [string appendFormat:NSLocalizedString(@"Page %@", @""), [match firstPageLabel]]; string = string.appendingFormat(KMLocalizedString("Page %@", ""), "\(match.safeFirstPage()?.pageIndex() ?? 0)") // [string appendFormat:@"", [[match contextString] string]]; string = string.appendingFormat(": %@\n", match.string() ?? "") } let pboard = NSPasteboard.general pboard.clearContents() pboard.writeObjects([string as NSPasteboardWriting]) } else if aTableView.isEqual(to: self.groupedFindTableView) { var string = "" for idx in rowIndexes { let result = self.groupSearchResults[idx] let matches = result.datas string.append("* ") string = string.appendingFormat(KMLocalizedString("Page %@", ""), "\(result.selectionPageIndex)") string.append(": ") string = string.appendingFormat(KMLocalizedString("%ld Results", ""), matches.count) // [string appendFormat:@":\n\t%@\n", [[matches valueForKeyPath:@"contextString.string"] componentsJoinedByString:@"\n\t"]]; var tmpString = "" for model in matches { tmpString.append("\(model.selection.string() ?? "")") } string = string.appendingFormat(":\n\t%@\n", tmpString) } let pboard = NSPasteboard.general pboard.clearContents() pboard.writeObjects([string as NSPasteboardWriting]) } } func tableView(_ tableView: NSTableView, namesOfPromisedFilesDroppedAtDestination dropDestination: URL, forDraggedRowsWith indexSet: IndexSet) -> [String] { if self.thumbnailTableView.isEqual(to: tableView) { var fileURLArray: [String] = [] if indexSet.count > 1 { var docmentName = "" var tFileName = String(format: "%@", self.fileNameWithSelectedPages(indexSet)) var pdf = CPDFDocument() for idx in indexSet { if idx != NSNotFound && self.listView.document.isLocked == false { let copyPage = self.listView.document.page(at: UInt(idx)).copy() as? CPDFPage pdf?.insertPageObject(copyPage, at: pdf?.pageCount ?? 0) } } var fileURL = dropDestination.appendingPathComponent(tFileName).appendingPathExtension("pdf").uniqueFileURL() docmentName = fileURL.path let success = pdf?.write(toFile: docmentName) ?? false if(success) { fileURLArray.append(fileURL.lastPathComponent) } } else { if let page = self.listView.document.page(at: UInt(indexSet.first ?? 0)) { var fileURL = dropDestination.appendingPathComponent(self.draggedFileName(for: page)) var pathExt = "" var fileData: Data? if let data = self.listView?.document?.allowsPrinting, data { pathExt = "pdf" // data = [page dataRepresentation]; } else { pathExt = "tiff" fileData = page.PDFListViewTIFFData(for: page.bounds(for: self.listView?.displayBox ?? .cropBox)) } fileURL = fileURL.appendingPathExtension(pathExt).uniqueFileURL() let success = try?fileData?.write(to: fileURL) if success != nil { fileURLArray.append(fileURL.lastPathComponent) } } } return fileURLArray } else if self.snapshotTableView.isEqual(to: tableView) { let idx = indexSet.first ?? NSNotFound if (idx != NSNotFound) { if let snapshot = (self.snapshots.safe_element(for: idx) as? KMSnapshotModel)?.windowC { if let page = self.listView?.document?.page(at: snapshot.pageIndex()) { var fileURL = dropDestination.appendingPathComponent(self.draggedFileName(for: page)).appendingPathExtension("tiff") fileURL = fileURL.uniqueFileURL() if ((try?snapshot.thumbnailWithSize(0)?.tiffRepresentation?.write(to: fileURL)) != nil) { return [fileURL.lastPathComponent] } } } } } return[] } func tableViewColumnDidResize(_ notification: Notification) { guard let column = notification.userInfo?["NSTableColumn"] as? NSTableColumn else { return } if column.identifier.rawValue == IMAGE_COLUMNID { if self.thumbnailTableView.isEqual(to: notification.object) { self.thumbnailTableView.noteHeightOfRows(withIndexesChanged: NSIndexSet(indexesIn: NSMakeRange(0, self.thumbnailTableView.numberOfRows)) as IndexSet) } else if self.snapshotTableView.isEqual(to: notification.object) { self.snapshotTableView.noteHeightOfRows(withIndexesChanged: NSIndexSet(indexesIn: NSMakeRange(0, self.snapshotTableView.numberOfRows)) as IndexSet) } } } func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) { if tableView.isEqual(to: self.groupedFindTableView) { // [leftSideController.groupedFindArrayController setSortDescriptors:[tv sortDescriptors]]; } } /* #pragma mark NSTableView delegate protocol - (void)tableView:(NSTableView *)tv shareRowsWithIndexes:(NSIndexSet *)rowIndexes { if ([tv isEqual:leftSideController.thumbnailTableView]) { NSUInteger idx = [rowIndexes firstIndex]; if (idx != NSNotFound) { PDFPage *page = [[pdfView document] pageAtIndex:idx]; NSString *fileName = [[[pdfView document] documentURL] lastPathComponent]; NSString *folderPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *filePath = [folderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"Untitled"]]; filePath = [filePath stringByAppendingPathExtension:[fileName pathExtension]]; [self fileWithPage:page atPath:filePath]; if (rint(NSAppKitVersionNumber) < NSAppKitVersionNumber10_8) { [KMMailHelper sendFileWithPaths:[NSArray arrayWithObject:filePath]]; } else { NSSharingService *service = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail]; [service performWithItems:[NSArray arrayWithObject:[NSURL fileURLWithPath:filePath]]]; } } } } - (void)fileWithPage:(PDFPage *)page atPath:(NSString *)filePath { NSData *data = [page dataRepresentation]; PDFDocument *document = [[PDFDocument alloc] initWithData:data]; [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; BOOL success = [document writeToURL:[NSURL fileURLWithPath:filePath]]; if (success) { NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; NSURL *url = [NSURL fileURLWithPath:filePath]; [workspace activateFileViewerSelectingURLs:[NSArray arrayWithObject:url]]; } [document release]; */ } // MARK: - NSFilePromiseProviderDelegate extension KMLeftSideViewController: NSFilePromiseProviderDelegate { func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, fileNameForType fileType: String) -> String { var fileName: String = "Untitle" if let _string = self.listView?.document?.documentURL.deletingPathExtension().lastPathComponent { fileName = _string } fileName.append(" pages") var indexs = IndexSet() for indexpath in self.dragedIndexPaths { indexs.insert(indexpath) } fileName.append(" ") fileName.append(KMPageRangeTools.newParseSelectedIndexs(selectedIndex: indexs.sorted())) fileName.append(".pdf") return fileName } func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, writePromiseTo url: URL, completionHandler: @escaping (Error?) -> Void) { do { /** Copy the file to the location provided to you. You always do a copy, not a move. It's important you call the completion handler. */ if let _urlString = self.dragFilePath, !self.dragFlag { self.dragFlag = true // if let should = self.delegate?.thumbnailView?(thumbanView: self, shouldPasteboardWriterForItemAt: IndexPath(item: 0, section: 0)), !should { // completionHandler(nil) // return // } try FileManager.default.copyItem(at: URL(fileURLWithPath: _urlString), to: url) } completionHandler(nil) } catch let error { OperationQueue.main.addOperation { self.presentError(error, modalFor: self.view.window!, delegate: nil, didPresent: nil, contextInfo: nil) } completionHandler(error) } } /** You should provide a non main operation queue (e.g. one you create) via this function. This way you don't stall the main thread while writing the promise file. */ func operationQueue(for filePromiseProvider: NSFilePromiseProvider) -> OperationQueue { return self.filePromiseQueue } } extension KMLeftSideViewController { var dragTempFloderPath: String? { get { return NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("KMPDFThumbnailView_Drag_Temp") } } var dragTempFilePath: String? { get { return self.dragTempFloderPath?.stringByAppendingPathComponent("drag_tmp.pdf") } } } // MARK: - NSOutlineViewDelegate, NSOutlineViewDataSource /* func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { if item is KMBOTAAnnotationSection { let section = item as? KMBOTAAnnotationSection return section!.annotations?[index] as Any } else if item is KMBOTAAnnotationItem { return item as Any } else { var tempArray: [KMBOTAAnnotationSection] = [] for temp in self.data { if temp.annotations?.count != 0 { tempArray.append(temp) } } let section = tempArray[index] return section as Any } } func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { if item is KMBOTAAnnotationItem { let cell : KMAnnotationOutlineCellView = KMAnnotationOutlineCellView.init() cell.delegate = self cell.model = item as? KMBOTAAnnotationItem return cell } else if item is KMBOTAAnnotationSection { let section = item as? KMBOTAAnnotationSection let cell : KMAnnotationOutlineSectionView = KMAnnotationOutlineSectionView.init() cell.model = section return cell } return NSTableCellView() } func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? { let rowView = KMAnnotationOutlineRowView() if item is KMBOTAAnnotationItem { rowView.model = (item as? KMBOTAAnnotationItem) } else if item is KMBOTAAnnotationSection { rowView.section = (item as? KMBOTAAnnotationSection) } rowView.mouseDownAction = { [unowned self] view, event in if rowView.section != nil { let expanded = outlineView.isItemExpanded(outlineView.item(atRow: outlineView.selectedRow)) if expanded { outlineView.collapseItem(outlineView.item(atRow: outlineView.selectedRow), collapseChildren: true) outlineView.reloadItem(outlineView.item(atRow: outlineView.selectedRow)) } else { outlineView.expandItem(outlineView.item(atRow: outlineView.selectedRow), expandChildren: true) outlineView.reloadItem(outlineView.item(atRow: outlineView.selectedRow)) } } else if rowView.model != nil { self.didSelectItem(view: rowView, event: event) } } rowView.rightMouseDownAction = { [unowned self] view, event in if rowView.section != nil { } else if rowView.model != nil { if !KMOCToolClass.arrayContains(array: self.selectItems, annotation: item) || self.selectItems.count == 1 { self.selectItem(item: item as! KMBOTAAnnotationItem) } DispatchQueue.main.async { self.delegate?.annotationOutlineView(self, rightMouseDownDidSelectView: view, evnet: event) } } } rowView.hoverCallback = { [unowned self] (mouseEntered, mouseBox) in self.outlineView.enumerateAvailableRowViews { view, row in if view is KMAnnotationOutlineRowView { (view as? KMAnnotationOutlineRowView)?.model?.hover = false (view as? KMAnnotationOutlineRowView)?.reloadData() } } if mouseEntered { rowView.model?.hover = true } else { rowView.model?.hover = false } } return rowView } func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat { if item is KMBOTAAnnotationItem { return KMBOTAAnnotationTool.fetchCellHeight(annotation: (item as? KMBOTAAnnotationItem)!.annotation!, maxSize: CGSize(width: self.maxWidth - 16, height: 1000)) } else if item is KMBOTAAnnotationSection { return 40 } else { return 30 } } func outlineViewSelectionDidChange(_ notification: Notification) { if self.outlineView.selectedRow == -1 { self.cancelSelect() } } func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { if item is KMBOTAAnnotationItem { return false } else if item is KMBOTAAnnotationSection { let section = item as? KMBOTAAnnotationSection return section!.annotations?.count ?? 0 > 0 } return false } func outlineView(_ outlineView: NSOutlineView, shouldExpandItem item: Any) -> Bool { if let item = item as? KMBOTAAnnotationSection { if !item.isItemExpanded { item.isItemExpanded = true outlineView.animator().expandItem(item, expandChildren: true) return false } } return true } func outlineView(_ outlineView: NSOutlineView, shouldCollapseItem item: Any) -> Bool { if let item = item as? KMBOTAAnnotationSection { if item.isItemExpanded { item.isItemExpanded = false outlineView.animator().collapseItem(item, collapseChildren: true) return false } } return true } */ extension KMLeftSideViewController: NSOutlineViewDelegate, NSOutlineViewDataSource { func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { if outlineView.isEqual(to: self.tocOutlineView) { let isLocked = self.listView.document?.isLocked ?? true if isLocked { // 文档不存在 或 已加锁 return 0 } if item == nil { // 第一层 // 获取根 guard let outline = self.listView.document.outlineRoot() else { let view = self.tocOutlineView.enclosingScrollView! let emptyVcSize = self.leftSideEmptyVC.emptyOutlineView.frame.size // self.leftSideEmptyVC.emptyOutlineView.frame = NSMakeRect((view.frame.size.width-emptyVcSize.width)/2.0,(view.frame.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height) self.leftSideEmptyVC.emptyOutlineView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] self.tocOutlineView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.emptyOutlineView) self.leftSideEmptyVC.deleteOutlineBtn.isEnabled = false return 0 } if outline.numberOfChildren == 0 { // return 0 } if self.isSearchOutlineMode { // 是否为搜索模块 if self._hasContainString(self.outlineSearchField.stringValue, rootOutline: outline) { self.showSearchOutlineBlankState(false) } else { self.showSearchOutlineBlankState(true) return 0 } } else { if outline.numberOfChildren > 0 { // 有数据 self.leftSideEmptyVC.emptyOutlineView.removeFromSuperview() self.leftSideEmptyVC.deleteOutlineBtn.isEnabled = true } else { // 没有数据 let view = self.tocOutlineView.enclosingScrollView! let emptyVcSize = self.leftSideEmptyVC.emptyOutlineView.frame.size // self.leftSideEmptyVC.emptyOutlineView.frame = NSMakeRect((view.frame.size.width-emptyVcSize.width)/2.0,(view.frame.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height) self.leftSideEmptyVC.emptyOutlineView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] self.tocOutlineView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.emptyOutlineView) self.leftSideEmptyVC.deleteOutlineBtn.isEnabled = false return 0 } } // 搜索按钮 if outline.numberOfChildren > 0 { self.outlineSearchButton.isEnabled = true } else { self.outlineSearchButton.isEnabled = false } if (self.isSearchOutlineMode) { var num = 0 for i in 0 ..< outline.numberOfChildren { if let child = outline.child(at: i) { if self._hasContainString(self.outlineSearchField.stringValue, rootOutline: child) { num += 1 } } } return num } else { let array = self.listView.document.bookmarks() ?? [CPDFBookmark]() var bookmarkNum = 0 if array.isEmpty == false { bookmarkNum = 1 } return Int(outline.numberOfChildren) + bookmarkNum } } else { // 第二层 + if self.isSearchOutlineMode { if let data = item as? String, data == "Bookmarks" { // 书签group return 0 } else if item is CPDFOutline { // 大纲 let child = item as! CPDFOutline if child.numberOfChildren == 0 { return 0 } var num = 0 for i in 0 ..< child.numberOfChildren { if let _child = child.child(at: i) { if self._hasContainString(self.outlineSearchField.stringValue, rootOutline: _child) { num += 1 } } } return num } else if item is CPDFBookmark { // 书签 return 0 } } else { if let data = item as? String, data == "Bookmarks" { // 书签group return (self.listView.document?.bookmarks().count) ?? 0 } else if item is CPDFOutline { // 大纲 return Int((item as? CPDFOutline)?.numberOfChildren ?? 0) } else if item is CPDFBookmark { // 书签 return 0 } } } } else if outlineView.isEqual(to: self.noteOutlineView) { if self.noteSearchMode { return self.noteSearchArray.count } var count = 0 for section in self.annotations { if section.annotations?.count != 0 { count += section.annotations!.count } } // if (item == nil){ // NSInteger count = [[rightSideController.noteArrayController arrangedObjects] count]; if (count < 1) { // if (notes.count < 1) { self.noteSearchButton.isEnabled = false self.noteFilterButton.isEnabled = false // } self.noteOutlineView.usesAlternatingRowBackgroundColors = false let view = self.noteOutlineView.enclosingScrollView! var emptyVcSize = self.leftSideEmptyVC.emptyAnnotationView.frame.size self.leftSideEmptyVC.emptyAnnotationView.frame = NSMakeRect((view.frame.size.width-emptyVcSize.width)/2.0,(view.frame.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height) self.leftSideEmptyVC.emptyAnnotationView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] self.noteOutlineView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.emptyAnnotationView) self.leftSideEmptyVC.exportAnnotationBtn.isEnabled = false self.leftSideEmptyVC.deleteAnnotationBtn.isEnabled = false // if (self.leftView.segmentedControl.selectedSegment == KMSelectedSegmentType.annotation.rawValue) { self.noteHeaderView.isHidden = true self.toolButtonBoxLayoutConstraint.constant = 40.0 } } else { self.noteSearchButton.isEnabled = true self.noteFilterButton.isEnabled = true self.noteOutlineView.usesAlternatingRowBackgroundColors = false self.leftSideEmptyVC.emptyAnnotationView.removeFromSuperview() self.leftSideEmptyVC.exportAnnotationBtn.isEnabled = true self.leftSideEmptyVC.deleteAnnotationBtn.isEnabled = true // if (self.leftView.segmentedControl.selectedSegment == KMSelectedSegmentType.annotation.rawValue) { self.noteHeaderView.isHidden = false self.toolButtonBoxLayoutConstraint.constant = 64.0 } } return count // } else { // return [item hasNoteText]; // } } return 0 } func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { if outlineView.isEqual(to: self.tocOutlineView) { let isLocked = self.listView.document?.isLocked ?? true if isLocked { // 文档不存在 或 已加锁 return "" } let array = self.listView.document.bookmarks() ?? [CPDFBookmark]() var bookmarkNum = 0 if array.isEmpty == false { bookmarkNum = 1 } if item == nil { if self.isSearchOutlineMode { guard let outline = self.listView.document.outlineRoot() else { return "" } if outline.numberOfChildren == 0 { return "" } var array: [CPDFOutline] = [] for i in 0 ..< outline.numberOfChildren { if let child = outline.child(at: i) { if self._hasContainString(self.outlineSearchField.stringValue, rootOutline: child) { array.append(child) } } } if index < array.count { return array[index] } return "" } else { if index == 0 && bookmarkNum == 1 { return "Bookmarks" } else { var _index = bookmarkNum == 1 ? index-1 : index let outline = self.listView.document.outlineRoot() var obj = outline?.child(at: UInt(_index)) return obj as Any } } } else { if let data = item as? String, data == "Bookmarks" { return array[index] } else if item is CPDFOutline { if self.isSearchOutlineMode == false { return (item as! CPDFOutline).child(at: UInt(index)) } let child = item as! CPDFOutline // if child.numberOfChildren == 0 { // return 0 // } var array: [CPDFOutline] = [] for i in 0 ..< child.numberOfChildren { if let _child = child.child(at: i) { if self._hasContainString(self.outlineSearchField.stringValue, rootOutline: _child) { array.append(_child) } } } return array[index] } else if item is CPDFBookmark { return "" } } } else if outlineView.isEqual(to: self.noteOutlineView) { // if (item == nil) // { // return [[rightSideController.noteArrayController arrangedObjects] objectAtIndex:anIndex]; // // return [self.notes objectAtIndex:anIndex]; // } // else // return [item noteText]; if self.noteSearchMode { return self.noteSearchArray[index] } var tempArray: [KMBOTAAnnotationItem] = [] for secion in self.annotations { if secion.annotations?.count != 0 { for _item in secion.annotations! { tempArray.append(_item) } } } return tempArray[index] as Any } return item } func outlineView(_ outlineView: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any? { if outlineView.isEqual(to: self.tocOutlineView) { // let tcID = tableColumn?.identifier.rawValue ?? "" // var ol = item as? CPDFOutline // if(tcID == LABEL_COLUMNID) { // if (self.isSearchOutlineMode) { //// NSString *roughString = [[ol label] stringByCollapsingWhitespaceAndNewlinesAndRemovingSurroundingWhitespaceAndNewlines]? : @""; //// NSArray *arr = [self allRangeOfRoughString:roughString searchString:self.leftSideController.outlineSearchField.stringValue]; //// NSMutableAttributedString *attributeString = [[[NSMutableAttributedString alloc] initWithString:roughString] autorelease]; //// for (NSUInteger i = 0; i NSView? { if outlineView.isEqual(to: self.tocOutlineView) { let cell = outlineView.makeView(withIdentifier: KMTocTableCellView.km_identifier, owner: self) as! KMTocTableCellView let tcID = tableColumn?.identifier.rawValue var title = "" var pageLabel = "" if let data = item as? String, data == "Bookmarks" { title = NSLocalizedString("Bookmarks", comment: "") } else if let ol = item as? CPDFOutline { title = ol.label if ol.actionType == .page { pageLabel = "\((ol.destination?.pageIndex ?? 0) + 1)" } } else if let bk = item as? CPDFBookmark { title = bk.label pageLabel = "\(bk.pageIndex + 1)" } if tcID == kLabelColumnId.rawValue { if (self.isSearchOutlineMode) { // NSString *roughString = [[ol label] stringByCollapsingWhitespaceAndNewlinesAndRemovingSurroundingWhitespaceAndNewlines]; let roughString = title let attributeString = NSMutableAttributedString(string: roughString) let searchString = self.outlineSearchField.stringValue var _roughString = roughString var _searchString = searchString if self.outlineIgnoreCaseFlag { _roughString = _roughString.lowercased() _searchString = _searchString.lowercased() } let ranges = _roughString.ranges(of: _searchString) for range in ranges.nsRnage { attributeString.addAttribute(.font, value: NSFont.boldSystemFont(ofSize: 13), range: range) } cell.tocLabel.attributedStringValue = attributeString } else { cell.tocLabel.stringValue = title } cell.pageLabel.stringValue = pageLabel } // else if([tcID isEqualToString:PAGE_COLUMNID]) { // cell.pageLabel.stringValue = [ol pageLabel]; // } return cell } else if outlineView.isEqual(to: self.noteOutlineView) { let model = item as? KMBOTAAnnotationItem var note: CPDFAnnotation! if self.noteSearchMode { note = item as! CPDFAnnotation } else { note = (item as! KMBOTAAnnotationItem).annotation! } let cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMNoteTableViewCell"), owner: self) as! KMNoteTableViewCell cell.cellNote = note let noteColor = note.color var noteType = note.type ?? "" var noteString = KMBOTAAnnotationTool.fetchContentLabelString(annotation: note) // NSString *pageString = [[note page] displayLabel] ? : @""; var pageString = "\((note.page?.pageIndex() ?? 0) + 1)" let formatter = DateFormatter() formatter.dateFormat = "hh:mm" var dateString = "" if let date = note.modificationDate() { dateString = formatter.string(from: date) } var authorString = note.userName() ?? "" // NSString *noteTextString = [note noteText].string ? : @""; var noteTextString = note.string() ?? "" // // if([noteType isEqualToString:@"Redact"]) { // noteString = @"Redact"; // } // let timeKey = self.noteTypeDict[Self.Key.noteFilterTime] as? Bool if timeKey == nil || timeKey == false { // if (date != nil) { cell.timeLabel.stringValue = dateString // // } else { // cell.timeLabel.stringValue = "" // } cell.timeLabel.isHidden = false } else { cell.timeLabel.isHidden = true } let pageKey = self.noteTypeDict[Self.Key.noteFilterPage] as? Bool if pageKey == nil || pageKey == false { let labelsize = cell.pageLabel.stringValue.boundingRect(with: CGSizeMake(CGFloat(MAXFLOAT),CGRectGetHeight(cell.pageLabel.bounds)), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font : NSFont.systemFont(ofSize: 12)]) cell.pageLabelWidthConstraint.constant = labelsize.size.width + 5 cell.pageLabel.stringValue = pageString cell.pageLabel.isHidden = false } else { cell.pageLabel.isHidden = true } let autherKey = self.noteTypeDict[Self.Key.noteFilterAuther] as? Bool if autherKey == nil || autherKey == false { cell.autherLabel.stringValue = authorString cell.autherLabel.isHidden = false } else { cell.autherLabel.isHidden = true } let imageView = KMNoteTypeImageView() if note.isKind(of: CPDFStampAnnotation.self) { let annotation_stamp = note as! CPDFStampAnnotation if (annotation_stamp.stampImage() != nil) { noteType = SKNStampString } } else if note.isKind(of: CPDFLineAnnotation.self) { let annotation_line = note as! CPDFLineAnnotation if annotation_line.startLineStyle == .none && annotation_line.endLineStyle == .none { noteType = SKNLine_NoneString } else if annotation_line.startLineStyle == .none && annotation_line.endLineStyle == .openArrow { noteType = SKNLine_OpenArrowString } else { noteType = SKNLine_NoneString } } else if note.isKind(of: CPDFButtonWidgetAnnotation.self) { let buttonWidget = note as! CPDFButtonWidgetAnnotation if buttonWidget.controlType() == .radioButtonControl { noteType = KMAnnotationFormRadioButtonKey } else if buttonWidget.controlType() == .checkBoxControl { noteType = KMAnnotationFormCheckBoxKey } else if buttonWidget.controlType() == .pushButtonControl { noteType = KMAnnotationFormActionButtonKey } } else if note.isKind(of: CPDFTextWidgetAnnotation.self) { noteType = KMAnnotationFormTextFieldKey } else if note.isKind(of: CPDFChoiceWidgetAnnotation.self) { let choiceWidget = note as! CPDFChoiceWidgetAnnotation if choiceWidget.isListChoice { noteType = KMAnnotationFormListMenuKey } else { noteType = KMAnnotationFormComboBoxKey } } else if note.isKind(of: KMTableAnnotation.self) { noteType = "Ink_Table" } else if note.isKind(of: KMSelfSignAnnotation.self) { let selfSignNote = note as! KMSelfSignAnnotation if selfSignNote.annotationType == .signFalse { noteType = "KMSelfSignTypeFalseActionButtonKey" } else if (selfSignNote.annotationType == .signature) { noteType = "KMSelfSignTypeTureActionButtonKey" } else if (selfSignNote.annotationType == .signCircle) { noteType = "KMSelfSignTypeCircleActionButtonKey" } else if (selfSignNote.annotationType == .signLine) { noteType = "KMSelfSignTypeLineActionButtonKey" } else if (selfSignNote.annotationType == .signDot) { noteType = "KMSelfSignTypeDotActionButtonKey" } else if (selfSignNote.annotationType == .signText) { noteType = "KMSelfSignTypeTextActionButtonKey" } } else if note.isKind(of: CPDFSquareAnnotation.self) { noteType = SKNSquareString } else if note.isKind(of: CPDFTextAnnotation.self) { noteType = SKNNoteString } else if note.isKind(of: CPDFFreeTextAnnotation.self) { noteType = SKNFreeTextString } else if note.isKind(of: CPDFCircleAnnotation.self) { noteType = SKNCircleString } else if note.isKind(of: CPDFSignatureAnnotation.self) { noteType = SKNSignatureString } else if note.isKind(of: CPDFInkAnnotation.self) { noteType = SKNInkString } else if note.isKind(of: CPDFMarkupAnnotation.self) { let anno = note as! CPDFMarkupAnnotation if anno.markupType() == .highlight { noteType = SKNHighlightString } else if anno.markupType() == .underline { noteType = SKNUnderlineString } else if anno.markupType() == .strikeOut { noteType = SKNStrikeOutString } } cell.typeImageView.image = imageView.noteTypeImage(withType: noteType, color: noteColor ?? .red) cell.typeImageView.isHidden = false if note.isKind(of: CPDFMarkupAnnotation.self) { cell.isFold = true } cell.noteContentBox.isHidden = true cell.noteImageView.isHidden = true cell.foldButton.isHidden = true cell.annotationContentLabel.isHidden = false cell.noteContentLabel.stringValue = noteString cell.contentView.isHidden = false cell.contentViewHidden(false) if note.isKind(of: CPDFMarkupAnnotation.self) { let markup = note as! CPDFMarkupAnnotation var contentString = KMBOTAAnnotationTool.fetchText(text: markup.markupContent() ?? "") contentString = contentString.replacingOccurrences(of: "\r", with: "") contentString = contentString.replacingOccurrences(of: "\n", with: "") cell.noteContentLabel.stringValue = contentString if(contentString.isEmpty == false) { cell.foldButton.isHidden = false } var attributeStr = NSMutableAttributedString(string: contentString) if (markup.markupType() == .highlight) { attributeStr.addAttribute(.backgroundColor, value: noteColor as Any, range: NSMakeRange(0, contentString.count)) } else if (markup.markupType() == .strikeOut) { attributeStr.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, contentString.count)) attributeStr.addAttribute(.strikethroughColor, value: noteColor as Any, range: NSMakeRange(0, contentString.count)) } else if (markup.markupType() == .underline) { attributeStr.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, contentString.count)) attributeStr.addAttribute(.underlineColor, value: noteColor as Any, range: NSMakeRange(0, contentString.count)) } cell.annotationContentLabel.attributedStringValue = attributeStr if self.foldType == .unfold { // if let data = model?.foldType, data == .unfold { // 全部展开 cell.isFold = false } else if self.foldType == .fold { // else if let data = model?.foldType, data == .fold { // 全部折叠 cell.isFold = true } else { // 混合 if self.allFoldNotes.isEmpty == false && self.allFoldNotes.contains(note) { cell.isFold = false } else { cell.isFold = true } } // noteString.isEmpty == false && if (contentString.isEmpty == false) { cell.contentViewHidden(false) } else { cell.contentViewHidden(true) } } else if note.isKind(of: CPDFLineAnnotation.self) || noteType == SKNSquareString || noteType == SKNCircleString || noteType == SKNInkString { cell.annotationContentLabel.stringValue = noteString if (noteString.isEmpty == false) { cell.contentViewHidden(false) } else { cell.contentViewHidden(true) } } else if note.isKind(of: CPDFStampAnnotation.self) { if note.isKind(of: KMSelfSignAnnotation.self) { let newAnnotation = note as! KMSelfSignAnnotation let type = newAnnotation.annotationType var returnString = "" if (type == .signFalse) { returnString = KMLocalizedString("X", nil) } else if (type == .signature) { returnString = KMLocalizedString("Check mark", nil) } else if (type == .signCircle) { returnString = KMLocalizedString("Circle", nil) } else if (type == .signLine) { returnString = KMLocalizedString("Line", nil) } else if (type == .signDot) { returnString = KMLocalizedString("Dot", nil) } else if (type == .signText) { returnString = KMLocalizedString("Text", nil) } cell.annotationContentLabel.stringValue = returnString } else { cell.annotationContentLabel.isHidden = true cell.noteImageView.isHidden = false let anno = note as! CPDFStampAnnotation cell.noteImageView.image = anno.stampImage() } } else if note.isKind(of: CPDFTextAnnotation.self) { cell.foldButton.isHidden = false if noteString.isEmpty { cell.annotationContentLabel.stringValue = noteTextString }else{ cell.annotationContentLabel.stringValue = noteString cell.noteContentLabel.stringValue = noteTextString } if (self.foldType == .unfold) { cell.isFold = false } else if (self.foldType == .fold) { cell.isFold = true } else { if self.allFoldNotes.isEmpty == false && self.allFoldNotes.contains(note) { cell.isFold = false } else { cell.isFold = true } } if (noteString.isEmpty == false || noteTextString.isEmpty == false) { cell.contentView.isHidden = false } else { cell.contentView.isHidden = true cell.contentViewHidden(true) } // noteString.isEmpty == false && if (noteTextString.isEmpty == false) { cell.foldButton.isHidden = false }else{ cell.foldButton.isHidden = true } } else { cell.annotationContentLabel.stringValue = noteString cell.imageViewHeightConstraint.constant = cell.contentView.frame.size.height if (noteString.isEmpty == false) { cell.contentViewHidden(false) } else { cell.contentViewHidden(true) } } cell.autherLayoutConstraint.constant = cell.autherLabel.isHidden ? -(cell.autherLabel.bounds.size.width) + 10.0 : 10.0 cell.typeImageViewLayoutConstraint.constant = cell.typeImageView.isHidden ? -(cell.typeImageView.bounds.size.width) : 0.0 cell.contentBoxLayoutConstraint.constant = cell.noteContentBox.isHidden ? -(cell.noteContentBox.bounds.size.height+8.0) : 8.0 if note.isKind(of: CPDFStampAnnotation.self) && note.isKind(of: KMSelfSignAnnotation.self) == false { } else { if note.isKind(of: CPDFMarkupAnnotation.self) { if (!cell.isFold) { self.allFoldNotes.append(note) } else { cell.imageViewHeightConstraint.constant = 18.0 + 8 } } else { cell.imageViewHeightConstraint.constant = 18.0 + 8 } } cell.isUnFoldNote = { [unowned self] cellNote, isUnfold in model?.foldType = isUnfold ? .unfold : .fold // let COLUMN_INDENTATION: CGFloat = 16 // if let _cell = tableColumn?.dataCell as? NSCell { // _cell.objectValue = cell.annotationContentLabel.attributedStringValue // let bound = NSMakeRect(0.0, 0.0, fmax(10.0, NSWidth(outlineView.frame) - COLUMN_INDENTATION - outlineView.indentationPerLevel-40), CGFLOAT_MAX) // let height = _cell.cellSize(forBounds: bound).height // KMPrint(height) // } if cellNote is CPDFMarkupAnnotation { // let content = cell.annotationContentLabel.attributedStringValue // let bound = content.string.boundingRect(with: NSMakeSize(150, CGFLOAT_MAX), options: [.usesFontLeading, .usesLineFragmentOrigin], attributes: nil) // KMPrint(bound) // 154 34 273 // 273 - 188 = 85 noteContentHeightConstraint maltlineLabelLayoutConstraint model?.foldH = isUnfold ? 30 : (cell.noteContentHeightConstraint.constant + cell.maltlineLabelLayoutConstraint.constant + 85) } if (isUnfold) { if (self.allFoldNotes.contains(note)) { self.allFoldNotes.removeObject(note) } if (self.allFoldNotes.count == 0) { self.foldType = .fold } else { self.foldType = .none } } else { if self.allFoldNotes.contains(note) == false { self.allFoldNotes.append(note) } // if (rightSideController.allFoldNotes.count == rightSideController.canFoldNotes.count) { self.foldType = .unfold // } else { // self.foldType = KMFoldAllAnnotationType_None; // } } } return cell } return nil } func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat { if outlineView.isEqual(self.noteOutlineView) { if item is KMBOTAAnnotationItem { let model = item as! KMBOTAAnnotationItem if model.foldType == .fold { return model.foldH } return KMBOTAAnnotationTool.fetchCellHeight(annotation: (item as? KMBOTAAnnotationItem)!.annotation!, maxSize: CGSize(width: 260+40 - 16, height: 1000)) } return 30 } else if outlineView.isEqual(self.tocOutlineView) { if item is CPDFOutline { let tempItem = item as! CPDFOutline let string: NSString = tempItem.label as NSString let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineHeightMultiple = 1.32 paragraphStyle.alignment = .left let attributes = [NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font : NSFont.SFProTextRegularFont(14.0)] let size = string.boundingRect(with: CGSizeMake(outlineView.frame.size.width - 30, 200), options: NSString.DrawingOptions(rawValue: 3), attributes: attributes) return max(40, size.height + 16) } return 40 } return outlineView.rowHeight } func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { if outlineView.isEqual(self.tocOutlineView) { // var _item: CPDFOutline? // if item == nil && self.listView.document.isLocked == false { // _item = self.listView.document.outlineRoot() // } if let data = item as? String, data == "Bookmarks" { return true } else if item is CPDFOutline { return ((item as! CPDFOutline).numberOfChildren ?? 0) != 0 } else if item is CPDFBookmark { return false } if (self.isSearchOutlineMode) { // return [self subOutLineContainString:self.leftSideController.outlineSearchField.stringValue rootOutline:(PDFOutline *)item]; } else { // return ((_item?.numberOfChildren ?? 0) != 0) } } else if outlineView.isEqual(to: self.noteOutlineView) { // return [item hasNoteText]; return false } return false } func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? { if outlineView.isEqual(self.tocOutlineView) { let itemView = KMBotaTableRowView() return itemView } else if outlineView.isEqual(self.noteOutlineView) { let itemView = KMBotaTableRowView() return itemView; } return nil } func outlineViewSelectionDidChange(_ notification: Notification) { if self.tocOutlineView.isEqual(to: notification.object) { if self.dragIn { return } if self.updatingOutlineSelection == false { self.updatingOutlineSelection = true self.goToSelectedOutlineItem(nil) self.updatingOutlineSelection = false } } } func outlineViewItemDidExpand(_ notification: Notification) { if self.tocOutlineView.isEqual(to: notification.object) { self.updateOutlineSelection() } } func outlineViewItemDidCollapse(_ notification: Notification) { if self.tocOutlineView.isEqual(to: notification.object) { self.updateOutlineSelection() } } func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { var dragOp = NSDragOperation(rawValue: 0) if outlineView.isEqual(to: self.noteOutlineView) { let pboard = info.draggingPasteboard if pboard.canReadObject(forClasses: [NSColor.self], options: [:]) && index == NSOutlineViewDropOnItemIndex { if let note = item as? CPDFAnnotation, note.type != nil { dragOp = .every } } } else if outlineView.isEqual(to: self.tocOutlineView) { if (index == -1) { dragOp = NSDragOperation(rawValue: 0) } else { dragOp = .move } } return dragOp } func outlineView(_ outlineView: NSOutlineView, writeItems items: [Any], to pasteboard: NSPasteboard) -> Bool { if outlineView.isEqual(to: self.tocOutlineView) { if (self.tocOutlineView.selectedRowIndexes.count > 1) { return false } let tIndex = IndexSet(integer: self.tocOutlineView.clickedRow) self.tocOutlineView.deselectRow(tIndex.first ?? 0) self._dragPDFOutline = items.first as? CPDFOutline let set = IndexSet(integer: 0) let zNSIndexSetData = NSKeyedArchiver.archivedData(withRootObject: set) // [pasteboard declareTypes:[NSArray arrayWithObject:kKMPDFViewOutlineDragDataType] owner:self]; pasteboard.declareTypes([.localDraggedTypes], owner: self) pasteboard.setData(zNSIndexSetData, forType: .localDraggedTypes) return true } return false } func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool { if outlineView.isEqual(to: self.noteOutlineView) { let pboard = info.draggingPasteboard if pboard.canReadObject(forClasses: [NSColor.self], options: [:]) { // [item setColor:[NSColor colorFromPasteboard:pboard] alternate:isAlt updateDefaults:isShift]; let isShift = NSEvent.modifierFlags.contains(.shift) let isAlt = NSEvent.modifierFlags.contains(.option) guard let note = item as? CPDFAnnotation else { NSSound.beep() return false } if let color = NSColor(from: pboard) { note.setColor(color, alternate: isAlt, updateDefaults: isShift) return true } return false } } else if outlineView.isEqual(to: self.tocOutlineView) { if (index < 0) { return false } // guard let outline = item as? CPDFOutline else { // return false // } let outline = item as? CPDFOutline guard let dragPDFOL = self._dragPDFOutline else { return false } //root,drag item to root if ((outline?.parent == nil)) { //fetch root var root = dragPDFOL while (root.parent != nil) { root = root.parent } if dragPDFOL.parent.isEqual(to: root) { if dragPDFOL.index > index { self.dragPDFOutline(self._dragPDFOutline, toIndex: index, newParentOutline: root) } else { self.dragPDFOutline(self._dragPDFOutline, toIndex: index-1, newParentOutline: root) } } else { self.dragPDFOutline(self._dragPDFOutline, toIndex: index, newParentOutline: root) } } else { //在同一个层级内移动 if dragPDFOL.parent.isEqual(to: item) { if (dragPDFOL.index > 0) { if dragPDFOL.index > index { self.dragPDFOutline(self._dragPDFOutline, toIndex: index, newParentOutline: outline) }else{ self.dragPDFOutline(self._dragPDFOutline, toIndex: index-1, newParentOutline: outline) } } else { return false } } else { var tOutlline: CPDFOutline? = outline var isContains = false while (tOutlline != nil) { if tOutlline!.isEqual(to: self._dragPDFOutline) { isContains = true break } tOutlline = tOutlline?.parent } if (!isContains) { self.dragPDFOutline(self._dragPDFOutline, toIndex: index, newParentOutline: outline) } } } return true } return false } func outlineView(_ outlineView: NSOutlineView, willDisplayOutlineCell cell: Any, for tableColumn: NSTableColumn?, item: Any) { if outlineView.isEqual(to: self.tocOutlineView) { if outlineView.selectionHighlightStyle == .regular && outlineView.isRowSelected(outlineView.row(forItem: item)) { (cell as? NSCell)?.backgroundStyle = .lowered } } } func outlineView(_ outlineView: NSOutlineView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, byItem item: Any?) { if outlineView.isEqual(to: self.noteOutlineView) { // PDFAnnotation *note = item; var note = item as? CPDFAnnotation if note == nil && note is KMBOTAAnnotationItem { note = (note as? KMBOTAAnnotationItem)?.annotation } if let data = note?.type, data.isEmpty == false { if tableColumn?.identifier.rawValue == self.noteColumnId.rawValue { let string1 = (object as? String) ?? "" let string2 = note?.string() ?? "" if string1 != string2 { note?.setString(string1) } } else if tableColumn?.identifier.rawValue == self.authorColumnId.rawValue { let string1 = (object as? String) ?? "" let string2 = note?.userName() ?? "" if string1 != string2 { note?.setUserName(string1) } } } } } func outlineView(_ outlineView: NSOutlineView, dataCellFor tableColumn: NSTableColumn?, item: Any) -> NSCell? { if self.noteOutlineView.isEqual(to: outlineView) { if tableColumn == nil { if let anno = item as? CPDFAnnotation, anno.type == nil { return outlineView.tableColumn(withIdentifier: self.noteColumnId)?.dataCell(forRow: outlineView.row(forItem: item)) as? NSCell } } } return tableColumn?.dataCell(forRow: outlineView.row(forItem: item)) as? NSCell } func outlineView(_ outlineView: NSOutlineView, shouldEdit tableColumn: NSTableColumn?, item: Any) -> Bool { if outlineView.isEqual(to: self.noteOutlineView) { if (tableColumn == nil) { if self.listView.hideNotes == false { if let anno = item as? CPDFAnnotation, anno.isNote() { // if ([pdfView hideNotes] == NO && [[(SKNoteText *)item note] isNote]) { // PDFAnnotation *annotation = [(SKNoteText *)item note]; self.listView.scrollAnnotationToVisible(anno) self.listView.updateActiveAnnotations([anno]) // [self showNote:annotation]; // SKNoteWindowController *noteController = (SKNoteWindowController *)[self windowControllerForNote:annotation]; // [[noteController window] makeFirstResponder:[noteController textView]]; // [[noteController textView] selectAll:nil]; } } return false } else if tableColumn?.identifier.rawValue == self.noteColumnId.rawValue || tableColumn?.identifier.rawValue == self.authorColumnId.rawValue { return true } } return false } func outlineView(_ outlineView: NSOutlineView, toolTipFor cell: NSCell, rect: NSRectPointer, tableColumn: NSTableColumn?, item: Any, mouseLocation: NSPoint) -> String { if outlineView.isEqual(to: self.noteOutlineView) { if tableColumn == nil || tableColumn?.identifier.rawValue == self.noteColumnId.rawValue { return (item as? CPDFAnnotation)?.string() ?? "" } } return "" } func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItems draggedItems: [Any]) { self.dragIn = true } func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, operation: NSDragOperation) { self.dragIn = false } /* #pragma mark NSOutlineView delegate protocol - (void)outlineView:(NSOutlineView *)ov didClickTableColumn:(NSTableColumn *)tableColumn { if ([ov isEqual:rightSideController.noteOutlineView]) { NSTableColumn *oldTableColumn = [ov highlightedTableColumn]; NSTableColumn *newTableColumn = ([NSEvent modifierFlags] & NSEventModifierFlagCommand) ? nil : tableColumn; NSMutableArray *sortDescriptors = nil; BOOL ascending = YES; if ([oldTableColumn isEqual:newTableColumn]) { sortDescriptors = [[[rightSideController.noteArrayController sortDescriptors] mutableCopy] autorelease]; [sortDescriptors replaceObjectAtIndex:0 withObject:[[sortDescriptors firstObject] reversedSortDescriptor]]; ascending = [[sortDescriptors firstObject] ascending]; } else { NSString *tcID = [newTableColumn identifier]; NSSortDescriptor *pageIndexSortDescriptor = [[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationPageIndexKey ascending:ascending] autorelease]; NSSortDescriptor *boundsSortDescriptor = [[[NSSortDescriptor alloc] initWithKey:SKPDFAnnotationBoundsOrderKey ascending:ascending selector:@selector(compare:)] autorelease]; sortDescriptors = [NSMutableArray arrayWithObjects:pageIndexSortDescriptor, boundsSortDescriptor, nil]; if ([tcID isEqualToString:TYPE_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationTypeKey ascending:YES selector:@selector(noteTypeCompare:)] autorelease] atIndex:0]; } else if ([tcID isEqualToString:COLOR_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationColorKey ascending:YES selector:@selector(colorCompare:)] autorelease] atIndex:0]; } else if ([tcID isEqualToString:NOTE_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationStringKey ascending:YES selector:@selector(localizedCaseInsensitiveNumericCompare:)] autorelease] atIndex:0]; } else if ([tcID isEqualToString:AUTHOR_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationUserNameKey ascending:YES selector:@selector(localizedCaseInsensitiveNumericCompare:)] autorelease] atIndex:0]; } else if ([tcID isEqualToString:DATE_COLUMNID]) { [sortDescriptors insertObject:[[[NSSortDescriptor alloc] initWithKey:SKNPDFAnnotationModificationDateKey ascending:YES] autorelease] atIndex:0]; } if (oldTableColumn) [ov setIndicatorImage:nil inTableColumn:oldTableColumn]; [ov setHighlightedTableColumn:newTableColumn]; } [rightSideController.noteArrayController setSortDescriptors:sortDescriptors]; if (newTableColumn) [ov setIndicatorImage:[NSImage imageNamed:ascending ? @"NSAscendingSortIndicator" : @"NSDescendingSortIndicator"] inTableColumn:newTableColumn]; [ov reloadData]; } } - (void)outlineViewColumnDidResize:(NSNotification *)notification{ if (mwcFlags.autoResizeNoteRows && [[notification object] isEqual:rightSideController.noteOutlineView] && [[[[notification userInfo] objectForKey:@"NSTableColumn"] identifier] isEqualToString:NOTE_COLUMNID] && [(SKScrollView *)[[notification object] enclosingScrollView] isResizingSubviews] == NO) { [rowHeights removeAllFloats]; [rightSideController.noteOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [rightSideController.noteOutlineView numberOfRows])]]; } } - (void)sizeOutlineViewToContents:(NSOutlineView*) outlineView; { NSInteger rowCount = [outlineView numberOfRows]; for (NSInteger i = 0; i < rowCount; i++){ CGFloat rowHeight = 0; PDFOutline *outline = [leftSideController.tocOutlineView itemAtRow:i]; if (!outline){ continue; } NSMutableAttributedString *attributedString = [[[NSMutableAttributedString alloc]init] autorelease]; NSDictionary *dictAttr1 = @{NSForegroundColorAttributeName:[KMAppearance KMColor_Layout_H0]}; NSAttributedString *attr1 = [[NSAttributedString alloc]initWithString:outline.label attributes:dictAttr1]; [attributedString appendAttributedString:attr1]; // NSTableCellView *viewS = [leftSideController.tocOutlineView viewAtColumn:0 row:i makeIfNecessary:YES]; NSTableColumn *tableColumn = [leftSideController.tocOutlineView tableColumnWithIdentifier:LABEL_COLUMNID]; // id cell = [tableColumn dataCell]; id cell = [tableColumn dataCellForRow:i]; [cell setObjectValue:attributedString]; CGFloat w = leftSideController.view.frame.size.width - 86;//[tableColumn width] > 260 ? [tableColumn width] : 260; NSInteger num = [self getNum:outline]; CGFloat gap = [leftSideController.tocOutlineView indentationPerLevel]; rowHeight = [cell cellSizeForBounds:NSMakeRect(0.0, 0.0, w - (num > 0?16:0) - gap*num, CGFLOAT_MAX)].height; rowHeight = fmax(rowHeight, [leftSideController.tocOutlineView rowHeight]) + 25; [rowHeights setFloat:rowHeight forKey:outline]; if (@available(macOS 10.13, *)) { } else { rowHeight = 40.0; } // CGRect fram = viewS.frame; // viewS.frame = CGRectMake(fram.origin.x, fram.origin.y, fram.size.width, rowHeight); } [leftSideController.tocOutlineView reloadData]; } */ func noteItems(_ items: NSArray) -> NSArray { var noteItems = NSMutableArray() for item in items { guard let anno = (item as? KMBOTAAnnotationItem)?.annotation else { continue } if anno.type == nil { // item = [(SKNoteText *)item note]; } if noteItems.contains(anno) == false { noteItems.add(anno) } } return noteItems } } // MARK: - KMCustomOutlineViewDelegate, KMCustomOutlineViewDataSource extension KMLeftSideViewController: KMCustomOutlineViewDelegate, KMCustomOutlineViewDataSource { func outlineView(_ anOutlineView: NSOutlineView, canDeleteItems items: [Any]) -> Bool { if anOutlineView.isEqual(to: self.noteOutlineView) { return self.listView.hideNotes == false && items.count > 0 } else if anOutlineView.isEqual(to: self.tocOutlineView) { return items.count > 0 } return false } func outlineView(_ anOutlineView: NSOutlineView, deleteItems items: [Any]) { if anOutlineView.isEqual(to: self.noteOutlineView) { if (items.isEmpty) { return } for item in self.noteItems(items as NSArray) { guard let anno = item as? CPDFAnnotation else { continue } self.listView.remove(anno) } self.listView.undoManager?.setActionName(KMLocalizedString("Remove Note", "Undo action name")) self.reloadAnnotation() self.annotationSort(sortArray: []) self.noteOutlineView.reloadData() } else if anOutlineView.isEqual(to: self.tocOutlineView) { self.outlineContextMenuItemClicked_RemoveEntry(nil) } } func outlineView(_ anOutlineView: NSOutlineView, canCopyItems items: [Any]) -> Bool { if anOutlineView.isEqual(to: self.noteOutlineView) { if (items.count == 1) { // PDFAnnotation *annotation = [[self noteItems:items] lastObject]; // if ([annotation isKindOfClass:[PDFAnnotationStamp class]] || // [annotation isKindOfClass:[PDFAnnotationLink class]]) { // return NO; // } } return items.count > 0 } return false } func outlineView(_ anOutlineView: NSOutlineView, copyItems items: [Any]) { if anOutlineView.isEqual(to: self.noteOutlineView) && items.isEmpty == false { let pboard = NSPasteboard.general var copiedItems: [Any] = [] var attrString = NSMutableAttributedString() var isAttributed = false var item: AnyObject? // for (item in [self noteItems:items]) { // if ([item isMovable]) // [copiedItems addObject:item]; // } // for (item in items) { // if ([attrString length]) // [attrString replaceCharactersInRange:NSMakeRange([attrString length], 0) withString:@"\n\n"]; // if ([(PDFAnnotation *)item type] == nil && [[(SKNoteText *)item note] isNote]) { // [attrString appendAttributedString:[(SKNoteText *)item text]]; // isAttributed = YES; // } else { // [attrString replaceCharactersInRange:NSMakeRange([attrString length], 0) withString:[item string] ?: @""]; // } // } // // [pboard clearContents]; // if (isAttributed) // [pboard writeObjects:[NSArray arrayWithObjects:attrString, nil]]; // else // [pboard writeObjects:[NSArray arrayWithObjects:[attrString string], nil]]; // if ([copiedItems count] > 0) // [pboard writeObjects:copiedItems]; } } func outlineView(_ anOutlineView: NSOutlineView, typeSelectHelperSelectionStrings aTypeSelectHelper: SKTypeSelectHelper) -> NSArray { if self.noteOutlineView.isEqual(to: anOutlineView) { let count = self.noteOutlineView.numberOfRows var texts = NSMutableArray(capacity: count) for i in 0 ..< count { let item = self.noteOutlineView.item(atRow: i) let string = (item as? CPDFAnnotation)?.string() ?? "" texts.add(string) } return texts } else if self.tocOutlineView.isEqual(to: anOutlineView) { let count = self.tocOutlineView.numberOfRows var array = NSMutableArray(capacity: count) for i in 0 ..< count { // [array addObject:[[(PDFOutline *)[leftSideController.tocOutlineView itemAtRow:i] label] lossyStringUsingEncoding:NSASCIIStringEncoding]]; let item = self.tocOutlineView.item(atRow: i) as? CPDFOutline array.add(item?.label ?? "") } return array } return NSArray() } func outlineView(_ anOutlineView: NSOutlineView, typeSelectHelper aTypeSelectHelper: SKTypeSelectHelper, didFailToFindMatchForSearchString searchString: String) { // if ([ov isEqual:rightSideController.noteOutlineView]) { // [statusBar setRightStringValue:[NSString stringWithFormat:NSLocalizedString(@"No match: \"%@\"", @"Status message"), searchString]]; // } else if ([ov isEqual:leftSideController.tocOutlineView]) { // [statusBar setLeftStringValue:[NSString stringWithFormat:NSLocalizedString(@"No match: \"%@\"", @"Status message"), searchString]]; // } } func tableView(_ aTableView: NSTableView, typeSelectHelper aTypeSelectHelper: SKTypeSelectHelper, updateSearchString searchString: String) { // if ([typeSelectHelper isEqual:[leftSideController.thumbnailTableView typeSelectHelper]] || [typeSelectHelper isEqual:[pdfView typeSelectHelper]]) { // if (searchString) // [statusBar setLeftStringValue:[NSString stringWithFormat:NSLocalizedString(@"Go to page: %@", @"Status message"), searchString]]; // else // [self updateLeftStatus]; // } else if ([typeSelectHelper isEqual:[rightSideController.noteOutlineView typeSelectHelper]]) { // if (searchString) // [statusBar setRightStringValue:[NSString stringWithFormat:NSLocalizedString(@"Finding note: \"%@\"", @"Status message"), searchString]]; // else // [self updateRightStatus]; // } else if ([typeSelectHelper isEqual:[leftSideController.tocOutlineView typeSelectHelper]]) { // if (searchString) // [statusBar setLeftStringValue:[NSString stringWithFormat:NSLocalizedString(@"Finding: \"%@\"", @"Status message"), searchString]]; // else // [self updateLeftStatus]; // } } } // MARK: - Other extension KMLeftSideViewController { @objc func goToSelectedOutlineItem(_ sender: AnyObject?) { let outlineItem = self.tocOutlineView.item(atRow: self.tocOutlineView.selectedRow) let outline = self.tocOutlineView if let cnt = outline?.selectedRowIndexes.count, cnt == 1 { if outlineItem is CPDFOutline { let outline = (outlineItem as! CPDFOutline) if let des = outline.destination { self.listView.go(to: des) } else if let action = outline.action { self.listView.perform(action) } } else if outlineItem is CPDFBookmark { let bookmark = outlineItem as! CPDFBookmark self.listView.go(toPageIndex: bookmark.pageIndex, animated: true) } } } @objc func goToSelectedFindResults(_ sender: AnyObject?) { KMPrint("KMLeftSideViewController-goToSelectedFindResults...") } func tableView(_ tv: NSTableView, extractRowsWithIndexes rowIndexes: IndexSet) { if tv.isEqual(to: self.thumbnailTableView) { guard let document = self.listView.document else { return } var pages = NSMutableArray() for idx in self.thumbnailTableView.selectedRowIndexes { if (idx < document.pageCount) { if let page = self.listView.document.page(at: UInt(idx)) { pages.add(page) } } } let fileName = document.getFileNameAccordingSelctPages(pages as! [CPDFPage]) let saveAccessCtr = KMSavePanelAccessoryController() let outputSavePanel = NSSavePanel() outputSavePanel.allowedFileTypes = ["pdf"] outputSavePanel.accessoryView = saveAccessCtr.view outputSavePanel.nameFieldStringValue = fileName outputSavePanel.beginSheetModal(for: self.view.window!) { result in if (result == .OK) { DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { let vc = KMProgressWindowController() self.view.window?.beginSheet(vc.window!) let saveFilePath = outputSavePanel.url?.path DispatchQueue.global().async { let pdf = CPDFDocument() let success = pdf?.extractAsOneDocument(withPages: pages as! [CPDFPage], savePath: saveFilePath) ?? false DispatchQueue.main.async { if (success) { if (saveAccessCtr.openAutomaticButton.state == .on) { NSDocumentController.shared.km_safe_openDocument(withContentsOf: outputSavePanel.url!, display: true) { _, _, _ in } } else { KMTools.viewFile(at: saveFilePath!) } } NSApp.endSheet(vc.window!) vc.close() } } } } } } } func fileNameWithSelectedPages(_ itemIndexes: IndexSet) -> String { var pagesName = "" if (itemIndexes.count > 1) { pagesName.append(" pages") } else { pagesName.append("page") } let docmentName = self.listView.document?.documentURL.deletingPathExtension().lastPathComponent ?? "" var fileName = "" if (itemIndexes.count > 0) { if (itemIndexes.count == 1) { let idx = itemIndexes.first! + 1 let tFileName = String(format: "%@ %@", pagesName, "\(idx)") return String(format: "%@%@", docmentName, tFileName) } var sortIndex = IndexSet() for idx in itemIndexes { let _idx = idx + 1 sortIndex.insert(_idx) } // NSSortDescriptor * sort = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES]; // NSArray *sortDesc = @[sort]; // NSArray *sortArray = [sortIndex sortedArrayUsingDescriptors:sortDesc]; let sortArray = sortIndex.sorted() var a = 0 var b = 0 for num in sortArray { // for (NSNumber *num in sortArray) { if (fileName.isEmpty == false) { if (num == b+1) { b = num if (num == sortArray.last) { fileName = "\(fileName)\(a)-\(b)" } } else { if (a == b) { fileName = "\(fileName)\(a)," } else { fileName = "\(fileName)\(a)-\(b)," } b = num a = b if (num == sortArray.last) { fileName = "\(fileName)\(a)" } } } else { fileName = "" b = num a = b } } let tFileName = String(format: "%@ %@", pagesName,fileName) return String(format: "%@%@", docmentName,tFileName) } return "" } func draggedFileName(for page: CPDFPage) -> String { let pageIndex = "\(page.pageIndex() + 1)" var fileName = "" if let doc = self.view.window?.windowController?.document as? NSDocument { fileName = doc.displayName.deletingPathExtension } return "\(fileName)-Page \(pageIndex)" } func switchType(_ type: BotaType) { if type == .Thumbnail { self.leftView.segmentedControl.selectedSegment = 0 } else if type == .Outline { self.leftView.segmentedControl.selectedSegment = 1 } else if type == .Annotation { self.leftView.segmentedControl.selectedSegment = 2 } else if type == .snapshot { self.leftView.segmentedControl.selectedSegment = 3 } else if type == .Search { self.leftView.segmentedControl.selectedSegment = 4 } } var TINY_SIZE: Float { get { return 32.0 } } var SMALL_SIZE: Float { get { return 64.0 } } var LARGE_SIZE: Float { get { return 128.0 } } var HUGE_SIZE: Float { get { return 256.0 } } var FUDGE_SIZE: Float { get { return 0.1 } } func updateTableFont() { let font = NSFont.systemFont(ofSize: KMPreference.shared.outlineFontSize.cgFloat) self.tocOutlineView.font = font self.noteOutlineView.font = font self.findTableView.font = font self.groupedFindTableView.font = font } }