From b24b9e9b143f78f0dd3fa8bc18a959b739e6d0a8 Mon Sep 17 00:00:00 2001 From: qinyuhang Date: Mon, 22 Aug 2016 19:18:35 +0800 Subject: [PATCH] Add duplicate server profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add duplicate server profile button 2. Update status bar icon (改进了之前颜色太浅的问题) 3. Fix layout on server preference window 4. Add alert or shake window when profile is not completed --- ShadowsocksX-NG.xcodeproj/project.pbxproj | 4 +- .../PreferencesWindowController.xib | 27 +++++++++--- .../PreferencesWindowController.swift | 41 +++++++++++++++++- ShadowsocksX-NG/menu_icon_disabled.png | Bin 338 -> 14767 bytes ShadowsocksX-NG/menu_icon_disabled@2x.png | Bin 575 -> 15007 bytes .../PreferencesWindowController.strings | 35 ++++++++------- 6 files changed, 81 insertions(+), 26 deletions(-) diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index 1d5d472..f0f01bd 100644 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -92,6 +92,7 @@ /* Begin PBXFileReference section */ 088EC3961D5F5B8600E40791 /* whitelist.pac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = whitelist.pac; sourceTree = ""; }; 088EC3981D5F5BA300E40791 /* whiteiplist.pac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = whiteiplist.pac; sourceTree = ""; }; + 08AF56C01D6AFA7C00DC4F46 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/PreferencesWindowController.strings"; sourceTree = ""; }; 19083CFCED87354F006967FF /* Pods_ShadowsocksX_NGUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShadowsocksX_NGUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1E7783AEDB4A3BDDC9FF16AC /* libPods-proxy_conf_helper.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-proxy_conf_helper.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 283ED1A8E9B711AC65670031 /* Pods_ShadowsocksX_NG.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShadowsocksX_NG.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -154,7 +155,6 @@ 9BB706A51D1B982300551F0E /* SWBApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWBApplication.m; sourceTree = ""; }; 9BB706A61D1B982300551F0E /* SWBApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWBApplication.h; sourceTree = ""; }; 9BE8FBBD1D0B1FB900CAFD01 /* libcrypto.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libcrypto.1.0.0.dylib; sourceTree = ""; }; - 9BE8FBC11D0B71CF00CAFD01 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/PreferencesWindowController.strings"; sourceTree = ""; }; 9BEEF0651D04CB8500FC52B3 /* install_ss_local.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = install_ss_local.sh; sourceTree = ""; }; 9BEEF0661D04CE8D00FC52B3 /* start_ss_local.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = start_ss_local.sh; sourceTree = ""; }; 9BEEF0671D04CE9A00FC52B3 /* stop_ss_local.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = stop_ss_local.sh; sourceTree = ""; }; @@ -708,7 +708,7 @@ isa = PBXVariantGroup; children = ( 9B2491B51D0ACC3A003BBECC /* Base */, - 9BE8FBC11D0B71CF00CAFD01 /* zh-Hans */, + 08AF56C01D6AFA7C00DC4F46 /* zh-Hans */, ); name = PreferencesWindowController.xib; sourceTree = ""; diff --git a/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib b/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib index 4a8296f..c3ff705 100644 --- a/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib +++ b/ShadowsocksX-NG/Base.lproj/PreferencesWindowController.xib @@ -12,6 +12,7 @@ + @@ -257,24 +258,24 @@ - - + + - - - + + + - - + + @@ -318,6 +319,7 @@ + @@ -335,6 +337,7 @@ + @@ -401,6 +404,16 @@ DQ + diff --git a/ShadowsocksX-NG/PreferencesWindowController.swift b/ShadowsocksX-NG/PreferencesWindowController.swift index ade9abc..fbfcf86 100644 --- a/ShadowsocksX-NG/PreferencesWindowController.swift +++ b/ShadowsocksX-NG/PreferencesWindowController.swift @@ -24,6 +24,7 @@ class PreferencesWindowController: NSWindowController @IBOutlet weak var ObfsTextField: NSComboBox! @IBOutlet weak var ObfsParamTextField: NSTextField! + @IBOutlet weak var duplicateProfileButton: NSButton! @IBOutlet weak var passwordTextField: NSTextField! @IBOutlet weak var remarkTextField: NSTextField! @@ -120,7 +121,26 @@ class PreferencesWindowController: NSWindowController @IBAction func ok(sender: NSButton) { if editingProfile != nil { if !editingProfile.isValid() { - // TODO Shake window? + let numberOfShakes:Int = 8 + let durationOfShake:Float = 0.5 + let vigourOfShake:Float = 0.05 + + let frame:CGRect = (self.window?.frame)! + let shakeAnimation = CAKeyframeAnimation() + + let shakePath = CGPathCreateMutable() + CGPathMoveToPoint(shakePath, nil, NSMinX(frame), NSMinY(frame)) + + for _ in 1...numberOfShakes{ + CGPathAddLineToPoint(shakePath, nil, NSMinX(frame) - frame.size.width * CGFloat(vigourOfShake), NSMinY(frame)) + CGPathAddLineToPoint(shakePath, nil, NSMinX(frame) + frame.size.width * CGFloat(vigourOfShake), NSMinY(frame)) + } + + CGPathCloseSubpath(shakePath) + shakeAnimation.path = shakePath + shakeAnimation.duration = CFTimeInterval(durationOfShake) + self.window?.animations = ["frameOrigin":shakeAnimation] + self.window?.animator().setFrameOrigin(self.window!.frame.origin) return } } @@ -135,6 +155,25 @@ class PreferencesWindowController: NSWindowController window?.performClose(self) } + @IBAction func duplicateProfile(sender: NSButton) { + //读取当前profile,并且保存 + if editingProfile != nil && !editingProfile.isValid(){ + return + } + let index = profilesTableView.selectedRow + if index >= 0 { + let profile = profileMgr.profiles[index] + profilesTableView.beginUpdates() + profileMgr.profiles.append(profile) + let index = NSIndexSet(index: profileMgr.profiles.count-1) + profilesTableView.insertRowsAtIndexes(index, withAnimation: .EffectFade) + self.profilesTableView.scrollRowToVisible(self.profileMgr.profiles.count-1) + self.profilesTableView.selectRowIndexes(index, byExtendingSelection: false) + profilesTableView.endUpdates() + updateProfileBoxVisible() + } + } + @IBAction func copyCurrentProfileURL2Pasteboard(sender: NSButton) { let index = profilesTableView.selectedRow if index >= 0 { diff --git a/ShadowsocksX-NG/menu_icon_disabled.png b/ShadowsocksX-NG/menu_icon_disabled.png index 5cf230e325319ce4bf16b42862e6d53a16a3e3a3..2d09313b919395f5cd1a567dda1608bbba8eb7d8 100644 GIT binary patch literal 14767 zcmeI3Z)_7~9LFCp4j9fq5Ce{xTuLw?w0FI&+j?mWvmJKEj%6#`0FB(P&$!|L1Gr8fFZs?GzOw##E>W$c_HBiukeK?Vnl;tVgyIxpXb`H{k3a(!0_^E zy7sr{`F?-D=RVJ$-kaOk)BR*aePcaBsG%zw=_TKboWJ>Xcf+px_I;iResUDrN4?c&r# z&^6$T2{9v#N7U%JiTlR8)RM9kH+`e?ixXZ5tBLXEGU2rrD#LNn-c=c|qhw(M=R?YfQ7{tXs2|=abCj ziC{}HRl`_RdGH4KS#71tdHtX>uc^_T zzD*R!i}yO81uN{Xv~Ie!6?RuzOU{j&YKO!^wTfooa!%m<(a=Y37I3K5=WF%__{!!= zM~|AtslkX!_ORuwOmBO^Q`u3~xT%&AbQFyFsRBRixtYwjlA8u{zrq)9Sjs24xEbzp zP`aYKoEWBKMIa4o%y3Z=K>@=BX;5Q^i;4&e7%oVI8Z%r}L{PwRK^oMU;i4jf0)`9H zpvDXr6%iCLT#yDeX1J(`pn&0mG^jDdMMVS!3>Tz9jTtT~A}C8P zs4>GuMFa&57oPwFRVvRiammv@MMW#Q!LLLs}lxS}Zq45<6P3%Ev`d{+>GeV;RLceT9NSZ=u ziN622v+IfW@2*HkJbUTPfp@eog$MVZj;~ngPRqWN7yf)?{YzUqckX&Tbxmk8k6+9z zZTMzH?Hg=r9ADA>wW*T6uEw+*4@TfP3D@p8H3SyZjSJ#XmM2`}*8u)2=-aPaB?t zFFe*i-SN}KW6>XCt1>&DdgjEyk&c6JtgAb4UVW``@jd&#JG5%U<<-WPy$en}^y(Lr p@o-o8aN}Q1ivQ+&ul(AIZrrzd$0zZVNdf|Ob#_NScw+1B{{R|L7ry`i delta 311 zcmV-70m%NZbJ7BkB!3xnMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ami&o0002= zNklk=0d3XNgSm8(f`rb>_z~yE?me#R=moj?P_K@mn!|J(yLRk!5j=I2rrl~?I;*TV1X^}lgr;pbs$jo>yp=+cc-apchKM64wOFTf(y} zk|eH7%i)!MQcK6M92{5A7V{j-Tg_+=M46rg?jfI`3I8e!szvhnM7~88x;MX+00bBhra z*{w!vv5hs_tsF3#OMuzZ%$bWhAjBYgy{LUeYPT%5={qKo+V>w*BlZ!gs}%{Jc0vHr z=%fFyB{HNLQOYB!aE*2#YZtEI*m+h5?it=X)De(k=TcZuRG{xVr(tN@B@eRXIi-7I zyO$S4{X}z#`lbLr2Q#cSHLRpFVUzyv1jh3t_R(uZT0!V+8!DbErW!qoDz|76K>=lc zxF-JXevfA!ZcQ)YVDw*sfHZtE;;qByXlT`roKuH>xbbj3z_AIjc`!Se5pAi;aeae6 zuYtChzL;a-i!tk;2`k#2Zf!if741&9rkt}wlIG$P)zX>~mwp1rkA^yUvjAL0Hk-xf zVA7k@9W_!E1RC5D+{3EAGA%_3PkKj2W8-K>q$6RBPZjdB9-GN{E4*od_bW{DhGqC9 zCpW`64pLWijuWHlND-6K}8rYcN!XiQi1Q$vJ8xveu zM5ut^LTO-Qf(wfX6%bq~4QxzsVG*GMf(xaAjR`I+B2++dp){~D!G%SH3J5Ng1~w+R zu!v9r!G+Sm#sn7@5h@_KP#V~n;KCw81q2sL0~-@uSVX9R;6iC&V}c8d2o(@qC=F~( zaA6Ul0)h*rfsF|+EFx4uaG^A?F~NmJgbD~Qlm<2?xUh&&0l|gRz{Ug@77;2SxKJ9{ znBc-9LIngDN&_386<2QR5kL@v&;3Q<1Ae>Isn@`V18LDy>!qm9DHPScn4*q>$S$*{2v6as1Z%Z#(QZRW=XwBNSQ;fYIt~l9L zb>P+;o8CQreycQd+RnTwpASB^p=(ObK-JOq%}-S9-BU1WRf%WW6@@1czUcQ4KJirB zcdNx`j&*l!+u-cYSD((eMD`zEJ^AU&a&}~2)U$S1K{cghpQaw3_UZ*^ZdCSPTJceT z>G!)UyWW5B^zn}$Sf4X%{)RjET)ybo*s0W>uTHF|f?41EZs}jzxv01F;|cv`rCHY& zzI@=f!qT2KjoJRZ!Hzi#7xiS9-Aa!!%{wo=W9jxwj^;-x6F*qE$@Ar;*7r6QcF)iI zq{H)5=&SDYCV0o()3bgH;^sSFAAeut?aM3b zN%=wh{!LJJ=3&vloKSEkrQRVKRF?Z2Fxu%Lc delta 550 zcmV+>0@?kab-x6VB!3xnMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ami&o0005u zNklZKqAm-eeC0K$a(`(fskb4jXxkrKR^Z`T#jb}>Hu<*KTd!&K}yh| zEnHSW1|G?&*LY_EG7JqiyjzKPe)S(?AVUxg(H3-3Yk>A1>X57j=mzwzWljzWU1|e$ ztQri#n!+?U>3=^`pswW$lE8|$Fh8Vkm1hNH0fO=hFnh3p`LfiUBL^awM@4z6lJ^B* z60m`lv{F=5YySKIQMZg~g&6-(0}c(YVJky8URN7bN_hcrB>$~!I??nLGxDH6t;Jv z4T$ee2rPv0XVv-8)s8WsZ`Ws2k0Fn|k@j|!#;~QfMqB~${m*EAHf&|zrXhfwgv!;a oqtHoI6CYv!Kdk;5(6;~s09~fm5{}fUqW}N^07*qoM6N<$f}{WXNdN!< diff --git a/ShadowsocksX-NG/zh-Hans.lproj/PreferencesWindowController.strings b/ShadowsocksX-NG/zh-Hans.lproj/PreferencesWindowController.strings index 7158303..b599efe 100644 --- a/ShadowsocksX-NG/zh-Hans.lproj/PreferencesWindowController.strings +++ b/ShadowsocksX-NG/zh-Hans.lproj/PreferencesWindowController.strings @@ -14,6 +14,18 @@ /* Class = "NSTextFieldCell"; title = "Remarks:"; ObjectID = "MiN-VE-FtC"; */ "MiN-VE-FtC.title" = "备注:"; +/* Class = "NSTextFieldCell"; title = "SSR Settings (Left empty if not sure what it is)"; ObjectID = "Vtp-4J-er7"; */ +"Vtp-4J-er7.title" = "SSR设置,不清楚请留空"; + +/* Class = "NSTextFieldCell"; title = "Obfs"; ObjectID = "aQj-D7-XWZ"; */ +"aQj-D7-XWZ.title" = "混淆:"; + +/* Class = "NSTextFieldCell"; title = "Protocol Param"; ObjectID = "dCk-qx-L1X"; */ +"dCk-qx-L1X.title" = "协议参数:"; + +/* Class = "NSTextFieldCell"; title = "Obfs Param"; ObjectID = "g7c-fW-7J4"; */ +"g7c-fW-7J4.title" = "混淆参数:"; + /* Class = "NSButtonCell"; title = "Enable OTA"; ObjectID = "hRZ-Rp-I5e"; */ "hRZ-Rp-I5e.title" = "启用 OTA"; @@ -26,6 +38,12 @@ /* Class = "NSTextFieldCell"; title = "Encryption:"; ObjectID = "mOh-nD-7EX"; */ "mOh-nD-7EX.title" = "加密方法:"; +/* Class = "NSTextFieldCell"; title = "Protocol:"; ObjectID = "nmk-Ue-wYb"; */ +"nmk-Ue-wYb.title" = "协议"; + +/* Class = "NSButtonCell"; title = "Duplicate"; ObjectID = "s5r-hM-4Gh"; */ +"s5r-hM-4Gh.title" = "复制"; + /* Class = "NSButtonCell"; title = "Copy URL to Clipboard"; ObjectID = "t3T-Kd-5La"; */ "t3T-Kd-5La.title" = "复制服务器URL到剪贴板"; @@ -33,19 +51,4 @@ "yHs-XE-XEB.title" = "地址:"; /* Class = "NSButtonCell"; title = "OK"; ObjectID = "zPE-oD-PwK"; */ -"zPE-oD-PwK.title" = "确定"; - -/* Class = "NSTextFieldCell"; title = "SSR Settings (Left empty if not sure what it is"; ObjectID = "Vtp-4J-er7.title";*/ -"Vtp-4J-er7.title" = "SSR设置,不清楚请留空"; - -/* Class = "NSTextFieldCell"; title = "Obfs"; ObjectID = "Vtp-4J-er7.title";*/ -"aQj-D7-XWZ.title" = "混淆" - -/* Class = "NSTextFieldCell"; title = "Obfs Param"; ObjectID = "Vtp-4J-er7.title";*/ -"g7c-fW-7J4.title" = "混淆参数" - -/* Class = "NSTextFieldCell"; title = "Protocol"; ObjectID = "nmk-Ue-wYb,title";*/ -"nmk-Ue-wYb,title" = "协议" - -/* Class = "NSTextFieldCell"; title = "Protocol Param"; ObjectID = "Vtp-4J-er7.title";*/ -"dCk-qx-L1X.title" = "协议参数" +"zPE-oD-PwK.title" = "OK";