// // KMLeftSideViewController.swift // PDF Reader Pro // // Created by lxy on 2022/10/10. // import Cocoa private let KMLeftSideViewNoteSortTypeKey = "KMLeftSideViewNoteSortTypeKey" private let KMLeftSideViewAscendSortBoolKey = "KMLeftSideViewAscendSortBoolKey" @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) } 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") 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(_themeChanged), name: NSApplication.interfaceThemeChangedNotification, object: nil) self.isDisplayPageSize = UserDefaults.standard.bool(forKey: "kKMThumbnailDisplayPageSizeKey") } 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 static let noteSortTypeKey = KMLeftSideViewNoteSortTypeKey static let ascendSorKey = KMLeftSideViewAscendSortBoolKey private let SKDisableTableToolTipsKey = "SKDisableTableToolTips" 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 preThumbnailRow: Int = 0 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 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 UserDefaults.standard.bool(forKey: SKDisableTableToolTipsKey) == 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 let sud = UserDefaults.standard self.isAscendSort = sud.bool(forKey: KMLeftSideViewAscendSortBoolKey) self.mwcFlags.wholeWordSearch = UserDefaults.standard.integer(forKey: SKWholeWordSearchKey) self.mwcFlags.caseInsensitiveSearch = UserDefaults.standard.integer(forKey: SKCaseInsensitiveSearchKey) 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 { self.thumbnailTableView.reloadData() } } @IBAction @objc func sortTypeAction(_ sender: NSMenuItem) { let item = sender let tag = item.tag if (item.state == .on) { item.state = .off } else { item.state = .on } if (tag == 0) { self.noteSortType = .page self.sortTypeLabel.stringValue = KMLocalizedString("Page", nil) self.sortTypeBox.toolTip = KMLocalizedString("Page", nil) } else if (tag == 1) { self.noteSortType = .time self.sortTypeLabel.stringValue = KMLocalizedString("Time", nil) self.sortTypeBox.toolTip = KMLocalizedString("Time", nil) } UserDefaults.standard.set(self.noteSortType.rawValue, forKey: KMLeftSideViewNoteSortTypeKey) UserDefaults.standard.synchronize() self.annotationSort(sortArray: []) } @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 sud = UserDefaults.standard let tag = sender.tag if (tag == 0 || tag == 1) { var scaling = sud.float(forKey: "KMThumbnailSizeScalingKey") if (scaling <= 0) { scaling = 1 } if (tag == 0) { // thumbnail Zoom In scaling += 0.1 if scaling >= 2.2 { return } } else if (tag == 1) { // thumbnail Zoom Out scaling -= 0.1 if scaling <= 0.4 { return } } sud.setValue(scaling, forKey: "KMThumbnailSizeScalingKey") let selectRow = self.thumbnailTableView.selectedRow self.thumbnailTableView.reloadData() self.thumbnailTableView.selectRowIndexes(IndexSet(integer: selectRow), byExtendingSelection: false) } else if (tag == 2 || tag == 3) { var scaling = sud.float(forKey: "KMSnapshotSizeScalingKey") if (scaling <= 0) { scaling = 1 } if (tag == 2) { // snapshot Zoom In scaling += 0.1 } else if (tag == 3) { // snapshot Zoom Out scaling -= 0.1 } sud.setValue(scaling, forKey: "KMSnapshotSizeScalingKey") 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) if (index != NSNotFound) { // 记录当前选中 UserDefaults.standard.set(index, forKey: "KMBOTASelectedIndexKey") UserDefaults.standard.synchronize() } } 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 clearAnnotationFilterData() { if let _key = self.listView?.document?.documentURL?.path { let userDefaults = UserDefaults.standard let typeData = try?NSKeyedArchiver.archivedData(withRootObject: [Any](), requiringSecureCoding: false) userDefaults.set(typeData, forKey: "KMNoteOutlineFilterSelectArray_Type" + _key) let colorData = try?NSKeyedArchiver.archivedData(withRootObject: [Any](), requiringSecureCoding: false) userDefaults.set(colorData, forKey: "KMNoteOutlineFilterSelectArray_Color" + _key) let authorData = try?NSKeyedArchiver.archivedData(withRootObject: [Any](), requiringSecureCoding: false) userDefaults.set(authorData, forKey: "KMNoteOutlineFilterSelectArray_Author" + _key) userDefaults.synchronize() } } func clearNotification() { } } // MARK: - Analytics (埋点) extension KMLeftSideViewController { func trackEvent(type: BotaType) -> Void { if (type == .Thumbnail) { KMAnalytics.trackEvent(eventName: "Btn_LeftSideBar_Thumbnail", parameters: [ KMAnalytics.Parameter.categoryKey : KMAnalytics.Category.leftSideBar, KMAnalytics.Parameter.labelKey : KMAnalytics.Label.leftSideBar_Btn], platform: .AppCenter, appTarget: .all) } else if (type == .Outline) { KMAnalytics.trackEvent(eventName: "Btn_LeftSideBar_Outline", parameters: [ KMAnalytics.Parameter.categoryKey : KMAnalytics.Category.leftSideBar, KMAnalytics.Parameter.labelKey : KMAnalytics.Label.leftSideBar_Btn], platform: .AppCenter, appTarget: .all) } else if (type == .BookMark) { KMAnalytics.trackEvent(eventName: "Btn_LeftSideBar_BookMark", parameters: [ KMAnalytics.Parameter.categoryKey : KMAnalytics.Category.leftSideBar, KMAnalytics.Parameter.labelKey : KMAnalytics.Label.leftSideBar_Btn], platform: .AppCenter, appTarget: .all) } else if (type == .Annotation) { KMAnalytics.trackEvent(eventName: "Btn_LeftSideBar_Annotation", parameters: [ KMAnalytics.Parameter.categoryKey : KMAnalytics.Category.leftSideBar, KMAnalytics.Parameter.labelKey : KMAnalytics.Label.leftSideBar_Btn], platform: .AppCenter, appTarget: .all) } else if (type == .Search) { KMAnalytics.trackEvent(eventName: "Btn_LeftSideBar_Search", parameters: [ KMAnalytics.Parameter.categoryKey : KMAnalytics.Category.leftSideBar, KMAnalytics.Parameter.labelKey : KMAnalytics.Label.leftSideBar_Btn], platform: .AppCenter, appTarget: .all) } } } // MARK: - Private Methods extension KMLeftSideViewController { @objc private func _themeChanged(_ notification: NSNotification) { DispatchQueue.main.asyncAfter(deadline: .now()+0.3) { 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) // self.leftView.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor // self.leftView.layer?.backgroundColor = NSColor.red.cgColor } 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) // self.leftView.layer?.backgroundColor = .white } self.leftView.wantsLayer = true self.leftView.layer?.backgroundColor = KMAppearance.controlBackgroundColor().cgColor } 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 } return false } } // 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 = UserDefaults.standard.float(forKey: "KMThumbnailSizeScalingKey") // 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 != nil && scaling > 0) { thumbSize = NSMakeSize(thumbnailSize.width * scaling.cgFloat, cellHeight * scaling.cgFloat) } else { thumbSize = NSMakeSize(thumbnailSize.width, cellHeight) } return thumbSize.height // NSSize cellSize = NSMakeSize([[tv tableColumnWithIdentifier:IMAGE_COLUMNID] width], fmin(thumbSize.height, roundedThumbnailSize)); // if (thumbSize.height < [tv rowHeight]) // return [tv rowHeight]; // else if (thumbSize.width / thumbSize.height < cellSize.width / cellSize.height) // return cellSize.height; // else // return fmax([tv rowHeight], fmin(cellSize.width, thumbSize.width) * thumbSize.height / thumbSize.width); } else if tableView.isEqual(to: self.snapshotTableView) { let scaling = UserDefaults.standard.float(forKey: "KMSnapshotSizeScalingKey") 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]; // // if ([self interactionMode] == SKPresentationMode && [[NSUserDefaults standardUserDefaults] boolForKey:SKAutoHidePresentationContentsKey]) // [self hideLeftSideWindow]; 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]; // // if ([self interactionMode] == SKPresentationMode && [[NSUserDefaults standardUserDefaults] boolForKey:SKAutoHidePresentationContentsKey]) // [self hideLeftSideWindow]; } 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) { // [pdfView goToPage:[[pdfView document] pageAtIndex:row]]; self.listView.go(toPageIndex: row, animated: true) } // if ([self interactionMode] == SKPresentationMode && [[NSUserDefaults standardUserDefaults] boolForKey:SKAutoHidePresentationContentsKey]) // [self hideLeftSideWindow]; // thumbnailSelectCount = row; // var rowIndexSet = IndexSet() // for i in 0 ..< self.thumbnailTableView.numberOfRows { // rowIndexSet.insert(i) // } // self.thumbnailTableView.reloadData(forRowIndexes: rowIndexSet, columnIndexes: IndexSet(integer: 0)) // } let view = self.thumbnailTableView.view(atColumn: 0, row: self.preThumbnailRow, makeIfNecessary: false) as? KMThumbnailTableviewCell view?.isSelectCell = false let view2 = self.thumbnailTableView.view(atColumn: 0, row: row, makeIfNecessary: false) as? KMThumbnailTableviewCell view2?.isSelectCell = true self.preThumbnailRow = row } 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) 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 } @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: - 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 { return (item as! CPDFOutline).child(at: UInt(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: NSUserInterfaceItemIdentifier(rawValue: "KMTocTableCellView"), 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 item is CPDFOutline { title = (item as! CPDFOutline).label pageLabel = "\(((item as! CPDFOutline).destination?.pageIndex ?? 0) + 1)" } else if item is CPDFBookmark { title = (item as! CPDFBookmark).label pageLabel = "\((item as! CPDFBookmark).pageIndex + 1)" } if tcID == LABEL_COLUMNID { if (self.isSearchOutlineMode) { // NSString *roughString = [[ol label] stringByCollapsingWhitespaceAndNewlinesAndRemovingSurroundingWhitespaceAndNewlines]; let roughString = title // NSArray *arr = [self allRangeOfRoughString:roughString searchString:self.leftSideController.outlineSearchField.stringValue]; // NSMutableAttributedString *attributeString = [[[NSMutableAttributedString alloc] initWithString:roughString] autorelease]; // // for (NSUInteger i = 0; i 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.updatingOutlineSelection == false { self.updatingOutlineSelection = true self.goToSelectedOutlineItem(nil) self.updatingOutlineSelection = false // if ([self interactionMode] == SKPresentationMode && [[NSUserDefaults standardUserDefaults] boolForKey:SKAutoHidePresentationContentsKey]) // [self hideLeftSideWindow]; } } } 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 "" } /* #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 } }