// // QueryViewController.swift // MongoView // // Created by Shadowfacts on 1/9/20. // Copyright © 2020 Shadowfacts. All rights reserved. // import Cocoa import MongoSwift class QueryViewController: NSViewController { @IBOutlet weak var verticalSplitView: NSSplitView! @IBOutlet var queryTextView: NSTextView! @IBOutlet weak var outlineView: NSOutlineView! @IBOutlet weak var documentCountLabel: NSTextField! let mongoController: MongoController let collection: DatabaseCollection var defaultQuery: String { "db.getCollection('\(collection.name)').find({}).toArray()" } var hasQueryChanged: Bool { return queryTextView.string != defaultQuery } var mostRecentQuery: String? = nil var rootNodes: [Node] = [] init(mongoController: MongoController, collection: DatabaseCollection) { self.mongoController = mongoController self.collection = collection super.init(nibName: "QueryViewController", bundle: .main) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() refresh() verticalSplitView.delegate = self verticalSplitView.setHoldingPriority(.defaultHigh, forSubviewAt: 0) queryTextView.font = .monospacedSystemFont(ofSize: 13, weight: .regular) queryTextView.isAutomaticQuoteSubstitutionEnabled = false queryTextView.string = defaultQuery outlineView.dataSource = self outlineView.delegate = self outlineView.target = self outlineView.doubleAction = #selector(outlineCellDoubleClicked) } override func viewWillAppear() { super.viewWillAppear() verticalSplitView.setPosition(80, ofDividerAt: 0) } override func viewDidAppear() { super.viewDidAppear() view.window!.makeFirstResponder(outlineView) } func refresh(reload: Bool = true) { if let query = mostRecentQuery { let connStr = "\(mongoController.connectionString)/\(collection.database)" rootNodes = MongoEvaluator.eval(command: query, connectingTo: connStr).map { Node(value: $0) } title = query documentCountLabel.stringValue = "\(rootNodes.count) result\(rootNodes.count == 1 ? "" : "s")" } else { let documents = try! mongoController.collection(collection).find().all() rootNodes = documents.map { Node(document: $0) } title = "\(self.collection.database).\(self.collection.name)" documentCountLabel.stringValue = "\(documents.count) document\(documents.count == 1 ? "" : "s")" } if reload { outlineView.reloadData() } } func runQuery() { mostRecentQuery = queryTextView.string refresh() } rootNodes = MongoEvaluator.eval(command: query, connectingTo: connStr).map { Node(value: $0) } outlineView.reloadData() } @objc func outlineCellDoubleClicked() { if let item = outlineView.item(atRow: outlineView.clickedRow) { if outlineView.isItemExpanded(item) { outlineView.collapseItem(item) } else { outlineView.expandItem(item) } } } } extension QueryViewController: NSMenuItemValidation { func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { if menuItem.action == #selector(deleteNode(_:)) { return outlineView.clickedRow != -1 } return true } } extension QueryViewController: NSSplitViewDelegate { func splitView(_ splitView: NSSplitView, constrainSplitPosition proposedPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { return max(80, min(splitView.bounds.height / 2, proposedPosition)) } } extension QueryViewController: NSOutlineViewDataSource { func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { if item == nil { return rootNodes.count } else if let node = item as? Node { return node.numberOfChildren } else { return 0 } } func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { if let node = item as? Node { return node.hasChildren } else { return false } } func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { if item == nil { return rootNodes[index] } else if let node = item as? Node { return node.children[index] } else { fatalError("unreachable") } } } extension QueryViewController: NSOutlineViewDelegate { func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { guard let tableColumn = tableColumn, let node = item as? Node else { fatalError() } if tableColumn.identifier == .fieldNameColumn { let cell = outlineView.makeView(withIdentifier: .fieldNameCell, owner: nil) as! NSTableCellView cell.textField!.stringValue = node.keyString cell.textField!.isEditable = false return cell } else if tableColumn.identifier == .fieldValueColumn { let cell = outlineView.makeView(withIdentifier: .fieldValueCell, owner: nil) as! NSTableCellView cell.textField!.stringValue = node.valueString cell.textField!.isEditable = false return cell } else if tableColumn.identifier == .valueTypeColumn { let cell = outlineView.makeView(withIdentifier: .valueTypeCell, owner: nil) as! NSTableCellView cell.textField!.stringValue = node.typeString cell.textField!.isEditable = false return cell } else { return nil } } } extension NSUserInterfaceItemIdentifier { static let fieldNameColumn = NSUserInterfaceItemIdentifier(rawValue: "FieldNameCol") static let fieldValueColumn = NSUserInterfaceItemIdentifier(rawValue: "FieldValueCol") static let fieldNameCell = NSUserInterfaceItemIdentifier(rawValue: "FieldNameCell") static let fieldValueCell = NSUserInterfaceItemIdentifier(rawValue: "FieldValueCell") static let valueTypeColumn = NSUserInterfaceItemIdentifier(rawValue: "ValueTypeCol") static let valueTypeCell = NSUserInterfaceItemIdentifier(rawValue: "ValueTypeCell") }