Repo // Introduce ClientListMgr.
This commit is contained in:
parent
816d9fea18
commit
d21d0342bf
|
@ -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")
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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.";
|
||||||
|
|
|
@ -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.";
|
||||||
|
|
|
@ -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." = "毎行は1つ記録とみなす。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 ではないため、失敗とみなす。";
|
||||||
|
|
|
@ -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 档案。";
|
||||||
|
|
|
@ -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 檔案。";
|
||||||
|
|
|
@ -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: "")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -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 */,
|
||||||
|
|
Loading…
Reference in New Issue