KMBookmarkController.swift 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. //
  2. // KMBookmarkController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by lizhe on 2024/2/5.
  6. //
  7. import Cocoa
  8. let kBookmarksToolbarIdentifier = "BookmarksToolbarIdentifier"
  9. let kBookmarksNewFolderToolbarItemIdentifier = "BookmarksNewFolderToolbarItemIdentifier"
  10. let kBookmarksNewSeparatorToolbarItemIdentifier = "BookmarksNewSeparatorToolbarItemIdentifier"
  11. let kBookmarksDeleteToolbarItemIdentifier = "BookmarksDeleteToolbarItemIdentifier"
  12. class KMBookmarkController: NSWindowController {
  13. @IBOutlet weak var outlineView: NSOutlineView!
  14. var toolbarItems: [String: NSToolbarItem] = [:]
  15. override func windowDidLoad() {
  16. super.windowDidLoad()
  17. setupToolbar()
  18. // if let window = self.window, window.responds(to: #selector(setter: NSWindow.tabbingMode)) {
  19. // window.tabbingMode = .disallowed
  20. // }
  21. // windowFrameAutosaveName = SKBookmarksWindowFrameAutosaveName
  22. // window?.autorecalculatesContentBorderThickness = false
  23. // if UserDefaults.standard.bool(forKey: SKShowBookmarkStatusBarKey) == false {
  24. // toggleStatusBar(nil)
  25. // } else {
  26. // window?.setContentBorderThickness(22.0, for: .minY)
  27. // }
  28. // outlineView.setTypeSelectHelper(SKTypeSelectHelper.typeSelectHelper())
  29. // outlineView.registerForDraggedTypes([SKPasteboardTypeBookmarkRows, kUTTypeFileURL as String, NSFilenamesPboardType])
  30. // outlineView.doubleAction = #selector(doubleClickBookmark(_:))
  31. // outlineView.supportsQuickLook = true
  32. }
  33. // func updateStatus() {
  34. // let row = outlineView.selectedRow
  35. // var message = ""
  36. // if row != -1 {
  37. // if let bookmark = outlineView.item(atRow: row) as? KMBookmark {
  38. // switch bookmark.bookmarkType {
  39. // case .bookmark:
  40. // message = bookmark.fileURL?.path ?? ""
  41. // case .folder:
  42. // let count = bookmark.children.count
  43. // message = count == 1 ? NSLocalizedString("1 item", comment: "Bookmark folder description") : String(format: NSLocalizedString("%ld items", comment: "Bookmark folder description"), count)
  44. // default:
  45. // break
  46. // }
  47. // }
  48. // }
  49. //// statusBar.leftStringValue = message
  50. // }
  51. //
  52. static func showBookmarkController() -> KMBookmarkController {
  53. let controller = KMBookmarkController.init(windowNibName: "KMBookmarkController")
  54. NSWindow.currentWindow().addChildWindow(controller.window!, ordered: NSWindow.OrderingMode.above)
  55. controller.window?.center()
  56. return controller
  57. }
  58. //
  59. //
  60. // //MARK: Recent Documents
  61. // func recentDocumentInfo(at fileURL: URL) -> [String: Any]? {
  62. // let path = fileURL.path
  63. // for info in recentDocuments as! [[String: Any]] {
  64. // if let aliasData = info[ALIASDATA_KEY] as? Data,
  65. // let alias = SKAlias(aliasData),
  66. // alias.fileURLNoUI?.path.caseInsensitiveCompare(path) == .orderedSame {
  67. // return info
  68. // }
  69. // }
  70. // return nil
  71. // }
  72. //
  73. // func addRecentDocument(for fileURL: URL, pageIndex: UInt, scaleFactor factor: CGFloat, snapshots setups: [Any]?) {
  74. // guard let fileURL = fileURL else { return }
  75. //
  76. // if let info = recentDocumentInfo(at: fileURL) {
  77. // recentDocuments.removeObject(identicalTo: info)
  78. // }
  79. //
  80. // if let alias = SKAlias(url: fileURL) {
  81. // var bm: [String: Any] = [
  82. // PAGEINDEX_KEY: pageIndex,
  83. // SCALE_KEY: factor,
  84. // ALIASDATA_KEY: alias.data,
  85. // ALIAS_KEY: alias,
  86. // SNAPSHOTS_KEY: setups ?? []
  87. // ]
  88. // recentDocuments.insert(bm, at: 0)
  89. // if recentDocuments.count > maxRecentDocumentsCount {
  90. // recentDocuments.removeLastObject()
  91. // }
  92. // }
  93. // }
  94. //
  95. // func pageIndex(forRecentDocumentAt fileURL: URL) -> UInt {
  96. // guard let fileURL = fileURL else { return UInt.max }
  97. // if let pageIndex = recentDocumentInfo(at: fileURL)?[PAGEINDEX_KEY] as? UInt {
  98. // return pageIndex
  99. // }
  100. // return UInt.max
  101. // }
  102. //
  103. // func scaleFactor(forRecentDocumentAt fileURL: URL) -> CGFloat {
  104. // guard let fileURL = fileURL else { return 0 }
  105. // if let scaleFactor = recentDocumentInfo(at: fileURL)?[SCALE_KEY] as? CGFloat {
  106. // return scaleFactor
  107. // }
  108. // return 0
  109. // }
  110. //
  111. // func snapshots(forRecentDocumentAt fileURL: URL) -> [Any]? {
  112. // guard let fileURL = fileURL else { return nil }
  113. // if let setups = recentDocumentInfo(at: fileURL)?[SNAPSHOTS_KEY] as? [Any], !setups.isEmpty {
  114. // return setups
  115. // }
  116. // return nil
  117. // }
  118. //
  119. // //MARK: Bookmarks support
  120. func getInsertionFolder(_ bookmarkPtr: inout KMBookmark?, childIndex indexPtr: inout UInt) {
  121. let rowIndex = outlineView.clickedRow
  122. var indexes = outlineView.selectedRowIndexes
  123. if rowIndex != -1 && !indexes.contains(rowIndex) {
  124. indexes = IndexSet(integer: rowIndex)
  125. }
  126. let rowIdx = indexes.last ?? NSNotFound
  127. var item = KMBookmarkManager.manager.rootBookmark
  128. var idx = item.children.count
  129. if rowIdx != NSNotFound {
  130. if let selectedItem = outlineView.item(atRow: rowIdx) as? KMBookmark {
  131. if outlineView.isItemExpanded(selectedItem) {
  132. item = selectedItem as! KMRootBookmark
  133. idx = item.children.count
  134. } else if let parent = selectedItem.parent, let itemIdx = parent.children.firstIndex(of: selectedItem) {
  135. item = parent as! KMRootBookmark
  136. idx = itemIdx + 1
  137. }
  138. }
  139. }
  140. bookmarkPtr = item
  141. indexPtr = UInt(idx)
  142. }
  143. //
  144. // @IBAction func openBookmark(_ sender: Any) {
  145. // if let bookmark = (sender as AnyObject).representedObject as? SKBookmark {
  146. // bookmark.open()
  147. // }
  148. // }
  149. //
  150. // @IBAction func doubleClickBookmark(_ sender: Any) {
  151. // let row = outlineView.clickedRow
  152. // if let bm = (row != -1 ? outlineView.item(atRow: row) : nil) as? SKBookmark,
  153. // [SKBookmarkType.bookmark, .session].contains(bm.bookmarkType) {
  154. // bm.open()
  155. // }
  156. // }
  157. func deleteBookmarks(bookmarks: [KMBookmark]) {
  158. for item in minimumCoverForBookmarks(bookmarks).reversed() {
  159. guard let parent = item.parent, let itemIndex = parent.children.firstIndex(of: item) else { continue }
  160. parent.removeObjectFromChildren(index: itemIndex)
  161. }
  162. }
  163. @IBAction func insertBookmarkFolder(_ sender: Any) {
  164. let folder = KMFolderBookmark.folderBookmark(label: NSLocalizedString("Folder", comment: "default folder name"))
  165. var item: KMBookmark?
  166. var idx: UInt = 0
  167. getInsertionFolder(&item, childIndex: &idx)
  168. item?.insert(child: folder, atIndex: idx)
  169. let row = outlineView.row(forItem: folder)
  170. outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
  171. outlineView.editColumn(0, row: row, with: nil, select: true)
  172. }
  173. @IBAction func insertBookmarkSeparator(_ sender: Any) {
  174. let separator = KMSeparatorBookmark()
  175. var item: KMBookmark?
  176. var idx: UInt = 0
  177. getInsertionFolder(&item, childIndex: &idx)
  178. item?.insert(child: separator, atIndex: idx)
  179. let row = outlineView.row(forItem: separator)
  180. outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
  181. }
  182. //
  183. // @IBAction func addBookmark(_ sender: Any) {
  184. // let openPanel = NSOpenPanel()
  185. // var types = [String]()
  186. // for docClass in NSDocumentController.shared.documentClassNames {
  187. // if let docClass = NSClassFromString(docClass) as? NSDocument.Type {
  188. // types += docClass.readableTypes
  189. // }
  190. // }
  191. // openPanel.allowsMultipleSelection = true
  192. // openPanel.canChooseDirectories = true
  193. // openPanel.allowedFileTypes = types
  194. // openPanel.beginSheetModal(for: self.window!) { (result) in
  195. // guard result == .OK else { return }
  196. // let newBookmarks = SKBookmark.bookmarks(forURLs: openPanel.urls)
  197. // if !newBookmarks.isEmpty {
  198. // var item: SKBookmark?
  199. // var index: UInt = 0
  200. // self.getInsertionFolder(&item, childIndex: &index)
  201. // var indexes = IndexSet(integersIn: Int(index)..<Int(index + UInt(newBookmarks.count)))
  202. // item?.mutableArrayValue(forKey: "children").insert(newBookmarks, at: indexes)
  203. // if item == self.bookmarkRoot || self.outlineView.isItemExpanded(item) {
  204. // if item != self.bookmarkRoot {
  205. // indexes.shift(startingAt: 0, by: self.outlineView.row(forItem: item)! + 1)
  206. // }
  207. // self.outlineView.selectRowIndexes(indexes, byExtendingSelection: false)
  208. // }
  209. // }
  210. // }
  211. // }
  212. //
  213. @IBAction func deleteBookmark(_ sender: Any) {
  214. self.deleteBookmarks(bookmarks: [])
  215. }
  216. //
  217. // @IBAction func toggleStatusBar(_ sender: Any) {
  218. // UserDefaults.standard.set(!statusBar.isVisible, forKey: SKShowBookmarkStatusBarKey)
  219. // statusBar.toggle(below: outlineView.enclosingScrollView, animate: sender != nil)
  220. // }
  221. //
  222. // func clickedBookmarks() -> [Any]? {
  223. // let row = outlineView.clickedRow
  224. // guard row != -1 else { return nil }
  225. // var indexes = outlineView.selectedRowIndexes
  226. // if !indexes.contains(row) {
  227. // indexes = IndexSet(integer: row)
  228. // }
  229. // return indexes.compactMap { outlineView.item(atRow: $0) }
  230. // }
  231. //
  232. // @IBAction func deleteBookmarks(_ sender: Any) {
  233. // guard let items = clickedBookmarks() as? [SKBookmark] else { return }
  234. // for item in items.reversed() {
  235. // if let parent = item.parent, let itemIndex = parent.children.index(of: item) {
  236. // parent.removeObject(fromChildrenAtIndex: itemIndex)
  237. // }
  238. // }
  239. // }
  240. //
  241. // @IBAction func openBookmarks(_ sender: Any) {
  242. // guard let items = clickedBookmarks() as? [SKBookmark] else { return }
  243. // for item in items.reversed() {
  244. // item.open()
  245. // }
  246. // }
  247. //
  248. // @IBAction func previewBookmarks(_ sender: Any) {
  249. // if QLPreviewPanel.sharedPreviewPanelExists() && QLPreviewPanel.shared().isVisible {
  250. // QLPreviewPanel.shared().orderOut(nil)
  251. // } else if let row = outlineView.clickedRow {
  252. // outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
  253. // QLPreviewPanel.shared().makeKeyAndOrderFront(nil)
  254. // }
  255. // }
  256. //
  257. //
  258. // // MARK: - NSMenu delegate methods
  259. //
  260. // func addItemForBookmark(_ bookmark: SKBookmark, toMenu menu: NSMenu, isFolder: Bool, isAlternate: Bool) {
  261. // var item: NSMenuItem?
  262. // if isFolder {
  263. // item = menu.addItem(withSubmenuAndTitle: bookmark.label)
  264. // item?.submenu?.delegate = self
  265. // } else {
  266. // item = menu.addItem(withTitle: bookmark.label, action: #selector(openBookmark(_:)), target: self)
  267. // }
  268. // item?.representedObject = bookmark
  269. // if isAlternate {
  270. // item?.keyEquivalentModifierMask = .alternate
  271. // item?.isAlternate = true
  272. // item?.setImageAndSize(bookmark.alternateIcon)
  273. // } else {
  274. // item?.setImageAndSize(bookmark.icon)
  275. // }
  276. // }
  277. //
  278. // func menuNeedsUpdate(_ menu: NSMenu) {
  279. // if menu == outlineView.menu {
  280. // let row = outlineView.clickedRow
  281. // menu.removeAllItems()
  282. // if row != -1 {
  283. // menu.addItem(withTitle: NSLocalizedString("Remove", comment: "Menu item title"), action: #selector(deleteBookmarks(_:)), target: self)
  284. // menu.addItem(withTitle: NSLocalizedString("Open", comment: "Menu item title"), action: #selector(openBookmarks(_:)), target: self)
  285. // menu.addItem(withTitle: NSLocalizedString("Quick Look", comment: "Menu item title"), action: #selector(previewBookmarks(_:)), target: self)
  286. // menu.addItem(.separator())
  287. // }
  288. // menu.addItem(withTitle: NSLocalizedString("New Folder", comment: "Menu item title"), action: #selector(insertBookmarkFolder(_:)), target: self)
  289. // menu.addItem(withTitle: NSLocalizedString("New Separator", comment: "Menu item title"), action: #selector(insertBookmarkSeparator(_:)), target: self)
  290. // } else {
  291. // 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 }
  292. //
  293. // let bookmarks = bm.children
  294. // var i = menu.numberOfItems
  295. //
  296. // while i > 0 {
  297. // if let menuItem = menu.item(at: i - 1), menuItem.isSeparatorItem || menuItem.representedObject != nil {
  298. // menu.removeItem(menuItem)
  299. // }
  300. // i -= 1
  301. // }
  302. //
  303. // if supermenu == NSApp.mainMenu, let previousSession = previousSession {
  304. // menu.addItem(.separator())
  305. // addItemForBookmark(previousSession, toMenu: menu, isFolder: false, isAlternate: false)
  306. // addItemForBookmark(previousSession, toMenu: menu, isFolder: true, isAlternate: true)
  307. // }
  308. //
  309. // if menu.numberOfItems > 0, bookmarks.count > 0 {
  310. // menu.addItem(.separator())
  311. // }
  312. //
  313. // for bm in bookmarks {
  314. // switch bm.bookmarkType {
  315. // case .folder:
  316. // addItemForBookmark(bm, toMenu: menu, isFolder: true, isAlternate: false)
  317. // addItemForBookmark(bm, toMenu: menu, isFolder: false, isAlternate: true)
  318. // case .session:
  319. // addItemForBookmark(bm, toMenu: menu, isFolder: false, isAlternate: false)
  320. // addItemForBookmark(bm, toMenu: menu, isFolder: true, isAlternate: true)
  321. // case .separator:
  322. // menu.addItem(.separator())
  323. // default:
  324. // addItemForBookmark(bm, toMenu: menu, isFolder: false, isAlternate: false)
  325. // }
  326. // }
  327. // }
  328. // }
  329. //
  330. // // avoid rebuilding the bookmarks menu on every key event
  331. // func menuHasKeyEquivalent(_ menu: NSMenu, for event: NSEvent, target: AutoreleasingUnsafeMutablePointer<AnyObject?>?, action: UnsafeMutablePointer<Selector?>?) -> Bool { false }
  332. //
  333. // // MARK: - Toolbar
  334. //
  335. func setupToolbar() {
  336. // Create a new toolbar instance, and attach it to our document window
  337. let toolbar = NSToolbar(identifier: kBookmarksToolbarIdentifier)
  338. var dict = [String: NSToolbarItem]()
  339. // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
  340. toolbar.allowsUserCustomization = true
  341. toolbar.autosavesConfiguration = true
  342. toolbar.displayMode = .default
  343. // We are the delegate
  344. toolbar.delegate = self
  345. // Add template toolbar items
  346. var item = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(kBookmarksNewFolderToolbarItemIdentifier))
  347. item.label = NSLocalizedString("New Folder", comment: "Toolbar item label")
  348. item.paletteLabel = NSLocalizedString("New Folder", comment: "Toolbar item label")
  349. item.toolTip = NSLocalizedString("Add a New Folder", comment: "Tool tip message")
  350. // item.image = NSImage(named: "NewFolder")
  351. item.image = NSImage(named: NSImage.folderName)!
  352. item.target = self
  353. item.action = #selector(insertBookmarkFolder(_:))
  354. dict[kBookmarksNewFolderToolbarItemIdentifier] = item
  355. item = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(kBookmarksNewSeparatorToolbarItemIdentifier))
  356. item.label = NSLocalizedString("New Separator", comment: "Toolbar item label")
  357. item.paletteLabel = NSLocalizedString("New Separator", comment: "Toolbar item label")
  358. item.toolTip = NSLocalizedString("Add a New Separator", comment: "Tool tip message")
  359. // item.image = NSImage(named: "NewSeparator")
  360. item.image = NSImage(named: NSImage.shareTemplateName)!
  361. item.target = self
  362. item.action = #selector(insertBookmarkSeparator(_:))
  363. dict[kBookmarksNewSeparatorToolbarItemIdentifier] = item
  364. item = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(kBookmarksDeleteToolbarItemIdentifier))
  365. item.label = NSLocalizedString("Delete", comment: "Toolbar item label")
  366. item.paletteLabel = NSLocalizedString("Delete", comment: "Toolbar item label")
  367. item.toolTip = NSLocalizedString("Delete Selected Items", comment: "Tool tip message")
  368. item.image = NSWorkspace.shared.icon(forFileType: NSFileTypeForHFSTypeCode(OSType(kToolbarDeleteIcon)))
  369. item.target = self
  370. item.action = #selector(deleteBookmark(_:))
  371. dict[kBookmarksDeleteToolbarItemIdentifier] = item
  372. toolbarItems = dict
  373. // Attach the toolbar to the window
  374. self.window?.toolbar = toolbar
  375. }
  376. //
  377. // // MARK: - Quick Look Panel Support
  378. //
  379. // func acceptsPreviewPanelControl(_ panel: QLPreviewPanel) -> Bool {
  380. // return true
  381. // }
  382. //
  383. // func beginPreviewPanelControl(_ panel: QLPreviewPanel) {
  384. // panel.delegate = self
  385. // panel.dataSource = self
  386. // }
  387. //
  388. // func endPreviewPanelControl(_ panel: QLPreviewPanel) {
  389. // }
  390. //
  391. // func previewItems() -> [SKBookmark] {
  392. // var items = [SKBookmark]()
  393. //
  394. // outlineView.selectedRowIndexes.enumerated().forEach { (idx, _) in
  395. // if let item = outlineView.item(atRow: idx) as? SKBookmark {
  396. // if item.bookmarkType == .bookmark {
  397. // items.append(item)
  398. // } else if item.bookmarkType == .session {
  399. // items.append(contentsOf: item.children)
  400. // }
  401. // }
  402. // }
  403. // return items
  404. // }
  405. //
  406. // func numberOfPreviewItems(in panel: QLPreviewPanel) -> Int {
  407. // return previewItems().count
  408. // }
  409. //
  410. // func previewPanel(_ panel: QLPreviewPanel, previewItemAt anIndex: Int) -> QLPreviewItem {
  411. // return previewItems()[anIndex]
  412. // }
  413. //
  414. // func previewPanel(_ panel: QLPreviewPanel, sourceFrameOnScreenForPreviewItem item: QLPreviewItem) -> NSRect {
  415. // var item = item
  416. // if let parent = (item as? SKBookmark)?.parent, parent.bookmarkType == .session {
  417. // item = parent
  418. // }
  419. // let row = outlineView.row(forItem: item)
  420. // var iconRect = NSZeroRect
  421. // if let item = item as? SKBookmark, row != -1 {
  422. // let cell = outlineView.preparedCell(atColumn: 0, row: row) as? SKTextWithIconCell
  423. // iconRect = cell?.iconRect(forBounds: outlineView.frameOfCell(atColumn: 0, row: row)) ?? NSZeroRect
  424. // if outlineView.visibleRect.intersects(iconRect) {
  425. // iconRect = outlineView.convert(iconRect, to: nil)
  426. // } else {
  427. // iconRect = NSZeroRect
  428. // }
  429. // }
  430. // return iconRect
  431. // }
  432. //
  433. // func previewPanel(_ panel: QLPreviewPanel, transitionImageForPreviewItem item: QLPreviewItem, contentRect: UnsafeMutablePointer<NSRect>) -> NSImage? {
  434. // var item = item
  435. // if let parent = (item as? SKBookmark)?.parent, parent.bookmarkType == .session {
  436. // item = parent
  437. // }
  438. // return (item as? SKBookmark)?.icon
  439. // }
  440. //
  441. // func previewPanel(_ panel: QLPreviewPanel, handle event: NSEvent) -> Bool {
  442. // if event.type == .keyDown {
  443. // outlineView.keyDown(with: event)
  444. // return true
  445. // }
  446. // return false
  447. // }
  448. //
  449. }
  450. extension KMBookmarkController: NSOutlineViewDelegate, NSOutlineViewDataSource {
  451. //MARK: NSOutlineViewDataSource
  452. func minimumCoverForBookmarks(_ items: [KMBookmark]) -> [KMBookmark] {
  453. var lastBm: KMBookmark?
  454. var minimalCover = [KMBookmark]()
  455. for bm in items {
  456. if lastBm != nil && !(bm.isDescendant(of: lastBm!)) {
  457. minimalCover.append(bm)
  458. lastBm = bm
  459. }
  460. }
  461. return minimalCover
  462. }
  463. //
  464. // func outlineView(_ ov: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
  465. // let bookmark = item as? SKBookmark ?? bookmarkRoot
  466. // return bookmark.bookmarkType == .folder ? bookmark.countOfChildren : 0
  467. // }
  468. //
  469. // func outlineView(_ ov: NSOutlineView, isItemExpandable item: Any) -> Bool {
  470. // let bookmark = item as! SKBookmark
  471. // return bookmark.bookmarkType == .folder
  472. // }
  473. //
  474. // func outlineView(_ ov: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
  475. // let bookmark = (item as? SKBookmark) ?? bookmarkRoot
  476. // return bookmark.objectInChildren(at: index)!
  477. // }
  478. //
  479. // func outlineView(_ ov: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any? {
  480. // guard let column = tableColumn, let tcID = column.identifier else { return nil }
  481. // guard let bm = item as? SKBookmark else { return nil }
  482. //
  483. // switch tcID {
  484. // case LABEL_COLUMNID:
  485. // return [SKTextWithIconStringKey: bm.label, SKTextWithIconImageKey: bm.icon]
  486. // case FILE_COLUMNID:
  487. // if bm.bookmarkType == .folder || bm.bookmarkType == .session {
  488. // let count = bm.countOfChildren
  489. // return count == 1 ? NSLocalizedString("1 item", comment: "Bookmark folder description") : String.localizedStringWithFormat(NSLocalizedString("%ld items", comment: "Bookmark folder description"), count)
  490. // } else {
  491. // return bm.fileURL?.path ?? ""
  492. // }
  493. // case PAGE_COLUMNID:
  494. // return bm.pageNumber
  495. // default:
  496. // return nil
  497. // }
  498. // }
  499. //
  500. // func outlineView(_ ov: NSOutlineView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, byItem item: Any?) {
  501. // guard let column = tableColumn, let tcID = column.identifier else { return }
  502. // guard let bm = item as? SKBookmark else { return }
  503. //
  504. // switch tcID {
  505. // case LABEL_COLUMNID:
  506. // if let newLabel = (object as? [String: Any])?[SKTextWithIconStringKey] as? String, newLabel != bm.label {
  507. // bm.label = newLabel
  508. // }
  509. // case PAGE_COLUMNID:
  510. // if let newPageNumber = object as? Int, newPageNumber != bm.pageNumber {
  511. // bm.pageNumber = newPageNumber
  512. // }
  513. // default:
  514. // break
  515. // }
  516. // }
  517. //
  518. // func outlineView(_ ov: NSOutlineView, writeItems items: [Any], to pboard: NSPasteboard) -> Bool {
  519. // draggedBookmarks = minimumCoverForBookmarks(items as! [SKBookmark])
  520. // pboard.clearContents()
  521. // pboard.setData(Data(), forType: SKPasteboardTypeBookmarkRows)
  522. // return true
  523. // }
  524. //
  525. // func outlineView(_ ov: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
  526. // guard index != NSOutlineViewDropOnItemIndex else { return .none }
  527. // guard let pboard = info.draggingPasteboard else { return .none }
  528. //
  529. // if pboard.canReadItem(withDataConformingToTypes: [SKPasteboardTypeBookmarkRows]) && info.draggingSource as? NSOutlineView == ov {
  530. // return .move
  531. // } else if NSURL.canReadFileURL(from: pboard) {
  532. // return .every
  533. // }
  534. // return .none
  535. // }
  536. //
  537. // func outlineView(_ ov: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
  538. // guard let pboard = info.draggingPasteboard else { return false }
  539. //
  540. // if pboard.canReadItem(withDataConformingToTypes: [SKPasteboardTypeBookmarkRows]) && info.draggingSource as? NSOutlineView == ov {
  541. // var movedBookmarks = [SKBookmark]()
  542. // var indexes = IndexSet()
  543. // var insertionIndex = index
  544. //
  545. // let targetItem = item as? SKBookmark ?? bookmarkRoot
  546. //
  547. // for bookmark in draggedBookmarks {
  548. // guard let parent = bookmark.parent else { continue }
  549. // guard let bookmarkIndex = parent.children.firstIndex(of: bookmark) else { continue }
  550. //
  551. // if targetItem == parent {
  552. // if insertionIndex > bookmarkIndex {
  553. // insertionIndex -= 1
  554. // }
  555. // if insertionIndex == bookmarkIndex {
  556. // continue
  557. // }
  558. // }
  559. // parent.removeObjectFromChildren(at: bookmarkIndex)
  560. // targetItem.insertObject(bookmark, inChildrenAtIndex: insertionIndex)
  561. // movedBookmarks.append(bookmark)
  562. // insertionIndex += 1
  563. // }
  564. //
  565. // for bookmark in movedBookmarks {
  566. // let row = ov.row(forItem: bookmark)
  567. // if row != -1 {
  568. // indexes.insert(row)
  569. // }
  570. // }
  571. // if !indexes.isEmpty {
  572. // ov.selectRowIndexes(indexes, byExtendingSelection: false)
  573. // }
  574. // return true
  575. // } else {
  576. // guard let urls = NSURL.readFileURLs(from: pboard) else { return false }
  577. //
  578. // let newBookmarks = SKBookmark.bookmarks(for: urls)
  579. // if !newBookmarks.isEmpty {
  580. // let indexes = IndexSet(integersIn: index..<(index + newBookmarks.count))
  581. // (item as? SKBookmark ?? bookmarkRoot).mutableArrayValue(forKey: "children").insert(newBookmarks, at: indexes)
  582. // if item === bookmarkRoot || ov.isItemExpanded(item) {
  583. // if item !== bookmarkRoot {
  584. // indexes.shift(startingAt: 0, by: ov.row(forItem: item) + 1)
  585. // }
  586. // ov.selectRowIndexes(indexes, byExtendingSelection: false)
  587. // }
  588. // return true
  589. // }
  590. // return false
  591. // }
  592. // }
  593. //
  594. // func outlineView(_ ov: NSOutlineView, dragEndedWith operation: NSDragOperation) {
  595. // draggedBookmarks = nil
  596. // }
  597. //MARK: NSOutlineViewDelegate
  598. //
  599. // func outlineView(_ ov: NSOutlineView, dataCellFor tableColumn: NSTableColumn?, item: Any) -> Any? {
  600. // if tableColumn == nil {
  601. // return (item as? SKBookmark)?.bookmarkType == .separator ? SKSeparatorCell() : nil
  602. // }
  603. // return tableColumn?.dataCell(forRow: ov.row(forItem: item))
  604. // }
  605. //
  606. // func outlineView(_ ov: NSOutlineView, willDisplayCell cell: Any, for tableColumn: NSTableColumn?, item: Any) {
  607. // guard let column = tableColumn else { return }
  608. //
  609. // if column.identifier == FILE_COLUMNID {
  610. // if let bm = item as? SKBookmark {
  611. // if bm.bookmarkType == .folder || bm.bookmarkType == .session {
  612. // (cell as? NSCell)?.textColor = .disabledControlTextColor
  613. // } else {
  614. // (cell as? NSCell)?.textColor = .controlTextColor
  615. // }
  616. // }
  617. // }
  618. // }
  619. //
  620. // func outlineView(_ ov: NSOutlineView, shouldEditTableColumn tableColumn: NSTableColumn?, item: Any) -> Bool {
  621. // guard let column = tableColumn, let tcID = column.identifier else { return false }
  622. // guard let bm = item as? SKBookmark else { return false }
  623. //
  624. // switch tcID {
  625. // case LABEL_COLUMNID:
  626. // return bm.bookmarkType != .separator
  627. // case PAGE_COLUMNID:
  628. // return bm.pageIndex != NSNotFound
  629. // default:
  630. // return false
  631. // }
  632. // }
  633. //
  634. // func outlineView(_ ov: NSOutlineView, toolTipFor cell: NSCell, rect: UnsafeMutablePointer<NSRect>, tableColumn tc: NSTableColumn?, item: Any, mouseLocation: NSPoint) -> String {
  635. // guard let column = tc, let tcID = column.identifier else { return "" }
  636. // guard let bm = item as? SKBookmark else { return "" }
  637. //
  638. // switch tcID {
  639. // case LABEL_COLUMNID:
  640. // return bm.label
  641. // case FILE_COLUMNID:
  642. // if bm.bookmarkType == .session {
  643. // return bm.children.map { $0.path ?? "" }.joined(separator: "\n")
  644. // } else if bm.bookmarkType == .folder {
  645. // let count = bm.countOfChildren
  646. // return count == 1 ? NSLocalizedString("1 item", comment: "Bookmark folder description") : String.localizedStringWithFormat(NSLocalizedString("%ld items", comment: "Bookmark folder description"), count)
  647. // } else {
  648. // return bm.fileURL?.path ?? ""
  649. // }
  650. // case PAGE_COLUMNID:
  651. // return bm.pageNumber?.stringValue ?? ""
  652. // default:
  653. // return ""
  654. // }
  655. // }
  656. //
  657. // func outlineViewSelectionDidChange(_ notification: Notification) {
  658. // updateStatus()
  659. // if QLPreviewPanel.sharedPreviewPanelExists(), let previewPanel = QLPreviewPanel.shared() as? QLPreviewPanel, previewPanel.isVisible, previewPanel.dataSource === self {
  660. // previewPanel.reloadData()
  661. // }
  662. // }
  663. //
  664. // func outlineView(_ ov: NSOutlineView, deleteItems items: [Any]) {
  665. // for item in minimumCoverForBookmarks(items as! [SKBookmark]).reversed() {
  666. // guard let parent = item.parent, let itemIndex = parent.children.firstIndex(of: item) else { continue }
  667. // parent.removeObjectFromChildren(at: itemIndex)
  668. // }
  669. // }
  670. //
  671. // func outlineView(_ ov: NSOutlineView, canDeleteItems items: [Any]) -> Bool {
  672. // return !items.isEmpty
  673. // }
  674. //
  675. // func outlineView(_ ov: NSOutlineView, copyItems items: [Any]) {
  676. // var urls = [URL]()
  677. // addBookmarkURLsToArray(minimumCoverForBookmarks(items as! [SKBookmark]), urls)
  678. // if !urls.isEmpty {
  679. // let pboard = NSPasteboard.general
  680. // pboard.clearContents()
  681. // pboard.writeObjects(urls as [NSPasteboardWriting])
  682. // } else {
  683. // NSBeep()
  684. // }
  685. // }
  686. //
  687. // func outlineView(_ ov: NSOutlineView, canCopyItems items: [Any]) -> Bool {
  688. // return !items.isEmpty
  689. // }
  690. //
  691. // func outlineView(_ ov: NSOutlineView, pasteFromPasteboard pboard: NSPasteboard) {
  692. // guard let urls = NSURL.readFileURLs(from: pboard), !urls.isEmpty else { NSBeep(); return }
  693. //
  694. // let newBookmarks = SKBookmark.bookmarks(for: urls)
  695. // if !newBookmarks.isEmpty {
  696. // var item: SKBookmark?
  697. // var anIndex = 0
  698. // getInsertionFolder(&item, childIndex: &anIndex)
  699. // let indexes = IndexSet(integersIn: anIndex..<(anIndex + newBookmarks.count))
  700. // (item ?? bookmarkRoot).mutableArrayValue(forKey: "children").insert(newBookmarks, at: indexes)
  701. // if item === bookmarkRoot || ov.isItemExpanded(item) {
  702. // if item !== bookmarkRoot {
  703. // indexes.shift(startingAt: 0, by: ov.row(forItem: item) + 1)
  704. // }
  705. // ov.selectRowIndexes(indexes, byExtendingSelection: false)
  706. // }
  707. // } else {
  708. // NSBeep()
  709. // }
  710. // }
  711. //
  712. // func outlineView(_ ov: NSOutlineView, canPasteFromPasteboard pboard: NSPasteboard) -> Bool {
  713. // return NSURL.canReadFileURL(from: pboard)
  714. // }
  715. //
  716. // func outlineView(_ ov: NSOutlineView, typeSelectHelperSelectionStrings typeSelectHelper: SKTypeSelectHelper) -> [String] {
  717. // let count = ov.numberOfRows
  718. // var labels = [String]()
  719. // for i in 0..<count {
  720. // if let label = ov.item(atRow: i) as? SKBookmark?.label {
  721. // labels.append(label)
  722. // }
  723. // }
  724. // return labels
  725. // }
  726. //
  727. // func outlineView(_ ov: NSOutlineView, typeSelectHelper typeSelectHelper: SKTypeSelectHelper, didFailToFindMatchForSearchString searchString: String) {
  728. // statusBar.setLeftStringValue(String.localizedStringWithFormat(NSLocalizedString("No match: \"%@\"", comment: "Status message"), searchString))
  729. // }
  730. //
  731. // func outlineView(_ ov: NSOutlineView, typeSelectHelper typeSelectHelper: SKTypeSelectHelper, updateSearchString searchString: String?) {
  732. // if let searchString = searchString {
  733. // statusBar.setLeftStringValue(String.localizedStringWithFormat(NSLocalizedString("Finding: \"%@\"", comment: "Status message"), searchString))
  734. // } else {
  735. // updateStatus()
  736. // }
  737. // }
  738. }
  739. extension KMBookmarkController: NSToolbarDelegate, NSToolbarItemValidation {
  740. func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
  741. // return [.flexibleSpace, .yourItem1, .yourItem2, .yourItem3]
  742. return [
  743. NSToolbarItem.Identifier(kBookmarksNewFolderToolbarItemIdentifier),
  744. NSToolbarItem.Identifier(kBookmarksNewSeparatorToolbarItemIdentifier),
  745. NSToolbarItem.Identifier(kBookmarksDeleteToolbarItemIdentifier)
  746. ]
  747. }
  748. func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
  749. // return [.yourItem1, .yourItem2, .yourItem3, .flexibleSpace, .space]
  750. return [
  751. NSToolbarItem.Identifier(kBookmarksNewFolderToolbarItemIdentifier),
  752. NSToolbarItem.Identifier(kBookmarksNewSeparatorToolbarItemIdentifier),
  753. NSToolbarItem.Identifier(kBookmarksDeleteToolbarItemIdentifier),
  754. .flexibleSpace,
  755. .space
  756. ]
  757. }
  758. func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
  759. return toolbarItems[itemIdentifier.rawValue]
  760. }
  761. func validateToolbarItem(_ item: NSToolbarItem) -> Bool {
  762. // guard let toolbar = self.window?.toolbar else { return false }
  763. //
  764. // if toolbar.customizationPaletteIsRunning {
  765. // return false
  766. // } else if toolbarItem.itemIdentifier == kBookmarksDeleteToolbarItemIdentifier {
  767. // return outlineView.canDelete
  768. // }
  769. return true
  770. }
  771. }
  772. extension KMBookmarkController: NSMenuDelegate, NSMenuItemValidation {
  773. func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
  774. // if menuItem.action == #selector(toggleStatusBar(_:)) {
  775. // if statusBar.isVisible {
  776. // menuItem.title = NSLocalizedString("Hide Status Bar", comment: "Menu item title")
  777. // } else {
  778. // menuItem.title = NSLocalizedString("Show Status Bar", comment: "Menu item title")
  779. // }
  780. // return true
  781. // } else if menuItem.action == #selector(addBookmark(_:)) {
  782. // return menuItem.tag == 0
  783. // }
  784. return true
  785. }
  786. }