// // KMLeftSideViewController.swift // PDF Master // // Created by lxy on 2022/10/10. // import Cocoa @objc protocol KMLeftSideViewControllerDelegate { @objc optional func controlStateChange(_ obj: KMLeftSideViewController,show:Bool) @objc optional func enterEditMode(_ obj: KMLeftSideViewController, _ pages: [Int]) @objc optional func searchAction(searchString:String, isCase:Bool) @objc optional func controller(_ controller: KMLeftSideViewController, dispayDidChange dispay: KMPDFDisplayType) @objc optional func controller(controller: KMLeftSideViewController, itemClick item: Any?, itemKey: KMItemKey, params: Any?) @objc optional func controller(controller: KMLeftSideViewController, bookMarkDidChange bookMarks: [KMBookMarkItem]) @objc optional func controller(controller: KMLeftSideViewController, rotateType: KMRotateType) } 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? deinit { KMPrint("KMLeftSideViewController deinit.") NotificationCenter.default.removeObserver(self) } override var nibName: NSNib.Name? { return "LeftSideView" } convenience init(type : KMLeftMethodMode) { self.init() self.type = type } override func viewDidLoad() { super.viewDidLoad() 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.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; } - (void)applySearchTableHeader:(NSString *)message; */ @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 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 private let KMLeftSideViewNoteSortTypeKey = "KMLeftSideViewNoteSortTypeKey" private let KMLeftSideViewAscendSortBoolKey = "KMLeftSideViewAscendSortBoolKey" private let SKDisableTableToolTipsKey = "SKDisableTableToolTips" var tocType: KMFoldType = .none var snapshots: [KMSnapshotModel] = [] 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 } } private var _annotations: [KMBOTAAnnotationSection] = [] private var _allAnnotations: [CPDFAnnotation] = [] var allAnnotations: [CPDFAnnotation] { get { return self._allAnnotations } } private let kKMPDFViewOutlineDragDataType = "kKMPDFViewOutlineDragDataType" private let KPDFThumbnailDoucumentURLForDraggedTypes = "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() override func loadView() { super.loadView() self.view.wantsLayer = true self.view.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.view.addSubview(self.leftView) self.leftView.frame = NSMakeRect(0, 0, 44, NSHeight(self.view.frame)) self.leftView.autoresizingMask = [.height] self.leftView.wantsLayer = true self.leftView.layer?.backgroundColor = .white self.outlineView.wantsLayer = true self.outlineView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.noteView.wantsLayer = true self.noteView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.thumbnailView.wantsLayer = true self.thumbnailView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.snapshotNormalView.wantsLayer = true self.snapshotNormalView.layer?.backgroundColor = KMAppearance.Layout.l0Color().cgColor self.tocOutlineView.backgroundColor = KMAppearance.Layout.l0Color() self.noteOutlineView.backgroundColor = KMAppearance.Layout.l0Color() self.findTableView.backgroundColor = KMAppearance.Layout.l0Color() self.groupedFindTableView.backgroundColor = KMAppearance.Layout.l0Color() self.snapshotTableView.backgroundColor = KMAppearance.Layout.l0Color() self.thumbnailTableView.backgroundColor = KMAppearance.Layout.l0Color() 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.thumbnailTitleLabel.stringValue = KMLocalizedString("Thumbnails", nil) self.thumbnailTitleLabel.textColor = KMAppearance.Layout.h0Color() self.thumbnailZoomInButton.action = #selector(thumbnailSizeScaling) self.thumbnailZoomInButton.target = self self.thumbnailZoomInButton.tag = 1 self.thumbnailZoomOutButton.action = #selector(thumbnailSizeScaling) self.thumbnailZoomOutButton.target = self self.thumbnailZoomOutButton.tag = 0 self.snapshotTableView.delegate = self self.snapshotTableView.dataSource = self self.snapshotTableView.menu = NSMenu() self.snapshotTableView.menu?.delegate = self self.snapshotTableView.doubleAction = #selector(toggleSelectedSnapshots) self.snapshotTableView.target = self self.snapshotTableView.backgroundColor = .clear // [snapshotTableView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO]; self.snapshotLabel.stringValue = KMLocalizedString("Snapshots", nil) self.snapshotLabel.textColor = KMAppearance.Layout.h0Color() self.snapshotNormalZoomInButton.action = #selector(thumbnailSizeScaling) self.snapshotNormalZoomInButton.target = self self.snapshotNormalZoomInButton.tag = 2 self.snapshotNormalZoomOutButton.action = #selector(thumbnailSizeScaling) self.snapshotNormalZoomOutButton.target = self self.snapshotNormalZoomOutButton.tag = 3 self.snapshotNormalSearchButton.toolTip = KMLocalizedString("Search", nil) self.snapshotNormalMoreButton.action = #selector(leftSideViewMoreButtonAction) self.snapshotNormalMoreButton.target = self self.snapshotNormalMoreButton.tag = 300 self.snapshotSearchZoomInButton.action = #selector(thumbnailSizeScaling) self.snapshotSearchZoomInButton.target = self self.snapshotSearchZoomInButton.tag = 2 self.snapshotSearchZoomOutButton.action = #selector(thumbnailSizeScaling) self.snapshotSearchZoomOutButton.target = self self.snapshotSearchZoomOutButton.tag = 3 self.snapshotDoneButton.title = KMLocalizedString("Done", nil) self.snapshotDoneButton.toolTip = KMLocalizedString("Done", nil) self.snapshotDoneButton.setTitleColor(KMAppearance.Layout.w0Color()) self.snapshotDoneButton.wantsLayer = true self.snapshotDoneButton.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor self.snapshotDoneButton.layer?.cornerRadius = 4.0 self.snapshotDoneButton.action = #selector(leftSideViewDoneButtonAction) self.snapshotDoneButton.target = self self.snapshotDoneButton.tag = 312 self.snapshotDoneButton.isHidden = true self.noteOutlineView.autoresizesOutlineColumn = false self.noteOutlineView.delegate = self self.noteOutlineView.dataSource = self self.noteOutlineView.botaDelegate = self self.noteOutlineView.botaDataSource = self self.noteOutlineView.noteDelegate = self self.noteOutlineView.menu = NSMenu() self.noteOutlineView.menu?.delegate = self // [noteOutlineView setTypeSelectHelper:[SKTypeSelectHelper typeSelectHelperWithMatchOption:SKSubstringMatch]]; self.noteOutlineView.indentationPerLevel = 0 // [noteOutlineView registerForDraggedTypes:[NSColor readableTypesForPasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]]]; self.noteOutlineView.target = self self.noteOutlineView.doubleAction = #selector(selectSelectedNote) self.tocOutlineView.menu = NSMenu() self.tocOutlineView.menu?.delegate = self self.outlineSearchField.delegate = self self.snapshotSearchField.backgroundColor = KMAppearance.Layout.l_1Color() self.outlineSearchField.backgroundColor = KMAppearance.Layout.l_1Color() self.noteSearchField.backgroundColor = KMAppearance.Layout.l_1Color() self.snapshotSearchField.wantsLayer = true self.outlineSearchField.wantsLayer = true self.noteSearchField.wantsLayer = true self.snapshotSearchField.layer?.backgroundColor = KMAppearance.Layout.l_1Color().cgColor self.outlineSearchField.layer?.backgroundColor = KMAppearance.Layout.l_1Color().cgColor self.noteSearchField.layer?.backgroundColor = KMAppearance.Layout.l_1Color().cgColor self.snapshotSearchField.layer?.borderWidth = 1.0 self.outlineSearchField.layer?.borderWidth = 1.0 self.noteSearchField.layer?.borderWidth = 1.0 self.snapshotSearchField.layer?.borderColor = KMAppearance.Interactive.a0Color().cgColor self.outlineSearchField.layer?.borderColor = KMAppearance.Interactive.a0Color().cgColor self.noteSearchField.layer?.borderColor = KMAppearance.Interactive.a0Color().cgColor // __block typeof(self) blockSelf = self; // _snapshotSearchField.changeCallBack = ^(NSString *changeContent) { // NSString *editContent = @""; // if (changeContent) { // editContent = changeContent; // } // [blockSelf.mainController searchFieldChangeAction:editContent]; // }; self.snapshotSearchField.isHidden = true self.snapshotSearchZoomOutButton.isHidden = true self.snapshotSearchZoomInButton.isHidden = true // _snapshotSearchField.endEditCallBack = ^(BOOL isEndEdit) { // if (isEndEdit/* && searchViewController.segmentedControl.segmentCount == 3*/) { // _snapshotSearchField.hidden = YES; // _snapshotSearchZoomOutButton.hidden = YES; // _snapshotSearchZoomInButton.hidden = YES; // _snapshotLabel.hidden = NO; // _snapshotNormalZoomOutButton.hidden = NO; // _snapshotNormalZoomInButton.hidden = NO; // } // }; self.thumbnailZoomInButton.toolTip = KMLocalizedString("Zoom In", nil) self.snapshotNormalZoomInButton.toolTip = KMLocalizedString("Zoom In", nil) self.snapshotSearchZoomInButton.toolTip = KMLocalizedString("Zoom In", nil) self.thumbnailZoomOutButton.toolTip = KMLocalizedString("Zoom Out", nil) self.snapshotNormalZoomOutButton.toolTip = KMLocalizedString("Zoom Out", nil) self.snapshotSearchZoomOutButton.toolTip = KMLocalizedString("Zoom Out", nil) self.outlineLabel.stringValue = KMLocalizedString("Outline", nil); self.outlineLabel.textColor = KMAppearance.Layout.h0Color() self.outlineAddButton.toolTip = KMLocalizedString("Add Item", nil) self.outlineAddButton.action = #selector(outlineContextMenuItemClicked_AddEntry) self.outlineAddButton.target = self self.outlineSearchButton.toolTip = KMLocalizedString("Search", nil) self.outlineMoreButton.action = #selector(leftSideViewMoreButtonAction) self.outlineMoreButton.target = self self.outlineMoreButton.tag = 302 self.outlineDoneButton.title = KMLocalizedString("Done", nil); self.outlineDoneButton.toolTip = KMLocalizedString("Done", nil); self.outlineDoneButton.setTitleColor(KMAppearance.Layout.w0Color()) self.outlineDoneButton.wantsLayer = true self.outlineDoneButton.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor self.outlineDoneButton.layer?.cornerRadius = 4.0 self.outlineDoneButton.action = #selector(leftSideViewDoneButtonAction) self.outlineDoneButton.target = self self.outlineDoneButton.tag = 310 self.outlineDoneButton.isHidden = true self.outlineSearchField.isHidden = true // _outlineSearchField.endEditCallBack = ^(BOOL isEndEdit) { // if (isEndEdit) { // _outlineSearchField.hidden = YES; // _outlineLabel.hidden = NO; // _outlineSearchButton.hidden = NO; // } // }; let menuOutline = NSMenu() menuOutline.addItem(title: KMLocalizedString("Ignore Case", "Menu item title"), action: #selector(toggleOutlineCaseInsensitiveSearch), target: self) (self.outlineSearchField.cell as? NSSearchFieldCell)?.searchMenuTemplate = menuOutline (self.outlineSearchField.cell as? NSSearchFieldCell)?.placeholderString = KMLocalizedString("Search Outline", nil) self.outlineSearchField.target = self let sud = UserDefaults.standard self.noteTitleLabel.stringValue = KMLocalizedString("Notes", nil); self.noteTitleLabel.textColor = KMAppearance.Layout.h0Color() self.noteMoreButton.action = #selector(leftSideViewMoreButtonAction) self.noteMoreButton.target = self self.noteMoreButton.tag = 304 self.noteMoreButton.wantsLayer = true self.moreButtonLayer = KMButtonLayer() self.noteMoreButton.layer?.addSublayer(self.moreButtonLayer!) self.moreButtonLayer?.frame = CGRectMake(0, 0, CGRectGetWidth(self.noteMoreButton.bounds), CGRectGetHeight(self.noteMoreButton.bounds)) self.moreButtonLayer?.layerType = .none self.moreButtonLayer?.isHidden = true self.noteFilterButton.action = #selector(noteFilterAction) self.noteFilterButton.target = self self.noteFilterButton.toolTip = KMLocalizedString("Sort", nil) self.noteFilterButton.wantsLayer = true self.filterButtonLayer = NSView() self.noteFilterButton.addSubview(self.filterButtonLayer!) self.filterButtonLayer?.frame = CGRectMake(14, 2, 8, 8) self.filterButtonLayer?.isHidden = true self.filterButtonLayer?.wantsLayer = true self.filterButtonLayer?.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor self.filterButtonLayer?.layer?.cornerRadius = 4.0 self.noteDoneButton.title = KMLocalizedString("Done", nil) self.noteDoneButton.toolTip = KMLocalizedString("Done", nil) self.noteDoneButton.setTitleColor(KMAppearance.Layout.w0Color()) self.noteDoneButton.wantsLayer = true self.noteDoneButton.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor self.noteDoneButton.layer?.cornerRadius = 4.0 self.noteDoneButton.action = #selector(leftSideViewDoneButtonAction) self.noteDoneButton.target = self self.noteDoneButton.tag = 311 self.noteDoneButton.isHidden = true self.noteSearchButton.toolTip = KMLocalizedString("Search", nil) self.noteSearchField.delegate = self self.noteSearchField.isHidden = true self.noteSearchField.endEditCallBack = { [unowned self] isEndEdit in if (isEndEdit) { self.noteSearchField.isHidden = true self.noteSearchButton.isHidden = false self.noteTitleLabel.isHidden = false } }; self.noteHeaderView.wantsLayer = true self.noteHeaderView.layer?.backgroundColor = KMAppearance.Else.textTagColor().cgColor self.noteHeaderView.layer?.cornerRadius = 1.0 let sortType = sud.integer(forKey: KMLeftSideViewNoteSortTypeKey) if (sortType == 1) { self.noteSortType = KMNoteSortType(rawValue: sortType)! if (self.noteSortType == .time) { self.sortTypeLabel.stringValue = KMLocalizedString("Time", nil) self.sortTypeBox.toolTip = KMLocalizedString("Time", nil) } else if (self._noteSortType == .page) { self.sortTypeLabel.stringValue = KMLocalizedString("Page", nil) self.sortTypeBox.toolTip = KMLocalizedString("Page", nil) } } else { self.noteSortType = .time self.sortTypeLabel.stringValue = KMLocalizedString("Time", nil) } self.sortTypeLabel.textColor = KMAppearance.Layout.h1Color() self.isAscendSort = sud.bool(forKey: KMLeftSideViewAscendSortBoolKey) if (self.isAscendSort) { self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankReverse) self.noteSortButton.toolTip = KMLocalizedString("ascending sort", nil) } else { self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankPositive) self.noteSortButton.toolTip = KMLocalizedString("descending sort", nil) } self.sortTypeBox.downCallback = { [unowned self] downEntered, mouseBox, _ in if (downEntered) { let menu = NSMenu() let timeItem = menu.addItem(title: KMLocalizedString("Time", nil), action: #selector(sortTypeAction), target: self) timeItem?.representedObject = self timeItem?.tag = 0 let pageItem = menu.addItem(title: KMLocalizedString("Page", nil), action: #selector(sortTypeAction), target: self) pageItem?.representedObject = self timeItem?.tag = 1 if (self.noteSortType == .time) { timeItem?.state = .on pageItem?.state = .off } else if (self.noteSortType == .page) { timeItem?.state = .off pageItem?.state = .on } menu.popUp(positioning: nil, at: CGPointMake(-10, 0), in: self.sortTypeBox) } } 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) { 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 self.tocOutlineView.autoresizesOutlineColumn = false self.tocOutlineView.allowsMultipleSelection = true self.tocOutlineView.allowsEmptySelection = true self.tocOutlineView.delegate = self self.tocOutlineView.dataSource = self self.tocOutlineView.botaDelegate = self self.tocOutlineView.botaDataSource = self self.tocOutlineView.tocDelegate = self self.thumbnailTableView.delegate = self self.thumbnailTableView.dataSource = self self.thumbnailTableView.thumbDelegate = self self.thumbnailTableView.allowsMultipleSelection = true self.findTableView.delegate = self self.findTableView.dataSource = self self.findTableView.botaDelegate = self self.groupedFindTableView.delegate = self self.groupedFindTableView.dataSource = self self.groupedFindTableView.botaDelegate = self self.thumbnailTableView.menu?.delegate = self self.findTableView.menu?.delegate = self self.groupedFindTableView.menu?.delegate = self self.tocOutlineView.doubleAction = #selector(goToSelectedOutlineItem) self.tocOutlineView.target = self self.findTableView.doubleAction = #selector(goToSelectedFindResults) self.findTableView.target = self self.groupedFindTableView.doubleAction = #selector(goToSelectedFindResults) self.groupedFindTableView.target = self // [thumbnailTableView setTypeSelectHelper:[SKTypeSelectHelper typeSelectHelperWithMatchOption:SKFullStringMatch]]; // //支持拖拽的文字类型 // [thumbnailTableView registerForDraggedTypes:@[KPDFThumbnailLocalForDraggedTypes,NSFilenamesPboardType]]; // self.thumbnailTableView.registerForDraggedTypes([.localDraggedTypes, .fileURL]) self.thumbnailTableView.registerForDraggedTypes([.localDraggedTypes, .fileURL,.string,.pdf]) // self.thumbnailTableView.registerForDraggedTypes(NSFilePromiseReceiver.readableDraggedTypes.map { NSPasteboard.PasteboardType($0) }) // self.thumbnailTableView.setDraggingSourceOperationMask([.copy, .delete], forLocal: true) // [tocOutlineView setTypeSelectHelper:[SKTypeSelectHelper typeSelectHelperWithMatchOption:SKSubstringMatch]]; // [tocOutlineView registerForDraggedTypes:[NSArray arrayWithObject:kKMPDFViewOutlineDragDataType]]; // [[[findTableView tableColumnWithIdentifier:PAGE_COLUMNID] headerCell] setTitle:NSLocalizedString(@"Page", @"Table header title")]; // [[[groupedFindTableView tableColumnWithIdentifier:PAGE_COLUMNID] headerCell] setTitle:NSLocalizedString(@"Page", @"Table header title")]; // [[[groupedFindTableView tableColumnWithIdentifier:RELEVANCE_COLUMNID] dataCell] setEnabled:NO]; self.thumbnailTableView.selectionHighlightStyle = .none // NSSortDescriptor *countDescriptor = [[[NSSortDescriptor alloc] initWithKey:SKGroupedSearchResultCountKey ascending:NO] autorelease]; // [groupedFindArrayController setSortDescriptors:[NSArray arrayWithObjects:countDescriptor, nil]]; // // self.thumbnailTableView.setDraggingSourceOperationMask(.every, forLocal: false) if UserDefaults.standard.bool(forKey: SKDisableTableToolTipsKey) == false { self.tocOutlineView.hasImageToolTips = true self.findTableView.hasImageToolTips = true self.groupedFindTableView.hasImageToolTips = true } self._updateViewColor() self.mwcFlags.wholeWordSearch = UserDefaults.standard.integer(forKey: SKWholeWordSearchKey) } 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 updataLeftSideSnapView() { if (snapshots.count < 1) { self.snapshotNormalSearchButton.isEnabled = false // leftSideController.snapshotSearchZoomOutButton.enabled = NO; self.snapshotNormalZoomOutButton.isEnabled = false // leftSideController.snapshotSearchZoomInButton.enabled = NO; self.snapshotNormalZoomInButton.isEnabled = false self.snapshotNormalMoreButton.isEnabled = false self.leftSideEmptyVC.deleteSnapBtn.isEnabled = false self.leftSideEmptyVC.exportSnapBtn.isEnabled = false self.leftSideEmptyVC.printSnapBtn.isEnabled = false self.snapshotTableView.usesAlternatingRowBackgroundColors = false let view = self.snapshotTableView.enclosingScrollView! let emptyVcSize = self.leftSideEmptyVC.emptySnapView.frame.size self.leftSideEmptyVC.emptySnapView.frame = NSMakeRect((view.frame.size.width-emptyVcSize.width)/2.0,(view.frame.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height); self.leftSideEmptyVC.emptySnapView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] self.snapshotTableView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.emptySnapView) } else { self.snapshotNormalSearchButton.isEnabled = true // leftSideController.snapshotSearchZoomOutButton.enabled = YES; self.snapshotNormalZoomOutButton.isEnabled = true // leftSideController.snapshotSearchZoomInButton.enabled = YES; self.snapshotNormalZoomInButton.isEnabled = true self.snapshotNormalMoreButton.isEnabled = true self.leftSideEmptyVC.emptySnapView.removeFromSuperview() self.leftSideEmptyVC.deleteSnapBtn.isEnabled = true self.leftSideEmptyVC.exportSnapBtn.isEnabled = true self.leftSideEmptyVC.printSnapBtn.isEnabled = true } } 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.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 = rightSideController.noteOutlineView.enclosingScrollView.frame; // frame.origin.y = 0; // frame.size.height = rightSideController.noteOutlineView.enclosingScrollView.superview.frame.size.height; // rightSideController.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 } /* - (void)applySearchTableHeader:(NSString *)message { [[[findTableView tableColumnWithIdentifier:RESULTS_COLUMNID] headerCell] setStringValue:message]; [[findTableView headerView] setNeedsDisplay:YES]; [[[groupedFindTableView tableColumnWithIdentifier:RELEVANCE_COLUMNID] headerCell] setStringValue:message]; [[groupedFindTableView headerView] setNeedsDisplay:YES]; } */ @IBAction func search(_ sender: NSSearchField) { self.delegate?.searchAction?(searchString: sender.stringValue, isCase: false) } @IBAction func snapshotNormalSearchButtonAction(_ sender: NSButton) { self.snapshotSearchField.isHidden = false // _snapshotSearchZoomOutButton.hidden = YES; self.snapshotSearchZoomInButton.isHidden = true self.snapshotNormalSearchButton.isHidden = true self.snapshotDoneButton.isHidden = false self.snapshotLabel.isHidden = true self.snapshotNormalZoomOutButton.isHidden = true self.snapshotNormalZoomInButton.isHidden = true self.snapshotSearchField.becomeFirstResponder() } 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() } @IBAction func noteSortAction(_ sender: AnyObject?) { if (self.isAscendSort) { self.isAscendSort = false self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankPositive) self.noteSortButton.toolTip = KMLocalizedString("descending sort", nil) } else { self.isAscendSort = true self.noteSortButton.image = NSImage(named: KMImageNameBtnSidebarRankReverse) self.noteSortButton.toolTip = KMLocalizedString("ascending sort", nil) } UserDefaults.standard.setValue(self.isAscendSort, forKey: KMLeftSideViewAscendSortBoolKey) UserDefaults.standard.synchronize() } @IBAction func noteSearchAction(_ sender: NSButton) { self.noteSearchField.isHidden = false self.noteTitleLabel.isHidden = true self.noteSearchButton.isHidden = true self.noteDoneButton.isHidden = false self.noteFilterButton.isHidden = true self.noteMoreButton.isHidden = true self.noteSearchField.becomeFirstResponder() } @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.snapshotSearchZoomOutButton.hidden = YES; // 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 leftSideViewMoreButtonAction(_ sender: AnyObject?) { let button = sender as? NSButton let tag = button?.tag ?? 0 if (tag == 300) { var selectedRow = 0 if (self.snapshotTableView.selectedRow >= 0) { selectedRow = self.snapshotTableView.selectedRow; } else { return } let model = self.snapshots[selectedRow] let controller = model.windowC let menu = NSMenu() let itemExport = menu.addItem(title: KMLocalizedString("Export", "Menu item title"), action: nil, target: self) let subMenu = NSMenu() var t = subMenu.addItem(title: KMLocalizedString("PNG", "Menu item title"), action: #selector(menuItemClick_ExportPNG), target: self) t?.representedObject = controller t = subMenu.addItem(title: KMLocalizedString("JPG", "Menu item title"), action: #selector(menuItemClick_ExportJPG), target: self) t?.representedObject = controller t = subMenu.addItem(title: KMLocalizedString("PDF", "Menu item title"), action: #selector(menuItemClick_ExportPDF), target: self) t?.representedObject = controller itemExport?.submenu = subMenu let itemPrint = menu.addItem(title: KMLocalizedString("Print", "Menu item title"), action: #selector(menuItemClick_Print), target: self) itemPrint?.representedObject = controller menu.addItem(.separator()) let itemSelectAll = menu.addItem(title: KMLocalizedString("Select All", "Menu item title"), action: #selector(menuItemClick_SelectAll), target: self) itemSelectAll?.representedObject = controller menu.addItem(.separator()) let itemDeleteAllSnapshot = menu.addItem(title: KMLocalizedString("Delete All Snapshots", "Menu item title"), action: #selector(deleteAllSnapshot), target: self) itemDeleteAllSnapshot?.representedObject = controller // NSMenu.popUpContextMenu(menu, with: NSApp.currentEvent!, for: sender as! NSButton) } else if (tag == 302) { let menu = NSMenu() let expandAllCommentsItem = menu.addItem(title: KMLocalizedString("Expand All", nil), action: #selector(toc_expandAllComments), target: self) expandAllCommentsItem?.representedObject = self.tocOutlineView let foldAllCommentsItem = menu.addItem(title: KMLocalizedString("Collapse All", nil), action: #selector(toc_foldAllComments), target: self) expandAllCommentsItem?.representedObject = self.tocOutlineView let item = self.listView.document.outlineRoot() var num = 0 for i in 0 ..< Int(item?.numberOfChildren ?? 0) { let outline = item?.child(at: UInt(i)) if self.tocOutlineView.isItemExpanded(outline) { num += 1 } } if let cnt = item?.numberOfChildren, cnt > 0 && num == 0 { self.tocType = .fold } else if let cnt = item?.numberOfChildren, cnt > 0 && num == cnt { self.tocType = .unfold } else { self.tocType = .none } if (self.tocType == .unfold) { expandAllCommentsItem?.state = .on foldAllCommentsItem?.state = .off } else if (self.tocType == .fold) { expandAllCommentsItem?.state = .off foldAllCommentsItem?.state = .on } else { expandAllCommentsItem?.state = .off foldAllCommentsItem?.state = .off } let removeEntryItem = menu.addItem(title: KMLocalizedString("Remove All Outlines", nil), action: #selector(leftSideEmptyAnnotationClick_DeleteOutline), target: self) removeEntryItem?.representedObject = self.tocOutlineView NSMenu.popUpContextMenu(menu, with: NSApp.currentEvent!, for: sender as! NSButton) } else if (tag == 304) { let menu = NSMenu() let object = KMPopupMenuObject() object.menuTag = 1001 menu.delegate = object object.enterControllerCallback = { [weak self] isEnter in if (isEnter) { self?.moreButtonLayer?.isHidden = false } else { self?.moreButtonLayer?.isHidden = true } } let expandAllCommentsItem = menu.addItem(title: KMLocalizedString("Expand All", nil), action: #selector(note_expandAllComments), target: self) expandAllCommentsItem?.representedObject = self.noteOutlineView let foldAllCommentsItem = menu.addItem(title: KMLocalizedString("Collapse All", nil), action: #selector(note_foldAllComments), target: self) foldAllCommentsItem?.representedObject = self.noteOutlineView if (self.foldType == .unfold) { expandAllCommentsItem?.state = .on foldAllCommentsItem?.state = .off } else if (self.foldType == .fold) { expandAllCommentsItem?.state = .off foldAllCommentsItem?.state = .on } else { expandAllCommentsItem?.state = .off foldAllCommentsItem?.state = .off } let showAnnotationItem = menu.addItem(title: KMLocalizedString("Show Note", nil), action: nil, target: self) let subMenu = NSMenu() var t = subMenu.addItem(title: KMLocalizedString("Page", nil), action: #selector(noteShowNoteAction), target: self) let pageKey = self.noteTypeDict["kKMNoteFilterAnnotationPageKey"] as? Bool ?? false if pageKey { t?.state = .off } else { t?.state = .on } t?.representedObject = self.noteOutlineView t?.tag = 101 t = subMenu.addItem(title: KMLocalizedString("Time", nil) , action: #selector(noteShowNoteAction), target: self) let timeKey = self.noteTypeDict["kKMNoteFilterAnnotationTimeKey"] as? Bool ?? false if timeKey { t?.state = .off } else { t?.state = .on } t?.representedObject = self.noteOutlineView t?.tag = 102 t = subMenu.addItem(title: KMLocalizedString("Author", nil) , action: #selector(noteShowNoteAction), target: self) let authorKey = self.noteTypeDict["kKMNoteFilterAnnotationAutherKey"] as? Bool ?? false if authorKey { t?.state = .off } else { t?.state = .on } t?.representedObject = self.noteOutlineView t?.tag = 103 showAnnotationItem?.submenu = subMenu menu.addItem(.separator()) let exportAnnotationsItem = menu.addItem(title: NSLocalizedString("Export Annotations…", tableName: "MainMenu", comment: ""), action: nil, target: self) let subMenu2 = NSMenu() var t2 = subMenu2.addItem(title: NSLocalizedString("PDF", tableName: "MainMenu", comment: "Menu item title"), action: #selector(exportAnnotationNotes), target: self) t2?.representedObject = self.noteOutlineView t2?.tag = 0 t2 = subMenu2.addItem(title: NSLocalizedString("PDF Bundle", tableName: "MainMenu", comment: "Menu item title"), action: #selector(exportAnnotationNotes), target: self) t2?.representedObject = self.noteOutlineView t2?.tag = 1 t2 = subMenu2.addItem(title: NSLocalizedString("PDF Reader Pro Edition Notes", tableName: "MainMenu", comment: "Menu item title"), action: #selector(exportAnnotationNotes), target: self) t2?.representedObject = self.noteOutlineView t2?.tag = 2 t2 = subMenu2.addItem(title: NSLocalizedString("Notes as Text", tableName: "MainMenu", comment: "Menu item title"), action: #selector(exportAnnotationNotes), target: self) t2?.representedObject = self.noteOutlineView t2?.tag = 3 t2 = subMenu2.addItem(title: NSLocalizedString("Notes as RTF", tableName: "MainMenu", comment: "Menu item title"), action: #selector(exportAnnotationNotes), target: self) t2?.representedObject = self.noteOutlineView t2?.tag = 4 t2 = subMenu2.addItem(title: NSLocalizedString("Notes as RTFD", tableName: "MainMenu", comment: "Menu item title"), action: #selector(exportAnnotationNotes), target: self) t2?.representedObject = self.noteOutlineView t2?.tag = 5 t2 = subMenu2.addItem(title: NSLocalizedString("Notes as FDF", tableName: "MainMenu", comment: "Menu item title"), action: #selector(exportAnnotationNotes), target: self) t2?.representedObject = self.noteOutlineView t2?.tag = 6 exportAnnotationsItem?.submenu = subMenu2 menu.addItem(.separator()) let removeAllAnnotationsItem = menu.addItem(title: NSLocalizedString("Remove All Annotations", tableName: "MainMenu", comment: ""), action: #selector(leftSideEmptyAnnotationClick_DeleteAnnotation), target: self) removeAllAnnotationsItem?.representedObject = self.noteOutlineView NSMenu.popUpContextMenu(menu, with: NSApp.currentEvent!, for: button!, with: nil) } } @IBAction func noteFilterAction(_ sender: AnyObject?) { let button = sender as? NSButton let menu = NSMenu() let filterViewController = KMNoteOutlineFilterViewController() filterViewController.view.layer?.backgroundColor = .clear var notes = NSMutableArray() for section in self._annotations { if section.annotations?.count != 0 { for item in section.annotations! { notes.add(item.annotation!) } } } filterViewController.setNotesArray(notes) filterViewController.applyFilterCallback = { [weak self] typeArr, colorArr, authorArr, isEmpty in menu.cancelTracking() if (isEmpty) { self?.filterButtonLayer?.isHidden = true } else { self?.filterButtonLayer?.isHidden = false } self?.annotationSort(sortArray: []) } filterViewController.cancelCallback = { isCancel in if (isCancel) { menu.cancelTracking() } } let item = menu.addItem(withTitle: "", action: nil, keyEquivalent: "") item.target = self item.representedObject = filterViewController item.view = filterViewController.view menu.popUp(positioning: nil, at: CGPointMake(-130, 30), in: button) } @IBAction func outlineNormalSearchButtonAction(_ sender: NSButton) { self.outlineSearchField.isHidden = false self.outlineDoneButton.isHidden = false self.outlineLabel.isHidden = true self.outlineSearchButton.isHidden = true self.outlineMoreButton.isHidden = true self.outlineAddButton.isHidden = true self.outlineSearchField.becomeFirstResponder() } @IBAction func toc_expandAllComments(_ sender: AnyObject?) { if (self.tocType == .unfold) { return } self.tocType = .unfold self.tocOutlineView.reloadData() self.tocOutlineView.expandItem(nil, expandChildren: true) } @IBAction func toc_foldAllComments(_ sender: AnyObject?) { if (self.tocType == .fold) { return } self.tocType = .fold self.tocOutlineView.reloadData() self.tocOutlineView.collapseItem(nil, collapseChildren: true) } @objc func leftSideEmptyAnnotationClick_DeleteOutline(_ sender: AnyObject?) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = "" alert.informativeText = KMLocalizedString("This will permanently remove all outlines. Are you sure to continue?", nil) alert.addButton(withTitle: KMLocalizedString("Yes", nil)) alert.addButton(withTitle: KMLocalizedString("No", nil)) let response = alert.runModal() if response == .alertFirstButtonReturn { if let item = self.listView.document.outlineRoot() { self.removeAllOutline(item) } } } @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 reloadAnnotation() { if self.listView != nil { var dataArray: [KMBOTAAnnotationSection] = [] var annotationArray: [CPDFAnnotation] = [] for i in 0 ..< self.listView.document.pageCount { var annotationItemArray: [KMBOTAAnnotationItem] = [] let page = self.listView.document.page(at: i) let types = ["Highlight","Underline","Strikeout","Freehand","FreeText","Note","Square","Circle","Line","Stamp","Arrow","Image","Redact","Sign"] var pageAnnotations: [CPDFAnnotation] = KMOCToolClass.filterAnnotation(annotations: page!.annotations,types: types) as! [CPDFAnnotation] //添加签名注释 for annotation in page!.annotations { if annotation.isKind(of: CPDFSignatureAnnotation.self) { pageAnnotations.append(annotation) } } for annotation in pageAnnotations { if annotation.annotationShouldDisplay() == false { pageAnnotations.removeObject(annotation) } } //转换所有annotation类型 let section = KMBOTAAnnotationSection() for annotation in pageAnnotations { let item = KMBOTAAnnotationItem() item.section = section item.annotation = annotation item.index = Int(annotation.page.pageIndex()) annotationItemArray.append(item) } if annotationItemArray.count != 0 { section.annotations = annotationItemArray section.page = page section.isItemExpanded = true dataArray.append(section) } //添加所有annotation 用于筛选 annotationArray += pageAnnotations } //转换对象,用于数据显示 self._annotations = dataArray self._allAnnotations = annotationArray // if self.annotations.count < 1 { // self.filtrateButton.isEnabled = false // } else { // self.filtrateButton.isEnabled = true // } } } func annotationSort(sortArray:[[Any]]) { if self.listView != nil { var typeArr: [Any] = [] var colorArr: [Any] = [] var authorArr: [Any] = [] let sud = UserDefaults.standard // let typeData = sud.object(forKey: "KMNoteOutlineFilterSelectArray_Type" + self.listView.document.documentURL.path) as? Data let typeData = sud.object(forKey: "KMNoteOutlineFilterSelectArray_Type") as? Data if typeData != nil { typeArr = NSKeyedUnarchiver.unarchiveObject(with: typeData!) as! [Any] } // let colorData = sud.object(forKey: "KMNoteOutlineFilterSelectArray_Color" + self.listView.document.documentURL.path) as? Data let colorData = sud.object(forKey: "KMNoteOutlineFilterSelectArray_Color") as? Data if colorData != nil { colorArr = NSKeyedUnarchiver.unarchiveObject(with: colorData!) as! [Any] } // let authorData = sud.object(forKey: "KMNoteOutlineFilterSelectArray_Author" + self.listView.document.documentURL.path) as? Data let authorData = sud.object(forKey: "KMNoteOutlineFilterSelectArray_Author") as? Data if authorData != nil { authorArr = NSKeyedUnarchiver.unarchiveObject(with: authorData!) as! [Any] } if typeArr.count == 0 && colorArr.count == 0 && authorArr.count == 0 { // self.filtrateButton.image = NSImage(named: "KMImageNameAnnotationsFiltrate") self.reloadAnnotation() } else { // self.filtrateButton.image = NSImage(named: "icon_annotation_screening_select") var dataArray: [KMBOTAAnnotationSection] = [] for i in 0 ..< self.listView.document.pageCount { var annotationItemArray: [KMBOTAAnnotationItem] = [] let page = self.listView.document.page(at: i) if page!.annotations.count > 0 { var filterAnnotations: [CPDFAnnotation] = page!.annotations if typeArr.count > 0 { filterAnnotations = (KMOCToolClass.filterAnnotation(annotations: filterAnnotations, types: typeArr) as! [CPDFAnnotation]) } if (colorArr.count > 0) { filterAnnotations = (KMOCToolClass.filterAnnotation(annotations: filterAnnotations,colors: colorArr) as! [CPDFAnnotation]) } if (authorArr.count > 0) { filterAnnotations = (KMOCToolClass.filterAnnotation(annotations: filterAnnotations,authors: authorArr) as! [CPDFAnnotation]) } let section = KMBOTAAnnotationSection() for annotation in filterAnnotations { let item = KMBOTAAnnotationItem() item.section = section item.annotation = annotation item.index = Int(page!.pageIndex()) annotationItemArray.append(item) } if annotationItemArray.count != 0 { section.annotations = annotationItemArray section.page = page section.isItemExpanded = true dataArray.append(section) } } } self._annotations = dataArray } Task { @MainActor in self.noteOutlineView.reloadData() } } } func showSearchOutlineBlankState(_ toShowState: Bool) { if (toShowState) { self.leftSideEmptyVC.outlineSearchView.frame = CGRectMake((self.tocOutlineView.enclosingScrollView!.documentView!.frame.size.width - self.leftSideEmptyVC.outlineSearchView.bounds.size.width)/2.0, (self.tocOutlineView.enclosingScrollView!.documentView!.frame.size.height - self.leftSideEmptyVC.outlineSearchView.bounds.size.height)/2.0, self.leftSideEmptyVC.outlineSearchView.bounds.size.width, self.leftSideEmptyVC.outlineSearchView.bounds.size.height); self.tocOutlineView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.outlineSearchView) self.leftSideEmptyVC.outlineSearchView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] } else { self.leftSideEmptyVC.outlineSearchView.removeFromSuperview() } } // func allRangeOfRoughString(_ roughString: String, searchString: String) -> [NSRange] { // // } func removeAllOutline(_ outline: CPDFOutline) { self.listView.document.setNewOutlineRoot() for i in 0 ..< self.listView.document.pageCount { self.listView.document.removeBookmark(forPageIndex: i) } self.listView.layoutDocumentView() DispatchQueue.main.async { self.tocOutlineView.reloadData() } } func updateNoteFilterPredicate() { //注释筛选 // [rightSideController.noteArrayController setFilterPredicate:[noteTypeSheetController filterPredicateForSearchString:[rightSideController.searchField stringValue] caseInsensitive:mwcFlags.caseInsensitiveNoteSearch]]; let stringValue = self.noteSearchField.stringValue // NSPredicate *predicate = [noteTypeSheetController filterPredicateForSearchString:[rightSideController.searchField stringValue] caseInsensitive:mwcFlags.caseInsensitiveNoteSearch]; // [self loadAnnotationSortData:[NSArray arrayWithObjects:predicate, nil]]; self.noteSearchArray.removeAll() if stringValue.isEmpty { self.noteSearchArray = self.allAnnotations } else { for note in self.allAnnotations { var noteString = KMBOTAAnnotationTool.fetchContentLabelString(annotation: note) if let anno = note as? CPDFMarkupAnnotation { noteString = anno.markupContent() } if noteString.contains(stringValue) { self.noteSearchArray.append(note) } } } Task { @MainActor in self.noteOutlineView.reloadData() } } @objc func selectSelectedNote(_ sender: AnyObject?) { if self.listView.hideNotes == false { let selectedNotes = self.selectedNotes() if selectedNotes.count == 1 { let annotation = selectedNotes.last! self.listView.go(to: annotation.bounds, on: annotation.page, animated: true) // [pdfView scrollAnnotationToVisible:annotation]; // [pdfView setActiveAnnotation:annotation]; self.listView.updateActiveAnnotations([annotation]) self.listView.setNeedsDisplayAnnotationViewForVisiblePages() // } } // NSInteger column = [sender clickedColumn]; // if (column != -1) { // NSString *colID = [[[sender tableColumns] objectAtIndex:column] identifier]; // // if ([colID isEqualToString:@"color"]){ // for (PDFAnnotation *annotation in self.pdfView.activeAnnotations) { // if (![annotation isKindOfClass:[PDFAnnotationChoiceWidget class]] && // ![annotation isKindOfClass:[PDFAnnotationButtonWidget class]] && // ![annotation isKindOfClass:[PDFAnnotationTextWidget class]]) { // [[NSColorPanel sharedColorPanel] orderFront:nil]; // break; // } // // } // } // } } } func selectedNotes() -> [CPDFAnnotation] { var selectedNotes: [CPDFAnnotation] = [] let rowIndexes = self.noteOutlineView.selectedRowIndexes for row in rowIndexes { let item = self.noteOutlineView.item(atRow: row) if item is KMBOTAAnnotationItem { if let anno = (item as! KMBOTAAnnotationItem).annotation { // if anno.type == nil { // item = [(SKNoteText *)item note]; // } if selectedNotes.contains(anno) == false { selectedNotes.append(anno) } } } } return selectedNotes } } // MARK: - Note extension KMLeftSideViewController { func loadAnnotationSortData(_ morePredicates: NSArray) { var isLink = false var typeMutableArr = NSMutableArray() if self.allAnnotations.count > 0 { for annotation in self.allAnnotations { if annotation is CPDFLinkAnnotation || annotation is CPDFTextWidgetAnnotation || annotation is CPDFButtonWidgetAnnotation || annotation is CPDFChoiceWidgetAnnotation { // [annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeSignature] || // [annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeText] || // [annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeButton] || // [annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeChoice]) { isLink = true } else { if typeMutableArr.contains(annotation.type) == false { typeMutableArr.add(annotation.type) } } } } var colorMutableArray = NSMutableArray() var typeMutableArray = NSMutableArray() var authorMutableArray = NSMutableArray() for annotation in self.allAnnotations { if annotation is CPDFLinkAnnotation || annotation is CPDFTextWidgetAnnotation || annotation is CPDFButtonWidgetAnnotation || annotation is CPDFChoiceWidgetAnnotation { // [annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeSignature] || // [annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeText] || // [annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeButton] || // [annotation.widgetFieldType isEqualToString:PDFAnnotationWidgetSubtypeChoice]) { let noteColor = annotation.color ?? .clear let noteType = annotation.type ?? "" let authorString = annotation.userName() ?? "" if (noteColor != nil) { if (colorMutableArray.count > 0) { if colorMutableArray.contains(noteColor) == false { colorMutableArray.add(noteColor) } } else { colorMutableArray.add(noteColor) } } if noteType.isEmpty == false { if typeMutableArray.count > 0 { if typeMutableArray.contains(noteType) == false { typeMutableArray.add(noteType) } } else { typeMutableArray.add(noteType) } } if authorString.isEmpty == false { if authorString.count > 0 { if authorMutableArray.contains(authorString) == false { authorMutableArray.add(authorString) } } else { authorMutableArray.add(authorString) } } } else { if typeMutableArr.contains(annotation.type) == false { typeMutableArr.add(annotation.type) } } } let sud = UserDefaults.standard var typeArr = NSMutableArray() if let typeData = sud.object(forKey: "KMNoteOutlineFilterSelectArray_Type") as? Data { if let data = NSKeyedUnarchiver.unarchiveObject(with: typeData) as? NSArray { typeArr = NSMutableArray(array: data) } } var colorArr = NSMutableArray() if let colorData = sud.object(forKey: "KMNoteOutlineFilterSelectArray_Color") as? Data { if let data = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? NSArray { colorArr = NSMutableArray(array: data) } } var authorArr = NSMutableArray() if let authorData = sud.object(forKey: "KMNoteOutlineFilterSelectArray_Author") as? Data { if let data = NSKeyedUnarchiver.unarchiveObject(with: authorData) as? NSArray { authorArr = NSMutableArray(array: data) } } var temporaryArr1 = NSMutableArray() var temporaryArr2 = NSMutableArray() var temporaryArr3 = NSMutableArray() if (typeArr.count > 0) { for type in typeArr { if typeMutableArray.contains(type) == false { temporaryArr1.add(type) } } } if (colorArr.count > 0) { for color in colorArr { if colorMutableArray.contains(color) == false { temporaryArr2.add(color) } } } if (authorArr.count > 0) { for author in authorArr { if authorMutableArray.contains(author) == false { temporaryArr3.add(author) } } } if (temporaryArr1.count > 0) { for type in temporaryArr1 { typeArr.remove(type) } } if (temporaryArr2.count > 0) { for color in temporaryArr2 { colorArr.remove(color) } } if (temporaryArr3.count > 0) { for author in temporaryArr3 { authorArr.remove(author) } } var predicateMutableArr = NSMutableArray() // predicateMutableArr.add } } // 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 ([tv isEqual:rightSideController.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 defaultManager].isAvailableAllFunction) { // [[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: - KMThumbnailTableViewDelegate extension KMLeftSideViewController: KMThumbnailTableViewDelegate { func tableView(_ tableView: NSTableView, highlightLevelForRow row: Int) -> UInt { if tableView.isEqual(to: self.thumbnailTableView) { // NSUInteger i, iMax = [lastViewedPages count]; // for (i = 0; i < iMax; i++) { // if (row == (NSInteger)[lastViewedPages pointerAtIndex:i]) // return i; // } } return UInt.max } func tableView(_ tableView: NSTableView, commandSelectRow rowIndex: Int) -> Bool { if tableView.isEqual(to: self.thumbnailTableView) { // NSRect rect = [[[pdfView document] pageAtIndex:row] boundsForBox:kPDFDisplayBoxCropBox]; // // rect.origin.y = NSMidY(rect) - 0.5 * SNAPSHOT_HEIGHT; // rect.size.height = SNAPSHOT_HEIGHT; // [self showSnapshotAtPageNumber:row forRect:rect scaleFactor:[pdfView scaleFactor] autoFits:NO]; let thumbailTabCell = tableView.view(atColumn: 0, row: rowIndex, makeIfNecessary: true) as? KMThumbnailTableviewCell thumbailTabCell?.isSelectCell = true var rowIndexSet = IndexSet() for i in self.thumbnailTableView.selectedRowIndexes { rowIndexSet.insert(i) } rowIndexSet.insert(rowIndex) self.thumbnailTableView.selectRowIndexes(rowIndexSet, byExtendingSelection: true) return true } return false } func tableView(_ tableView: NSTableView, shiftSelectRow rowIndex: Int) -> Bool { if tableView.isEqual(to: self.thumbnailTableView) { if (self.thumbnailTableView.selectedRowIndexes.count == 0) { let thumbailTabCell = tableView.view(atColumn: 0, row: rowIndex, makeIfNecessary: true) as? KMThumbnailTableviewCell thumbailTabCell?.isSelectCell = true var rowIndexSet = IndexSet() self.thumbnailTableView.selectRowIndexes(rowIndexSet, byExtendingSelection: true) return true } else if (self.thumbnailTableView.selectedRowIndexes.count == 1 && self.thumbnailTableView.selectedRowIndexes.first == rowIndex) { return false } else { var fristIndex = self.thumbnailTableView.selectedRowIndexes.first ?? Int.min var lastIndex = self.thumbnailTableView.selectedRowIndexes.last ?? Int.max for idx in self.thumbnailTableView.selectedRowIndexes { // [leftSideController.thumbnailTableView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { if(idx < fristIndex) { fristIndex = idx } if(idx > lastIndex) { lastIndex = idx } } if(rowIndex < fristIndex) { fristIndex = rowIndex } else if (rowIndex > lastIndex) { lastIndex = rowIndex } var rowIndexSet = IndexSet() for i in fristIndex ... lastIndex { rowIndexSet.insert(i) } self.thumbnailTableView.selectRowIndexes(rowIndexSet, byExtendingSelection: true) return true } } return false } } // 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) } else { self.leftListView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.snapshotNormalView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.thumbnailView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.noteView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.outlineView.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.view.layer?.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1, alpha: 1).cgColor self.thumbnailTableView.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1.000, alpha: 1) self.groupedFindTableView.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1.000, alpha: 1) self.tocOutlineView.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1.000, alpha: 1) self.findTableView.backgroundColor = NSColor(red: 0.988, green: 0.992, blue: 1.000, alpha: 1) self.snapshotSearchField.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor self.outlineSearchField.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor self.noteSearchField.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor self.segmentedControl.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor self.snapshotSearchField.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1) self.outlineSearchField.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1) self.noteSearchField.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1) } } private func _hasContainString(_ searchString: String, rootOutline outline: CPDFOutline) -> Bool { let label_low = outline.label.lowercased() let searchString_low = searchString.lowercased() if label_low.contains(searchString_low) { // 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 == "results" { cell.resultLabel.attributedStringValue = selection.attributedString cell.resultLabel.textColor = KMAppearance.Layout.h0Color() } else if let data = tableColumn?.identifier.rawValue, data == "page" { 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") // NSSize snapshotSize = [[[[rightSideController.snapshotArrayController arrangedObjects] objectAtIndex:row] thumbnail] size]; let snapshotSize = 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 ([[aNotification object] isEqual:leftSideController.findTableView] || [[aNotification object] isEqual:leftSideController.groupedFindTableView]) { // [self updateFindResultHighlightsForDirection:NSDirectSelection]; // // if ([self interactionMode] == SKPresentationMode && [[NSUserDefaults standardUserDefaults] boolForKey:SKAutoHidePresentationContentsKey]) // [self hideLeftSideWindow]; // } else if self.findTableView.isEqual(to: notification.object) { 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.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 ([[aNotification object] isEqual:rightSideController.snapshotTableView]) { // NSInteger row = [rightSideController.snapshotTableView selectedRow]; // [selectCellList removeAllObjects]; // [selectCellList addObject:[NSString stringWithFormat:@"%ld",row]]; // if (row != -1) { // SKSnapshotWindowController *controller = [[rightSideController.snapshotArrayController arrangedObjects] objectAtIndex:row]; // if ([[controller window] isVisible]) // [[controller window] orderFront:self]; // } // NSMutableIndexSet *rowIndexSet = [[[NSMutableIndexSet alloc] init] autorelease]; // for (NSInteger i = 0; i < rightSideController.snapshotTableView.numberOfRows; i ++) { // [rowIndexSet addIndex:i]; // } // NSMutableIndexSet *columnIndexSet = [[[NSMutableIndexSet alloc] init] autorelease]; // [columnIndexSet addIndex:0]; // [rightSideController.snapshotTableView reloadDataForRowIndexes:rowIndexSet columnIndexes:columnIndexSet]; // // } } 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) } // guard let _allowedFileTypes = self.kmAllowedFileTypes else { // return .generic // } 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 (_allowedFileTypes.contains(type)) { hasValidFile = true // break // } } if (!hasValidFile) { return NSDragOperation(rawValue: 0) } } 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]) { // result = true // let rowData = pasteboard.data(forType: .localDraggedTypes) // let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: rowData!) // NSData *urlData = [pasteboard dataForType:KPDFThumbnailDoucumentURLForDraggedTypes]; // NSString *url = [NSKeyedUnarchiver unarchiveObjectWithData:urlData]; // if(![url isEqualToString:[pdfView document].documentURL.absoluteString] && url) { // if ([pasteboard availableTypeFromArray:[NSArray arrayWithObject:NSFilenamesPboardType]]){ // NSArray *fileNames = [pasteboard propertyListForType:NSFilenamesPboardType]; // if (fileNames.count == 1) { // NSString *path = fileNames.firstObject; // NSString *pathExtension = [path.pathExtension lowercaseString]; // if ([pathExtension isEqualToString:@"pdf"]) { // __block NSInteger index = row; // __block NSMutableIndexSet *insertIndexSet = [[[NSMutableIndexSet alloc] init] autorelease]; // PDFDocument *pdf = [[[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:path]] autorelease]; // if ([pdf isEncrypted]) { // KMDecryptWindowController *vc = [[KMDecryptWindowController alloc] init]; // vc.filePath = url; // [vc beginSheetModalForWindow:self.window completionHandler:^(NSString *password) { // if (password) { // [pdf unlockWithPassword:password]; // for(NSUInteger i=0; i Bool { if tableView.isEqual(to: self.thumbnailTableView) { // let idx = rowIndexes.first // if (idx != NSNotFound && [[pdfView document] isLocked] == NO) { // PDFPage *page = [[pdfView document] pageAtIndex:idx]; // NSString *fileExt = nil; // NSData *tiffData = [page TIFFDataForRect:[page boundsForBox:[pdfView displayBox]]]; // // if ([[pdfView document] allowsPrinting]) { // NSData *pdfData = [page dataRepresentation]; // fileExt = @"pdf"; // [pboard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypePDF, NSPasteboardTypeTIFF,NSFilenamesPboardType, NSFilesPromisePboardType, KPDFThumbnailLocalForDraggedTypes,KPDFThumbnailDoucumentURLForDraggedTypes,nil] owner:self]; // [pboard setData:pdfData forType:NSPasteboardTypePDF]; // NSData *zNSIndexSetData = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes]; // [pboard setData:zNSIndexSetData forType:KPDFThumbnailLocalForDraggedTypes]; // NSData *documentURL = [NSKeyedArchiver archivedDataWithRootObject:[[pdfView document].documentURL absoluteString]]; // NSString *docmentName = [[[pdfView.document.documentURL path] lastPathComponent] stringByDeletingPathExtension]; // __block NSMutableString *pagesName = nil; // if (rowIndexes.count > 1) { // pagesName = [NSMutableString stringWithString:@" pages"]; // // } else { // pagesName = [NSMutableString stringWithString:@" page"]; // // } // NSString * tFileName = [NSString stringWithFormat:@"%@ %@",pagesName,[self fileNameWithSelectedPages:rowIndexes]]; // PDFDocument * pdf = [[[PDFDocument alloc] init] autorelease]; // [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { // PDFPage *copyPage = [[pdfView.document pageAtIndex:idx] copy]; // [pdf insertPage:copyPage atIndex:pdf.pageCount]; // [copyPage release]; // }]; // NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); // NSString *cachesDir = [paths objectAtIndex:0]; // docmentName = [NSString stringWithFormat:@"%@%@",docmentName,tFileName]; // if (docmentName.length > 50) { // [docmentName substringWithRange:NSMakeRange(0, 50)]; // } // cachesDir = [[cachesDir stringByAppendingPathComponent:docmentName] stringByAppendingPathExtension:@"pdf"]; // // BOOL success = [pdf writeToFile:cachesDir]; // if (success) { // [pboard setPropertyList:@[cachesDir] forType:NSFilenamesPboardType]; // }else{ // [pboard setPropertyList:@[@""] forType:NSFilenamesPboardType]; // } // [pboard setData:documentURL forType:KPDFThumbnailDoucumentURLForDraggedTypes]; // } else { // fileExt = @"tiff"; // [pboard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSFilesPromisePboardType, nil] owner:self]; // } // [pboard setData:tiffData forType:NSPasteboardTypeTIFF]; // [pboard setPropertyList:[NSArray arrayWithObject:fileExt] forType:NSFilesPromisePboardType]; let data: Data = try! NSKeyedArchiver.archivedData(withRootObject: rowIndexes, requiringSecureCoding: true) pboard.declareTypes([.localDraggedTypes], owner: self) pboard.setData(data, forType: .localDraggedTypes) return true // } } // else if ([tv isEqual:rightSideController.snapshotTableView]) { // NSUInteger idx = [rowIndexes firstIndex]; // if (idx != NSNotFound) { // SKSnapshotWindowController *snapshot = [self objectInSnapshotsAtIndex:idx]; // [pboard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSFilesPromisePboardType, nil] owner:self]; // [pboard setData:[[snapshot thumbnailWithSize:0.0] TIFFRepresentation] forType:NSPasteboardTypeTIFF]; // [pboard setPropertyList:[NSArray arrayWithObject:@"tiff"] forType:NSFilesPromisePboardType]; // return YES; // } // } return false } @objc dynamic func tableView(_ aTableView: NSTableView, deleteRowsWithIndexes rowIndexes: IndexSet) { // if (![[IAPProductsManager defaultManager] isAvailableAllFunction]) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } // if ([tv isEqual:rightSideController.snapshotTableView]) { // NSArray *controllers = [[rightSideController.snapshotArrayController arrangedObjects] objectsAtIndexes:rowIndexes]; // [controllers makeObjectsPerformSelector:@selector(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 defaultManager].isAvailableAllFunction) { // [[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) } } // NSData *tiffData = [page TIFFDataForRect:[page boundsForBox:[pdfView displayBox]]]; // NSPasteboard *pboard = [NSPasteboard generalPasteboard]; // NSPasteboardItem *pboardItem = [[[NSPasteboardItem alloc] init] autorelease]; // if ([[pdfView document] allowsPrinting]) // [pboardItem setData:[page dataRepresentation] forType:NSPasteboardTypePDF]; // [pboardItem setData:tiffData forType:NSPasteboardTypeTIFF]; // [pboard clearContents]; // [pboard writeObjects:[NSArray arrayWithObjects:pboardItem, nil]]; } } // else if ([tv isEqual:leftSideController.findTableView]) { // NSMutableString *string = [NSMutableString string]; // [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { // PDFSelection *match = [searchResults objectAtIndex:idx]; // [string appendString:@"* "]; // [string appendFormat:NSLocalizedString(@"Page %@", @""), [match firstPageLabel]]; // [string appendFormat:@": %@\n", [[match contextString] string]]; // }]; // NSPasteboard *pboard = [NSPasteboard generalPasteboard]; // [pboard clearContents]; // [pboard writeObjects:[NSArray arrayWithObjects:string, nil]]; // } else if ([tv isEqual:leftSideController.groupedFindTableView]) { // NSMutableString *string = [NSMutableString string]; // [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { // SKGroupedSearchResult *result = [groupedSearchResults objectAtIndex:idx]; // NSArray *matches = [result matches]; // [string appendString:@"* "]; // [string appendFormat:NSLocalizedString(@"Page %@", @""), [[result page] displayLabel]]; // [string appendString:@": "]; // [string appendFormat:NSLocalizedString(@"%ld Results", @""), (long)[matches count]]; // [string appendFormat:@":\n\t%@\n", [[matches valueForKeyPath:@"contextString.string"] componentsJoinedByString:@"\n\t"]]; // }]; // NSPasteboard *pboard = [NSPasteboard generalPasteboard]; // [pboard clearContents]; // [pboard writeObjects:[NSArray arrayWithObjects:string, nil]]; // } } /* #pragma mark dragging - (NSArray *)tableView:(NSTableView *)tv namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes { if ([tv isEqual:leftSideController.thumbnailTableView]) { __block NSMutableArray *fileURLArray = [NSMutableArray array]; if (rowIndexes.count > 1) { NSString *docmentName = @""; NSString * tFileName = [NSString stringWithFormat:@"%@",[self fileNameWithSelectedPages:rowIndexes]]; PDFDocument *pdf = [[[PDFDocument alloc] init] autorelease]; [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { if (idx != NSNotFound && [[pdfView document] isLocked] == NO) { PDFPage *copyPage = [[[pdfView document] pageAtIndex:idx] copy]; [pdf insertPage:copyPage atIndex:pdf.pageCount]; [copyPage release]; } }]; NSURL *fileURL = [[[dropDestination URLByAppendingPathComponent:tFileName] URLByAppendingPathExtension:@"pdf"] uniqueFileURL]; docmentName = fileURL.path; BOOL success = [pdf writeToFile:docmentName]; if(success) { [fileURLArray addObject:[fileURL lastPathComponent]]; } } else { PDFPage *page = [[pdfView document] pageAtIndex:rowIndexes.firstIndex]; NSURL *fileURL = [dropDestination URLByAppendingPathComponent:[self draggedFileNameForPage:page]]; NSString *pathExt = nil; NSData *data = nil; if ([[pdfView document] allowsPrinting]) { pathExt = @"pdf"; data = [page dataRepresentation]; } else { pathExt = @"tiff"; data = [page TIFFDataForRect:[page boundsForBox:[pdfView displayBox]]]; } fileURL = [[fileURL URLByAppendingPathExtension:pathExt] uniqueFileURL]; if ([data writeToURL:fileURL atomically:YES]) { [fileURLArray addObject:[fileURL lastPathComponent]]; } } return fileURLArray; } else if ([tv isEqual:rightSideController.snapshotTableView]) { NSUInteger idx = [rowIndexes firstIndex]; if (idx != NSNotFound) { SKSnapshotWindowController *snapshot = [self objectInSnapshotsAtIndex:idx]; PDFPage *page = [[pdfView document] pageAtIndex:[snapshot pageIndex]]; NSURL *fileURL = [[dropDestination URLByAppendingPathComponent:[self draggedFileNameForPage:page]] URLByAppendingPathExtension:@"tiff"]; fileURL = [fileURL uniqueFileURL]; if ([[[snapshot thumbnailWithSize:0.0] TIFFRepresentation] writeToURL:fileURL atomically:YES]) return [NSArray arrayWithObjects:[fileURL lastPathComponent], nil]; } } return [NSArray array]; } - (void)tableView:(NSTableView *)tv sortDescriptorsDidChange:(NSArray *)oldDescriptors { if ([tv isEqual:leftSideController.groupedFindTableView]) { [leftSideController.groupedFindArrayController setSortDescriptors:[tv sortDescriptors]]; } } #pragma mark NSTableView delegate protocol - (void)tableViewColumnDidResize:(NSNotification *)aNotification { if ([[[[aNotification userInfo] objectForKey:@"NSTableColumn"] identifier] isEqualToString:IMAGE_COLUMNID]) { if ([[aNotification object] isEqual:leftSideController.thumbnailTableView]) { [leftSideController.thumbnailTableView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [leftSideController.thumbnailTableView numberOfRows])]]; } else if ([[aNotification object] isEqual:rightSideController.snapshotTableView]) { [rightSideController.snapshotTableView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [rightSideController.snapshotTableView numberOfRows])]]; } } } - (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 ([[notification object] isEqual:leftSideController.tocOutlineView] && (mwcFlags.updatingOutlineSelection == 0)){ // mwcFlags.updatingOutlineSelection = 1; self.goToSelectedOutlineItem(nil) // mwcFlags.updatingOutlineSelection = 0; // 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() } } /* #pragma mark NSOutlineView datasource protocol - (void)outlineView:(NSOutlineView *)ov setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item{ if ([ov isEqual:rightSideController.noteOutlineView]) { PDFAnnotation *note = item; if ([note type]) { if ([[tableColumn identifier] isEqualToString:NOTE_COLUMNID]) { if ([(object ?: @"") isEqualToString:([note string] ?: @"")] == NO) [note setString:object]; } else if ([[tableColumn identifier] isEqualToString:AUTHOR_COLUMNID]) { if ([(object ?: @"") isEqualToString:([note userName] ?: @"")] == NO) [note setUserName:object]; } } } } #pragma mark Drag - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pasteboard{ if ([outlineView isEqual:leftSideController.tocOutlineView]) { if (leftSideController.tocOutlineView.selectedRowIndexes.count > 1) { return NO; } NSIndexSet *tIndex = [[[NSIndexSet alloc] initWithIndex:leftSideController.tocOutlineView.clickedRow] autorelease]; [leftSideController.tocOutlineView deselectRow:tIndex.firstIndex]; self.dragPDFOutline = items.firstObject; NSIndexSet *set = [NSIndexSet indexSetWithIndex:0]; NSData *zNSIndexSetData = [NSKeyedArchiver archivedDataWithRootObject:set]; [pasteboard declareTypes:[NSArray arrayWithObject:kKMPDFViewOutlineDragDataType] owner:self]; [pasteboard setData:zNSIndexSetData forType:kKMPDFViewOutlineDragDataType]; return YES; } return NO; } - (NSDragOperation)outlineView:(NSOutlineView *)ov validateDrop:(id )info proposedItem:(id)item proposedChildIndex:(NSInteger)anIndex { NSDragOperation dragOp = NSDragOperationNone; if ([ov isEqual:rightSideController.noteOutlineView]) { NSPasteboard *pboard = [info draggingPasteboard]; if ([pboard canReadObjectForClasses:[NSArray arrayWithObject:[NSColor class]] options:[NSDictionary dictionary]] && anIndex == NSOutlineViewDropOnItemIndex && [(PDFAnnotation *)item type] != nil) dragOp = NSDragOperationEvery; } else if ([ov isEqual:leftSideController.tocOutlineView]) { if (anIndex == -1) { dragOp = NSDragOperationNone; } else { dragOp = NSDragOperationMove; } } return dragOp; } - (BOOL)outlineView:(NSOutlineView *)ov acceptDrop:(id )info item:(id)item childIndex:(NSInteger)anIndex { if ([ov isEqual:rightSideController.noteOutlineView]) { NSPasteboard *pboard = [info draggingPasteboard]; if ([pboard canReadObjectForClasses:[NSArray arrayWithObject:[NSColor class]] options:[NSDictionary dictionary]]) { BOOL isShift = ([NSEvent standardModifierFlags] & NSEventModifierFlagShift) != 0; BOOL isAlt = ([NSEvent standardModifierFlags] & NSEventModifierFlagOption) != 0; [item setColor:[NSColor colorFromPasteboard:pboard] alternate:isAlt updateDefaults:isShift]; return YES; } } else if ([ov isEqual:leftSideController.tocOutlineView]) { if (anIndex < 0) { return NO; } PDFOutline *outline = item; //root,drag item to root if (!outline.parent) { //fetch root PDFOutline *root = self.dragPDFOutline; while (root.parent) { root = root.parent; } if ([self.dragPDFOutline.parent isEqual:root]) { if (self.dragPDFOutline.index > (NSUInteger)anIndex) { [self dragPDFOutline:self.dragPDFOutline toIndex:anIndex newParentOutline:root]; } else { [self dragPDFOutline:self.dragPDFOutline toIndex:anIndex - 1 newParentOutline:root]; } } else { [self dragPDFOutline:self.dragPDFOutline toIndex:anIndex newParentOutline:root]; } } else { //在同一个层级内移动 if ([self.dragPDFOutline.parent isEqual:item]) { if (self.dragPDFOutline.index) { if (self.dragPDFOutline.index > (NSUInteger)anIndex) { [self dragPDFOutline:self.dragPDFOutline toIndex:anIndex newParentOutline:outline]; }else{ [self dragPDFOutline:self.dragPDFOutline toIndex:anIndex - 1 newParentOutline:outline]; } } else { return NO; } } else { PDFOutline *tOutlline = outline; BOOL isContains = NO; while (tOutlline) { if ([tOutlline isEqual:self.dragPDFOutline]) { isContains = YES; break; } tOutlline = tOutlline.parent; } if (!isContains) { [self dragPDFOutline:self.dragPDFOutline toIndex:anIndex newParentOutline:outline]; } } } return YES; } return NO; } #pragma mark NSOutlineView delegate protocol - (NSCell *)outlineView:(NSOutlineView *)ov dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item { if ([ov isEqual:rightSideController.noteOutlineView] && tableColumn == nil && [(PDFAnnotation *)item type] == nil) { return [[ov tableColumnWithIdentifier:NOTE_COLUMNID] dataCellForRow:[ov rowForItem:item]]; } return [tableColumn dataCellForRow:[ov rowForItem:item]]; } - (void)outlineView:(NSOutlineView *)ov willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item { if ([ov isEqual:leftSideController.tocOutlineView] && [ov selectionHighlightStyle] == NSTableViewSelectionHighlightStyleRegular && [ov isRowSelected:[ov rowForItem:item]]) { [cell setBackgroundStyle:NSBackgroundStyleLowered]; } } - (BOOL)outlineView:(NSOutlineView *)ov shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item{ if ([ov isEqual:rightSideController.noteOutlineView]) { if (tableColumn == nil) { if ([pdfView hideNotes] == NO && [[(SKNoteText *)item note] isNote]) { PDFAnnotation *annotation = [(SKNoteText *)item note]; [pdfView scrollAnnotationToVisible:annotation]; [pdfView setActiveAnnotation:annotation]; [self showNote:annotation]; SKNoteWindowController *noteController = (SKNoteWindowController *)[self windowControllerForNote:annotation]; [[noteController window] makeFirstResponder:[noteController textView]]; [[noteController textView] selectAll:nil]; } return NO; } else if ([[tableColumn identifier] isEqualToString:NOTE_COLUMNID] || [[tableColumn identifier] isEqualToString:AUTHOR_COLUMNID]) { return YES; } } return NO; } - (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]; } } - (NSString *)outlineView:(NSOutlineView *)ov toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tableColumn item:(id)item mouseLocation:(NSPoint)mouseLocation { if ([ov isEqual:rightSideController.noteOutlineView] && (tableColumn == nil || [[tableColumn identifier] isEqualToString:NOTE_COLUMNID])) { return [item string]; } return @""; } - (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)updateSelectRowHeight{ CGFloat rowHeight = 0; PDFOutline *outline = [leftSideController.tocOutlineView itemAtRow:leftSideController.tocOutlineView.selectedRow]; if (!outline){ return; } 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]; NSInteger *row = [leftSideController.tocOutlineView selectedRow]; NSTableCellView *viewS = [leftSideController.tocOutlineView viewAtColumn:0 row:row makeIfNecessary:YES]; NSTableColumn *tableColumn = [leftSideController.tocOutlineView tableColumnWithIdentifier:LABEL_COLUMNID]; // id cell = [tableColumn dataCell]; id cell = [tableColumn dataCellForRow:row]; [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]; } - (NSInteger)getNum:(PDFOutline *)ol{ NSInteger num = 0; PDFOutline *outLine = [ol parent]; do { outLine = [outLine parent]; if (outLine){ num ++; } } while (outLine); return num; } - (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 } /* - (NSArray *)outlineView:(NSOutlineView *)ov typeSelectHelperSelectionStrings:(SKTypeSelectHelper *)typeSelectHelper { if ([ov isEqual:rightSideController.noteOutlineView]) { NSInteger i, count = [rightSideController.noteOutlineView numberOfRows]; NSMutableArray *texts = [NSMutableArray arrayWithCapacity:count]; for (i = 0; i < count; i++) { id item = [rightSideController.noteOutlineView itemAtRow:i]; NSString *string = [item string]; [texts addObject:string ?: @""]; } return texts; } else if ([ov isEqual:leftSideController.tocOutlineView]) { NSInteger i, count = [leftSideController.tocOutlineView numberOfRows]; NSMutableArray *array = [NSMutableArray arrayWithCapacity:count]; for (i = 0; i < count; i++) [array addObject:[[(PDFOutline *)[leftSideController.tocOutlineView itemAtRow:i] label] lossyStringUsingEncoding:NSASCIIStringEncoding]]; return array; } return nil; } - (void)outlineView:(NSOutlineView *)ov typeSelectHelper:(SKTypeSelectHelper *)typeSelectHelper didFailToFindMatchForSearchString:(NSString *)searchString { 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]]; } } - (void)outlineView:(NSOutlineView *)ov typeSelectHelper:(SKTypeSelectHelper *)typeSelectHelper updateSearchString:(NSString *)searchString { 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: - 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.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]; } } } // MARK: - KMTocOutlineViewDelegate extension KMLeftSideViewController: KMTocOutlineViewDelegate { // func outlineView(_ anOutlineView: NSOutlineView, highlightLevelForRow row: Int) -> Int { // if ([ov isEqual:leftSideController.tocOutlineView]) { // NSInteger numRows = [ov numberOfRows]; // NSUInteger firstPage = [[[ov itemAtRow:row] page] pageIndex]; // NSUInteger lastPage = row + 1 < numRows ? [[[ov itemAtRow:row + 1] page] pageIndex] : [[self pdfDocument] pageCount]; // NSRange range = NSMakeRange(firstPage, MAX(1LU, lastPage - firstPage)); // NSUInteger i, iMax = [lastViewedPages count]; // for (i = 0; i < iMax; i++) { // if (NSLocationInRange((NSUInteger)[lastViewedPages pointerAtIndex:i], range)) // return i; // } // } // return NSNotFound; // } func outlineView(_ anOutlineView: NSOutlineView, imageContextForItem item: Any?) -> AnyObject? { if anOutlineView.isEqual(to: self.tocOutlineView) { if item == nil { return true as AnyObject } if item is CPDFOutline { return (item as! CPDFOutline).destination } } return nil } } extension KMLeftSideViewController: KMNoteOutlineViewDelegate { func outlineView(_ anOutlineView: NSOutlineView, canResizeRowByItem item: AnyObject?) -> Bool? { if anOutlineView.isEqual(to: self.noteOutlineView) { return true } return false } func outlineView(_ anOutlineView: NSOutlineView, setHeight newHeight: CGFloat, ofRowByItem item: AnyObject?) { // [rowHeights setFloat:newHeight forKey:item]; } func outlineView(_ anOutlineView: NSOutlineView, didChangeHiddenOfTableColumn aTableColumn: NSTableColumn) { // if (mwcFlags.autoResizeNoteRows && // [ov isEqual:rightSideController.noteOutlineView] && // [[tableColumn identifier] isEqualToString:NOTE_COLUMNID]) { // [rowHeights removeAllFloats]; // [rightSideController.noteOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [rightSideController.noteOutlineView numberOfRows])]]; // } } func outlineViewCommandKeyPressedDuringNavigation(_ anOutlineView: NSOutlineView) { // PDFAnnotation *annotation = [[self selectedNotes] lastObject]; // if (annotation) { // [pdfView scrollAnnotationToVisible:annotation]; // [pdfView setActiveAnnotation:annotation]; // } } } // MARK: - Menu Item Actions extension KMLeftSideViewController { @objc func cutPage(_ sender: AnyObject?) { // if (![IAPProductsManager defaultManager].isAvailableAllFunction) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } self._tableView(self.thumbnailTableView, cutRowsWithIndexes: self.thumbnailTableView.selectedRowIndexes) } @objc func copyPage(_ sender: AnyObject?) { self.tableView(self.thumbnailTableView, copyRowsWithIndexes: self.thumbnailTableView.selectedRowIndexes) } @objc func pastePage(_ sender: AnyObject?) { self.tableView(self.thumbnailTableView, pasteFromPasteboard: nil) } @objc func deletePage(_ sender: AnyObject?) { // if (![IAPProductsManager defaultManager].isAvailableAllFunction) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } self.tableView(self.thumbnailTableView, deleteRowsWithIndexes: self.thumbnailTableView.selectedRowIndexes) } @objc func rotatePageMenuAction(_ sender: AnyObject?) { // if (![IAPProductsManager defaultManager].isAvailableAllFunction) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } // self.tableView(self.thumbnailTableView, rotateRowsWithIndexes: self.thumbnailTableView.selectedRowIndexes as NSIndexSet) } @objc func quickInsert(_ sender: AnyObject?) { // if (![[IAPProductsManager defaultManager] isAvailableAllFunction]) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } let idx = self.thumbnailTableView.selectedRowIndexes.first ?? NSNotFound if idx == NSNotFound || idx >= self.listView.document.pageCount { return } let result = self.listView.insertPage(KMNormalBlankSize, at: idx+1) if result == false { return } var selectedIndexSet = IndexSet() selectedIndexSet.insert(idx+1) self.insertPages(selectedIndexSet, pageAt: idx) } @objc func insert(_ sender: AnyObject?) { // if (![[IAPProductsManager defaultManager] isAvailableAllFunction]) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } guard let document = self.listView.document else { return } let idx = self.thumbnailTableView.selectedRowIndexes.first ?? NSNotFound if idx == NSNotFound || idx >= document.pageCount { return } if document.allowsCopying == false || document.allowsPrinting == false { Task { _ = await KMAlertTool.runModel(message: KMLocalizedString("This is a secured document. Editing is not permitted.", nil)) } return } let insertVC = KMPDFEditInsertBlankPageWindow(document: document) insertVC.insertLocation = 3 insertVC.currentPage = idx + 1 insertVC.callback = { [weak self] pdfDoc, _, pages, insertI in if let _pages = pages { // mm 单位的大小 guard let _winC = self?.kmCurrentWindowC as? KMPDFEditInsertBlankPageWindow else { self?.km_endSheet() return } var pageSize = _winC.pageSize let direction = _winC.pageRotation == 0 ? 0 : 1 if (direction == 0) { // 纵向 if (pageSize.width > pageSize.height) { // 需要交换 let tmp = pageSize.width pageSize.width = pageSize.height pageSize.height = tmp } else { // no things. } } else { // 横向 if (pageSize.width > pageSize.height) { // no things. } else { // 需要交换 let tmp = pageSize.width pageSize.width = pageSize.height pageSize.height = tmp } } /// 插入位置 let document = CPDFDocument() document?.insertPage(pageSize, at: 0) if let page: CPDFPage = (document?.page(at: 0)) { self?.insertPage(page, pageAt: insertI) } } self?.km_quick_endSheet() } self.km_beginSheet(windowC: insertVC) } @objc func insertPDF(_ sender: AnyObject?) { // if (![[IAPProductsManager defaultManager] isAvailableAllFunction]) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } guard let document = self.listView.document else { return } let idx = self.thumbnailTableView.selectedRowIndexes.first ?? NSNotFound if idx == NSNotFound || idx >= document.pageCount { return } if document.allowsCopying == false || document.allowsPrinting == false { Task { _ = await KMAlertTool.runModel(message: KMLocalizedString("This is a secured document. Editing is not permitted.", nil)) } return } let panel = NSOpenPanel() panel.allowedFileTypes = ["pdf"] panel.beginSheetModal(for: self.view.window!) { response in if response == .cancel { return } for fileURL in panel.urls { let pdfDoc = CPDFDocument(url: fileURL) if let data = pdfDoc?.isLocked, data { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { let com = PasswordWindowController(windowNibName: "PasswordWindowController") com.fileURL = fileURL self.km_beginSheet(windowC: com) com.closeCallBack = { [unowned self] password in self.km_quick_endSheet() // } if (password.isEmpty == false) { let insertVC = KMPDFEditInsertPageWindow(document: document, path: fileURL, password: password) insertVC.insertLocation = 3 insertVC.currentPage = idx + 1 self.km_beginSheet(windowC: insertVC) insertVC.callback = { [weak self] pdfDoc, pwd, pages, insertIdx in var indexs = IndexSet() guard let _winC = self?.kmCurrentWindowC as? KMPDFEditInsertPageWindow, _winC.insertDocument != nil else { self?.km_quick_endSheet() return } let doc = _winC.insertDocument! let fileAttribute = _winC.fileAttribute var insertIndex = insertIdx var insertPages: [CPDFPage] = [] for number in fileAttribute.fetchSelectPages() { if let page = doc.page(at: UInt(number.intValue-1)) { insertPages.append(page) indexs.insert(insertIndex) insertIndex += 1 } } for (i, page) in insertPages.enumerated() { self?.listView.document.insertPageObject(page, at: UInt(insertIdx + i)) } self?.insertPages(indexs, pageAt: insertIdx) self?.km_quick_endSheet() } } } } } else { let insertVC = KMPDFEditInsertPageWindow(document: document, path: fileURL) insertVC.insertLocation = 3 insertVC.currentPage = idx + 1 self.km_beginSheet(windowC: insertVC) insertVC.callback = { [weak self] pdfDoc, pwd, pages, insertIdx in var indexs = IndexSet() guard let _winC = self?.kmCurrentWindowC as? KMPDFEditInsertPageWindow, _winC.insertDocument != nil else { self?.km_quick_endSheet() return } let doc = _winC.insertDocument! let fileAttribute = _winC.fileAttribute var insertIndex = insertIdx var insertPages: [CPDFPage] = [] for number in fileAttribute.fetchSelectPages() { if let page = doc.page(at: UInt(number.intValue-1)) { insertPages.append(page) indexs.insert(insertIndex) insertIndex += 1 } } for (i, page) in insertPages.enumerated() { self?.listView.document.insertPageObject(page, at: UInt(insertIdx + i)) } self?.insertPages(indexs, pageAt: insertIdx) self?.km_quick_endSheet() } } } } } @objc func extractPage(_ sender: AnyObject?) { // if (![IAPProductsManager defaultManager].isAvailableAllFunction) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } self.tableView(self.thumbnailTableView, extractRowsWithIndexes: self.thumbnailTableView.selectedRowIndexes) } @objc func pageEdit(_ sender: AnyObject?) { self.delegate?.controller?(controller: self, itemClick: nil, itemKey: .pageEdit, params: nil) } @objc func displayPageSize(_ sender: AnyObject?) { // if (![IAPProductsManager defaultManager].isAvailableAllFunction) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } self.isDisplayPageSize = !self.isDisplayPageSize; UserDefaults.standard.setValue(self.isDisplayPageSize, forKey: "kKMThumbnailDisplayPageSizeKey") UserDefaults.standard.synchronize() Task { @MainActor in self.thumbnailTableView.reloadData() } } @objc func sharePage(_ sender: AnyObject?) { // if (![IAPProductsManager defaultManager].isAvailableAllFunction) { // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil]; // return; // } 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 pdf = CPDFDocument() for page in pages { // PDFPage *copyPage = [[page copy] autorelease]; if let _page = page as? CPDFPage { pdf?.insertPageObject(_page, at: pdf?.pageCount ?? 0) } } // NSString * = nil; var fileName = "" // NSString * = [self.pdfDocument.documentURL.lastPathComponent stringByDeletingPathExtension]; var documentFileName = document.documentURL?.deletingPathExtension().lastPathComponent ?? "" var tName = self.fileNameWithSelectedPages(self.thumbnailTableView.selectedRowIndexes) if (tName.count > 50) { tName = tName.substring(to: 50) } if (pages.count > 1) { fileName = String(format: "%@ pages %@", documentFileName, tName) } else { fileName = String(format: "%@ page %@", documentFileName, tName) } let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true) var cachesDir = paths.first ?? "" cachesDir = "\(cachesDir)/\(fileName).pdf" var array = NSMutableArray() let url = URL(fileURLWithPath: cachesDir) array.add(url) let vc = KMProgressWindowController() // // [NSApp beginSheet:[vc window] // // modalForWindow:self.window // // modalDelegate:nil // // didEndSelector:nil // // contextInfo:NULL]; self.view.window?.beginSheet(vc.window!, completionHandler: { [unowned self] returnCode in NSApp.endSheet(self.view.window!, returnCode: returnCode.rawValue) }) DispatchQueue.global().async { let sucess = pdf?.write(toFile: cachesDir) ?? false if (sucess) { DispatchQueue.main.async { // [[sender representedObject] performWithItems:array]; let represent : NSSharingService = (sender as! NSMenuItem).representedObject as! NSSharingService represent.perform(withItems: [url]) NSApp.endSheet(vc.window!) vc.close() } } else { NSApp.endSheet(vc.window!) vc.close() } } // [vc release]; } @objc func toggleCaseInsensitiveSearch(_ sender: AnyObject?) { KMPrint("KMLeftSideViewController-toggleCaseInsensitiveSearch...") } @objc func toggleCaseInsensitiveNoteSearch(_ sender: AnyObject?) { KMPrint("KMLeftSideViewController-toggleCaseInsensitiveNoteSearch...") } @objc func searchNotes(_ sender: AnyObject?) { self.noteSearchMode = false if self.findState == .note { if self.noteSearchField.isEqual(to: sender) { self.noteSearchMode = true } self.updateNoteFilterPredicate() } else { self.updateSnapshotFilterPredicate() } let textfield = sender as? NSSearchField if let data = textfield?.stringValue.isEmpty, data == false { let findPboard = NSPasteboard(name: .find) findPboard.clearContents() // findPboard.writeObjects([textfield!.stringValue]) } } @objc func toggleSelectedSnapshots(_ sender: AnyObject?) { let indexs = self.snapshotTableView.selectedRowIndexes if indexs.isEmpty { return } let model = self.snapshots[indexs.last!] let windowC = model.windowC if let data = windowC?.window?.isVisible, data { windowC?.miniaturize() } else { windowC?.deminiaturize() } var rowIndexSet = IndexSet() let row = self.snapshotTableView.selectedRow if row >= 0 && row < self.snapshots.count { rowIndexSet.insert(row) } var columnIndexSet = IndexSet() columnIndexSet.insert(0) self.snapshotTableView.reloadData(forRowIndexes: rowIndexSet, columnIndexes: columnIndexSet) } @objc func toggleOutlineCaseInsensitiveSearch(_ sender: AnyObject?) { KMPrint("KMLeftSideViewController-toggleOutlineCaseInsensitiveSearch...") } private 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: - Snapshot @objc func menuItemClick_ExportPNG(_ sender: AnyObject?) { guard let snapshot = (sender as? NSMenuItem)?.representedObject as? KMSnapshotWindowController else { return } let image = snapshot.thumbnailWithSize(0) NSPanel.savePanel_data_success(self.view.window!, imageData: image?.pngData(), allowedTypes: ["png"]) { url in NSWorkspace.shared.selectFile(url.path, inFileViewerRootedAtPath: "") } } @objc func menuItemClick_ExportJPG(_ sender: AnyObject?) { guard let snapshot = (sender as? NSMenuItem)?.representedObject as? KMSnapshotWindowController else { return } let image = snapshot.thumbnailWithSize(0) NSPanel.savePanel_data_success(self.view.window!, imageData: image?.jpgData(), allowedTypes: ["jpg"]) { url in NSWorkspace.shared.selectFile(url.path, inFileViewerRootedAtPath: "") } } @objc func menuItemClick_ExportPDF(_ sender: AnyObject?) { guard let snapshot = (sender as? NSMenuItem)?.representedObject as? KMSnapshotWindowController else { return } if let image = snapshot.thumbnailWithSize(0) { let document = CPDFDocument() _ = document?.km_insert(image: image, at: 0) NSPanel.savePanel_pdf_success(self.view.window!, document: document) { url in NSWorkspace.shared.selectFile(url.path, inFileViewerRootedAtPath: "") } } } @objc func menuItemClick_Print(_ sender: AnyObject?) { guard let snapshot = (sender as? NSMenuItem)?.representedObject as? KMSnapshotWindowController else { return } if let image = snapshot.thumbnailWithSize(0) { self.delegate?.controller?(controller: self, itemClick: nil, itemKey: .print, params: image) } } @objc func menuItemClick_SelectAll(_ sender: AnyObject?) { let selected = self.snapshotListIsAllSelected() for model in self.snapshots { model.isSelected = !selected } Task { @MainActor in self.snapshotTableView.reloadData() } } @objc func deleteAllSnapshot(_ sender: AnyObject?) { for model in self.snapshots { model.windowC?.close() } } @objc func hideSnapshot(_ sender: AnyObject?) { guard let snapshot = (sender as? NSMenuItem)?.representedObject as? KMSnapshotWindowController else { return } if let data = snapshot.window?.isVisible, data { snapshot.miniaturize() } } @objc func showSnapshot(_ sender: AnyObject?) { guard let snapshot = (sender as? NSMenuItem)?.representedObject as? KMSnapshotWindowController else { return } if let data = snapshot.window?.isVisible, data { snapshot.window?.orderFront(nil) } else { snapshot.deminiaturize() } } @objc func deleteSnapshot(_ sender: AnyObject?) { guard let snapshot = (sender as? NSMenuItem)?.representedObject as? KMSnapshotWindowController else { return } snapshot.close() } @objc func menuItemClick_Copy(_ sender: AnyObject?) { guard let snapshot = (sender as? NSMenuItem)?.representedObject as? KMSnapshotWindowController else { return } let image = snapshot.thumbnailWithSize(0) if let tiffData = image?.tiffRepresentation { let pasteboardItem = NSPasteboardItem() pasteboardItem.setData(tiffData, forType: .tiff) let pboard = NSPasteboard.general pboard.clearContents() pboard.writeObjects([pasteboardItem]) } } func updateSnapshotFilterPredicate() { let searchString = self.snapshotSearchField.stringValue self.searchSnapshots.removeAll() if self.findState == .snapshot && searchString.isEmpty == false { self.searchSnapshots = self.snapshots.filter({ model in let data = model.windowC?.string.contains(searchString) ?? false return data }) } Task { @MainActor in self.updataLeftSideSnapView() self.snapshotTableView.reloadData() } } func snapshotListIsAllSelected() -> Bool { if self.snapshots.isEmpty { return false } for model in self.snapshots { if model.isSelected == false { return false } } return true } } // MARK: - Undo & Redo extension KMLeftSideViewController { @objc dynamic func tableView(_ tv: NSTableView, rotateRowsWithIndexes rowIndexes: NSIndexSet) { if tv.isEqual(to: self.thumbnailTableView) { for idx in rowIndexes { if (idx != NSNotFound) { let page = self.listView.document.page(at: UInt(idx)) (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).rotatePage(page, pageAt: idx) page?.rightRotate() self.listView.layoutDocumentView() self.resetThumbnails() // NSInteger pageIndex = MIN(idx, [[pdfView document] pageCount]-1); // [pdfView goToPage:[[pdfView document] pageAtIndex:pageIndex]]; } self.thumbnailTableView.selectRowIndexes(rowIndexes as IndexSet, byExtendingSelection: true) } } } @objc dynamic func rotatePage(_ page: CPDFPage?, pageAt index: Int) { (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).tableView(self.thumbnailTableView, rotateRowsWithIndexes: NSIndexSet(index: index)) page?.leftRotate() self.listView.layoutDocumentView() self.resetThumbnails() } @objc dynamic func insertPage(_ page: CPDFPage, pageAt index: Int) { (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).tableView(self.thumbnailTableView, deleteRowsWithIndexes: IndexSet(integer: index)) self.listView.document.insertPageObject(page, at: UInt(index)) self.listView.layoutDocumentView() // [pageLabels setArray:[[pdfView document] pageLabels]]; self.resetThumbnails() let pageIndex = min(index, Int((self.listView.document?.pageCount ?? 0))-1) self.listView.go(toPageIndex: pageIndex, animated: false) } @objc dynamic func insertPages(_ selectedIndexSet: IndexSet, pageAt index: Int) { (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).deletePages(selectedIndexSet, pageAt: index) self.listView.layoutDocumentView() // [pageLabels setArray:[[pdfView document] pageLabels]]; self.resetThumbnails() if let pageIndex = selectedIndexSet.first { self.listView.go(toPageIndex: pageIndex, animated: false) } } @objc dynamic func deletePages(_ selectedIndexSet: IndexSet, pageAt index: Int) { (self.listView.undoManager?.prepare(withInvocationTarget: self) as AnyObject).insertPages(selectedIndexSet, pageAt: index) for idx in selectedIndexSet { if idx < self.listView.document.pageCount { self.listView.document.removePage(at: UInt(idx)) } } self.listView.layoutDocumentView() // [pageLabels setArray:[[pdfView document] pageLabels]]; self.resetThumbnails() let pageIndex = min(index, Int(self.listView.document.pageCount)-1) self.listView.go(toPageIndex: pageIndex, animated: false) } } // 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 "" } }