Show an import window instead of importing from pasteboard.

This commit is contained in:
Qiu Yuzhou
2019-09-11 23:33:02 +08:00
parent 2163aad80d
commit 00631f3b81
8 changed files with 198 additions and 15 deletions

View File

@ -54,6 +54,8 @@
9B7297E7214D69C300FD24AA /* libmbedcrypto.2.12.0.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 9B7297E5214D68F800FD24AA /* libmbedcrypto.2.12.0.dylib */; };
9B7297EA214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B7297E8214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift */; };
9B7297EC214DA88A00FD24AA /* ShareServerProfilesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B7297EE214DA88A00FD24AA /* ShareServerProfilesWindowController.xib */; };
9B72FB62232782A300C6AAAE /* ImportWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B72FB60232782A300C6AAAE /* ImportWindowController.swift */; };
9B74B5E9232949B100DEA386 /* ImportWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B74B5EB232949B100DEA386 /* ImportWindowController.xib */; };
9B84DAED2163A72F00DFF068 /* Diagnose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B84DAEC2163A72F00DFF068 /* Diagnose.swift */; };
9B86459D1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B86459C1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift */; };
9B938D991E864B38005F5636 /* menu_g_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B938D931E864B38005F5636 /* menu_g_icon.png */; };
@ -214,6 +216,9 @@
9B7297E8214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareServerProfilesWindowController.swift; sourceTree = "<group>"; };
9B7297ED214DA88A00FD24AA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ShareServerProfilesWindowController.xib; sourceTree = "<group>"; };
9B7297F0214DA89000FD24AA /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ShareServerProfilesWindowController.strings"; sourceTree = "<group>"; };
9B72FB60232782A300C6AAAE /* ImportWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportWindowController.swift; sourceTree = "<group>"; };
9B74B5EF232949D400DEA386 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ImportWindowController.xib; sourceTree = "<group>"; };
9B74B5F1232949E800DEA386 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ImportWindowController.strings"; sourceTree = "<group>"; };
9B84DAEC2163A72F00DFF068 /* Diagnose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Diagnose.swift; sourceTree = "<group>"; };
9B86459C1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxyInterfacesViewCtrl.swift; sourceTree = "<group>"; };
9B938D931E864B38005F5636 /* menu_g_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_g_icon.png; sourceTree = "<group>"; };
@ -433,6 +438,8 @@
9B3546711E802B1200B510B4 /* ToastWindowController.xib */,
9B7297E8214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift */,
9B7297EE214DA88A00FD24AA /* ShareServerProfilesWindowController.xib */,
9B72FB60232782A300C6AAAE /* ImportWindowController.swift */,
9B74B5EB232949B100DEA386 /* ImportWindowController.xib */,
);
name = UI;
sourceTree = "<group>";
@ -652,6 +659,7 @@
9B3F7BFF1E82BF5B00C68B75 /* libev.4.dylib in Resources */,
1C82DBAA1FA96FB600B32551 /* install_simple_obfs.sh in Resources */,
9B938D9A1E864B38005F5636 /* menu_g_icon@2x.png in Resources */,
9B74B5E9232949B100DEA386 /* ImportWindowController.xib in Resources */,
9BBECA07232404FB00C632DB /* terminal-logo.png in Resources */,
9B938D9E1E864B38005F5636 /* menu_p_icon@2x.png in Resources */,
9B9CBCAF1E263B1600FC61AA /* libpcre.1.dylib in Resources */,
@ -810,6 +818,7 @@
9B3FFF141D0705810019A709 /* Notifications.swift in Sources */,
9BEEF0701D04DDB100FC52B3 /* ServerProfileManager.swift in Sources */,
9BEEF06E1D04DCE400FC52B3 /* ServerProfile.swift in Sources */,
9B72FB62232782A300C6AAAE /* ImportWindowController.swift in Sources */,
9B3FFF0D1D05FEB30019A709 /* Utils.swift in Sources */,
9BEEF0751D04EF3E00FC52B3 /* PreferencesWindowController.swift in Sources */,
9B0BFFE91D0460A70040E62B /* AppDelegate.swift in Sources */,
@ -891,6 +900,15 @@
name = ShareServerProfilesWindowController.xib;
sourceTree = "<group>";
};
9B74B5EB232949B100DEA386 /* ImportWindowController.xib */ = {
isa = PBXVariantGroup;
children = (
9B74B5EF232949D400DEA386 /* Base */,
9B74B5F1232949E800DEA386 /* zh-Hans */,
);
name = ImportWindowController.xib;
sourceTree = "<group>";
};
9BAFE2E41E83ED7F00F71CCE /* PreferencesWinController.xib */ = {
isa = PBXVariantGroup;
children = (

View File

@ -20,6 +20,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
var editUserRulesWinCtrl: UserRulesController!
var allInOnePreferencesWinCtrl: PreferencesWinController!
var toastWindowCtrl: ToastWindowController!
var importWinCtrl: ImportWindowController!
@IBOutlet weak var window: NSWindow!
@IBOutlet weak var statusMenu: NSMenu!
@ -266,6 +267,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
shareWinCtrl.window?.makeKeyAndOrderFront(nil)
}
@IBAction func showImportWindow(_ sender: NSMenuItem) {
if importWinCtrl != nil {
importWinCtrl.close()
}
importWinCtrl = ImportWindowController(windowNibName: .init(rawValue: "ImportWindowController"))
importWinCtrl.showWindow(self)
NSApp.activate(ignoringOtherApps: true)
importWinCtrl.window?.makeKeyAndOrderFront(nil)
}
@IBAction func scanQRCodeFromScreen(_ sender: NSMenuItem) {
ScanQRCodeOnScreen()
}
@ -276,7 +287,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
if let text = pb.string(forType: NSPasteboard.PasteboardType.URL) {
if let url = URL(string: text) {
NotificationCenter.default.post(
name: Notification.Name(rawValue: "NOTIFY_FOUND_SS_URL"), object: nil
name: NOTIFY_FOUND_SS_URL, object: nil
, userInfo: [
"urls": [url],
"source": "pasteboard",
@ -293,7 +304,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
urls = urls.filter { $0.scheme == "ss" }
NotificationCenter.default.post(
name: Notification.Name(rawValue: "NOTIFY_FOUND_SS_URL"), object: nil
name: NOTIFY_FOUND_SS_URL, object: nil
, userInfo: [
"urls": urls,
"source": "pasteboard",
@ -543,7 +554,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
if let urlString = event.paramDescriptor(forKeyword: AEKeyword(keyDirectObject))?.stringValue {
if let url = URL(string: urlString) {
NotificationCenter.default.post(
name: Notification.Name(rawValue: "NOTIFY_FOUND_SS_URL"), object: nil
name: NOTIFY_FOUND_SS_URL, object: nil
, userInfo: [
"urls": [url],
"source": "url",
@ -570,7 +581,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
let urls: [URL] = userInfo["urls"] as! [URL]
let mgr = ServerProfileManager.instance
var addCount = 0
var subtitle: String = ""
if userInfo["source"] as! String == "qrcode" {
@ -581,17 +591,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
subtitle = "By import from pasteboard".localized
}
for url in urls {
if let profile = ServerProfile(url: url) {
mgr.profiles.append(profile)
addCount = addCount + 1
}
}
let addCount = mgr.addServerProfileByURL(urls: urls)
if addCount > 0 {
sendNotify("Add \(addCount) Shadowsocks Server Profile".localized, subtitle, "")
mgr.save()
self.updateServersMenu()
} else {
sendNotify("", "", "Not found valid qrcode or url of shadowsocks profile".localized)
}

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ImportWindowController" customModule="ShadowsocksX_NG" customModuleProvider="target">
<connections>
<outlet property="inputBox" destination="EgI-Py-Op1" id="zNx-W4-NkW"/>
<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="Import Server URLs" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="544" y="557" width="680" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="680" height="270"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="EgI-Py-Op1">
<rect key="frame" x="20" y="49" width="640" height="201"/>
<constraints>
<constraint firstAttribute="height" constant="201" id="Nj4-mV-JHl"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="Copy the shadowsocks URLs to here" drawsBackground="YES" id="h31-8G-f2d">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="doh-o9-XXh">
<rect key="frame" x="516" y="13" width="150" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="138" id="uoX-dr-Ak1"/>
</constraints>
<buttonCell key="cell" type="push" title="Import" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="EW8-ld-aGr">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="handleImport:" target="-2" id="Ch1-iY-BAo"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="EgI-Py-Op1" firstAttribute="trailing" secondItem="doh-o9-XXh" secondAttribute="trailing" id="K62-uO-jYd"/>
<constraint firstItem="EgI-Py-Op1" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="ghl-kH-ozW"/>
<constraint firstAttribute="trailing" secondItem="EgI-Py-Op1" secondAttribute="trailing" constant="20" symbolic="YES" id="mTU-X2-mS7"/>
<constraint firstItem="EgI-Py-Op1" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="oet-Qd-ugX"/>
<constraint firstItem="doh-o9-XXh" firstAttribute="top" secondItem="EgI-Py-Op1" secondAttribute="bottom" constant="8" symbolic="YES" id="wZC-Am-Hdd"/>
</constraints>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
</window>
</objects>
</document>

View File

@ -80,10 +80,10 @@
<action selector="scanQRCodeFromScreen:" target="Voe-Tx-rLC" id="zQZ-IT-H4a"/>
</connections>
</menuItem>
<menuItem title="Import Server URLs From Pasteboard" id="7Eq-XD-K5c">
<menuItem title="Import Server URLs..." id="geG-dQ-OYl">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="importProfileURLFromPasteboard:" target="Voe-Tx-rLC" id="RwL-Ms-BaC"/>
<action selector="showImportWindow:" target="-1" id="CsE-vW-Wcn"/>
</connections>
</menuItem>
<menuItem title="Share Server Profiles..." image="NSShareTemplate" id="r5z-RB-LIZ">
@ -98,7 +98,7 @@
<action selector="showAllInOnePreferences:" target="Voe-Tx-rLC" id="2of-nZ-atc"/>
</connections>
</menuItem>
<menuItem title="Copy HTTP Proxy Shell Export Line" image="terminal-logo" id="lg6-To-GZA">
<menuItem title="HTTP Proxy Export Line To Pasteboard" image="terminal-logo" id="lg6-To-GZA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="copyExportCommand:" target="Voe-Tx-rLC" id="2U4-3M-sAK"/>

View File

@ -0,0 +1,57 @@
//
// ImportWindowController.swift
// ShadowsocksX-NG
//
// Created by on 2019/9/10.
// Copyright © 2019 qiuyuzhou. All rights reserved.
//
import Cocoa
class ImportWindowController: NSWindowController {
@IBOutlet weak var inputBox: NSTextField!
override func windowDidLoad() {
super.windowDidLoad()
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
let pb = NSPasteboard.general
if #available(OSX 10.13, *) {
if let text = pb.string(forType: NSPasteboard.PasteboardType.URL) {
if let url = URL(string: text) {
if url.scheme == "ss" {
inputBox.stringValue = text
}
}
}
}
if let text = pb.string(forType: NSPasteboard.PasteboardType.string) {
let urls = ServerProfileManager.findURLSInText(text)
if urls.count > 0 {
inputBox.stringValue = text
}
}
}
@IBAction func handleImport(_ sender: NSButton) {
let mgr = ServerProfileManager.instance
let urls = ServerProfileManager.findURLSInText(inputBox.stringValue)
let addCount = mgr.addServerProfileByURL(urls: urls)
if addCount > 0 {
let alert = NSAlert.init()
alert.alertStyle = .informational;
alert.messageText = "Success to add \(addCount) server.".localized
alert.addButton(withTitle: "OK")
alert.runModal()
self.close()
} else {
let alert = NSAlert.init()
alert.alertStyle = .informational;
alert.messageText = "Not found valid shadowsocks server urls.".localized
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
}

View File

@ -62,4 +62,33 @@ class ServerProfileManager: NSObject {
return nil
}
}
func addServerProfileByURL(urls: [URL]) -> Int {
var addCount = 0
for url in urls {
if let profile = ServerProfile(url: url) {
profiles.append(profile)
addCount = addCount + 1
}
}
if addCount > 0 {
save()
NotificationCenter.default
.post(name: NOTIFY_SERVER_PROFILES_CHANGED, object: nil)
}
return addCount
}
static func findURLSInText(_ text: String) -> [URL] {
var urls = text.split(separator: "\n")
.map { String($0).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) }
.map { URL(string: $0) }
.filter { $0 != nil }
.map { $0! }
urls = urls.filter { $0.scheme == "ss" }
return urls
}
}

View File

@ -0,0 +1,9 @@
/* Class = "NSButtonCell"; title = "Import"; ObjectID = "EW8-ld-aGr"; */
"EW8-ld-aGr.title" = "导入";
/* Class = "NSWindow"; title = "Import Server URLs"; ObjectID = "F0z-JX-Cv5"; */
"F0z-JX-Cv5.title" = "导入服务器URLs";
/* Class = "NSTextFieldCell"; placeholderString = "Copy your shadowsocks URLs to here"; ObjectID = "h31-8G-f2d"; */
"h31-8G-f2d.placeholderString" = "复制shadowsocks URLs到这里";

View File

@ -112,3 +112,6 @@
/* Class = "NSMenuItem"; title = "Export Diagnosis..."; ObjectID = "eNh-vY-utd"; */
"eNh-vY-utd.title" = "导出诊断信息...";
/* Class = "NSMenuItem"; title = "Import Server URLs..."; ObjectID = "geG-dQ-OYl"; */
"geG-dQ-OYl.title" = "导入服务器URLs...";