Repo // Introduce ClientListMgr.

This commit is contained in:
ShikiSuen 2022-09-11 18:32:15 +08:00
parent 816d9fea18
commit d21d0342bf
10 changed files with 386 additions and 0 deletions

View File

@ -25,6 +25,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
// let vChewingKeyLayoutBundle = Bundle.init(path: URL(fileURLWithPath: Bundle.main.resourcePath ?? "").appendingPathComponent("vChewingKeyLayout.bundle").path) // let vChewingKeyLayoutBundle = Bundle.init(path: URL(fileURLWithPath: Bundle.main.resourcePath ?? "").appendingPathComponent("vChewingKeyLayout.bundle").path)
@IBOutlet var window: NSWindow? @IBOutlet var window: NSWindow?
private var ctlClientListMgrInstance: ctlClientListMgr?
private var ctlPrefWindowInstance: ctlPrefWindow? private var ctlPrefWindowInstance: ctlPrefWindow?
private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window
public lazy var folderMonitor = FolderMonitor( public lazy var folderMonitor = FolderMonitor(
@ -80,6 +81,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
folderMonitor.startMonitoring() folderMonitor.startMonitoring()
} }
func showClientListMgr() {
if ctlClientListMgrInstance == nil {
ctlClientListMgrInstance = ctlClientListMgr.init(windowNibName: "frmClientListMgr")
}
ctlClientListMgrInstance?.window?.center()
ctlClientListMgrInstance?.window?.orderFrontRegardless() //
ctlClientListMgrInstance?.window?.level = .statusBar
ctlClientListMgrInstance?.window?.titlebarAppearsTransparent = true
NSApp.setActivationPolicy(.accessory)
}
func showPreferences() { func showPreferences() {
if ctlPrefWindowInstance == nil { if ctlPrefWindowInstance == nil {
ctlPrefWindowInstance = ctlPrefWindow.init(windowNibName: "frmPrefWindow") ctlPrefWindowInstance = ctlPrefWindow.init(windowNibName: "frmPrefWindow")

View File

@ -156,6 +156,12 @@ extension ctlInputMethod {
withTitle: NSLocalizedString("vChewing Preferences…", comment: ""), withTitle: NSLocalizedString("vChewing Preferences…", comment: ""),
action: #selector(showPreferences(_:)), keyEquivalent: "" action: #selector(showPreferences(_:)), keyEquivalent: ""
) )
}
menu.addItem(
withTitle: NSLocalizedString("Client Manager", comment: "") + "",
action: #selector(showClientListMgr(_:)), keyEquivalent: ""
)
if !optionKeyPressed {
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Check for Updates…", comment: ""), withTitle: NSLocalizedString("Check for Updates…", comment: ""),
action: #selector(checkForUpdate(_:)), keyEquivalent: "" action: #selector(checkForUpdate(_:)), keyEquivalent: ""
@ -208,6 +214,11 @@ extension ctlInputMethod {
NSWorkspace.shared.openFile(url.path, withApplication: "Safari") NSWorkspace.shared.openFile(url.path, withApplication: "Safari")
} }
@objc func showClientListMgr(_: Any?) {
(NSApp.delegate as? AppDelegate)?.showClientListMgr()
NSApp.activate(ignoringOtherApps: true)
}
@objc func toggleSCPCTypingMode(_: Any? = nil) { @objc func toggleSCPCTypingMode(_: Any? = nil) {
resetKeyHandler() resetKeyHandler()
NotifierController.notify( NotifierController.notify(

View File

@ -1,4 +1,15 @@
"vChewing" = "vChewing"; "vChewing" = "vChewing";
"Please enter the client app bundle identifier(s) you want to register." = "Please enter the client app bundle identifier(s) you want to register.";
"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed.";
"Just Select" = "Just Select";
"Client Manager" = "Client Manager";
"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable.";
"Add Client" = "Add Client";
"Remove Selected" = "Remove Selected";
"Choose the target application bundle." = "Choose the target application bundle.";
"The selected item is not a valid macOS application bundle." = "The selected item is not a valid macOS application bundle.";
"Please try again." = "Please try again.";
"The selected item's identifier is already in the list." = "The selected item's identifier is already in the list.";
"Update Check Completed" = "Update Check Completed"; "Update Check Completed" = "Update Check Completed";
"You are already using the latest version." = "You are already using the latest version."; "You are already using the latest version." = "You are already using the latest version.";
"Plist downloaded is nil." = "Plist downloaded is nil."; "Plist downloaded is nil." = "Plist downloaded is nil.";

View File

@ -1,4 +1,15 @@
"vChewing" = "vChewing"; "vChewing" = "vChewing";
"Please enter the client app bundle identifier(s) you want to register." = "Please enter the client app bundle identifier(s) you want to register.";
"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed.";
"Just Select" = "Just Select";
"Client Manager" = "Client Manager";
"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable.";
"Add Client" = "Add Client";
"Remove Selected" = "Remove Selected";
"Choose the target application bundle." = "Choose the target application bundle.";
"The selected item is not a valid macOS application bundle." = "The selected item is not a valid macOS application bundle.";
"Please try again." = "Please try again.";
"The selected item's identifier is already in the list." = "The selected item's identifier is already in the list.";
"Update Check Completed" = "Update Check Completed"; "Update Check Completed" = "Update Check Completed";
"You are already using the latest version." = "You are already using the latest version."; "You are already using the latest version." = "You are already using the latest version.";
"Plist downloaded is nil." = "Plist downloaded is nil."; "Plist downloaded is nil." = "Plist downloaded is nil.";

View File

@ -1,4 +1,15 @@
"vChewing" = "威注音入力アプリ"; "vChewing" = "威注音入力アプリ";
"Please enter the client app bundle identifier(s) you want to register." = "登録したい客体アプリの唯一識別子Bundle Identifierをご入力ください。";
"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "毎行はつ記録とみなす。Option+Enter キーで改行。\n空白の記録値は無視される。";
"Just Select" = "直接に選ぶ";
"Client Manager" = "客体アプリの管理";
"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "IMKTextInput 議定規約に従っていない客体アプリはここでご登録ください。登録済みのアプリは客体アプリ(文字入力を受くアプリ)とされた時に、威注音入力アプリは「吹き出し入力緩衝列ウィンドウ」と「緩衝列容量制限」を起用し、その容量制限は最大限音読み20箇とする。";
"Add Client" = "入れる";
"Remove Selected" = "外す";
"Choose the target application bundle." = "登録したいアプリのバンドルのお選びを。";
"The selected item is not a valid macOS application bundle." = "今選んだんのは正しい macOS アプリバンドルではないと考えられる。";
"Please try again." = "お選び直しください。";
"The selected item's identifier is already in the list." = "今選んだバンドルの唯一識別子Bundle Identifierは既に登録済みである。";
"Update Check Completed" = "新バージョンチェック完了"; "Update Check Completed" = "新バージョンチェック完了";
"You are already using the latest version." = "現在稼働中のは最新バージョンである。"; "You are already using the latest version." = "現在稼働中のは最新バージョンである。";
"Plist downloaded is nil." = "受けた新バージョンお知らせ情報データは Plist ではないため、失敗とみなす。"; "Plist downloaded is nil." = "受けた新バージョンお知らせ情報データは Plist ではないため、失敗とみなす。";

View File

@ -1,4 +1,15 @@
"vChewing" = "威注音输入法"; "vChewing" = "威注音输入法";
"Please enter the client app bundle identifier(s) you want to register." = "请键入您要登记的客体应用的唯一标帜Bundle Identifier。";
"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "每行一笔记录,用 Option+Enter 换行。\n空白值会被无视。";
"Just Select" = "直接选取";
"Client Manager" = "管理客体应用";
"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "请在此管理那些不遵守 IMKTextInput 协定的客体应用。威注音输入法对于任何位列在此的客体应用均启用浮动组字窗、且对组字区内容设定容量上限(最多二十个读音)。";
"Add Client" = "登记新客体";
"Remove Selected" = "移除所选条目";
"Choose the target application bundle." = "请选择要登记的应用程式的封包。";
"The selected item is not a valid macOS application bundle." = "当前所选之物并非 macOS 应用程式封包。";
"Please try again." = "请重试。";
"The selected item's identifier is already in the list." = "当前所选之封包的唯一标帜Bundle Identifier已被登记。";
"Update Check Completed" = "更新检查完毕"; "Update Check Completed" = "更新检查完毕";
"You are already using the latest version." = "您正在使用目前最新的发行版。"; "You are already using the latest version." = "您正在使用目前最新的发行版。";
"Plist downloaded is nil." = "下载来的更新资讯并非 Plist 档案。"; "Plist downloaded is nil." = "下载来的更新资讯并非 Plist 档案。";

View File

@ -1,4 +1,15 @@
"vChewing" = "威注音輸入法"; "vChewing" = "威注音輸入法";
"Please enter the client app bundle identifier(s) you want to register." = "請鍵入您要登記的客體應用的唯一標幟Bundle Identifier。";
"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "每行一筆記錄,用 Option+Enter 換行。\n空白值會被無視。";
"Just Select" = "直接選取";
"Client Manager" = "管理客體應用";
"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "請在此管理那些不遵守 IMKTextInput 協定的客體應用。威注音輸入法對於任何位列在此的客體應用均啟用浮動組字窗、且對組字區內容設定容量上限(最多二十個讀音)。";
"Add Client" = "登記新客體";
"Remove Selected" = "移除所選條目";
"Choose the target application bundle." = "請選擇要登記的應用程式的封包。";
"The selected item is not a valid macOS application bundle." = "當前所選之物並非 macOS 應用程式封包。";
"Please try again." = "請重試。";
"The selected item's identifier is already in the list." = "當前所選之封包的唯一標幟Bundle Identifier已被登記。";
"Update Check Completed" = "更新檢查完畢"; "Update Check Completed" = "更新檢查完畢";
"You are already using the latest version." = "您正在使用目前最新的發行版。"; "You are already using the latest version." = "您正在使用目前最新的發行版。";
"Plist downloaded is nil." = "下載來的更新資訊並非 Plist 檔案。"; "Plist downloaded is nil." = "下載來的更新資訊並非 Plist 檔案。";

View File

@ -0,0 +1,167 @@
// (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 Cocoa
class ctlClientListMgr: NSWindowController, NSTableViewDelegate, NSTableViewDataSource {
@IBOutlet var tblClients: NSTableView!
@IBOutlet var btnRemoveClient: NSButton!
@IBOutlet var btnAddClient: NSButton!
@IBOutlet var lblClientMgrWindow: NSTextField!
override func windowDidLoad() {
super.windowDidLoad()
localize()
tblClients.delegate = self
tblClients.dataSource = self
tblClients.reloadData()
}
}
// MARK: - Implementations
extension ctlClientListMgr {
func numberOfRows(in _: NSTableView) -> Int {
mgrPrefs.clientsIMKTextInputIncapable.count
}
func callAlert(_ window: NSWindow, title: String, text: String? = nil) {
let alert = NSAlert()
alert.messageText = title
if let text = text {
alert.informativeText = text
}
alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
alert.beginSheetModal(for: window)
}
@IBAction func btnAddClientClicked(_: Any) {
guard let window = window else { return }
let alert = NSAlert()
alert.messageText = NSLocalizedString(
"Please enter the client app bundle identifier(s) you want to register.", comment: ""
)
alert.informativeText = NSLocalizedString(
"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed.", comment: ""
)
alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Just Select", comment: "") + "")
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
let maxFloat = CGFloat(Float.greatestFiniteMagnitude)
let scrollview = NSScrollView(frame: NSRect(x: 0, y: 0, width: 370, height: 380))
let contentSize = scrollview.contentSize
scrollview.borderType = .noBorder
scrollview.hasVerticalScroller = true
scrollview.hasHorizontalScroller = true
scrollview.horizontalScroller?.scrollerStyle = .legacy
scrollview.verticalScroller?.scrollerStyle = .legacy
scrollview.autoresizingMask = [.width, .height]
let theTextView = NSTextView(frame: NSRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height))
theTextView.minSize = NSSize(width: 0.0, height: contentSize.height)
theTextView.maxSize = NSSize(width: maxFloat, height: maxFloat)
theTextView.isVerticallyResizable = true
theTextView.isHorizontallyResizable = false
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]
theTextView.textContainer?.containerSize = NSSize(width: maxFloat, height: maxFloat)
theTextView.textContainer?.widthTracksTextView = false
alert.accessoryView = scrollview
alert.beginSheetModal(for: window) { result in
switch result {
case .alertFirstButtonReturn:
theTextView.textContainer?.textView?.string.components(separatedBy: "\n").filter { !$0.isEmpty }.forEach {
self.applyNewValue($0)
}
case .alertSecondButtonReturn:
IME.dlgOpenPath.title = NSLocalizedString(
"Choose the target application bundle.", comment: ""
)
IME.dlgOpenPath.showsResizeIndicator = true
IME.dlgOpenPath.showsHiddenFiles = true
IME.dlgOpenPath.canChooseFiles = true
IME.dlgOpenPath.canChooseDirectories = false
IME.dlgOpenPath.beginSheetModal(for: window) { result in
switch result {
case .OK:
guard let url = IME.dlgOpenPath.url else { return }
var title = NSLocalizedString("The selected item is not a valid macOS application bundle.", comment: "")
let text = NSLocalizedString("Please try again.", comment: "")
guard let bundle = Bundle(url: url) else {
self.callAlert(window, title: title, text: text)
return
}
guard let identifier = bundle.bundleIdentifier else {
self.callAlert(window, title: title, text: text)
return
}
if mgrPrefs.clientsIMKTextInputIncapable.contains(identifier) {
title = NSLocalizedString(
"The selected item's identifier is already in the list.", comment: ""
)
self.callAlert(window, title: title)
return
}
self.applyNewValue(identifier)
default: return
}
}
default: return
}
}
}
private func applyNewValue(_ newValue: String) {
guard !newValue.isEmpty else { return }
var arrResult = mgrPrefs.clientsIMKTextInputIncapable
arrResult.append(newValue)
mgrPrefs.clientsIMKTextInputIncapable = arrResult.sorted()
tblClients.reloadData()
btnRemoveClient.isEnabled = (0..<mgrPrefs.clientsIMKTextInputIncapable.count).contains(
tblClients.selectedRow)
}
@IBAction func btnRemoveClientClicked(_: Any) {
if tblClients.selectedRow >= mgrPrefs.clientsIMKTextInputIncapable.count { return }
if tblClients.selectedRow < 0 { return }
let isLastRow: Bool = {
if mgrPrefs.clientsIMKTextInputIncapable.count < 2 { return false }
return tblClients.selectedRow == mgrPrefs.clientsIMKTextInputIncapable.count - 1
}()
mgrPrefs.clientsIMKTextInputIncapable.remove(at: tblClients.selectedRow)
if isLastRow {
tblClients.selectRowIndexes(.init(arrayLiteral: tblClients.selectedRow - 1), byExtendingSelection: false)
}
tblClients.reloadData()
btnRemoveClient.isEnabled = (0..<mgrPrefs.clientsIMKTextInputIncapable.count).contains(tblClients.selectedRow)
}
func tableView(_: NSTableView, objectValueFor _: NSTableColumn?, row: Int) -> Any? {
defer {
self.btnRemoveClient.isEnabled = (0..<mgrPrefs.clientsIMKTextInputIncapable.count).contains(
self.tblClients.selectedRow)
}
return mgrPrefs.clientsIMKTextInputIncapable[row]
}
private func localize() {
guard let window = window else { return }
window.title = NSLocalizedString("Client Manager", comment: "")
lblClientMgrWindow.stringValue = NSLocalizedString(
"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable.",
comment: ""
)
btnAddClient.title = NSLocalizedString("Add Client", comment: "")
btnRemoveClient.title = NSLocalizedString("Remove Selected", comment: "")
}
}

View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21225" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21225"/>
<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="664" 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="664" 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="494" height="46"/>
<constraints>
<constraint firstAttribute="height" constant="46" id="HDu-vv-Kcv"/>
<constraint firstAttribute="width" 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="624" height="232"/>
<clipView key="contentView" id="wKQ-IV-k62">
<rect key="frame" x="1" y="1" width="622" 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="622" height="230"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="17" height="0.0"/>
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="AutomaticTableColumnIdentifier.0" width="590" 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="518" 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"/>
<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"/>
<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"/>
</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="231" y="176.5"/>
</window>
</objects>
</document>

View File

@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; }; 5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; };
5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; }; 5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; };
5B0EF55D28CDBF7100F8F7CE /* frmClientListMgr.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */; };
5B0EF55F28CDBF8E00F8F7CE /* ctlClientListMgr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0EF55E28CDBF8E00F8F7CE /* ctlClientListMgr.swift */; };
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; }; 5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; };
5B16B84C28C9A89000ABA692 /* FolderMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B16B84B28C9A89000ABA692 /* FolderMonitor.swift */; }; 5B16B84C28C9A89000ABA692 /* FolderMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B16B84B28C9A89000ABA692 /* FolderMonitor.swift */; };
5B175FFB28C5CDDC0078D1B4 /* IMKHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */; }; 5B175FFB28C5CDDC0078D1B4 /* IMKHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */; };
@ -211,6 +213,8 @@
5B09307B28B6FC410021F8C5 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; lineEnding = 0; name = ja; path = ja.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>"; };
5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringExtension.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringExtension.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 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>"; };
5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B16B84B28C9A89000ABA692 /* FolderMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderMonitor.swift; sourceTree = "<group>"; }; 5B16B84B28C9A89000ABA692 /* FolderMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderMonitor.swift; sourceTree = "<group>"; };
5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMKHelper.swift; sourceTree = "<group>"; }; 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMKHelper.swift; sourceTree = "<group>"; };
@ -594,6 +598,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */, 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */,
5B0EF55E28CDBF8E00F8F7CE /* ctlClientListMgr.swift */,
D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */, D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */,
); );
path = WindowControllers; path = WindowControllers;
@ -602,6 +607,7 @@
5B62A33B27AE7C7F00A19448 /* WindowNIBs */ = { 5B62A33B27AE7C7F00A19448 /* WindowNIBs */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */,
5BBBB76927AED5DB0023B93A /* frmAboutWindow.xib */, 5BBBB76927AED5DB0023B93A /* frmAboutWindow.xib */,
5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */, 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */,
6A187E2816004C5900466B2E /* MainMenu.xib */, 6A187E2816004C5900466B2E /* MainMenu.xib */,
@ -1079,6 +1085,7 @@
D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */, D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */,
5BF9DA2828840E6200DBD48E /* template-exclusions.txt in Resources */, 5BF9DA2828840E6200DBD48E /* template-exclusions.txt in Resources */,
5B7DA80328BF6BC600D7B2AD /* fixinstall.sh in Resources */, 5B7DA80328BF6BC600D7B2AD /* fixinstall.sh in Resources */,
5B0EF55D28CDBF7100F8F7CE /* frmClientListMgr.xib in Resources */,
5BDCBB2E27B4E67A00D0CC59 /* vChewingPhraseEditor.app in Resources */, 5BDCBB2E27B4E67A00D0CC59 /* vChewingPhraseEditor.app in Resources */,
5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */, 5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */,
6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */, 6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */,
@ -1225,6 +1232,7 @@
5B5F8AEE28C8826D007C11F1 /* ctlTooltip.swift in Sources */, 5B5F8AEE28C8826D007C11F1 /* ctlTooltip.swift in Sources */,
5BD0113B28180D6100609769 /* LMInstantiator.swift in Sources */, 5BD0113B28180D6100609769 /* LMInstantiator.swift in Sources */,
5B2170E7289FACAD00BE7304 /* 1_Compositor.swift in Sources */, 5B2170E7289FACAD00BE7304 /* 1_Compositor.swift in Sources */,
5B0EF55F28CDBF8E00F8F7CE /* ctlClientListMgr.swift in Sources */,
5B21177028753B9D000443A9 /* ctlInputMethod_Delegates.swift in Sources */, 5B21177028753B9D000443A9 /* ctlInputMethod_Delegates.swift in Sources */,
5B21176E28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift in Sources */, 5B21176E28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift in Sources */,
5B84579F2871AD2200C93B01 /* HotenkaChineseConverter.swift in Sources */, 5B84579F2871AD2200C93B01 /* HotenkaChineseConverter.swift in Sources */,