ClientListMgr // Reimplement without XIB.

This commit is contained in:
ShikiSuen 2024-02-08 01:33:03 +08:00
parent 8679943b9d
commit 962e61f6f3
4 changed files with 252 additions and 272 deletions

View File

@ -0,0 +1,62 @@
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import AppKit
import Foundation
public class CtlClientListMgr: NSWindowController {
let viewController = VwrClientListMgr()
public static var shared: CtlClientListMgr?
public init() {
super.init(
window: .init(
contentRect: CGRect(x: 401, y: 295, width: 770, height: 335),
styleMask: [.titled, .closable, .miniaturizable],
backing: .buffered,
defer: true
)
)
viewController.loadView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
public static func show() {
if shared == nil {
shared = CtlClientListMgr()
}
guard let shared = shared, let sharedWindow = shared.window else { return }
if !sharedWindow.isVisible {
shared.windowDidLoad()
}
sharedWindow.setPosition(vertical: .center, horizontal: .right, padding: 20)
sharedWindow.orderFrontRegardless() //
sharedWindow.title = "Client Manager".localized
sharedWindow.level = .statusBar
if #available(macOS 10.10, *) {
sharedWindow.titlebarAppearsTransparent = true
}
shared.showWindow(shared)
NSApp.popup()
}
override public func windowDidLoad() {
super.windowDidLoad()
let view = viewController.view
window?.contentView = view
if let window = window {
var frame = window.frame
frame.size = view.fittingSize
window.setFrame(frame, display: true)
}
window?.setPosition(vertical: .center, horizontal: .right, padding: 20)
}
}

View File

@ -7,47 +7,119 @@
// requirements defined in MIT License.
import AppKit
import MainAssembly
import Foundation
import Shared
import UniformTypeIdentifiers
class CtlClientListMgr: NSWindowController, NSTableViewDelegate, NSTableViewDataSource {
@IBOutlet var tblClients: NSTableView!
@IBOutlet var btnRemoveClient: NSButton!
@IBOutlet var btnAddClient: NSButton!
@IBOutlet var lblClientMgrWindow: NSTextField!
public class VwrClientListMgr: NSViewController {
let windowWidth: CGFloat = 770
let contentWidth: CGFloat = 750
let buttonWidth: CGFloat = 150
public static var shared: CtlClientListMgr?
lazy var tblClients: NSTableView = .init()
lazy var btnAddClient = NSButton("Add Client", target: self, action: #selector(btnAddClientClicked(_:)))
lazy var btnRemoveClient = NSButton("Remove Selected", target: self, action: #selector(btnRemoveClientClicked(_:)))
lazy var tableColumn1Cell = NSButtonCell()
lazy var tableColumn1 = NSTableColumn()
lazy var tableColumn2Cell = NSTextFieldCell()
lazy var tableColumn2 = NSTableColumn()
static func show() {
if shared == nil { shared = CtlClientListMgr(windowNibName: "frmClientListMgr") }
guard let shared = shared, let sharedWindow = shared.window else { return }
sharedWindow.setPosition(vertical: .center, horizontal: .right, padding: 20)
sharedWindow.orderFrontRegardless() //
sharedWindow.level = .statusBar
sharedWindow.titlebarAppearsTransparent = true
shared.showWindow(shared)
NSApp.popup()
override public func loadView() {
tblClients.reloadData()
view = body ?? .init()
(view as? NSStackView)?.alignment = .centerX
view.makeSimpleConstraint(.width, relation: .equal, value: windowWidth)
btnRemoveClient.keyEquivalent = .init(NSEvent.SpecialKey.delete.unicodeScalar)
}
override func windowDidLoad() {
super.windowDidLoad()
window?.setPosition(vertical: .center, horizontal: .right, padding: 20)
localize()
var body: NSView? {
NSStackView.build(.vertical, insets: .new(all: 14)) {
makeScrollableTable()
.makeSimpleConstraint(.height, relation: .equal, value: 232)
NSStackView.build(.horizontal) {
let descriptionWidth = contentWidth - buttonWidth - 20
NSStackView.build(.vertical) {
let strDescription = "Please manage the list of those clients here which are: 1) IMKTextInput-incompatible; 2) suspected from abusing the contents of the inline composition buffer. A client listed here, if checked, will use popup composition buffer with maximum 20 reading counts holdable."
strDescription.makeNSLabel(descriptive: true, fixWidth: descriptionWidth)
.makeSimpleConstraint(.width, relation: .greaterThanOrEqual, value: descriptionWidth)
NSView()
}
NSStackView.build(.vertical) {
btnAddClient
.makeSimpleConstraint(.width, relation: .equal, value: buttonWidth)
btnRemoveClient
.makeSimpleConstraint(.width, relation: .equal, value: buttonWidth)
}
}
}
}
func makeScrollableTable() -> NSScrollView {
let scrollContainer = NSScrollView()
scrollContainer.scrollerStyle = .legacy
scrollContainer.autohidesScrollers = true
scrollContainer.documentView = tblClients
if #available(macOS 11.0, *) {
tblClients.style = .inset
}
tblClients.addTableColumn(tableColumn1)
tblClients.addTableColumn(tableColumn2)
tblClients.headerView = nil
tblClients.delegate = self
tblClients.registerForDraggedTypes([.fileURL])
tblClients.allowsExpansionToolTips = true
tblClients.allowsMultipleSelection = true
tblClients.autoresizingMask = [.width, .height]
tblClients.autosaveTableColumns = false
tblClients.backgroundColor = NSColor.controlBackgroundColor
tblClients.columnAutoresizingStyle = .lastColumnOnlyAutoresizingStyle
tblClients.frame = CGRect(x: 0, y: 0, width: 728, height: 230)
tblClients.gridColor = NSColor.clear
tblClients.intercellSpacing = CGSize(width: 17, height: 0)
tblClients.rowHeight = 24
tblClients.setContentHuggingPriority(.defaultHigh, for: .vertical)
tblClients.registerForDraggedTypes([.init(rawValue: kUTTypeFileURL as String)])
tblClients.dataSource = self
tblClients.action = #selector(onItemClicked(_:))
tblClients.target = self
tblClients.reloadData()
if #available(macOS 11.0, *) { tblClients.style = .inset }
tableColumn1.identifier = NSUserInterfaceItemIdentifier("colPCBEnabled")
tableColumn1.maxWidth = 20
tableColumn1.minWidth = 20
tableColumn1.resizingMask = [.autoresizingMask, .userResizingMask]
tableColumn1.width = 20
tableColumn1.dataCell = tableColumn1Cell
if #available(macOS 11.0, *) { tableColumn1Cell.controlSize = .large }
tableColumn1Cell.font = NSFont.systemFont(ofSize: 13)
tableColumn1Cell.setButtonType(.switch)
tableColumn1Cell.bezelStyle = .rounded
tableColumn2.identifier = NSUserInterfaceItemIdentifier("colClient")
tableColumn2.maxWidth = 1000
tableColumn2.minWidth = 40
tableColumn2.resizingMask = [.autoresizingMask, .userResizingMask]
tableColumn2.width = 546
tableColumn2.dataCell = tableColumn2Cell
tableColumn2Cell.backgroundColor = NSColor.controlBackgroundColor
tableColumn2Cell.font = NSFont.systemFont(ofSize: 20)
tableColumn2Cell.isEditable = true
tableColumn2Cell.isSelectable = true
tableColumn2Cell.lineBreakMode = .byTruncatingTail
tableColumn2Cell.stringValue = "Text Cell"
tableColumn2Cell.textColor = NSColor.controlTextColor
tableColumn2Cell.isEditable = true
return scrollContainer
}
}
// MARK: - UserDefaults Handlers
// MARK: - UserDefaults Handlers.
extension CtlClientListMgr {
public static var clientsList: [String] { PrefMgr.shared.clientsIMKTextInputIncapable.keys.sorted() }
public static func removeClient(at index: Int) {
public extension VwrClientListMgr {
static var clientsList: [String] { PrefMgr.shared.clientsIMKTextInputIncapable.keys.sorted() }
static func removeClient(at index: Int) {
guard index < Self.clientsList.count else { return }
let key = Self.clientsList[index]
var dict = PrefMgr.shared.clientsIMKTextInputIncapable
@ -56,15 +128,75 @@ extension CtlClientListMgr {
}
}
// MARK: - Implementations
// MARK: - Common Operation Methods.
extension CtlClientListMgr {
func numberOfRows(in _: NSTableView) -> Int {
Self.clientsList.count
extension VwrClientListMgr {
func applyNewValue(_ newValue: String, highMitigation mitigation: Bool = true) {
guard !newValue.isEmpty else { return }
var dict = PrefMgr.shared.clientsIMKTextInputIncapable
dict[newValue] = mitigation
PrefMgr.shared.clientsIMKTextInputIncapable = dict
tblClients.reloadData()
btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(
tblClients.selectedRow)
}
/// NSDraggingInfo URL App Bundle
/// - Parameters:
/// - info: NSDraggingInfo
/// - onError: 滿 lambda expression
/// - handler: 滿 lambda expression URL
private func validatePasteboardForAppBundles(
neta info: NSDraggingInfo, onError: @escaping () -> Void?, handler: (([URL]) -> Void)? = nil
) {
let board = info.draggingPasteboard
let type = NSPasteboard.PasteboardType(rawValue: kUTTypeApplicationBundle as String)
let options: [NSPasteboard.ReadingOptionKey: Any] = [
.urlReadingFileURLsOnly: true,
.urlReadingContentsConformToTypes: [type],
]
guard let urls = board.readObjects(forClasses: [NSURL.self], options: options) as? [URL], !urls.isEmpty else {
onError()
return
}
if let handler = handler {
handler(urls)
}
}
}
// MARK: - IBActions.
extension VwrClientListMgr {
@IBAction func onItemClicked(_: Any!) {
guard tblClients.clickedColumn == 0 else { return }
PrefMgr.shared.clientsIMKTextInputIncapable[Self.clientsList[tblClients.clickedRow]]?.toggle()
tblClients.reloadData()
}
@IBAction func btnRemoveClientClicked(_: Any) {
guard let minIndexSelected = tblClients.selectedRowIndexes.min() else { return }
if minIndexSelected >= Self.clientsList.count { return }
if minIndexSelected < 0 { return }
var isLastRow = false
tblClients.selectedRowIndexes.sorted().reversed().forEach { index in
isLastRow = {
if Self.clientsList.count < 2 { return false }
return minIndexSelected == Self.clientsList.count - 1
}()
if index < Self.clientsList.count {
Self.removeClient(at: index)
}
}
if isLastRow {
tblClients.selectRowIndexes(.init(arrayLiteral: minIndexSelected - 1), byExtendingSelection: false)
}
tblClients.reloadData()
btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(minIndexSelected)
}
@IBAction func btnAddClientClicked(_: Any) {
guard let window = window else { return }
guard let window = NSApp.keyWindow else { return }
let alert = NSAlert()
alert.messageText = NSLocalizedString(
"Please enter the client app bundle identifier(s) you want to register.", comment: ""
@ -86,6 +218,7 @@ extension CtlClientListMgr {
scrollview.verticalScroller?.scrollerStyle = .legacy
scrollview.autoresizingMask = [.width, .height]
let theTextView = NSTextView(frame: NSRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height))
scrollview.documentView = theTextView
theTextView.minSize = NSSize(width: 0.0, height: contentSize.height)
theTextView.maxSize = NSSize(width: maxFloat, height: maxFloat)
theTextView.isVerticallyResizable = true
@ -93,7 +226,6 @@ extension CtlClientListMgr {
theTextView.autoresizingMask = .width
theTextView.textContainer?.containerSize = NSSize(width: contentSize.width, height: maxFloat)
theTextView.textContainer?.widthTracksTextView = true
scrollview.documentView = theTextView
theTextView.enclosingScrollView?.hasHorizontalScroller = true
theTextView.isHorizontallyResizable = true
theTextView.autoresizingMask = [.width, .height]
@ -109,7 +241,7 @@ extension CtlClientListMgr {
}()
alert.accessoryView = scrollview
alert.beginSheetModal(for: window) { result in
alert.beginSheetModal(at: window) { result in
resultCheck: switch result {
case .alertFirstButtonReturn, .alertSecondButtonReturn:
theTextView.textContainer?.textView?.string.components(separatedBy: "\n").filter { !$0.isEmpty }.forEach {
@ -126,12 +258,16 @@ extension CtlClientListMgr {
)
dlgOpenPath.showsResizeIndicator = true
dlgOpenPath.allowsMultipleSelection = true
dlgOpenPath.allowedContentTypes = [UTType.applicationBundle]
if #available(macOS 11.0, *) {
dlgOpenPath.allowedContentTypes = [.applicationBundle]
} else {
dlgOpenPath.allowedFileTypes = ["app"]
}
dlgOpenPath.allowsOtherFileTypes = false
dlgOpenPath.showsHiddenFiles = true
dlgOpenPath.canChooseFiles = true
dlgOpenPath.canChooseDirectories = false
dlgOpenPath.beginSheetModal(for: window) { result in
dlgOpenPath.beginSheetModal(at: window) { result in
switch result {
case .OK:
for url in dlgOpenPath.urls {
@ -141,11 +277,11 @@ extension CtlClientListMgr {
)
let text = url.path + "\n\n" + NSLocalizedString("Please try again.", comment: "")
guard let bundle = Bundle(url: url) else {
self.window?.callAlert(title: title, text: text)
NSApp.keyWindow?.callAlert(title: title, text: text)
return
}
guard let identifier = bundle.bundleIdentifier else {
self.window?.callAlert(title: title, text: text)
NSApp.keyWindow?.callAlert(title: title, text: text)
return
}
let isIdentifierAlreadyRegistered = Self.clientsList.contains(identifier)
@ -171,49 +307,20 @@ extension CtlClientListMgr {
}
}
}
private func applyNewValue(_ newValue: String, highMitigation mitigation: Bool = true) {
guard !newValue.isEmpty else { return }
var dict = PrefMgr.shared.clientsIMKTextInputIncapable
dict[newValue] = mitigation
PrefMgr.shared.clientsIMKTextInputIncapable = dict
tblClients.reloadData()
btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(
tblClients.selectedRow)
}
@IBAction func btnRemoveClientClicked(_: Any) {
guard let minIndexSelected = tblClients.selectedRowIndexes.min() else { return }
if minIndexSelected >= Self.clientsList.count { return }
if minIndexSelected < 0 { return }
var isLastRow = false
tblClients.selectedRowIndexes.sorted().reversed().forEach { index in
isLastRow = {
if Self.clientsList.count < 2 { return false }
return minIndexSelected == Self.clientsList.count - 1
}()
if index < Self.clientsList.count {
Self.removeClient(at: index)
}
}
if isLastRow {
tblClients.selectRowIndexes(.init(arrayLiteral: minIndexSelected - 1), byExtendingSelection: false)
}
tblClients.reloadData()
btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(minIndexSelected)
// MARK: - TableView Extensions.
extension VwrClientListMgr: NSTableViewDelegate, NSTableViewDataSource {
public func numberOfRows(in _: NSTableView) -> Int {
Self.clientsList.count
}
@objc func onItemClicked(_: Any!) {
guard tblClients.clickedColumn == 0 else { return }
PrefMgr.shared.clientsIMKTextInputIncapable[Self.clientsList[tblClients.clickedRow]]?.toggle()
tblClients.reloadData()
}
func tableView(_: NSTableView, shouldEdit _: NSTableColumn?, row _: Int) -> Bool {
public func tableView(_: NSTableView, shouldEdit _: NSTableColumn?, row _: Int) -> Bool {
false
}
func tableView(_: NSTableView, objectValueFor column: NSTableColumn?, row: Int) -> Any? {
public func tableView(_: NSTableView, objectValueFor column: NSTableColumn?, row: Int) -> Any? {
defer {
self.btnRemoveClient.isEnabled = (0 ..< Self.clientsList.count).contains(
self.tblClients.selectedRow)
@ -232,30 +339,7 @@ extension CtlClientListMgr {
return Self.clientsList[row]
}
/// NSDraggingInfo URL App Bundle
/// - Parameters:
/// - info: NSDraggingInfo
/// - onError: 滿 lambda expression
/// - handler: 滿 lambda expression URL
private func validatePasteboardForAppBundles(
neta info: NSDraggingInfo, onError: @escaping () -> Void?, handler: (([URL]) -> Void)? = nil
) {
let board = info.draggingPasteboard
let type = UTType.applicationBundle
let options: [NSPasteboard.ReadingOptionKey: Any] = [
.urlReadingFileURLsOnly: true,
.urlReadingContentsConformToTypes: [type],
]
guard let urls = board.readObjects(forClasses: [NSURL.self], options: options) as? [URL], !urls.isEmpty else {
onError()
return
}
if let handler = handler {
handler(urls)
}
}
func tableView(
public func tableView(
_: NSTableView, validateDrop info: NSDraggingInfo, proposedRow _: Int,
proposedDropOperation _: NSTableView.DropOperation
) -> NSDragOperation {
@ -266,7 +350,7 @@ extension CtlClientListMgr {
return result
}
func tableView(
public func tableView(
_: NSTableView, acceptDrop info: NSDraggingInfo,
row _: Int, dropOperation _: NSTableView.DropOperation
) -> Bool {
@ -285,15 +369,11 @@ extension CtlClientListMgr {
defer { if result { tblClients.reloadData() } }
return result
}
}
private func localize() {
guard let window = window else { return }
window.title = NSLocalizedString("Client Manager", comment: "")
lblClientMgrWindow.stringValue = NSLocalizedString(
"Please manage the list of those clients here which are: 1) IMKTextInput-incompatible; 2) suspected from abusing the contents of the inline composition buffer. A client listed here, if checked, will use popup composition buffer with maximum 20 reading counts holdable.",
comment: ""
)
btnAddClient.title = NSLocalizedString("Add Client", comment: "")
btnRemoveClient.title = NSLocalizedString("Remove Selected", comment: "")
}
// MARK: - Preview.
@available(macOS 14.0, *)
#Preview(traits: .fixedLayout(width: 770, height: 335)) {
VwrClientListMgr()
}

View File

@ -1,154 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="CtlClientListMgr" customModule="vChewing" customModuleProvider="target">
<connections>
<outlet property="btnAddClient" destination="JcI-Ff-tDM" id="XbA-OK-zl8"/>
<outlet property="btnRemoveClient" destination="UNR-wG-8n1" id="vkb-81-1hA"/>
<outlet property="lblClientMgrWindow" destination="3ev-ns-FKY" id="VqL-RC-OdY"/>
<outlet property="tblClients" destination="vIw-vc-WKJ" id="2lw-U5-JAh"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Client Manager" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" titlebarAppearsTransparent="YES" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" fullSizeContentView="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="770" height="335"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
<view key="contentView" id="se5-gp-TjO" customClass="NSVisualEffectView" customModule="vChewing" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="770" height="335"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="3ev-ns-FKY" userLabel="lblClientMgrWindow">
<rect key="frame" x="18" y="20" width="600" height="46"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="46" id="HDu-vv-Kcv"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="490" id="LhL-fH-PVL"/>
</constraints>
<textFieldCell key="cell" title="Placeholder for description texts." id="hvd-Wx-n7n">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MUP-Im-g6c">
<rect key="frame" x="20" y="76" width="730" height="232"/>
<clipView key="contentView" drawsBackground="NO" id="wKQ-IV-k62">
<rect key="frame" x="1" y="1" width="728" height="230"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" tableStyle="inset" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" id="vIw-vc-WKJ" userLabel="tblClients">
<rect key="frame" x="0.0" y="0.0" width="728" height="230"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="17" height="0.0"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="separatorColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="colPCBEnabled" width="20" minWidth="20" maxWidth="20" id="6D6-O8-Zrc">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<buttonCell key="dataCell" type="check" bezelStyle="regularSquare" imagePosition="left" controlSize="large" inset="2" id="5Bp-jJ-Sf1">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn identifier="colClient" width="546" minWidth="40" maxWidth="1000" id="kWw-Lj-EFx">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="evF-0c-DTr">
<font key="font" metaFont="system" size="20"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
</tableView>
</subviews>
<nil key="backgroundColor"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="6Wq-mB-m8K">
<rect key="frame" x="1" y="215" width="622" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="rYb-HR-4th">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="qCN-8Z-3x3">
<rect key="frame" x="624" y="20" width="126" height="46"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JcI-Ff-tDM" userLabel="btnAddClient">
<rect key="frame" x="-7" y="19" width="140" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="126" id="YS5-dM-Awj"/>
</constraints>
<buttonCell key="cell" type="push" title="Add" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7rO-JZ-kgV">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="btnAddClientClicked:" target="-2" id="klb-NT-gqr"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="UNR-wG-8n1" userLabel="btnRemoveClient">
<rect key="frame" x="-7" y="-7" width="140" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="126" id="t4F-yV-r4E"/>
</constraints>
<buttonCell key="cell" type="push" title="Remove" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="wNc-xy-U4i">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
CA
</string>
</buttonCell>
<connections>
<action selector="btnRemoveClientClicked:" target="-2" id="VbY-dw-fRP"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="JcI-Ff-tDM" firstAttribute="trailing" secondItem="UNR-wG-8n1" secondAttribute="trailing" id="3Fe-KL-u9x"/>
<constraint firstAttribute="bottom" secondItem="UNR-wG-8n1" secondAttribute="bottom" id="5MS-Xg-KDM"/>
<constraint firstItem="JcI-Ff-tDM" firstAttribute="top" secondItem="qCN-8Z-3x3" secondAttribute="top" id="DZc-Jc-kVr"/>
<constraint firstAttribute="trailing" secondItem="JcI-Ff-tDM" secondAttribute="trailing" id="foA-xu-NF8"/>
<constraint firstItem="JcI-Ff-tDM" firstAttribute="leading" secondItem="UNR-wG-8n1" secondAttribute="leading" id="hOp-FU-UKN"/>
<constraint firstItem="JcI-Ff-tDM" firstAttribute="leading" secondItem="qCN-8Z-3x3" secondAttribute="leading" id="r1x-vo-9qt"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstItem="3ev-ns-FKY" firstAttribute="top" secondItem="MUP-Im-g6c" secondAttribute="bottom" constant="10" id="2tx-uw-Zdh"/>
<constraint firstAttribute="bottom" secondItem="3ev-ns-FKY" secondAttribute="bottom" constant="20" symbolic="YES" id="9KE-bP-0Yi"/>
<constraint firstItem="MUP-Im-g6c" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="27" id="BkF-ED-Uva"/>
<constraint firstAttribute="trailing" secondItem="MUP-Im-g6c" secondAttribute="trailing" constant="20" symbolic="YES" id="E0U-Vh-g3t"/>
<constraint firstItem="MUP-Im-g6c" firstAttribute="trailing" secondItem="qCN-8Z-3x3" secondAttribute="trailing" id="TqZ-q6-quu"/>
<constraint firstItem="MUP-Im-g6c" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="WXt-cJ-HaW"/>
<constraint firstItem="3ev-ns-FKY" firstAttribute="top" secondItem="qCN-8Z-3x3" secondAttribute="top" id="b4O-RW-K56"/>
<constraint firstItem="MUP-Im-g6c" firstAttribute="leading" secondItem="3ev-ns-FKY" secondAttribute="leading" id="g8i-I5-2Dw"/>
<constraint firstItem="3ev-ns-FKY" firstAttribute="bottom" secondItem="qCN-8Z-3x3" secondAttribute="bottom" id="ktC-2I-aWz"/>
<constraint firstItem="qCN-8Z-3x3" firstAttribute="leading" secondItem="3ev-ns-FKY" secondAttribute="trailing" constant="8" symbolic="YES" id="opE-OC-8Dh"/>
</constraints>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="284" y="176.5"/>
</window>
</objects>
</document>

View File

@ -8,8 +8,6 @@
/* Begin PBXBuildFile section */
5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; };
5B0EF55D28CDBF7100F8F7CE /* frmClientListMgr.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */; };
5B0EF55F28CDBF8E00F8F7CE /* CtlClientListMgr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0EF55E28CDBF8E00F8F7CE /* CtlClientListMgr.swift */; };
5B2CA1E62B130869002634EE /* vChewingFactoryDatabase.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = 5B2CA1E52B130869002634EE /* vChewingFactoryDatabase.sqlite */; };
5B30BF282944867800BD87A9 /* CtlRevLookupWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B30BF272944867800BD87A9 /* CtlRevLookupWindow.swift */; };
5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; };
@ -111,8 +109,6 @@
5B09307A28B6FC400021F8C5 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; lineEnding = 0; name = en; path = en.lproj/shortcuts.html; sourceTree = "<group>"; };
5B09307B28B6FC410021F8C5 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; lineEnding = 0; name = ja; path = ja.lproj/shortcuts.html; sourceTree = "<group>"; };
5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = frmClientListMgr.xib; sourceTree = "<group>"; };
5B0EF55E28CDBF8E00F8F7CE /* CtlClientListMgr.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CtlClientListMgr.swift; sourceTree = "<group>"; };
5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHS.txt"; sourceTree = "<group>"; };
5B18BA7027C7BD8B0056EB19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
5B18BA7127C7BD8B0056EB19 /* README-CHS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "README-CHS.md"; sourceTree = "<group>"; };
@ -293,7 +289,6 @@
5B62A33A27AE7C7500A19448 /* WindowControllers */ = {
isa = PBXGroup;
children = (
5B0EF55E28CDBF8E00F8F7CE /* CtlClientListMgr.swift */,
5B30BF272944867800BD87A9 /* CtlRevLookupWindow.swift */,
);
path = WindowControllers;
@ -302,7 +297,6 @@
5B62A33B27AE7C7F00A19448 /* WindowNIBs */ = {
isa = PBXGroup;
children = (
5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */,
6A187E2816004C5900466B2E /* MainMenu.xib */,
);
path = WindowNIBs;
@ -597,7 +591,6 @@
5B70F4EC2A0BE900005EA8C4 /* MenuIcon-TCVIM@2x.png in Resources */,
5BF9DA2828840E6200DBD48E /* template-exclusions.txt in Resources */,
5B7DA80328BF6BC600D7B2AD /* fixinstall.sh in Resources */,
5B0EF55D28CDBF7100F8F7CE /* frmClientListMgr.xib in Resources */,
5B70F4E92A0BE900005EA8C4 /* MenuIcon-TCVIM.png in Resources */,
5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */,
6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */,
@ -727,7 +720,6 @@
files = (
D427F76C278CA2B0004A2160 /* AppDelegateImpl.swift in Sources */,
D47B92C027972AD100458394 /* main.swift in Sources */,
5B0EF55F28CDBF8E00F8F7CE /* CtlClientListMgr.swift in Sources */,
5B30BF282944867800BD87A9 /* CtlRevLookupWindow.swift in Sources */,
5BB802DA27FABA8300CF1C19 /* SessionCtl_Menu.swift in Sources */,
);