123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917 |
- import Cocoa
- private let kLabelIdentifier = NSUserInterfaceItemIdentifier("label")
- private let kFileIdentifier = NSUserInterfaceItemIdentifier("file")
- private let kPageIdentifier = 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 bookmarkOutlineView: KMBookmarkOutlineView!
- @IBOutlet weak var outlineView: KMCustomOutlineView!
-
- var previousSession: KMBookmark?
- var draggedBookmarks: [KMBookmark] = []
- var recentDocuments: [[String: Any]] {
- get {
- return KMBookmarkManager.manager.recentDocuments
- }
-
- set {
-
- }
- }
-
- var bookmarkRoot: KMRootBookmark {
- get {
- if let lastOpenFiles = UserDefaults.standard.array(forKey: SKLastOpenFileNamesKey), !lastOpenFiles.isEmpty {
- previousSession = KMSessionBookmark.bookmarkSession(setups: lastOpenFiles as NSArray, label: NSLocalizedString("Restore Previous Session", comment: "Menu item title"))
- }
- return KMBookmarkManager.manager.rootBookmark
- }
- set {
-
- }
- }
-
- var toolbarItems: [String: NSToolbarItem] = [:]
- override func windowDidLoad() {
- super.windowDidLoad()
-
- self.window?.title = NSLocalizedString("Bookmarks", comment: "");
-
- setupToolbar()
- bookmarkOutlineView.outlineView.menu = NSMenu()
- bookmarkOutlineView.outlineView.menu?.delegate = self
-
- bookmarkOutlineView.data = self.bookmarkRoot
- bookmarkOutlineView.doubleClickAction = { [unowned self] view in
- self.doubleClickBookmark(nil)
- }
- }
- 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
- }
- }
- }
- }
- static func showBookmarkController() -> KMBookmarkController {
- let controller = KMBookmarkController.init(windowNibName: "KMBookmarkController")
- NSWindow.currentWindow().addChildWindow(controller.window!, ordered: NSWindow.OrderingMode.above)
- controller.window?.center()
- return controller
- }
- func recentDocumentInfo(at fileURL: URL) -> [String: Any]? {
- let path = fileURL.path
- for info in recentDocuments {
- }
- return nil
- }
- func addRecentDocument(for fileURL: URL, pageIndex: UInt, scaleFactor factor: CGFloat, snapshots setups: [Any]?) {
- }
- func getInsertionFolder(_ bookmarkPtr: inout KMBookmark?, childIndex indexPtr: inout Int) {
- let rowIndex = outlineView.clickedRow
- var indexes = outlineView.selectedRowIndexes
- if rowIndex != -1 && !indexes.contains(rowIndex) {
- indexes = IndexSet(integer: rowIndex)
- }
- let rowIdx = indexes.last ?? NSNotFound
-
- var item = KMBookmarkManager.manager.rootBookmark
- var idx = item.children.count
-
- if rowIdx != NSNotFound {
- if let selectedItem = outlineView.item(atRow: rowIdx) as? KMBookmark {
- if outlineView.isItemExpanded(selectedItem) {
- item = selectedItem as! KMRootBookmark
- idx = item.children.count
- } else if let parent = selectedItem.parent, let itemIdx = parent.children.firstIndex(of: selectedItem) {
- item = parent as! KMRootBookmark
- idx = itemIdx + 1
- }
- }
- }
-
- bookmarkPtr = item
- 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 = bookmarkOutlineView.outlineView.clickedRow
- if let bm = (row != -1 ? bookmarkOutlineView.outlineView.item(atRow: row) : nil) as? KMBookmark,
- [KMBookmarkType.bookmark, .session, .file].contains(bm.bookmarkType) {
- bm.open()
- }
- }
-
- @objc func deleteBookmarks(bookmarks: [KMBookmark]) {
- for item in minimumCoverForBookmarks(bookmarks).reversed() {
- guard let parent = item.parent, let itemIndex = parent.children.firstIndex(of: item) else { continue }
- parent.removeObjectFromChildren(index: itemIndex)
- }
- bookmarkOutlineView.reloadData()
-
- KMBookmarkManager.manager.saveData()
- }
-
-
- @IBAction func insertBookmarkFolder(_ sender: Any) {
- let folder = KMFolderBookmark.folderBookmark(label: NSLocalizedString("Folder", comment: "default folder name"))
- var item: KMBookmark?
- var idx: Int = 0
-
- getInsertionFolder(&item, childIndex: &idx)
- item?.insert(child: folder, atIndex: idx)
-
- bookmarkOutlineView.outlineView.reloadData()
- let row = bookmarkOutlineView.outlineView.row(forItem: folder)
- if row > 0 {
- bookmarkOutlineView.outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
- bookmarkOutlineView.outlineView.editColumn(0, row: row, with: nil, select: true)
- }
- }
- @IBAction func insertBookmarkSeparator(_ sender: Any) {
- let separator = KMSeparatorBookmark()
- var item: KMBookmark?
- var idx: Int = 0
-
- getInsertionFolder(&item, childIndex: &idx)
- item?.insert(child: separator, atIndex: idx)
-
- bookmarkOutlineView.outlineView.reloadData()
-
- }
- @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) {
- print("deleteBookmark")
- guard let bookmark = bookmarkOutlineView.outlineView.selectedItem() as? KMBookmark else { return }
- self.deleteBookmarks(bookmarks: [bookmark])
- }
- @IBAction func deleteMenuBookmarks(_ sender: Any) {
- guard let items = clickedBookmarks() as? [KMBookmark] else { return }
- self.deleteBookmarks(bookmarks: items)
- }
- @IBAction func openBookmarks(_ sender: Any) {
- guard let items = clickedBookmarks() as? [KMBookmark] else { return }
- for item in items.reversed() {
- item.open()
- }
- }
- @IBAction func previewBookmarks(_ sender: Any) {
- if QLPreviewPanel.sharedPreviewPanelExists() && QLPreviewPanel.shared().isVisible {
- QLPreviewPanel.shared().orderOut(nil)
- } else {
- let row = bookmarkOutlineView.outlineView.clickedRow
- if row > 0 {
- bookmarkOutlineView.outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
- QLPreviewPanel.shared().makeKeyAndOrderFront(nil)
- }
- }
- }
- func menuHasKeyEquivalent(_ menu: NSMenu, for event: NSEvent, target: AutoreleasingUnsafeMutablePointer<AnyObject?>?, action: UnsafeMutablePointer<Selector?>?) -> Bool {
- return false
- }
-
- func setupToolbar() {
-
- let toolbar = NSToolbar(identifier: kBookmarksToolbarIdentifier)
- var dict = [String: NSToolbarItem]()
-
-
- toolbar.allowsUserCustomization = true
- toolbar.autosavesConfiguration = true
- toolbar.displayMode = .default
-
-
- toolbar.delegate = self
-
-
-
- var item = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(kBookmarksNewFolderToolbarItemIdentifier))
- item.label = NSLocalizedString("New Folder", comment: "Toolbar item label")
- item.paletteLabel = NSLocalizedString("New Folder", comment: "Toolbar item label")
- item.toolTip = NSLocalizedString("Add a New Folder", comment: "Tool tip message")
- item.image = NSImage(named: NSImage.folderName)!
- item.target = self
- item.action = #selector(insertBookmarkFolder(_:))
- dict[kBookmarksNewFolderToolbarItemIdentifier] = item
-
- item = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(kBookmarksNewSeparatorToolbarItemIdentifier))
- item.label = NSLocalizedString("New Separator", comment: "Toolbar item label")
- item.paletteLabel = NSLocalizedString("New Separator", comment: "Toolbar item label")
- item.toolTip = NSLocalizedString("Add a New Separator", comment: "Tool tip message")
- item.image = NSImage(named: NSImage.shareTemplateName)!
- item.target = self
- item.action = #selector(insertBookmarkSeparator(_:))
- dict[kBookmarksNewSeparatorToolbarItemIdentifier] = item
-
- item = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(kBookmarksDeleteToolbarItemIdentifier))
- item.label = NSLocalizedString("Delete", comment: "Toolbar item label")
- item.paletteLabel = NSLocalizedString("Delete", comment: "Toolbar item label")
- item.toolTip = NSLocalizedString("Delete Selected Items", comment: "Tool tip message")
- item.image = NSWorkspace.shared.icon(forFileType: NSFileTypeForHFSTypeCode(OSType(kToolbarDeleteIcon)))
- item.target = self
- item.action = #selector(deleteBookmark(_:))
- dict[kBookmarksDeleteToolbarItemIdentifier] = item
-
- toolbarItems = dict
-
-
- self.window?.toolbar = toolbar
- }
- }
- extension KMBookmarkController: NSToolbarDelegate, NSToolbarItemValidation {
- func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
- return [
- NSToolbarItem.Identifier(kBookmarksNewFolderToolbarItemIdentifier),
- NSToolbarItem.Identifier(kBookmarksNewSeparatorToolbarItemIdentifier),
- NSToolbarItem.Identifier(kBookmarksDeleteToolbarItemIdentifier)
- ]
- }
-
- func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
- return [
- NSToolbarItem.Identifier(kBookmarksNewFolderToolbarItemIdentifier),
- NSToolbarItem.Identifier(kBookmarksNewSeparatorToolbarItemIdentifier),
- NSToolbarItem.Identifier(kBookmarksDeleteToolbarItemIdentifier),
- .flexibleSpace,
- .space
- ]
- }
-
- func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
- return toolbarItems[itemIdentifier.rawValue]
- }
-
- func validateToolbarItem(_ item: NSToolbarItem) -> Bool {
- return true
- }
- }
- extension KMBookmarkController: NSMenuDelegate, NSMenuItemValidation {
- func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
- return true
- }
-
-
- func addItemForBookmark(_ bookmark: KMBookmark, toMenu menu: NSMenu, isFolder: Bool, isAlternate: Bool) {
- var item: NSMenuItem?
- if isFolder {
- item = menu.addItemWithSubmenuAndTitle(bookmark.label)
- item?.submenu?.delegate = self
- } else {
- item = menu.addItem(withTitle: bookmark.label, action: #selector(openBookmark(_:)), target: self)
- }
- item?.representedObject = bookmark
- if isAlternate {
- item?.keyEquivalentModifierMask = NSEvent.ModifierFlags.option
- item?.isAlternate = true
- item?.setImageAndSize(bookmark.alternateIcon)
- } else {
- item?.setImageAndSize(bookmark.icon)
- }
- }
- func menuNeedsUpdate(_ menu: NSMenu) {
- if bookmarkOutlineView != nil && menu == bookmarkOutlineView.outlineView.menu {
- let row = bookmarkOutlineView.outlineView.clickedRow
- menu.removeAllItems()
- if row != -1 {
- menu.addItem(withTitle: NSLocalizedString("Remove", comment: "Menu item title"), action: #selector(deleteMenuBookmarks), target: self)
- menu.addItem(withTitle: NSLocalizedString("Open", comment: "Menu item title"), action: #selector(openBookmarks), target: self)
- menu.addItem(withTitle: NSLocalizedString("Quick Look", comment: "Menu item title"), action: #selector(previewBookmarks), target: self)
- menu.addItem(.separator())
- }
- 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 {
- let supermenu = menu.supermenu
- if supermenu != nil {
- let idx = supermenu!.indexOfItem(withSubmenu: menu)
- let bm = (supermenu == NSApp.mainMenu) ? bookmarkRoot : supermenu!.item(at: idx)?.representedObject as? KMBookmark
-
- let bookmarks = bm!.children
- var i = menu.numberOfItems
-
- while i > 0 {
- if let menuItem = menu.item(at: i - 1), menuItem.isSeparatorItem || menuItem.representedObject != nil {
- menu.removeItem(menuItem)
- }
- i -= 1
- }
-
- if supermenu == NSApp.mainMenu && previousSession != nil {
- menu.addItem(.separator())
- addItemForBookmark(previousSession!, toMenu: menu, isFolder: false, isAlternate: false)
- addItemForBookmark(previousSession!, toMenu: menu, isFolder: true, isAlternate: true)
- }
-
- if menu.numberOfItems > 0, bookmarks.count > 0 {
- menu.addItem(.separator())
- }
-
- for bm in bookmarks {
- switch bm.bookmarkType {
- case .folder:
- addItemForBookmark(bm, toMenu: menu, isFolder: true, isAlternate: false)
- addItemForBookmark(bm, toMenu: menu, isFolder: false, isAlternate: true)
- case .session:
- addItemForBookmark(bm, toMenu: menu, isFolder: false, isAlternate: false)
- addItemForBookmark(bm, toMenu: menu, isFolder: true, isAlternate: true)
- case .separator:
- menu.addItem(.separator())
- default:
- addItemForBookmark(bm, toMenu: menu, isFolder: false, isAlternate: false)
- }
- }
- }
- }
- }
- }
- 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 []
- }
- }
- extension KMBookmarkController {
- func minimumCoverForBookmarks(_ items: [KMBookmark]) -> [KMBookmark] {
- var lastBm: KMBookmark?
- var minimalCover = [KMBookmark]()
- for bm in items {
- if !(bm.isDescendant(of: lastBm)) {
- minimalCover.append(bm)
- lastBm = bm
- }
- }
- return minimalCover
- }
-
- func clickedBookmarks() -> [Any]? {
- let row = bookmarkOutlineView.outlineView.clickedRow
- guard row != -1 else { return nil }
- var indexes = bookmarkOutlineView.outlineView.selectedRowIndexes
- if !indexes.contains(row) {
- indexes = IndexSet(integer: row)
- }
- return indexes.compactMap { bookmarkOutlineView.outlineView.item(atRow: $0) }
- }
- }
|