Browse Source

【书签】书签内容显示

lizhe 1 year ago
parent
commit
7b21c513c9

+ 439 - 393
PDF Office/PDF Master/Class/PDFTools/KMBookmark/Controller/KMBookmarkController.swift

@@ -7,60 +7,72 @@
 
 import Cocoa
 
+private let kLabel = NSUserInterfaceItemIdentifier("label")
+private let kFile  = NSUserInterfaceItemIdentifier("file")
+private let kPage  = NSUserInterfaceItemIdentifier("page")
+
+let kTextWithIconStringKey = "string";
+let kTextWithIconImageKey = "image";
+
 let kBookmarksToolbarIdentifier                 = "BookmarksToolbarIdentifier"
 let kBookmarksNewFolderToolbarItemIdentifier    = "BookmarksNewFolderToolbarItemIdentifier"
 let kBookmarksNewSeparatorToolbarItemIdentifier = "BookmarksNewSeparatorToolbarItemIdentifier"
 let kBookmarksDeleteToolbarItemIdentifier       = "BookmarksDeleteToolbarItemIdentifier"
 
+let kPasteboardTypeBookmarkRows = NSPasteboard.PasteboardType(rawValue: "pasteboard.bookmarkrows")
 class KMBookmarkController: NSWindowController {
     
-    @IBOutlet weak var outlineView: NSOutlineView!
+    @IBOutlet weak var outlineView: KMCustomOutlineView!
+    
+    var draggedBookmarks: [KMBookmark] = []
+    var recentDocuments: [[String: Any]] {
+        get {
+            return KMBookmarkManager.manager.recentDocuments
+        }
+        
+        set {
+            
+        }
+    }
+    
+    var bookmarkRoot: KMRootBookmark {
+        get {
+            return KMBookmarkManager.manager.rootBookmark
+        }
+        set {
+            
+        }
+    }
     
     var toolbarItems: [String: NSToolbarItem] = [:]
     override func windowDidLoad() {
         super.windowDidLoad()
         setupToolbar()
         
-//        if let window = self.window, window.responds(to: #selector(setter: NSWindow.tabbingMode)) {
-//            window.tabbingMode = .disallowed
-//        }
-        
-//        windowFrameAutosaveName = SKBookmarksWindowFrameAutosaveName
-        
-//        window?.autorecalculatesContentBorderThickness = false
-//        if UserDefaults.standard.bool(forKey: SKShowBookmarkStatusBarKey) == false {
-//            toggleStatusBar(nil)
-//        } else {
-//            window?.setContentBorderThickness(22.0, for: .minY)
-//        }
-        
-//        outlineView.setTypeSelectHelper(SKTypeSelectHelper.typeSelectHelper())
-        
-//        outlineView.registerForDraggedTypes([SKPasteboardTypeBookmarkRows, kUTTypeFileURL as String, NSFilenamesPboardType])
-        
-//        outlineView.doubleAction = #selector(doubleClickBookmark(_:))
-        
-//        outlineView.supportsQuickLook = true
+        outlineView.delegate = self
+        outlineView.dataSource = self
+        outlineView.registerForDraggedTypes([kPasteboardTypeBookmarkRows, .fileURL, .string])
+        outlineView.doubleAction = #selector(doubleClickBookmark(_:))
     }
 
-//    func updateStatus() {
-//        let row = outlineView.selectedRow
-//        var message = ""
-//        if row != -1 {
-//            if let bookmark = outlineView.item(atRow: row) as? KMBookmark {
-//                switch bookmark.bookmarkType {
-//                case .bookmark:
-//                    message = bookmark.fileURL?.path ?? ""
-//                case .folder:
-//                    let count = bookmark.children.count
-//                    message = count == 1 ? NSLocalizedString("1 item", comment: "Bookmark folder description") : String(format: NSLocalizedString("%ld items", comment: "Bookmark folder description"), count)
-//                default:
-//                    break
-//                }
-//            }
-//        }
-////        statusBar.leftStringValue = message
-//    }
+    func updateStatus() {
+        let row = outlineView.selectedRow
+        var message = ""
+        if row != -1 {
+            if let bookmark = outlineView.item(atRow: row) as? KMBookmark {
+                switch bookmark.bookmarkType {
+                case .bookmark:
+                    message = bookmark.fileURL?.path ?? ""
+                case .folder:
+                    let count = bookmark.children.count
+                    message = count == 1 ? NSLocalizedString("1 item", comment: "Bookmark folder description") : String(format: NSLocalizedString("%ld items", comment: "Bookmark folder description"), count)
+                default:
+                    break
+                }
+            }
+        }
+//        statusBar.leftStringValue = message
+    }
 //    
     static func showBookmarkController() -> KMBookmarkController {
         let controller = KMBookmarkController.init(windowNibName: "KMBookmarkController")
@@ -71,23 +83,21 @@ class KMBookmarkController: NSWindowController {
 //    
 //    
 //    //MARK: Recent Documents
-//    func recentDocumentInfo(at fileURL: URL) -> [String: Any]? {
-//        let path = fileURL.path
-//        for info in recentDocuments as! [[String: Any]] {
+    func recentDocumentInfo(at fileURL: URL) -> [String: Any]? {
+        let path = fileURL.path
+        for info in recentDocuments {
 //            if let aliasData = info[ALIASDATA_KEY] as? Data,
 //               let alias = SKAlias(aliasData),
 //               alias.fileURLNoUI?.path.caseInsensitiveCompare(path) == .orderedSame {
 //                return info
 //            }
-//        }
-//        return nil
-//    }
+        }
+        return nil
+    }
 //
-//    func addRecentDocument(for fileURL: URL, pageIndex: UInt, scaleFactor factor: CGFloat, snapshots setups: [Any]?) {
-//        guard let fileURL = fileURL else { return }
-//        
+    func addRecentDocument(for fileURL: URL, pageIndex: UInt, scaleFactor factor: CGFloat, snapshots setups: [Any]?) {
 //        if let info = recentDocumentInfo(at: fileURL) {
-//            recentDocuments.removeObject(identicalTo: info)
+//            recentDocuments.removeObject(info)
 //        }
 //        
 //        if let alias = SKAlias(url: fileURL) {
@@ -103,7 +113,7 @@ class KMBookmarkController: NSWindowController {
 //                recentDocuments.removeLastObject()
 //            }
 //        }
-//    }
+    }
 //
 //    func pageIndex(forRecentDocumentAt fileURL: URL) -> UInt {
 //        guard let fileURL = fileURL else { return UInt.max }
@@ -130,7 +140,7 @@ class KMBookmarkController: NSWindowController {
 //    }
 //    
 //    //MARK: Bookmarks support
-    func getInsertionFolder(_ bookmarkPtr: inout KMBookmark?, childIndex indexPtr: inout UInt) {
+    func getInsertionFolder(_ bookmarkPtr: inout KMBookmark?, childIndex indexPtr: inout Int) {
         let rowIndex = outlineView.clickedRow
         var indexes = outlineView.selectedRowIndexes
         if rowIndex != -1 && !indexes.contains(rowIndex) {
@@ -154,22 +164,22 @@ class KMBookmarkController: NSWindowController {
         }
         
         bookmarkPtr = item
-        indexPtr = UInt(idx)
+        indexPtr = idx
+    }
+
+    @IBAction func openBookmark(_ sender: Any) {
+        if let bookmark = (sender as AnyObject).representedObject as? KMBookmark {
+            bookmark.open()
+        }
+    }
+
+    @IBAction func doubleClickBookmark(_ sender: Any) {
+        let row = outlineView.clickedRow
+        if let bm = (row != -1 ? outlineView.item(atRow: row) : nil) as? KMBookmark,
+           [KMBookmarkType.bookmark, .session].contains(bm.bookmarkType) {
+            bm.open()
+        }
     }
-//
-//    @IBAction func openBookmark(_ sender: Any) {
-//        if let bookmark = (sender as AnyObject).representedObject as? SKBookmark {
-//            bookmark.open()
-//        }
-//    }
-//
-//    @IBAction func doubleClickBookmark(_ sender: Any) {
-//        let row = outlineView.clickedRow
-//        if let bm = (row != -1 ? outlineView.item(atRow: row) : nil) as? SKBookmark,
-//           [SKBookmarkType.bookmark, .session].contains(bm.bookmarkType) {
-//            bm.open()
-//        }
-//    }
 
     
     func deleteBookmarks(bookmarks: [KMBookmark]) {
@@ -183,7 +193,7 @@ class KMBookmarkController: NSWindowController {
     @IBAction func insertBookmarkFolder(_ sender: Any) {
         let folder = KMFolderBookmark.folderBookmark(label: NSLocalizedString("Folder", comment: "default folder name"))
         var item: KMBookmark?
-        var idx: UInt = 0
+        var idx: Int = 0
         
         getInsertionFolder(&item, childIndex: &idx)
         item?.insert(child: folder, atIndex: idx)
@@ -196,7 +206,7 @@ class KMBookmarkController: NSWindowController {
     @IBAction func insertBookmarkSeparator(_ sender: Any) {
         let separator = KMSeparatorBookmark()
         var item: KMBookmark?
-        var idx: UInt = 0
+        var idx: Int = 0
         
         getInsertionFolder(&item, childIndex: &idx)
         item?.insert(child: separator, atIndex: idx)
@@ -204,37 +214,37 @@ class KMBookmarkController: NSWindowController {
         let row = outlineView.row(forItem: separator)
         outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
     }
-//
-//    @IBAction func addBookmark(_ sender: Any) {
-//        let openPanel = NSOpenPanel()
-//        var types = [String]()
-//        for docClass in NSDocumentController.shared.documentClassNames {
-//            if let docClass = NSClassFromString(docClass) as? NSDocument.Type {
-//                types += docClass.readableTypes
-//            }
-//        }
-//        openPanel.allowsMultipleSelection = true
-//        openPanel.canChooseDirectories = true
-//        openPanel.allowedFileTypes = types
-//        openPanel.beginSheetModal(for: self.window!) { (result) in
-//            guard result == .OK else { return }
-//            let newBookmarks = SKBookmark.bookmarks(forURLs: openPanel.urls)
-//            if !newBookmarks.isEmpty {
-//                var item: SKBookmark?
-//                var index: UInt = 0
-//                self.getInsertionFolder(&item, childIndex: &index)
-//                var indexes = IndexSet(integersIn: Int(index)..<Int(index + UInt(newBookmarks.count)))
-//                item?.mutableArrayValue(forKey: "children").insert(newBookmarks, at: indexes)
-//                if item == self.bookmarkRoot || self.outlineView.isItemExpanded(item) {
-//                    if item != self.bookmarkRoot {
-//                        indexes.shift(startingAt: 0, by: self.outlineView.row(forItem: item)! + 1)
-//                    }
-//                    self.outlineView.selectRowIndexes(indexes, byExtendingSelection: false)
-//                }
-//            }
-//        }
-//    }
-//
+
+    @IBAction func addBookmark(_ sender: Any) {
+        let openPanel = NSOpenPanel()
+        var types = [String]()
+        for docClass in NSDocumentController.shared.documentClassNames {
+            if let docClass = NSClassFromString(docClass) as? NSDocument.Type {
+                types += docClass.readableTypes
+            }
+        }
+        openPanel.allowsMultipleSelection = true
+        openPanel.canChooseDirectories = true
+        openPanel.allowedFileTypes = types
+        openPanel.beginSheetModal(for: self.window!) { (result) in
+            guard result == .OK else { return }
+            let newBookmarks = KMBookmark.bookmarks(urls: openPanel.urls)
+            if newBookmarks != nil {
+                var item: KMBookmark?
+                var index: Int = 0
+                self.getInsertionFolder(&item, childIndex: &index)
+                var indexes = IndexSet(integersIn: Int(index)..<Int(index + newBookmarks.count))
+                item?.mutableArrayValue(forKey: "children").insert(newBookmarks, at: indexes)
+                if item == self.bookmarkRoot || self.outlineView.isItemExpanded(item) {
+                    if item != self.bookmarkRoot {
+                        indexes.shift(startingAt: 0, by: self.outlineView.row(forItem: item) + 1)
+                    }
+                    self.outlineView.selectRowIndexes(indexes, byExtendingSelection: false)
+                }
+            }
+        }
+    }
+
     @IBAction func deleteBookmark(_ sender: Any) {
         self.deleteBookmarks(bookmarks: [])
     }
@@ -255,7 +265,7 @@ class KMBookmarkController: NSWindowController {
 //    }
 //
 //    @IBAction func deleteBookmarks(_ sender: Any) {
-//        guard let items = clickedBookmarks() as? [SKBookmark] else { return }
+//        guard let items = clickedBookmarks() as? [KMBookmark] else { return }
 //        for item in items.reversed() {
 //            if let parent = item.parent, let itemIndex = parent.children.index(of: item) {
 //                parent.removeObject(fromChildrenAtIndex: itemIndex)
@@ -264,7 +274,7 @@ class KMBookmarkController: NSWindowController {
 //    }
 //
 //    @IBAction func openBookmarks(_ sender: Any) {
-//        guard let items = clickedBookmarks() as? [SKBookmark] else { return }
+//        guard let items = clickedBookmarks() as? [KMBookmark] else { return }
 //        for item in items.reversed() {
 //            item.open()
 //        }
@@ -282,7 +292,7 @@ class KMBookmarkController: NSWindowController {
 //    
 //    // MARK: - NSMenu delegate methods
 //
-//    func addItemForBookmark(_ bookmark: SKBookmark, toMenu menu: NSMenu, isFolder: Bool, isAlternate: Bool) {
+//    func addItemForBookmark(_ bookmark: KMBookmark, toMenu menu: NSMenu, isFolder: Bool, isAlternate: Bool) {
 //        var item: NSMenuItem?
 //        if isFolder {
 //            item = menu.addItem(withSubmenuAndTitle: bookmark.label)
@@ -313,7 +323,7 @@ class KMBookmarkController: NSWindowController {
 //            menu.addItem(withTitle: NSLocalizedString("New Folder", comment: "Menu item title"), action: #selector(insertBookmarkFolder(_:)), target: self)
 //            menu.addItem(withTitle: NSLocalizedString("New Separator", comment: "Menu item title"), action: #selector(insertBookmarkSeparator(_:)), target: self)
 //        } else {
-//            guard let supermenu = menu.supermenu, let idx = supermenu.indexOfItem(withSubmenu: menu), let bm = (supermenu == NSApp.mainMenu) ? bookmarkRoot : supermenu.item(at: idx)?.representedObject as? SKBookmark else { return }
+//            guard let supermenu = menu.supermenu, let idx = supermenu.indexOfItem(withSubmenu: menu), let bm = (supermenu == NSApp.mainMenu) ? bookmarkRoot : supermenu.item(at: idx)?.representedObject as? KMBookmark else { return }
 //            
 //            let bookmarks = bm.children
 //            var i = menu.numberOfItems
@@ -422,11 +432,11 @@ class KMBookmarkController: NSWindowController {
 //    func endPreviewPanelControl(_ panel: QLPreviewPanel) {
 //    }
 //
-//    func previewItems() -> [SKBookmark] {
-//        var items = [SKBookmark]()
+//    func previewItems() -> [KMBookmark] {
+//        var items = [KMBookmark]()
 //        
 //        outlineView.selectedRowIndexes.enumerated().forEach { (idx, _) in
-//            if let item = outlineView.item(atRow: idx) as? SKBookmark {
+//            if let item = outlineView.item(atRow: idx) as? KMBookmark {
 //                if item.bookmarkType == .bookmark {
 //                    items.append(item)
 //                } else if item.bookmarkType == .session {
@@ -447,12 +457,12 @@ class KMBookmarkController: NSWindowController {
 //
 //    func previewPanel(_ panel: QLPreviewPanel, sourceFrameOnScreenForPreviewItem item: QLPreviewItem) -> NSRect {
 //        var item = item
-//        if let parent = (item as? SKBookmark)?.parent, parent.bookmarkType == .session {
+//        if let parent = (item as? KMBookmark)?.parent, parent.bookmarkType == .session {
 //            item = parent
 //        }
 //        let row = outlineView.row(forItem: item)
 //        var iconRect = NSZeroRect
-//        if let item = item as? SKBookmark, row != -1 {
+//        if let item = item as? KMBookmark, row != -1 {
 //            let cell = outlineView.preparedCell(atColumn: 0, row: row) as? SKTextWithIconCell
 //            iconRect = cell?.iconRect(forBounds: outlineView.frameOfCell(atColumn: 0, row: row)) ?? NSZeroRect
 //            if outlineView.visibleRect.intersects(iconRect) {
@@ -466,10 +476,10 @@ class KMBookmarkController: NSWindowController {
 //
 //    func previewPanel(_ panel: QLPreviewPanel, transitionImageForPreviewItem item: QLPreviewItem, contentRect: UnsafeMutablePointer<NSRect>) -> NSImage? {
 //        var item = item
-//        if let parent = (item as? SKBookmark)?.parent, parent.bookmarkType == .session {
+//        if let parent = (item as? KMBookmark)?.parent, parent.bookmarkType == .session {
 //            item = parent
 //        }
-//        return (item as? SKBookmark)?.icon
+//        return (item as? KMBookmark)?.icon
 //    }
 //
 //    func previewPanel(_ panel: QLPreviewPanel, handle event: NSEvent) -> Bool {
@@ -486,295 +496,310 @@ class KMBookmarkController: NSWindowController {
 extension KMBookmarkController: NSOutlineViewDelegate, NSOutlineViewDataSource {
     //MARK: NSOutlineViewDataSource
     
-        func minimumCoverForBookmarks(_ items: [KMBookmark]) -> [KMBookmark] {
-            var lastBm: KMBookmark?
-            var minimalCover = [KMBookmark]()
+    func minimumCoverForBookmarks(_ items: [KMBookmark]) -> [KMBookmark] {
+        var lastBm: KMBookmark?
+        var minimalCover = [KMBookmark]()
+
+        for bm in items {
+            if lastBm != nil && !(bm.isDescendant(of: lastBm!)) {
+                minimalCover.append(bm)
+                lastBm = bm
+            }
+        }
+        return minimalCover
+    }
     
-            for bm in items {
-                if lastBm != nil && !(bm.isDescendant(of: lastBm!)) {
-                    minimalCover.append(bm)
-                    lastBm = bm
+    func outlineView(_ ov: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
+        let bookmark = item as? KMBookmark ?? bookmarkRoot
+        return bookmark.bookmarkType == .folder ? bookmark.children.count : 0
+    }
+
+    func outlineView(_ ov: NSOutlineView, isItemExpandable item: Any) -> Bool {
+        let bookmark = item as! KMBookmark
+        return bookmark.bookmarkType == .folder
+    }
+
+    func outlineView(_ ov: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
+        let bookmark = (item as? KMBookmark) ?? bookmarkRoot
+        return bookmark.objectOfChidren(index: index)
+    }
+
+    func outlineView(_ ov: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any? {
+        guard let column = tableColumn else { return nil }
+        guard let bm = item as? KMBookmark else { return nil }
+        let tcID = column.identifier
+        
+        switch tcID {
+        case kLabel:
+            return [kTextWithIconStringKey: bm.label, kTextWithIconImageKey: bm.icon]
+        case kFile:
+            if bm.bookmarkType == .folder || bm.bookmarkType == .session {
+                let count = bm.children.count
+                return count == 1 ? NSLocalizedString("1 item", comment: "Bookmark folder description") : String.localizedStringWithFormat(NSLocalizedString("%ld items", comment: "Bookmark folder description"), count)
+            } else {
+                return bm.fileURL?.path ?? ""
+            }
+        case kPage:
+            return bm.pageNumber
+        default:
+            return nil
+        }
+    }
+
+    func outlineView(_ ov: NSOutlineView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, byItem item: Any?) {
+        guard let column = tableColumn else { return }
+        guard let bm = item as? KMBookmark else { return }
+        let tcID = column.identifier
+        
+        switch tcID {
+        case kLabel:
+            if let newLabel = (object as? [String: Any])?[kTextWithIconStringKey] as? String, newLabel != bm.label {
+                bm.label = newLabel
+            }
+        case kPage:
+            if let newPageNumber = object as? Int, newPageNumber != bm.pageNumber.intValue {
+                bm.pageNumber = newPageNumber as NSNumber
+            }
+        default:
+            break
+        }
+    }
+
+    func outlineView(_ ov: NSOutlineView, writeItems items: [Any], to pboard: NSPasteboard) -> Bool {
+        draggedBookmarks = minimumCoverForBookmarks(items as! [KMBookmark])
+        pboard.clearContents()
+        pboard.setData(Data(), forType: kPasteboardTypeBookmarkRows)
+        return true
+    }
+
+    func outlineView(_ ov: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
+        guard index != NSOutlineViewDropOnItemIndex else { return [] }
+        
+        let pboard = info.draggingPasteboard
+
+        if pboard.canReadItem(withDataConformingToTypes: [kPasteboardTypeBookmarkRows.rawValue]) && info.draggingSource as? NSOutlineView == ov {
+            return .move
+        } else if NSURL.canReadFileURL(from: pboard) {
+            return .every
+        }
+        return []
+    }
+
+    func outlineView(_ ov: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
+        let pboard = info.draggingPasteboard
+
+        if pboard.canReadItem(withDataConformingToTypes: [kPasteboardTypeBookmarkRows.rawValue]) && info.draggingSource as? NSOutlineView == ov {
+            var movedBookmarks = [KMBookmark]()
+            var indexes = IndexSet()
+            var insertionIndex = index
+
+            let targetItem = item as? KMBookmark ?? bookmarkRoot
+
+            for bookmark in draggedBookmarks {
+                guard let parent = bookmark.parent else { continue }
+                guard let bookmarkIndex = parent.children.firstIndex(of: bookmark) else { continue }
+
+                if targetItem == parent {
+                    if insertionIndex > bookmarkIndex {
+                        insertionIndex -= 1
+                    }
+                    if insertionIndex == bookmarkIndex {
+                        continue
+                    }
+                }
+                parent.removeObjectFromChildren(index: bookmarkIndex)
+                targetItem.insert(child: bookmark, atIndex: insertionIndex)
+                movedBookmarks.append(bookmark)
+                insertionIndex += 1
+            }
+
+            for bookmark in movedBookmarks {
+                let row = ov.row(forItem: bookmark)
+                if row != -1 {
+                    indexes.insert(row)
                 }
             }
-            return minimalCover
+            if !indexes.isEmpty {
+                ov.selectRowIndexes(indexes, byExtendingSelection: false)
+            }
+            return true
+        } else {
+            let urls = NSURL.readFileURLs(from: pboard)
+
+            let newBookmarks = KMBookmark.bookmarks(urls: urls)
+            if !newBookmarks.isEmpty {
+                var indexes = IndexSet(integersIn: index..<(index + newBookmarks.count))
+                (item as? KMBookmark ?? bookmarkRoot).mutableArrayValue(forKey: "children").insert(newBookmarks, at: indexes)
+                if (item as? KMBookmark ?? bookmarkRoot) === bookmarkRoot || ov.isItemExpanded(item) {
+                    if (item as? KMBookmark ?? bookmarkRoot) !== bookmarkRoot {
+                        indexes.shift(startingAt: 0, by: ov.row(forItem: item) + 1)
+                    }
+                    ov.selectRowIndexes(indexes, byExtendingSelection: false)
+                }
+                return true
+            }
+            return false
+        }
+    }
+
+    func outlineView(_ ov: NSOutlineView, dragEndedWith operation: NSDragOperation) {
+        draggedBookmarks.removeAll()
+    }
+
+
+    //    MARK: NSOutlineViewDelegate
+
+    func outlineView(_ ov: NSOutlineView, dataCellFor tableColumn: NSTableColumn?, item: Any) -> Any? {
+        if tableColumn == nil {
+            return (item as? KMBookmark)?.bookmarkType == .separator ? KMSeparatorCell() : nil
         }
-    //
-    //    func outlineView(_ ov: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
-    //        let bookmark = item as? SKBookmark ?? bookmarkRoot
-    //        return bookmark.bookmarkType == .folder ? bookmark.countOfChildren : 0
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, isItemExpandable item: Any) -> Bool {
-    //        let bookmark = item as! SKBookmark
-    //        return bookmark.bookmarkType == .folder
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
-    //        let bookmark = (item as? SKBookmark) ?? bookmarkRoot
-    //        return bookmark.objectInChildren(at: index)!
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any? {
-    //        guard let column = tableColumn, let tcID = column.identifier else { return nil }
-    //        guard let bm = item as? SKBookmark else { return nil }
-    //
-    //        switch tcID {
-    //        case LABEL_COLUMNID:
-    //            return [SKTextWithIconStringKey: bm.label, SKTextWithIconImageKey: bm.icon]
-    //        case FILE_COLUMNID:
-    //            if bm.bookmarkType == .folder || bm.bookmarkType == .session {
-    //                let count = bm.countOfChildren
-    //                return count == 1 ? NSLocalizedString("1 item", comment: "Bookmark folder description") : String.localizedStringWithFormat(NSLocalizedString("%ld items", comment: "Bookmark folder description"), count)
-    //            } else {
-    //                return bm.fileURL?.path ?? ""
-    //            }
-    //        case PAGE_COLUMNID:
-    //            return bm.pageNumber
-    //        default:
-    //            return nil
-    //        }
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, byItem item: Any?) {
-    //        guard let column = tableColumn, let tcID = column.identifier else { return }
-    //        guard let bm = item as? SKBookmark else { return }
-    //
-    //        switch tcID {
-    //        case LABEL_COLUMNID:
-    //            if let newLabel = (object as? [String: Any])?[SKTextWithIconStringKey] as? String, newLabel != bm.label {
-    //                bm.label = newLabel
-    //            }
-    //        case PAGE_COLUMNID:
-    //            if let newPageNumber = object as? Int, newPageNumber != bm.pageNumber {
-    //                bm.pageNumber = newPageNumber
-    //            }
-    //        default:
-    //            break
-    //        }
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, writeItems items: [Any], to pboard: NSPasteboard) -> Bool {
-    //        draggedBookmarks = minimumCoverForBookmarks(items as! [SKBookmark])
-    //        pboard.clearContents()
-    //        pboard.setData(Data(), forType: SKPasteboardTypeBookmarkRows)
-    //        return true
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
-    //        guard index != NSOutlineViewDropOnItemIndex else { return .none }
-    //        guard let pboard = info.draggingPasteboard else { return .none }
-    //
-    //        if pboard.canReadItem(withDataConformingToTypes: [SKPasteboardTypeBookmarkRows]) && info.draggingSource as? NSOutlineView == ov {
-    //            return .move
-    //        } else if NSURL.canReadFileURL(from: pboard) {
-    //            return .every
-    //        }
-    //        return .none
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
-    //        guard let pboard = info.draggingPasteboard else { return false }
-    //
-    //        if pboard.canReadItem(withDataConformingToTypes: [SKPasteboardTypeBookmarkRows]) && info.draggingSource as? NSOutlineView == ov {
-    //            var movedBookmarks = [SKBookmark]()
-    //            var indexes = IndexSet()
-    //            var insertionIndex = index
-    //
-    //            let targetItem = item as? SKBookmark ?? bookmarkRoot
-    //
-    //            for bookmark in draggedBookmarks {
-    //                guard let parent = bookmark.parent else { continue }
-    //                guard let bookmarkIndex = parent.children.firstIndex(of: bookmark) else { continue }
-    //
-    //                if targetItem == parent {
-    //                    if insertionIndex > bookmarkIndex {
-    //                        insertionIndex -= 1
-    //                    }
-    //                    if insertionIndex == bookmarkIndex {
-    //                        continue
-    //                    }
-    //                }
-    //                parent.removeObjectFromChildren(at: bookmarkIndex)
-    //                targetItem.insertObject(bookmark, inChildrenAtIndex: insertionIndex)
-    //                movedBookmarks.append(bookmark)
-    //                insertionIndex += 1
-    //            }
-    //
-    //            for bookmark in movedBookmarks {
-    //                let row = ov.row(forItem: bookmark)
-    //                if row != -1 {
-    //                    indexes.insert(row)
-    //                }
-    //            }
-    //            if !indexes.isEmpty {
-    //                ov.selectRowIndexes(indexes, byExtendingSelection: false)
-    //            }
-    //            return true
-    //        } else {
-    //            guard let urls = NSURL.readFileURLs(from: pboard) else { return false }
-    //
-    //            let newBookmarks = SKBookmark.bookmarks(for: urls)
-    //            if !newBookmarks.isEmpty {
-    //                let indexes = IndexSet(integersIn: index..<(index + newBookmarks.count))
-    //                (item as? SKBookmark ?? bookmarkRoot).mutableArrayValue(forKey: "children").insert(newBookmarks, at: indexes)
-    //                if item === bookmarkRoot || ov.isItemExpanded(item) {
-    //                    if item !== bookmarkRoot {
-    //                        indexes.shift(startingAt: 0, by: ov.row(forItem: item) + 1)
-    //                    }
-    //                    ov.selectRowIndexes(indexes, byExtendingSelection: false)
-    //                }
-    //                return true
-    //            }
-    //            return false
-    //        }
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, dragEndedWith operation: NSDragOperation) {
-    //        draggedBookmarks = nil
-    //    }
+        return tableColumn?.dataCell(forRow: ov.row(forItem: item))
+    }
     
+    func outlineView(_ ov: NSOutlineView, willDisplayCell cell: Any, for tableColumn: NSTableColumn?, item: Any) {
+        guard let column = tableColumn else { return }
+        guard let cell = cell as? NSCell else { return }
+
+        if column.identifier == kFile {
+            if let bm = item as? KMBookmark {
+//                if bm.bookmarkType == .folder || bm.bookmarkType == .session {
+//                    cell.textColor = .disabledControlTextColor
+//                } else {
+//                    cell.textColor = .controlTextColor
+//                }
+            }
+        }
+    }
+
+    func outlineView(_ ov: NSOutlineView, shouldEdit tableColumn: NSTableColumn?, item: Any) -> Bool {
+        guard let column = tableColumn else { return false }
+        guard let bm = item as? KMBookmark else { return false }
+
+        let tcID = column.identifier
+        switch tcID {
+        case kLabel:
+            return bm.bookmarkType != .separator
+        case kPage:
+            return bm.pageIndex != NSNotFound
+        default:
+            return false
+        }
+    }
+
+    func outlineView(_ ov: NSOutlineView, toolTipFor cell: NSCell, rect: UnsafeMutablePointer<NSRect>, tableColumn tc: NSTableColumn?, item: Any, mouseLocation: NSPoint) -> String {
+        guard let column = tc else { return "" }
+        guard let bm = item as? KMBookmark else { return "" }
+
+        let tcID = column.identifier
+        switch tcID {
+        case kLabel:
+            return bm.label
+        case kFile:
+            if bm.bookmarkType == .session {
+                return ""
+//                return bm.children.map { $0.path ?? "" }.joined(separator: "\n")
+            } else if bm.bookmarkType == .folder {
+                let count = bm.children.count
+                return count == 1 ? NSLocalizedString("1 item", comment: "Bookmark folder description") : String.localizedStringWithFormat(NSLocalizedString("%ld items", comment: "Bookmark folder description"), count)
+            } else {
+                return bm.fileURL?.path ?? ""
+            }
+        case kPage:
+            return bm.pageNumber.stringValue 
+        default:
+            return ""
+        }
+    }
+
+    func outlineViewSelectionDidChange(_ notification: Notification) {
+        updateStatus()
+        if QLPreviewPanel.sharedPreviewPanelExists(), let previewPanel = QLPreviewPanel.shared(), previewPanel.isVisible, previewPanel.dataSource === self {
+            previewPanel.reloadData()
+        }
+    }
+
+    func outlineView(_ ov: NSOutlineView, deleteItems items: [Any]) {
+        for item in minimumCoverForBookmarks(items as! [KMBookmark]).reversed() {
+            guard let parent = item.parent, let itemIndex = parent.children.firstIndex(of: item) else { continue }
+            parent.removeObjectFromChildren(index: itemIndex)
+        }
+    }
+
+    func outlineView(_ ov: NSOutlineView, canDeleteItems items: [Any]) -> Bool {
+        return !items.isEmpty
+    }
+
+    func outlineView(_ ov: NSOutlineView, copyItems items: [Any]) {
+        var urls = [URL]()
+        addBookmarkURLsToArray(minimumCoverForBookmarks(items as! [KMBookmark]), &urls)
+        if !urls.isEmpty {
+            let pboard = NSPasteboard.general
+            pboard.clearContents()
+            pboard.writeObjects(urls as [NSPasteboardWriting])
+        }
+    }
+
+    func outlineView(_ ov: NSOutlineView, canCopyItems items: [Any]) -> Bool {
+        return !items.isEmpty
+    }
+
+    func outlineView(_ ov: NSOutlineView, pasteFromPasteboard pboard: NSPasteboard) {
+        let urls = NSURL.readFileURLs(from: pboard)
+
+        let newBookmarks = KMBookmark.bookmarks(urls: urls)
+        if !newBookmarks.isEmpty {
+            var item: KMBookmark?
+            var anIndex = 0
+            getInsertionFolder(&item, childIndex: &anIndex)
+            var indexes = IndexSet(integersIn: anIndex..<(anIndex + newBookmarks.count))
+            (item ?? bookmarkRoot).mutableArrayValue(forKey: "children").insert(newBookmarks, at: indexes)
+            if item === bookmarkRoot || ov.isItemExpanded(item) {
+                if item !== bookmarkRoot {
+                    indexes.shift(startingAt: 0, by: ov.row(forItem: item) + 1)
+                }
+                ov.selectRowIndexes(indexes, byExtendingSelection: false)
+            }
+        }
+    }
+
+    func outlineView(_ ov: NSOutlineView, canPasteFromPasteboard pboard: NSPasteboard) -> Bool {
+        return NSURL.canReadFileURL(from: pboard)
+    }
+
+    func outlineView(_ ov: NSOutlineView, typeSelectHelperSelectionStrings typeSelectHelper: SKTypeSelectHelper) -> [String] {
+        let count = ov.numberOfRows
+        var labels = [String]()
+        for i in 0..<count {
+            if let label = ov.item(atRow: i) {
+                labels.append(label as! String)
+            }
+        }
+        return labels
+    }
+
+//    func outlineView(_ ov: NSOutlineView, typeSelectHelper typeSelectHelper: SKTypeSelectHelper, didFailToFindMatchForSearchString searchString: String) {
+//        statusBar.setLeftStringValue(String.localizedStringWithFormat(NSLocalizedString("No match: \"%@\"", comment: "Status message"), searchString))
+//    }
+//
+//    func outlineView(_ ov: NSOutlineView, typeSelectHelper typeSelectHelper: SKTypeSelectHelper, updateSearchString searchString: String?) {
+//        if let searchString = searchString {
+//            statusBar.setLeftStringValue(String.localizedStringWithFormat(NSLocalizedString("Finding: \"%@\"", comment: "Status message"), searchString))
+//        } else {
+//            updateStatus()
+//        }
+//    }
     
-    //MARK: NSOutlineViewDelegate
-    //
-    //    func outlineView(_ ov: NSOutlineView, dataCellFor tableColumn: NSTableColumn?, item: Any) -> Any? {
-    //        if tableColumn == nil {
-    //            return (item as? SKBookmark)?.bookmarkType == .separator ? SKSeparatorCell() : nil
-    //        }
-    //        return tableColumn?.dataCell(forRow: ov.row(forItem: item))
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, willDisplayCell cell: Any, for tableColumn: NSTableColumn?, item: Any) {
-    //        guard let column = tableColumn else { return }
-    //
-    //        if column.identifier == FILE_COLUMNID {
-    //            if let bm = item as? SKBookmark {
-    //                if bm.bookmarkType == .folder || bm.bookmarkType == .session {
-    //                    (cell as? NSCell)?.textColor = .disabledControlTextColor
-    //                } else {
-    //                    (cell as? NSCell)?.textColor = .controlTextColor
-    //                }
-    //            }
-    //        }
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, shouldEditTableColumn tableColumn: NSTableColumn?, item: Any) -> Bool {
-    //        guard let column = tableColumn, let tcID = column.identifier else { return false }
-    //        guard let bm = item as? SKBookmark else { return false }
-    //
-    //        switch tcID {
-    //        case LABEL_COLUMNID:
-    //            return bm.bookmarkType != .separator
-    //        case PAGE_COLUMNID:
-    //            return bm.pageIndex != NSNotFound
-    //        default:
-    //            return false
-    //        }
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, toolTipFor cell: NSCell, rect: UnsafeMutablePointer<NSRect>, tableColumn tc: NSTableColumn?, item: Any, mouseLocation: NSPoint) -> String {
-    //        guard let column = tc, let tcID = column.identifier else { return "" }
-    //        guard let bm = item as? SKBookmark else { return "" }
-    //
-    //        switch tcID {
-    //        case LABEL_COLUMNID:
-    //            return bm.label
-    //        case FILE_COLUMNID:
-    //            if bm.bookmarkType == .session {
-    //                return bm.children.map { $0.path ?? "" }.joined(separator: "\n")
-    //            } else if bm.bookmarkType == .folder {
-    //                let count = bm.countOfChildren
-    //                return count == 1 ? NSLocalizedString("1 item", comment: "Bookmark folder description") : String.localizedStringWithFormat(NSLocalizedString("%ld items", comment: "Bookmark folder description"), count)
-    //            } else {
-    //                return bm.fileURL?.path ?? ""
-    //            }
-    //        case PAGE_COLUMNID:
-    //            return bm.pageNumber?.stringValue ?? ""
-    //        default:
-    //            return ""
-    //        }
-    //    }
-    //
-    //    func outlineViewSelectionDidChange(_ notification: Notification) {
-    //        updateStatus()
-    //        if QLPreviewPanel.sharedPreviewPanelExists(), let previewPanel = QLPreviewPanel.shared() as? QLPreviewPanel, previewPanel.isVisible, previewPanel.dataSource === self {
-    //            previewPanel.reloadData()
-    //        }
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, deleteItems items: [Any]) {
-    //        for item in minimumCoverForBookmarks(items as! [SKBookmark]).reversed() {
-    //            guard let parent = item.parent, let itemIndex = parent.children.firstIndex(of: item) else { continue }
-    //            parent.removeObjectFromChildren(at: itemIndex)
-    //        }
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, canDeleteItems items: [Any]) -> Bool {
-    //        return !items.isEmpty
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, copyItems items: [Any]) {
-    //        var urls = [URL]()
-    //        addBookmarkURLsToArray(minimumCoverForBookmarks(items as! [SKBookmark]), urls)
-    //        if !urls.isEmpty {
-    //            let pboard = NSPasteboard.general
-    //            pboard.clearContents()
-    //            pboard.writeObjects(urls as [NSPasteboardWriting])
-    //        } else {
-    //            NSBeep()
-    //        }
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, canCopyItems items: [Any]) -> Bool {
-    //        return !items.isEmpty
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, pasteFromPasteboard pboard: NSPasteboard) {
-    //        guard let urls = NSURL.readFileURLs(from: pboard), !urls.isEmpty else { NSBeep(); return }
-    //
-    //        let newBookmarks = SKBookmark.bookmarks(for: urls)
-    //        if !newBookmarks.isEmpty {
-    //            var item: SKBookmark?
-    //            var anIndex = 0
-    //            getInsertionFolder(&item, childIndex: &anIndex)
-    //            let indexes = IndexSet(integersIn: anIndex..<(anIndex + newBookmarks.count))
-    //            (item ?? bookmarkRoot).mutableArrayValue(forKey: "children").insert(newBookmarks, at: indexes)
-    //            if item === bookmarkRoot || ov.isItemExpanded(item) {
-    //                if item !== bookmarkRoot {
-    //                    indexes.shift(startingAt: 0, by: ov.row(forItem: item) + 1)
-    //                }
-    //                ov.selectRowIndexes(indexes, byExtendingSelection: false)
-    //            }
-    //        } else {
-    //            NSBeep()
-    //        }
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, canPasteFromPasteboard pboard: NSPasteboard) -> Bool {
-    //        return NSURL.canReadFileURL(from: pboard)
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, typeSelectHelperSelectionStrings typeSelectHelper: SKTypeSelectHelper) -> [String] {
-    //        let count = ov.numberOfRows
-    //        var labels = [String]()
-    //        for i in 0..<count {
-    //            if let label = ov.item(atRow: i) as? SKBookmark?.label {
-    //                labels.append(label)
-    //            }
-    //        }
-    //        return labels
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, typeSelectHelper typeSelectHelper: SKTypeSelectHelper, didFailToFindMatchForSearchString searchString: String) {
-    //        statusBar.setLeftStringValue(String.localizedStringWithFormat(NSLocalizedString("No match: \"%@\"", comment: "Status message"), searchString))
-    //    }
-    //
-    //    func outlineView(_ ov: NSOutlineView, typeSelectHelper typeSelectHelper: SKTypeSelectHelper, updateSearchString searchString: String?) {
-    //        if let searchString = searchString {
-    //            statusBar.setLeftStringValue(String.localizedStringWithFormat(NSLocalizedString("Finding: \"%@\"", comment: "Status message"), searchString))
-    //        } else {
-    //            updateStatus()
-    //        }
-    //    }
+    func addBookmarkURLsToArray(_ items: [KMBookmark], _ array: inout [URL]) {
+        for bm in items {
+            if bm.bookmarkType == .bookmark {
+                if let url = bm.fileURL {
+                    array.append(url)
+                }
+            } else if bm.bookmarkType != .separator {
+                addBookmarkURLsToArray(bm.children, &array)
+            }
+        }
+    }
     
 }
 
@@ -830,3 +855,24 @@ extension KMBookmarkController: NSMenuDelegate, NSMenuItemValidation {
         return true
     }
 }
+
+extension NSURL {
+    static func canReadFileURL(from pboard: NSPasteboard) -> Bool {
+        let canReadFileURLsOnly = [NSPasteboard.ReadingOptionKey.urlReadingFileURLsOnly: true]
+        let canReadClasses = [NSURL.self]
+        
+        return pboard.canReadObject(forClasses: canReadClasses, options: canReadFileURLsOnly) ||
+        pboard.canReadItem(withDataConformingToTypes: [NSPasteboard.PasteboardType.fileURL.rawValue])
+    }
+    
+    static func readFileURLs(from pboard: NSPasteboard) -> [URL] {
+        if let fileURLs = pboard.readObjects(forClasses: [NSURL.self], options: [.urlReadingFileURLsOnly: true]) as? [URL], !fileURLs.isEmpty {
+            return fileURLs
+        } else if ((pboard.types?.contains(.fileURL)) != nil) {
+            if let filenames = pboard.propertyList(forType: .fileURL) as? [String] {
+                return filenames.compactMap { URL(fileURLWithPath: $0) }
+            }
+        }
+        return []
+    }
+}

+ 2 - 2
PDF Office/PDF Master/Class/PDFTools/KMBookmark/Manager/KMBookmarkManager.swift

@@ -15,7 +15,7 @@ let kProperties = "properties"
 class KMBookmarkManager {
     static let manager = KMBookmarkManager()
     
-    var recentDocuments: [NSDictionary] = []
+    var recentDocuments: [[String: Any]] = []
     var rootBookmark: KMRootBookmark = KMRootBookmark()
     
     private init() {
@@ -26,7 +26,7 @@ class KMBookmarkManager {
         let bookmarkDictionary: NSDictionary = UserDefaults.standard.persistentDomain(forName: bookmarksIdentifier)! as NSDictionary
         let documents: NSArray = bookmarkDictionary.object(forKey: kRecentDocuments) as! NSArray
         for info in documents {
-            recentDocuments.append(info as! NSDictionary)
+            recentDocuments.append(info as! [String : Any])
         }
         
         rootBookmark = KMRootBookmark.bookmarkRoot(childrenProperties: bookmarkDictionary[kBookmarks] as! NSArray)

+ 33 - 2
PDF Office/PDF Master/Class/PDFTools/KMBookmark/Model/KMBookmark.swift

@@ -12,6 +12,7 @@ enum KMBookmarkType: Int {
     case folder
     case session
     case separator
+    case file
 }
 
 class KMBookmark: NSObject {
@@ -24,7 +25,7 @@ class KMBookmark: NSObject {
     var pageIndex: UInt = 0
     var pageNumber: NSNumber = 0
     
-    var documentSetup: [String: Any] = [:]//文档
+    var documentSetup: [String: Any]?
     var parent: KMBookmark?
     var children: [KMBookmark] = [] //子
     
@@ -36,6 +37,16 @@ class KMBookmark: NSObject {
         return bookmark
     }
     
+    static func bookmarks(urls: [URL]) -> [KMBookmark] {
+        var bookmarks: [KMBookmark] = []
+        for url in urls {
+            let bookmark = KMBookmark()
+            bookmark.fileURL = url
+            bookmarks.append(bookmark)
+        }
+        return bookmarks
+    }
+    
     func initWithProperties(properties: NSDictionary) -> KMBookmark {
         
         return KMBookmark()
@@ -69,7 +80,7 @@ class KMBookmark: NSObject {
 //            }
 //        }
     
-    func insert(child: KMBookmark, atIndex: UInt) {
+    func insert(child: KMBookmark, atIndex: Int) {
         children.insert(child, at: Int(atIndex))
         child.parent = self
     }
@@ -99,6 +110,26 @@ class KMBookmark: NSObject {
         children[index].parent = nil
         children.remove(at: index)
     }
+    
+    func open() {
+        var document: Any?
+        var error: Error?
+        if documentSetup != nil {
+            document = try? NSDocumentController.shared.openDocument(nil)
+//            document = try? NSDocumentController.shared.openDocument(withSetup: properties)
+        } else {
+            guard let fileURL = fileURL else { return }
+            NSDocumentController.shared.openDocument(withContentsOf: fileURL, display: true, completionHandler: { document, result, error in
+//                pageIndex != NSNotFound {
+//                    document.page(at: pageIndex)?.show(in: document.windowForSheet, at: pageIndex + 1)
+//                }
+            })
+        }
+    }
+    
+    func objectOfChidren(index: Int) -> KMBookmark{
+        return children[index]
+    }
 }
 
 //MARK: Folder

+ 22 - 0
PDF Office/PDF Master/Class/PDFTools/KMBookmark/View/KMSeparatorCell.swift

@@ -0,0 +1,22 @@
+//
+//  KMSeparatorCell.swift
+//  PDF Reader Pro
+//
+//  Created by lizhe on 2024/2/21.
+//
+
+import Cocoa
+
+let SEPARATOR_LEFT_INDENT = 4.0
+let SEPARATOR_RIGHT_INDENT = 2.0
+
+class KMSeparatorCell: NSCell {
+    override func draw(withFrame cellFrame: NSRect, in controlView: NSView) {
+        NSGraphicsContext.saveGraphicsState()
+        NSColor.gridColor.setStroke()
+        NSBezierPath.defaultLineWidth = 1.0
+        NSBezierPath.strokeLine(from: NSPoint(x: cellFrame.minX + SEPARATOR_LEFT_INDENT, y: floor(cellFrame.midY) + 0.5),
+                                to: NSPoint(x: cellFrame.maxX - SEPARATOR_RIGHT_INDENT, y: floor(cellFrame.midY) + 0.5))
+        NSGraphicsContext.restoreGraphicsState()
+    }
+}

+ 8 - 0
PDF Office/PDF Reader Pro.xcodeproj/project.pbxproj

@@ -1243,6 +1243,9 @@
 		AD055E532B73220A0035F824 /* KMBookmarkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD055E522B73220A0035F824 /* KMBookmarkManager.swift */; };
 		AD055E542B73220A0035F824 /* KMBookmarkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD055E522B73220A0035F824 /* KMBookmarkManager.swift */; };
 		AD055E552B73220A0035F824 /* KMBookmarkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD055E522B73220A0035F824 /* KMBookmarkManager.swift */; };
+		AD055E5C2B85C9A70035F824 /* KMSeparatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD055E5B2B85C9A70035F824 /* KMSeparatorCell.swift */; };
+		AD055E5D2B85C9A70035F824 /* KMSeparatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD055E5B2B85C9A70035F824 /* KMSeparatorCell.swift */; };
+		AD055E5E2B85C9A70035F824 /* KMSeparatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD055E5B2B85C9A70035F824 /* KMSeparatorCell.swift */; };
 		AD0E8AB02A31B76300DBFD3C /* KMInAppPurchaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0E8AAF2A31B76300DBFD3C /* KMInAppPurchaseManager.swift */; };
 		AD0E8AB12A31B76300DBFD3C /* KMInAppPurchaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0E8AAF2A31B76300DBFD3C /* KMInAppPurchaseManager.swift */; };
 		AD0E8AB42A31B78900DBFD3C /* KMDMGPurchaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0E8AB32A31B78900DBFD3C /* KMDMGPurchaseManager.swift */; };
@@ -5557,6 +5560,7 @@
 		AD055E492B72346E0035F824 /* KMBookmarkSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMBookmarkSheetView.swift; sourceTree = "<group>"; };
 		AD055E4D2B7234810035F824 /* KMBookmarkSheetView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMBookmarkSheetView.xib; sourceTree = "<group>"; };
 		AD055E522B73220A0035F824 /* KMBookmarkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMBookmarkManager.swift; sourceTree = "<group>"; };
+		AD055E5B2B85C9A70035F824 /* KMSeparatorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMSeparatorCell.swift; sourceTree = "<group>"; };
 		AD0E8AAF2A31B76300DBFD3C /* KMInAppPurchaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMInAppPurchaseManager.swift; sourceTree = "<group>"; };
 		AD0E8AB32A31B78900DBFD3C /* KMDMGPurchaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMDMGPurchaseManager.swift; sourceTree = "<group>"; };
 		AD0E8AB82A31BDDD00DBFD3C /* KMProduct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMProduct.swift; sourceTree = "<group>"; };
@@ -8282,6 +8286,7 @@
 			children = (
 				AD055E492B72346E0035F824 /* KMBookmarkSheetView.swift */,
 				AD055E4D2B7234810035F824 /* KMBookmarkSheetView.xib */,
+				AD055E5B2B85C9A70035F824 /* KMSeparatorCell.swift */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -15216,6 +15221,7 @@
 				BB67EE292B54FFEF00573BF0 /* ASIDownloadCache.m in Sources */,
 				BBCE57102A72712200508EFC /* NSWindowController+KMExtension.swift in Sources */,
 				BBEFD0182AF9BD24003FABD8 /* KMDataVersionManager.swift in Sources */,
+				AD055E5C2B85C9A70035F824 /* KMSeparatorCell.swift in Sources */,
 				BBAFFB1B29CDD19C00C56112 /* KMMergeSelect.swift in Sources */,
 				BBE78F1B2B36F69F0071AC1A /* KMLeftSideViewController+Note.swift in Sources */,
 				BB853C7D2AF8B5D6009C20C1 /* KMBatchOperateAddPasswordViewController.swift in Sources */,
@@ -15802,6 +15808,7 @@
 				BB8F4566295AA3ED0037EA22 /* KMHeaderFooterManager.swift in Sources */,
 				BB183DDE2B4EBE1B00F99C7E /* KMTrialSuccessController.swift in Sources */,
 				BB8810C52B4F95A900AFA63E /* NSObject+DeviceInfo.m in Sources */,
+				AD055E5D2B85C9A70035F824 /* KMSeparatorCell.swift in Sources */,
 				ADE86A9E2B031FDB00414DFA /* KMCompareWindowController.swift in Sources */,
 				BB99ACC3292DE22E0048AFD9 /* KMMergeViewController.swift in Sources */,
 				9F5752EA2B58FF73005DC303 /* KMAnnotationFromViewController.swift in Sources */,
@@ -17858,6 +17865,7 @@
 				BB1BFF832AEA139F003EB179 /* KMLongerButton.swift in Sources */,
 				AD9527BC295291F20039D2BC /* KMPrintPage.swift in Sources */,
 				AD3AAD552B0D87E500DE5FE7 /* KMCompareThumbItem.swift in Sources */,
+				AD055E5E2B85C9A70035F824 /* KMSeparatorCell.swift in Sources */,
 				BB14702B299DC0D100784A6A /* OIDAuthorizationService.m in Sources */,
 				BB0B2CDE2B04B9510088FFD8 /* KMLeftSideViewSearchField.swift in Sources */,
 				AD9527D52952D51A0039D2BC /* KMPrintPresenter.swift in Sources */,