// // KMLeftSideViewController+Action.swift // PDF Reader Pro // // Created by tangchao on 2023/12/22. // import Foundation // MARK: - Action extension KMLeftSideViewController { @IBAction func leftSideViewMoreButtonAction(_ sender: AnyObject?) { guard let button = sender as? NSButton else { NSSound.beep() return } let tag = button.tag 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 if let data = NSApp.currentEvent { NSMenu.popUpContextMenu(menu, with: data, for: button) } } else if (tag == 302) { self.outlineMoreMenu(button) } else if (tag == 304) { self.annoListMoreMenu(button) } } func searchFieldChangeAction(_ editContent: String) { if self.findState == .note { self.updateNoteFilterPredicate() } else { self.updateSnapshotFilterPredicate() } if editContent.count > 0 { let findPboard = NSPasteboard(name: .find) findPboard.clearContents() findPboard.writeObjects([editContent as NSPasteboardWriting]) } } @IBAction func search(_ sender: NSSearchField) { if sender.stringValue.isEmpty { self.applySearchTableHeader("") } self.delegate?.searchAction?(searchString: sender.stringValue, isCase: self.mwcFlags.caseInsensitiveSearch == 1) } // RefreshUI public func refreshUIForDocumentChanged() { if self.type.methodType == .Thumbnail { self.resetThumbnails(ks: false) } else if self.type.methodType == .Outline { Task { @MainActor in self.tocOutlineView.reloadData() } } else if self.type.methodType == .Annotation { self.note_reloadDataIfNeed() } else if self.type.methodType == .snapshot { self.reloadSnapshotDataIfNeed() } else if self.type.methodType == .Search { if self.searchField.stringValue.isEmpty == false { self.search(self.searchField) } } } } // MARK: - Double Action extension KMLeftSideViewController { @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) } // MARK: - KMInterfaceThemeChangedProtocol override func interfaceThemeDidChanged(_ appearance: NSAppearance.Name) { super.interfaceThemeDidChanged(appearance) Task { @MainActor in self.updateViewColor() self.leftView.interfaceThemeDidChanged(appearance) if self.type.methodType == .Search { if self.findPaneState == .singular { self.findTableView.reloadData() } } } } } // MARK: - NSMenuDelegate extension KMLeftSideViewController: NSMenuDelegate { func menuNeedsUpdate(_ menu: NSMenu) { menu.removeAllItems() if menu.isEqual(to: self.tocOutlineView.menu) { self.outlineListMenu(menu) return } var item: NSMenuItem? if menu.isEqual(to: self.thumbnailTableView.menu) { let row = self.thumbnailTableView.clickedRow if self.pdfDocument()?.documentURL == nil || self.thumbnailTableView.selectedRowIndexes.contains(row) == false{ return } let isLocked = self.isLocked() let pageCount = self.pageCount() if (row != -1 && isLocked == false) { if(self.thumbnailTableView.selectedRowIndexes.count == pageCount) { item = menu.addItem(title: KMLocalizedString("Cut", "Menu item title"), action: nil, target: self) } else { item = menu.addItem(title: KMLocalizedString("Cut", "Menu item title"), action: #selector(cutPage), target: self) } item?.representedObject = IndexSet(integer: row) item = menu.addItem(title: KMLocalizedString("Copy", "Menu item title"), action: #selector(copyPage), target: self) item?.representedObject = IndexSet(integer: row) if (self.copyPages.count > 0) { item = menu.addItem(title: KMLocalizedString("Paste", "Menu item title"), action: #selector(pastePage), target: self) }else{ item = menu.addItem(title: KMLocalizedString("Paste", "Menu item title"), action: nil, target: self) } item?.representedObject = IndexSet(integer: row) menu.addItem(.separator()) if(self.thumbnailTableView.selectedRowIndexes.count == pageCount) { item = menu.addItem(title: KMLocalizedString("Delete", "Menu item title"), action: nil, target: self) } else { item = menu.addItem(title: KMLocalizedString("Delete", "Menu item title"), action: #selector(deletePage), target: self) } item?.representedObject = IndexSet(integer: row) menu.addItem(.separator()) item = menu.addItem(title: KMLocalizedString("Rotate", "Menu item title"), action: #selector(rotatePageMenuAction), target: self) item?.representedObject = IndexSet(integer: row) item = menu.addItem(title: KMLocalizedString("Insert", "Menu item title"), action: nil, target: self) let subMenu = NSMenu() _ = subMenu.addItem(title: KMLocalizedString("Blank Page", "Menu item title"), action: #selector(quickInsert), target: self) _ = subMenu.addItem(title: KMLocalizedString("Blank Page - Custom...", "Menu item title"), action: #selector(insert), target: self) _ = subMenu.addItem(title: KMLocalizedString("From PDF", "Menu item title"), action: #selector(insertPDF), target: self) item?.submenu = subMenu item?.representedObject = IndexSet(integer: row) item = menu.addItem(title: KMLocalizedString("Extract", "Menu item title"), action: #selector(extractPage), target: self) item?.representedObject = IndexSet(integer: row) item = menu.addItem(title: KMLocalizedString("Page Edit", "Menu item title"), action: #selector(pageEdit), target: self) menu.addItem(.separator()) var displayStr = "" if (self.isDisplayPageSize) { displayStr = KMLocalizedString("Hide Page Size", "Menu item title") } else { displayStr = KMLocalizedString("Display Page Size", "Menu item title") } item = menu.addItem(title: displayStr, action: #selector(displayPageSize), target: self) item?.representedObject = IndexSet(integer: row) if let doct = self.pdfDocument() { item = menu.addItem(title: KMLocalizedString("Share", "Menu item title"), action: nil, target: self) var tName = self.fileNameWithSelectedPages(self.thumbnailTableView.selectedRowIndexes) if (tName.count > 50) { tName = tName.substring(to: 50) } item?.submenu = NSSharingServicePicker.menu(forSharingItems: [doct.documentURL as Any], subjectContext: tName, withTarget: self, selector: #selector(sharePage), serviceDelegate: nil) } } } else if menu.isEqual(to: self.findTableView.menu) { var rowIndexes = self.findTableView.selectedRowIndexes let row = self.findTableView.clickedRow if (row != -1) { if rowIndexes.contains(row) == false { rowIndexes = IndexSet(integer: row) } var selections: [CPDFSelection] = [] for (i, data) in self.searchResults.enumerated() { if rowIndexes.contains(i) { selections.append(data.selection) } } let hideNotes = self.hideNotes() let allowsNotes = self.allowsNotes() if hideNotes == false && allowsNotes { item = menu.addItem(withTitle: KMLocalizedString("Add New Circle", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.circle.rawValue) item?.representedObject = selections item = menu.addItem(withTitle: KMLocalizedString("Add New Rectangle", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.square.rawValue) item?.representedObject = selections item = menu.addItem(withTitle: KMLocalizedString("Add New Highlight", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.highlight.rawValue) item?.representedObject = selections item = menu.addItem(withTitle: KMLocalizedString("Add New Underline", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.underline.rawValue) item?.representedObject = selections item = menu.addItem(withTitle: KMLocalizedString("Add New Strikethrough", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.strikeOut.rawValue) item?.representedObject = selections } } } else if menu.isEqual(to: self.groupedFindTableView.menu) { var rowIndexes = self.groupedFindTableView.selectedRowIndexes let row = self.groupedFindTableView.clickedRow if (row != -1) { if rowIndexes.contains(row) == false { rowIndexes = IndexSet(integer: row) } // NSArray *selections = [[[leftSideController.groupedFindArrayController arrangedObjects] objectsAtIndexes:rowIndexes] valueForKeyPath:@"@unionOfArrays.matches"]; var selections: [CPDFSelection] = [] for (i, data) in self.groupSearchResults.enumerated() { if rowIndexes.contains(i) { for searchM in data.datas { selections.append(searchM.selection) } } } item = menu.addItem(title: KMLocalizedString("Select", "Menu item title"), action: #selector(selectSelections), target: self) item?.representedObject = selections menu.addItem(.separator()) let hideNotes = self.hideNotes() let allowsNotes = self.allowsNotes() if hideNotes == false && allowsNotes { item = menu.addItem(withTitle: KMLocalizedString("Add New Circle", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.circle.rawValue) item?.representedObject = selections item = menu.addItem(withTitle: KMLocalizedString("Add New Rectangle", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.square.rawValue) item?.representedObject = selections item = menu.addItem(withTitle: KMLocalizedString("Add New Highlight", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.highlight.rawValue) item?.representedObject = selections item = menu.addItem(withTitle: KMLocalizedString("Add New Underline", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.underline.rawValue) item?.representedObject = selections item = menu.addItem(withTitle: KMLocalizedString("Add New Strikethrough", "Menu item title"), action: #selector(addAnnotationsForSelections), target: self, tag: CAnnotationType.strikeOut.rawValue) item?.representedObject = selections } } } else if menu.isEqual(to: self.snapshotTableView.menu) { let row = self.snapshotTableView.clickedRow if (row != -1) { let model = self.snapshots[row] let controller = model.windowC if let data = controller?.window?.isVisible, data { item = menu.addItem(title: KMLocalizedString("Hide", "Menu item title"), action: #selector(hideSnapshot), target: self) item?.representedObject = controller } else { item = menu.addItem(title: KMLocalizedString("Show", "Menu item title"), action: #selector(showSnapshot), target: self) item?.representedObject = controller } menu.addItem(.separator()) item = 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 item?.submenu = subMenu item = menu.addItem(title: KMLocalizedString("Print", "Menu item title"), action: #selector(menuItemClick_Print), target: self) item?.representedObject = controller menu.addItem(.separator()) item = menu.addItem(title: KMLocalizedString("Copy", "Menu item title"), action: #selector(menuItemClick_Copy), target: self) item?.representedObject = controller menu.addItem(.separator()) item = menu.addItem(title: KMLocalizedString("Delete", "Menu item title"), action: #selector(deleteSnapshot), target: self) item?.representedObject = controller item = menu.addItem(title: KMLocalizedString("Delete All Snapshots", "Menu item title"), action: #selector(deleteAllSnapshot), target: self) item?.representedObject = controller } } else if menu.isEqual(to: self.noteOutlineView.menu) { self.annoListMenu(menu) } } } // MARK: - NSMenuItemValidation extension KMLeftSideViewController: NSMenuItemValidation { func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { let isLocked = self.isLocked() if isLocked { return false } let action = menuItem.action if (action == #selector(toggleCaseInsensitiveSearch)) { let state = KMDataManager.ud_integer(forKey: SKCaseInsensitiveSearchKey) menuItem.state = state == 1 ? .on : .off return true } else if (action == #selector(toggleWholeWordSearch)) { menuItem.state = self.mwcFlags.wholeWordSearch == 1 ? .on : .off return true } else if action == #selector(toggleCaseInsensitiveNoteSearch) { menuItem.state = self.caseInsensitiveNoteSearch ? .on : .off return true } if (action == #selector(outlineContextMenuItemClicked_AddEntry) || action == #selector(outlineContextMenuItemClicked_AddChildEntry) || action == #selector(outlineContextMenuItemClicked_AddAuntEntry) || action == #selector(outlineContextMenuItemClicked_RemoveEntry) || action == #selector(outlineContextMenuItemClicked_Edit) || action == #selector(outlineContextMenuItemClicked_SetDestination) || action == #selector(outlineContextMenuItemClicked_Rename) || action == #selector(outlineContextMenuItemClicked_Promote) || action == #selector(outlineContextMenuItemClicked_Demote) || action == #selector(toggleOutlineCaseInsensitiveSearch) || action == #selector(leftSideEmptyAnnotationClick_DeleteOutline)) { // 大纲列表 return self.outlineListValidateMenuItem(menuItem) } if (action == #selector(note_expandAllComments) || action == #selector(note_foldAllComments) || action == #selector(exportAnnotationNotes) || action == #selector(removeAllAnnotations) || action == #selector(unfoldNoteAction) || action == #selector(foldNoteAction) || action == #selector(editNoteFromTable)) { // 注释列表 return self.annoListValidateMenuItem(menuItem) } if (action == #selector(menuItemClick_ExportPNG) || action == #selector(menuItemClick_ExportJPG) || action == #selector(menuItemClick_ExportPDF)) { if (self.snapshotTableView.selectedRow == -1 ) { return false } else { return true } } else if (action == #selector(menuItemClick_SelectAll)) { menuItem.state = self.snapshotListIsAllSelected() ? .on : .off return true } return true } } // MARK: - NSPopoverDelegate extension KMLeftSideViewController: NSPopoverDelegate { func popoverWillClose(_ notification: Notification) { guard let popover = notification.object as? NSPopover else { return } if let vc = popover.contentViewController as? KMOutlineEditViewController { self.editOutlineUI(vc) self.tocOutlineView.reloadData() } } } // MARK: - NSSearchFieldDelegate extension KMLeftSideViewController: NSSearchFieldDelegate { func controlTextDidChange(_ obj: Notification) { if self.outlineSearchField.isEqual(to: obj.object) { if (self.outlineSearchField.stringValue.isEmpty == false) { self.isSearchOutlineMode = true self.tocOutlineView.reloadData() self.tocOutlineView.expandItem(nil, expandChildren: true) self.tocType = .unfold } else { self.isSearchOutlineMode = false self.showSearchOutlineBlankState(false) self.tocOutlineView.reloadData() } self.outlineAddButton.isEnabled = !self.isSearchOutlineMode } else if self.snapshotSearchField.isEqual(to: obj.object) { let searchString = self.snapshotSearchField.stringValue // NSPredicate *filterPredicate = nil; // if ([searchString length] > 0) { // NSExpression *lhs = [NSExpression expressionForConstantValue:searchString]; // NSExpression *rhs = [NSExpression expressionForKeyPath:@"string"]; // NSUInteger options = NSDiacriticInsensitivePredicateOption; // if (mwcFlags.caseInsensitiveNoteSearch) // options |= NSCaseInsensitivePredicateOption; // filterPredicate = [NSComparisonPredicate predicateWithLeftExpression:lhs rightExpression:rhs modifier:NSDirectPredicateModifier type:NSInPredicateOperatorType options:options]; // } // [rightSideController.snapshotArrayController setFilterPredicate:filterPredicate]; // NSArray * snapshots = [rightSideController.snapshotArrayController arrangedObjects]; self.searchSnapshots.removeAll() if searchString.isEmpty { self.isSearchSnapshotMode = false self.searchSnapshots = self.snapshots.filter({ model in let data = model.windowC?.string.contains(searchString) ?? false return data }) } else { self.isSearchSnapshotMode = true } var snapshots = self.searchSnapshots if self.isSearchSnapshotMode == false { snapshots = self.snapshots } Task { @MainActor in self.updataLeftSideSnapView() self.snapshotTableView.reloadData() } if (snapshots.count > 0) { self.leftSideEmptyVC.outlineSearchView.removeFromSuperview() } else { let view = self.snapshotTableView.enclosingScrollView let viewFrmae = view?.frame ?? .zero let emptyVcSize = self.leftSideEmptyVC.outlineSearchView.frame.size self.leftSideEmptyVC.outlineSearchView.frame = NSMakeRect((viewFrmae.size.width-emptyVcSize.width)/2.0,(viewFrmae.size.height-emptyVcSize.height)/2.0, emptyVcSize.width, emptyVcSize.height) self.leftSideEmptyVC.outlineSearchView.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] self.snapshotTableView.enclosingScrollView?.documentView?.addSubview(self.leftSideEmptyVC.outlineSearchView) } } else if self.noteSearchField.isEqual(to: obj.object) { if let isEmpty = self.noteSearchField?.stringValue.isEmpty { self.noteSearchMode = !isEmpty } else { self.noteSearchMode = false } } } } // MARK: - NSTextFieldDelegate extension KMLeftSideViewController: NSTextFieldDelegate { func controlTextDidBeginEditing(_ obj: Notification) { if self.noteOutlineView.isEqual(to: obj.object) { // if (mwcFlags.isEditingTable == NO && mwcFlags.isEditingPDF == NO) // [[self document] objectDidBeginEditing:self]; // mwcFlags.isEditingTable = YES; } } func controlTextDidEndEditing(_ obj: Notification) { if let data = self.editNoteTextField, data.isEqual(to: obj.object) { // if self.noteOutlineView.isEqual(to: obj.object) { // if (mwcFlags.isEditingTable && mwcFlags.isEditingPDF == NO) // [[self document] objectDidEndEditing:self]; // mwcFlags.isEditingTable = NO; // (self.view.window?.windowController?.document as? NSDocument)?.objectDidEndEditing(self.view.window?.windowController as! NSEditor) if let data = self.editNote as? CPDFMarkupAnnotation { data.setMarkupText(self.editNoteTextField?.stringValue ?? "") } else if let data = self.editNote as? CPDFTextAnnotation { data.contents = self.editNoteTextField?.stringValue ?? "" } } else if let data = self.renamePDFOutlineTextField?.isEqual(to: obj.object), data { let tf = obj.object as! NSTextField if let ol = self.renamePDFOutline { if self.isRenameNoteOutline == false { if tf.stringValue == ol.label { return } } self.renamePDFOutline(ol, label: tf.stringValue) self.updateSelectRowHeight() self.tocOutlineView.reloadData() } } } } // MARK: - Menu Item Actions extension KMLeftSideViewController { @objc func toggleCaseInsensitiveNoteSearch(_ sender: AnyObject?) { self.caseInsensitiveNoteSearch = !self.caseInsensitiveNoteSearch if self.searchField.stringValue.isEmpty == false { self.searchNotes(self.searchField) } KMDataManager.ud_set(self.caseInsensitiveNoteSearch, forKey: SKCaseInsensitiveNoteSearchKey) } @objc func searchNotes(_ sender: AnyObject?) { self.noteSearchMode = false if self.findState == .note { if self.noteSearchField.isEqual(to: sender) { self.noteSearchMode = !self.noteSearchField.stringValue.isEmpty } 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]) } } 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 } }