1
0
Эх сурвалжийг харах

Show an import window instead of importing from pasteboard.

Qiu Yuzhou 5 жил өмнө
parent
commit
00631f3b81

+ 18 - 0
ShadowsocksX-NG.xcodeproj/project.pbxproj

@@ -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 = (

+ 15 - 12
ShadowsocksX-NG/AppDelegate.swift

@@ -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)
             }

+ 64 - 0
ShadowsocksX-NG/Base.lproj/ImportWindowController.xib

@@ -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>

+ 3 - 3
ShadowsocksX-NG/Base.lproj/MainMenu.xib

@@ -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"/>

+ 57 - 0
ShadowsocksX-NG/ImportWindowController.swift

@@ -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()
+        }
+    }
+}

+ 29 - 0
ShadowsocksX-NG/ServerProfileManager.swift

@@ -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
+    }
 }

+ 9 - 0
ShadowsocksX-NG/zh-Hans.lproj/ImportWindowController.strings

@@ -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到这里";

+ 3 - 0
ShadowsocksX-NG/zh-Hans.lproj/MainMenu.strings

@@ -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...";