diff options
19 files changed, 1079 insertions, 655 deletions
diff --git a/Interfaces/English.lproj/ConnectionView.xib b/Interfaces/English.lproj/ConnectionView.xib index 4c0d4dc2..f657096a 100644 --- a/Interfaces/English.lproj/ConnectionView.xib +++ b/Interfaces/English.lproj/ConnectionView.xib @@ -12,7 +12,7 @@ </object> <object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <bool key="EncodedWithXMLCoder">YES</bool> - <integer value="5739"/> + <integer value="5743"/> </object> <object class="NSArray" key="IBDocument.PluginDependencies"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -65,6 +65,7 @@ </object> <string key="NSFrame">{{0, 1}, {202, 22}}</string> <reference key="NSSuperview" ref="551584428"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSImageCell" key="NSCell" id="147458525"> <int key="NSCellFlags">130560</int> @@ -85,6 +86,7 @@ <int key="NSvFlags">292</int> <string key="NSFrame">{{61, -1}, {32, 25}}</string> <reference key="NSSuperview" ref="551584428"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="953363291"> <int key="NSCellFlags">67239424</int> @@ -113,6 +115,7 @@ <int key="NSvFlags">292</int> <string key="NSFrame">{{30, -1}, {32, 25}}</string> <reference key="NSSuperview" ref="551584428"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="340161940"> <int key="NSCellFlags">67239424</int> @@ -137,6 +140,7 @@ <int key="NSvFlags">292</int> <string key="NSFrame">{{-1, -1}, {36, 25}}</string> <reference key="NSSuperview" ref="551584428"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <string key="NSReuseIdentifierKey">_NS:791</string> <bool key="NSEnabled">YES</bool> <object class="NSPopUpButtonCell" key="NSCell" id="970960222"> @@ -311,6 +315,7 @@ </object> <string key="NSFrame">{{187, 1}, {15, 22}}</string> <reference key="NSSuperview" ref="551584428"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <string key="NSReuseIdentifierKey">_NS:2165</string> <bool key="NSEnabled">YES</bool> <object class="NSImageCell" key="NSCell" id="875077014"> @@ -341,8 +346,9 @@ <object class="NSTableView" id="1012579052"> <reference key="NSNextResponder" ref="233523429"/> <int key="NSvFlags">4352</int> - <string key="NSFrameSize">{202, 451}</string> + <string key="NSFrameSize">{202, 489}</string> <reference key="NSSuperview" ref="233523429"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="_NSCornerView" key="NSCornerView"> <nil key="NSNextResponder"/> @@ -438,9 +444,10 @@ <int key="NSTableViewGroupRowStyle">1</int> </object> </object> - <string key="NSFrameSize">{202, 451}</string> + <string key="NSFrameSize">{202, 489}</string> <reference key="NSSuperview" ref="524598165"/> <reference key="NSNextKeyView" ref="1012579052"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <reference key="NSDocView" ref="1012579052"/> <object class="NSColor" key="NSBGColor"> <int key="NSColorSpace">3</int> @@ -468,9 +475,10 @@ <double key="NSPercent">0.99507391452789307</double> </object> </object> - <string key="NSFrame">{{0, 23}, {202, 451}}</string> + <string key="NSFrame">{{0, 23}, {202, 489}}</string> <reference key="NSSuperview" ref="551584428"/> <reference key="NSNextKeyView" ref="233523429"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <int key="NSsFlags">133680</int> <reference key="NSVScroller" ref="533108700"/> <reference key="NSHScroller" ref="802793151"/> @@ -478,8 +486,9 @@ <bytes key="NSScrollAmts">QSAAAEEgAABBmAAAQZgAAA</bytes> </object> </object> - <string key="NSFrameSize">{202, 475}</string> + <string key="NSFrameSize">{202, 513}</string> <reference key="NSSuperview" ref="616749187"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> </object> <object class="NSView" id="563806501"> <reference key="NSNextResponder" ref="616749187"/> @@ -506,17 +515,99 @@ <int key="NSvFlags">301</int> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSCustomView" id="765073663"> + <reference key="NSNextResponder" ref="735564334"/> + <int key="NSvFlags">-2147483358</int> + <object class="NSMutableArray" key="NSSubviews"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSButton" id="148626614"> + <reference key="NSNextResponder" ref="765073663"/> + <int key="NSvFlags">268</int> + <string key="NSFrame">{{158.5, 3}, {125, 28}}</string> + <reference key="NSSuperview" ref="765073663"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> + <string key="NSReuseIdentifierKey">_NS:610</string> + <bool key="NSEnabled">YES</bool> + <object class="NSButtonCell" key="NSCell" id="1020482049"> + <int key="NSCellFlags">67239424</int> + <int key="NSCellFlags2">134348800</int> + <string key="NSContents">Save changes</string> + <reference key="NSSupport" ref="26"/> + <string key="NSCellIdentifier">_NS:610</string> + <reference key="NSControlView" ref="148626614"/> + <int key="NSButtonFlags">-2038284033</int> + <int key="NSButtonFlags2">268435585</int> + <string key="NSAlternateContents"/> + <string key="NSKeyEquivalent">s</string> + <int key="NSPeriodicDelay">200</int> + <int key="NSPeriodicInterval">25</int> + </object> + </object> + <object class="NSButton" id="463178943"> + <reference key="NSNextResponder" ref="765073663"/> + <int key="NSvFlags">268</int> + <string key="NSFrame">{{15, 3}, {145, 28}}</string> + <reference key="NSSuperview" ref="765073663"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> + <string key="NSReuseIdentifierKey">_NS:610</string> + <bool key="NSEnabled">YES</bool> + <object class="NSButtonCell" key="NSCell" id="1027746126"> + <int key="NSCellFlags">67239424</int> + <int key="NSCellFlags2">134348800</int> + <string key="NSContents">Add to Favorites</string> + <reference key="NSSupport" ref="26"/> + <string key="NSCellIdentifier">_NS:610</string> + <reference key="NSControlView" ref="463178943"/> + <int key="NSButtonFlags">-2038284033</int> + <int key="NSButtonFlags2">402653313</int> + <string key="NSAlternateContents"/> + <string key="NSKeyEquivalent">a</string> + <int key="NSPeriodicDelay">200</int> + <int key="NSPeriodicInterval">25</int> + </object> + </object> + <object class="NSButton" id="941479834"> + <reference key="NSNextResponder" ref="765073663"/> + <int key="NSvFlags">265</int> + <string key="NSFrame">{{286, 3}, {145, 28}}</string> + <reference key="NSSuperview" ref="765073663"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> + <string key="NSReuseIdentifierKey">_NS:610</string> + <bool key="NSEnabled">YES</bool> + <object class="NSButtonCell" key="NSCell" id="90221493"> + <int key="NSCellFlags">67239424</int> + <int key="NSCellFlags2">134348800</int> + <string key="NSContents">Test connection</string> + <reference key="NSSupport" ref="26"/> + <string key="NSCellIdentifier">_NS:610</string> + <reference key="NSControlView" ref="941479834"/> + <int key="NSButtonFlags">-2038284033</int> + <int key="NSButtonFlags2">129</int> + <string key="NSAlternateContents"/> + <string key="NSKeyEquivalent"/> + <int key="NSPeriodicDelay">200</int> + <int key="NSPeriodicInterval">25</int> + </object> + </object> + </object> + <string key="NSFrameSize">{446, 37}</string> + <reference key="NSSuperview" ref="735564334"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> + <string key="NSReuseIdentifierKey">_NS:1109</string> + <string key="NSClassName">NSView</string> + </object> <object class="NSTabView" id="134031646"> <reference key="NSNextResponder" ref="735564334"/> <int key="NSvFlags">274</int> - <string key="NSFrame">{{13, 25}, {420, 417}}</string> + <string key="NSFrame">{{13, 61}, {420, 417}}</string> <reference key="NSSuperview" ref="735564334"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <object class="NSMutableArray" key="NSTabViewItems"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSTabViewItem" id="253369045"> <string key="NSIdentifier">1</string> <object class="NSView" key="NSView" id="32802602"> - <reference key="NSNextResponder" ref="134031646"/> + <nil key="NSNextResponder"/> <int key="NSvFlags">256</int> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -836,9 +927,13 @@ <reference key="NSControlView" ref="839299955"/> <int key="NSButtonFlags">-922992385</int> <int key="NSButtonFlags2">34</int> - <object class="NSCustomResource" key="NSNormalImage" id="22774617"> + <object class="NSCustomResource" key="NSNormalImage" id="792471251"> <string key="NSClassName">NSImage</string> - <string key="NSResourceName">KeyTemplate</string> + <string key="NSResourceName">key-icon</string> + </object> + <object class="NSCustomResource" key="NSAlternateImage" id="503225194"> + <string key="NSClassName">NSImage</string> + <string key="NSResourceName">key-icon-alternate</string> </object> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> @@ -898,7 +993,8 @@ <reference key="NSControlView" ref="945929172"/> <int key="NSButtonFlags">-922992385</int> <int key="NSButtonFlags2">34</int> - <reference key="NSNormalImage" ref="22774617"/> + <reference key="NSNormalImage" ref="792471251"/> + <reference key="NSAlternateImage" ref="503225194"/> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> <int key="NSPeriodicDelay">400</int> @@ -956,7 +1052,8 @@ <reference key="NSControlView" ref="586011242"/> <int key="NSButtonFlags">-922992385</int> <int key="NSButtonFlags2">34</int> - <reference key="NSNormalImage" ref="22774617"/> + <reference key="NSNormalImage" ref="792471251"/> + <reference key="NSAlternateImage" ref="503225194"/> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> <int key="NSPeriodicDelay">400</int> @@ -970,7 +1067,6 @@ </object> </object> <string key="NSFrame">{{10, 33}, {400, 371}}</string> - <reference key="NSSuperview" ref="134031646"/> </object> <string key="NSLabel">Standard</string> <reference key="NSColor" ref="644242225"/> @@ -993,6 +1089,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{108, 11}, {251, 18}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="972347112"> <int key="NSCellFlags">67239424</int> @@ -1015,6 +1112,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{110, 141}, {247, 22}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="279442436"> <int key="NSCellFlags">-1804468671</int> @@ -1032,6 +1130,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{7, 143}, {98, 17}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="798396187"> <int key="NSCellFlags">68288064</int> @@ -1048,6 +1147,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{110, 107}, {247, 22}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="251554254"> <int key="NSCellFlags">-1804468671</int> @@ -1065,6 +1165,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{7, 109}, {98, 17}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="797213431"> <int key="NSCellFlags">68288064</int> @@ -1081,6 +1182,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{110, 73}, {247, 22}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="185246669"> <int key="NSCellFlags">-1804468671</int> @@ -1099,6 +1201,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{7, 75}, {98, 17}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="728524936"> <int key="NSCellFlags">68288064</int> @@ -1115,6 +1218,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{110, 39}, {247, 22}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="299457759"> <int key="NSCellFlags">-1804468671</int> @@ -1133,6 +1237,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{7, 41}, {98, 17}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="317867885"> <int key="NSCellFlags">68288064</int> @@ -1149,6 +1254,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{110, 175}, {247, 22}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="684614893"> <int key="NSCellFlags">-1804468671</int> @@ -1167,6 +1273,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{7, 177}, {98, 17}}</string> <reference key="NSSuperview" ref="394302879"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="352725628"> <int key="NSCellFlags">68288064</int> @@ -1181,6 +1288,7 @@ </object> <string key="NSFrame">{{6, 164}, {377, 224}}</string> <reference key="NSSuperview" ref="962671066"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <string key="NSClassName">NSView</string> </object> <object class="NSCustomView" id="866600720"> @@ -1193,6 +1301,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{-3, 15}, {107, 17}}</string> <reference key="NSSuperview" ref="866600720"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="730590428"> <int key="NSCellFlags">68288064</int> @@ -1209,6 +1318,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{109, 13}, {220, 22}}</string> <reference key="NSSuperview" ref="866600720"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="169093017"> <int key="NSCellFlags">-2073952703</int> @@ -1231,6 +1341,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{328, 12}, {29, 24}}</string> <reference key="NSSuperview" ref="866600720"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="308087760"> <int key="NSCellFlags">67239424</int> @@ -1240,7 +1351,8 @@ <reference key="NSControlView" ref="680939705"/> <int key="NSButtonFlags">-922992385</int> <int key="NSButtonFlags2">34</int> - <reference key="NSNormalImage" ref="22774617"/> + <reference key="NSNormalImage" ref="792471251"/> + <reference key="NSAlternateImage" ref="503225194"/> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> <int key="NSPeriodicDelay">400</int> @@ -1252,6 +1364,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{-3, 49}, {107, 17}}</string> <reference key="NSSuperview" ref="866600720"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="1025158673"> <int key="NSCellFlags">68288064</int> @@ -1268,6 +1381,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{109, 47}, {220, 22}}</string> <reference key="NSSuperview" ref="866600720"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="276609847"> <int key="NSCellFlags">-2073952703</int> @@ -1290,6 +1404,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{328, 46}, {29, 24}}</string> <reference key="NSSuperview" ref="866600720"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="559442548"> <int key="NSCellFlags">67239424</int> @@ -1299,7 +1414,8 @@ <reference key="NSControlView" ref="988136593"/> <int key="NSButtonFlags">-922992385</int> <int key="NSButtonFlags2">34</int> - <reference key="NSNormalImage" ref="22774617"/> + <reference key="NSNormalImage" ref="792471251"/> + <reference key="NSAlternateImage" ref="503225194"/> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> <int key="NSPeriodicDelay">400</int> @@ -1311,6 +1427,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{-3, 83}, {107, 17}}</string> <reference key="NSSuperview" ref="866600720"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="357553281"> <int key="NSCellFlags">68288064</int> @@ -1327,6 +1444,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{109, 81}, {220, 22}}</string> <reference key="NSSuperview" ref="866600720"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="575863174"> <int key="NSCellFlags">-2073952703</int> @@ -1348,6 +1466,7 @@ <int key="NSvFlags">268</int> <string key="NSFrame">{{328, 80}, {29, 24}}</string> <reference key="NSSuperview" ref="866600720"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="167444868"> <int key="NSCellFlags">67239424</int> @@ -1357,7 +1476,8 @@ <reference key="NSControlView" ref="237955939"/> <int key="NSButtonFlags">-922992385</int> <int key="NSButtonFlags2">34</int> - <reference key="NSNormalImage" ref="22774617"/> + <reference key="NSNormalImage" ref="792471251"/> + <reference key="NSAlternateImage" ref="503225194"/> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> <int key="NSPeriodicDelay">400</int> @@ -1367,10 +1487,12 @@ </object> <string key="NSFrame">{{6, 61}, {377, 104}}</string> <reference key="NSSuperview" ref="962671066"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <string key="NSClassName">NSView</string> </object> </object> <string key="NSFrame">{{10, 33}, {400, 371}}</string> + <int key="NSViewLayerContentsRedrawPolicy">2</int> </object> <string key="NSLabel">Socket</string> <reference key="NSColor" ref="644242225"/> @@ -1379,7 +1501,7 @@ <object class="NSTabViewItem" id="591192172"> <string key="NSIdentifier">Item 2</string> <object class="NSView" key="NSView" id="159800861"> - <nil key="NSNextResponder"/> + <reference key="NSNextResponder" ref="134031646"/> <int key="NSvFlags">256</int> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -1736,7 +1858,8 @@ <reference key="NSControlView" ref="183948302"/> <int key="NSButtonFlags">-922992385</int> <int key="NSButtonFlags2">34</int> - <reference key="NSNormalImage" ref="22774617"/> + <reference key="NSNormalImage" ref="792471251"/> + <reference key="NSAlternateImage" ref="503225194"/> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> <int key="NSPeriodicDelay">400</int> @@ -1786,27 +1909,29 @@ </object> </object> <string key="NSFrame">{{10, 33}, {400, 371}}</string> + <reference key="NSSuperview" ref="134031646"/> </object> <string key="NSLabel">SSH</string> <reference key="NSColor" ref="644242225"/> <reference key="NSTabView" ref="134031646"/> </object> </object> - <reference key="NSSelectedTabViewItem" ref="253369045"/> + <reference key="NSSelectedTabViewItem" ref="591192172"/> <reference key="NSFont" ref="807120225"/> <int key="NSTvFlags">0</int> <bool key="NSAllowTruncatedLabels">YES</bool> <bool key="NSDrawsBackground">YES</bool> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> - <reference ref="32802602"/> + <reference ref="159800861"/> </object> </object> <object class="NSButton" id="460592307"> <reference key="NSNextResponder" ref="735564334"/> <int key="NSvFlags">289</int> - <string key="NSFrame">{{285, -3}, {147, 32}}</string> + <string key="NSFrame">{{285, 33}, {147, 32}}</string> <reference key="NSSuperview" ref="735564334"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="424756162"> <int key="NSCellFlags">67239424</int> @@ -1822,32 +1947,13 @@ <int key="NSPeriodicInterval">25</int> </object> </object> - <object class="NSButton" id="476973680"> - <reference key="NSNextResponder" ref="735564334"/> - <int key="NSvFlags">289</int> - <string key="NSFrame">{{93, -3}, {192, 32}}</string> - <reference key="NSSuperview" ref="735564334"/> - <bool key="NSEnabled">YES</bool> - <object class="NSButtonCell" key="NSCell" id="220047154"> - <int key="NSCellFlags">67239424</int> - <int key="NSCellFlags2">134217728</int> - <string key="NSContents">Add to Favorites</string> - <reference key="NSSupport" ref="807120225"/> - <reference key="NSControlView" ref="476973680"/> - <int key="NSButtonFlags">-2038284033</int> - <int key="NSButtonFlags2">402653313</int> - <string key="NSAlternateContents"/> - <string key="NSKeyEquivalent">a</string> - <int key="NSPeriodicDelay">200</int> - <int key="NSPeriodicInterval">25</int> - </object> - </object> <object class="NSProgressIndicator" id="575228526"> <reference key="NSNextResponder" ref="735564334"/> <int key="NSvFlags">1316</int> <object class="NSPSMatrix" key="NSDrawMatrix"/> - <string key="NSFrame">{{20, 7}, {16, 16}}</string> + <string key="NSFrame">{{20, 43}, {16, 16}}</string> <reference key="NSSuperview" ref="735564334"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <int key="NSpiFlags">28938</int> <double key="NSMinValue">16</double> <double key="NSMaxValue">100</double> @@ -1855,8 +1961,9 @@ <object class="NSTextField" id="549907703"> <reference key="NSNextResponder" ref="735564334"/> <int key="NSvFlags">-2147483356</int> - <string key="NSFrame">{{41, 7}, {252, 17}}</string> + <string key="NSFrame">{{41, 43}, {252, 17}}</string> <reference key="NSSuperview" ref="735564334"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="321846713"> <int key="NSCellFlags">68288064</int> @@ -1871,11 +1978,12 @@ <object class="NSButton" id="990947983"> <reference key="NSNextResponder" ref="735564334"/> <int key="NSvFlags">292</int> - <string key="NSFrame">{{17, 1}, {25, 25}}</string> + <string key="NSFrame">{{15, 37}, {25, 25}}</string> <reference key="NSSuperview" ref="735564334"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="757039715"> - <int key="NSCellFlags">67239424</int> + <int key="NSCellFlags">-2080244224</int> <int key="NSCellFlags2">134217728</int> <string key="NSContents"/> <reference key="NSSupport" ref="807120225"/> @@ -1889,19 +1997,22 @@ </object> </object> </object> - <string key="NSFrame">{{116, 2}, {446, 436}}</string> + <string key="NSFrame">{{116, 5}, {446, 472}}</string> <reference key="NSSuperview" ref="315390047"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <string key="NSClassName">NSCustomView</string> </object> </object> - <string key="NSFrameSize">{679, 442}</string> + <string key="NSFrameSize">{679, 480}</string> <reference key="NSSuperview" ref="954788335"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <string key="NSClassName">SPFlippedView</string> </object> </object> - <string key="NSFrameSize">{679, 443}</string> + <string key="NSFrameSize">{679, 481}</string> <reference key="NSSuperview" ref="1058735001"/> <reference key="NSNextKeyView" ref="315390047"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <reference key="NSDocView" ref="315390047"/> <reference key="NSBGColor" ref="644242225"/> <int key="NScvFlags">4</int> @@ -1909,12 +2020,12 @@ <object class="NSScroller" id="508899576"> <reference key="NSNextResponder" ref="1058735001"/> <int key="NSvFlags">-2147483392</int> - <string key="NSFrame">{{664, 0}, {15, 453}}</string> + <string key="NSFrame">{{664, 0}, {15, 477}}</string> <reference key="NSSuperview" ref="1058735001"/> <reference key="NSTarget" ref="1058735001"/> <string key="NSAction">_doScroller:</string> <double key="NSCurValue">1</double> - <double key="NSPercent">0.99775278568267822</double> + <double key="NSPercent">0.99582463465553239</double> </object> <object class="NSScroller" id="108277656"> <reference key="NSNextResponder" ref="1058735001"/> @@ -1927,9 +2038,10 @@ <double key="NSPercent">0.97838616371154785</double> </object> </object> - <string key="NSFrameSize">{679, 443}</string> + <string key="NSFrameSize">{679, 481}</string> <reference key="NSSuperview" ref="563806501"/> <reference key="NSNextKeyView" ref="954788335"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <int key="NSsFlags">133680</int> <reference key="NSVScroller" ref="508899576"/> <reference key="NSHScroller" ref="108277656"/> @@ -1938,8 +2050,9 @@ <object class="NSTextField" id="570492783"> <reference key="NSNextResponder" ref="563806501"/> <int key="NSvFlags">266</int> - <string key="NSFrame">{{17, 451}, {645, 17}}</string> + <string key="NSFrame">{{17, 489}, {645, 17}}</string> <reference key="NSSuperview" ref="563806501"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="607446274"> <int key="NSCellFlags">68288064</int> @@ -1956,19 +2069,22 @@ </object> </object> </object> - <string key="NSFrame">{{203, 0}, {679, 475}}</string> + <string key="NSFrame">{{203, 0}, {679, 513}}</string> <reference key="NSSuperview" ref="616749187"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> </object> </object> - <string key="NSFrameSize">{882, 475}</string> + <string key="NSFrame">{{0, -2}, {882, 513}}</string> <reference key="NSSuperview" ref="733821228"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <bool key="NSIsVertical">YES</bool> <int key="NSDividerStyle">2</int> <string key="NSAutosaveName">DBViewSplitter</string> </object> </object> - <string key="NSFrameSize">{882, 475}</string> + <string key="NSFrameSize">{882, 511}</string> <reference key="NSSuperview"/> + <int key="NSViewLayerContentsRedrawPolicy">2</int> <string key="NSClassName">SPFlippedView</string> </object> <object class="NSWindowTemplate" id="958272936"> @@ -2893,14 +3009,6 @@ </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> - <string key="label">addToFavoritesButton</string> - <reference key="source" ref="545410097"/> - <reference key="destination" ref="476973680"/> - </object> - <int key="connectionID">5357</int> - </object> - <object class="IBConnectionRecord"> - <object class="IBOutletConnection" key="connection"> <string key="label">delegate</string> <reference key="source" ref="644973446"/> <reference key="destination" ref="545410097"/> @@ -4753,14 +4861,6 @@ </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> - <string key="label">addFavoriteUsingCurrentDetails:</string> - <reference key="source" ref="545410097"/> - <reference key="destination" ref="476973680"/> - </object> - <int key="connectionID">5821</int> - </object> - <object class="IBConnectionRecord"> - <object class="IBActionConnection" key="connection"> <string key="label">exportFavorites:</string> <reference key="source" ref="545410097"/> <reference key="destination" ref="567127977"/> @@ -4847,6 +4947,54 @@ </object> <int key="connectionID">5861</int> </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">addFavoriteUsingCurrentDetails:</string> + <reference key="source" ref="545410097"/> + <reference key="destination" ref="463178943"/> + </object> + <int key="connectionID">5871</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBOutletConnection" key="connection"> + <string key="label">saveFavoriteButton</string> + <reference key="source" ref="545410097"/> + <reference key="destination" ref="148626614"/> + </object> + <int key="connectionID">5874</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBOutletConnection" key="connection"> + <string key="label">testConnectButton</string> + <reference key="source" ref="545410097"/> + <reference key="destination" ref="941479834"/> + </object> + <int key="connectionID">5875</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">saveFavorite:</string> + <reference key="source" ref="545410097"/> + <reference key="destination" ref="148626614"/> + </object> + <int key="connectionID">5880</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">initiateConnection:</string> + <reference key="source" ref="545410097"/> + <reference key="destination" ref="941479834"/> + </object> + <int key="connectionID">5881</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBOutletConnection" key="connection"> + <string key="label">editButtonsView</string> + <reference key="source" ref="545410097"/> + <reference key="destination" ref="765073663"/> + </object> + <int key="connectionID">5882</int> + </object> </object> <object class="IBMutableOrderedSet" key="objectRecords"> <object class="NSArray" key="orderedObjects"> @@ -5169,7 +5317,7 @@ <reference ref="549907703"/> <reference ref="990947983"/> <reference ref="460592307"/> - <reference ref="476973680"/> + <reference ref="765073663"/> </object> <reference key="parent" ref="315390047"/> </object> @@ -5194,15 +5342,6 @@ <reference key="parent" ref="735564334"/> </object> <object class="IBObjectRecord"> - <int key="objectID">5159</int> - <reference key="object" ref="476973680"/> - <object class="NSMutableArray" key="children"> - <bool key="EncodedWithXMLCoder">YES</bool> - <reference ref="220047154"/> - </object> - <reference key="parent" ref="735564334"/> - </object> - <object class="IBObjectRecord"> <int key="objectID">5422</int> <reference key="object" ref="575228526"/> <reference key="parent" ref="735564334"/> @@ -5236,11 +5375,6 @@ <reference key="parent" ref="549907703"/> </object> <object class="IBObjectRecord"> - <int key="objectID">5160</int> - <reference key="object" ref="220047154"/> - <reference key="parent" ref="476973680"/> - </object> - <object class="IBObjectRecord"> <int key="objectID">5158</int> <reference key="object" ref="424756162"/> <reference key="parent" ref="460592307"/> @@ -6601,6 +6735,59 @@ <reference key="object" ref="875077014"/> <reference key="parent" ref="250765522"/> </object> + <object class="IBObjectRecord"> + <int key="objectID">5862</int> + <reference key="object" ref="765073663"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="148626614"/> + <reference ref="463178943"/> + <reference ref="941479834"/> + </object> + <reference key="parent" ref="735564334"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">5869</int> + <reference key="object" ref="148626614"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="1020482049"/> + </object> + <reference key="parent" ref="765073663"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">5865</int> + <reference key="object" ref="463178943"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="1027746126"/> + </object> + <reference key="parent" ref="765073663"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">5863</int> + <reference key="object" ref="941479834"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="90221493"/> + </object> + <reference key="parent" ref="765073663"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">5864</int> + <reference key="object" ref="90221493"/> + <reference key="parent" ref="941479834"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">5866</int> + <reference key="object" ref="1027746126"/> + <reference key="parent" ref="463178943"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">5870</int> + <reference key="object" ref="1020482049"/> + <reference key="parent" ref="148626614"/> + </object> </object> </object> <object class="NSMutableDictionary" key="flattenedProperties"> @@ -6636,10 +6823,6 @@ <string>5157.IBPluginDependency</string> <string>5157.IBViewBoundsToFrameTransform</string> <string>5158.IBPluginDependency</string> - <string>5159.IBAttributePlaceholdersKey</string> - <string>5159.IBPluginDependency</string> - <string>5159.IBViewBoundsToFrameTransform</string> - <string>5160.IBPluginDependency</string> <string>5162.IBPluginDependency</string> <string>5166.IBPluginDependency</string> <string>5171.IBAttributePlaceholdersKey</string> @@ -6915,6 +7098,16 @@ <string>5858.IBPluginDependency</string> <string>5858.IBViewBoundsToFrameTransform</string> <string>5859.IBPluginDependency</string> + <string>5862.IBPluginDependency</string> + <string>5862.IBViewBoundsToFrameTransform</string> + <string>5863.IBPluginDependency</string> + <string>5863.IBViewBoundsToFrameTransform</string> + <string>5864.IBPluginDependency</string> + <string>5865.IBPluginDependency</string> + <string>5865.IBViewBoundsToFrameTransform</string> + <string>5866.IBPluginDependency</string> + <string>5869.IBPluginDependency</string> + <string>5870.IBPluginDependency</string> </object> <object class="NSMutableArray" key="dict.values"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -6933,7 +7126,7 @@ <string>SPFavoritesOutlineView</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> - <string>SPTableTextFieldCell</string> + <string>SPFavoriteTextFieldCell</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <object class="NSMutableDictionary"> <string key="NS.key.0">InitialTabViewItem</string> @@ -6958,19 +7151,6 @@ <bytes key="NSTransformStruct">P4AAAL+AAABDjIAAwdgAAA</bytes> </object> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> - <object class="NSMutableDictionary"> - <string key="NS.key.0">ToolTip</string> - <object class="IBToolTipAttribute" key="NS.object.0"> - <string key="name">ToolTip</string> - <reference key="object" ref="476973680"/> - <string key="toolTip">Add to Favorites (⌥⌘A)</string> - </object> - </object> - <string>com.apple.InterfaceBuilder.CocoaPlugin</string> - <object class="NSAffineTransform"> - <bytes key="NSTransformStruct">P4AAAL+AAABCsgAAwdgAAA</bytes> - </object> - <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <object class="NSMutableDictionary"> @@ -7399,7 +7579,7 @@ <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> - <string>{{158, 366}, {882, 475}}</string> + <string>{{158, 330}, {882, 511}}</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>SPSplitView</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> @@ -7494,6 +7674,22 @@ <bytes key="NSTransformStruct">AUMRgABCkgAAA</bytes> </object> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <object class="NSAffineTransform"> + <bytes key="NSTransformStruct">AUNNAABC+gAAA</bytes> + </object> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <object class="NSAffineTransform"> + <bytes key="NSTransformStruct">P4AAAL+AAABDpIAAwmQAAA</bytes> + </object> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <object class="NSAffineTransform"> + <bytes key="NSTransformStruct">P4AAAL+AAABB8AAAwkgAAA</bytes> + </object> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> </object> </object> <object class="NSMutableDictionary" key="unlocalizedProperties"> @@ -7512,7 +7708,7 @@ </object> </object> <nil key="sourceID"/> - <int key="maxID">5861</int> + <int key="maxID">5882</int> </object> <object class="IBClassDescriber" key="IBDocument.Classes"> <object class="NSMutableArray" key="referencedPartialClassDescriptions"> @@ -7767,6 +7963,7 @@ <string>removeNode:</string> <string>renameNode:</string> <string>reverseSortFavorites:</string> + <string>saveFavorite:</string> <string>showHelp:</string> <string>sortFavorites:</string> <string>updateFavoriteSelection:</string> @@ -7794,6 +7991,7 @@ <string>id</string> <string>id</string> <string>id</string> + <string>id</string> </object> </object> <object class="NSMutableDictionary" key="actionInfosByName"> @@ -7814,6 +8012,7 @@ <string>removeNode:</string> <string>renameNode:</string> <string>reverseSortFavorites:</string> + <string>saveFavorite:</string> <string>showHelp:</string> <string>sortFavorites:</string> <string>updateFavoriteSelection:</string> @@ -7879,6 +8078,10 @@ <string key="candidateClassName">NSMenuItem</string> </object> <object class="IBActionInfo"> + <string key="name">saveFavorite:</string> + <string key="candidateClassName">id</string> + </object> + <object class="IBActionInfo"> <string key="name">showHelp:</string> <string key="candidateClassName">id</string> </object> @@ -7904,7 +8107,6 @@ <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSArray" key="dict.sortedKeys"> <bool key="EncodedWithXMLCoder">YES</bool> - <string>addToFavoritesButton</string> <string>connectButton</string> <string>connectionDetailsScrollView</string> <string>connectionInstructionsTextField</string> @@ -7912,6 +8114,7 @@ <string>connectionSplitView</string> <string>connectionView</string> <string>delegate</string> + <string>editButtonsView</string> <string>errorDetailText</string> <string>errorDetailWindow</string> <string>exportPanelAccessoryView</string> @@ -7920,6 +8123,7 @@ <string>helpButton</string> <string>progressIndicator</string> <string>progressIndicatorText</string> + <string>saveFavoriteButton</string> <string>socketConnectionFormContainer</string> <string>socketConnectionSSLDetailsContainer</string> <string>socketNameField</string> @@ -7948,17 +8152,18 @@ <string>standardSSLCertificateButton</string> <string>standardSSLKeyFileButton</string> <string>standardUserField</string> + <string>testConnectButton</string> </object> <object class="NSMutableArray" key="dict.values"> <bool key="EncodedWithXMLCoder">YES</bool> <string>NSButton</string> - <string>NSButton</string> <string>NSScrollView</string> <string>NSTextField</string> <string>NSView</string> <string>SPSplitView</string> <string>NSView</string> <string>id</string> + <string>NSView</string> <string>NSTextView</string> <string>NSWindow</string> <string>NSView</string> @@ -7967,6 +8172,7 @@ <string>NSButton</string> <string>NSProgressIndicator</string> <string>NSTextField</string> + <string>NSButton</string> <string>NSView</string> <string>NSView</string> <string>NSTextField</string> @@ -7995,13 +8201,13 @@ <string>NSButton</string> <string>NSButton</string> <string>NSTextField</string> + <string>NSButton</string> </object> </object> <object class="NSMutableDictionary" key="toOneOutletInfosByName"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSArray" key="dict.sortedKeys"> <bool key="EncodedWithXMLCoder">YES</bool> - <string>addToFavoritesButton</string> <string>connectButton</string> <string>connectionDetailsScrollView</string> <string>connectionInstructionsTextField</string> @@ -8009,6 +8215,7 @@ <string>connectionSplitView</string> <string>connectionView</string> <string>delegate</string> + <string>editButtonsView</string> <string>errorDetailText</string> <string>errorDetailWindow</string> <string>exportPanelAccessoryView</string> @@ -8017,6 +8224,7 @@ <string>helpButton</string> <string>progressIndicator</string> <string>progressIndicatorText</string> + <string>saveFavoriteButton</string> <string>socketConnectionFormContainer</string> <string>socketConnectionSSLDetailsContainer</string> <string>socketNameField</string> @@ -8045,14 +8253,11 @@ <string>standardSSLCertificateButton</string> <string>standardSSLKeyFileButton</string> <string>standardUserField</string> + <string>testConnectButton</string> </object> <object class="NSMutableArray" key="dict.values"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="IBToOneOutletInfo"> - <string key="name">addToFavoritesButton</string> - <string key="candidateClassName">NSButton</string> - </object> - <object class="IBToOneOutletInfo"> <string key="name">connectButton</string> <string key="candidateClassName">NSButton</string> </object> @@ -8081,6 +8286,10 @@ <string key="candidateClassName">id</string> </object> <object class="IBToOneOutletInfo"> + <string key="name">editButtonsView</string> + <string key="candidateClassName">NSView</string> + </object> + <object class="IBToOneOutletInfo"> <string key="name">errorDetailText</string> <string key="candidateClassName">NSTextView</string> </object> @@ -8113,6 +8322,10 @@ <string key="candidateClassName">NSTextField</string> </object> <object class="IBToOneOutletInfo"> + <string key="name">saveFavoriteButton</string> + <string key="candidateClassName">NSButton</string> + </object> + <object class="IBToOneOutletInfo"> <string key="name">socketConnectionFormContainer</string> <string key="candidateClassName">NSView</string> </object> @@ -8224,6 +8437,10 @@ <string key="name">standardUserField</string> <string key="candidateClassName">NSTextField</string> </object> + <object class="IBToOneOutletInfo"> + <string key="name">testConnectButton</string> + <string key="candidateClassName">NSButton</string> + </object> </object> </object> <object class="IBClassDescriptionSource" key="sourceIdentifier"> @@ -8260,6 +8477,14 @@ </object> </object> <object class="IBPartialClassDescription"> + <string key="className">SPFavoriteTextFieldCell</string> + <string key="superclassName">ImageAndTextCell</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">Source/SPFavoriteTextFieldCell.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> <string key="className">SPFavoritesOutlineView</string> <string key="superclassName">NSOutlineView</string> <object class="IBClassDescriptionSource" key="sourceIdentifier"> @@ -8326,14 +8551,6 @@ <string key="minorKey">Source/SPSplitView.h</string> </object> </object> - <object class="IBPartialClassDescription"> - <string key="className">SPTableTextFieldCell</string> - <string key="superclassName">ImageAndTextCell</string> - <object class="IBClassDescriptionSource" key="sourceIdentifier"> - <string key="majorKey">IBProjectSource</string> - <string key="minorKey">Source/SPTableTextFieldCell.h</string> - </object> - </object> </object> <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -9100,7 +9317,6 @@ <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSArray" key="dict.sortedKeys"> <bool key="EncodedWithXMLCoder">YES</bool> - <string>KeyTemplate</string> <string>NSMenuCheckmark</string> <string>NSMenuMixedState</string> <string>NSSwitch</string> @@ -9109,10 +9325,11 @@ <string>button_add_folder</string> <string>button_bar_handle</string> <string>button_bar_spacer</string> + <string>key-icon</string> + <string>key-icon-alternate</string> </object> <object class="NSMutableArray" key="dict.values"> <bool key="EncodedWithXMLCoder">YES</bool> - <string>{16, 8}</string> <string>{11, 11}</string> <string>{10, 3}</string> <string>{15, 15}</string> @@ -9121,6 +9338,8 @@ <string>{32, 23}</string> <string>{15, 23}</string> <string>{10, 23}</string> + <string>{16, 8}</string> + <string>{16, 8}</string> </object> </object> </data> diff --git a/Resources/English.lproj/ConnectionView.strings b/Resources/English.lproj/ConnectionView.strings Binary files differindex b6ae4e8c..b1fb2824 100644 --- a/Resources/English.lproj/ConnectionView.strings +++ b/Resources/English.lproj/ConnectionView.strings diff --git a/Resources/English.lproj/Localizable.strings b/Resources/English.lproj/Localizable.strings Binary files differindex 8fd66cfd..42c07f9d 100644 --- a/Resources/English.lproj/Localizable.strings +++ b/Resources/English.lproj/Localizable.strings diff --git a/Resources/Images/key-icon-alternate.png b/Resources/Images/key-icon-alternate.png Binary files differnew file mode 100644 index 00000000..4176f58d --- /dev/null +++ b/Resources/Images/key-icon-alternate.png diff --git a/Resources/Images/key-icon-alternate@2x.png b/Resources/Images/key-icon-alternate@2x.png Binary files differnew file mode 100644 index 00000000..1a0f68b8 --- /dev/null +++ b/Resources/Images/key-icon-alternate@2x.png diff --git a/Resources/Images/key-icon.png b/Resources/Images/key-icon.png Binary files differnew file mode 100644 index 00000000..9bd894a4 --- /dev/null +++ b/Resources/Images/key-icon.png diff --git a/Resources/Images/key-icon@2x.png b/Resources/Images/key-icon@2x.png Binary files differnew file mode 100644 index 00000000..e7a82660 --- /dev/null +++ b/Resources/Images/key-icon@2x.png diff --git a/Resources/Images/quick-connect-icon-white.pdf b/Resources/Images/quick-connect-icon-white.pdf Binary files differnew file mode 100644 index 00000000..090f4f08 --- /dev/null +++ b/Resources/Images/quick-connect-icon-white.pdf diff --git a/Resources/Images/quick-connect-icon.pdf b/Resources/Images/quick-connect-icon.pdf Binary files differnew file mode 100644 index 00000000..e9037f51 --- /dev/null +++ b/Resources/Images/quick-connect-icon.pdf diff --git a/Source/SPConnectionController.h b/Source/SPConnectionController.h index de517fdf..dda04343 100644 --- a/Source/SPConnectionController.h +++ b/Source/SPConnectionController.h @@ -42,7 +42,8 @@ SPSplitView #ifndef SP_REFACTOR /* class decl */ ,SPKeychain, - SPFavoriteNode + SPFavoriteNode, + SPFavoriteTextFieldCell #endif ; @@ -64,6 +65,8 @@ BOOL cancellingConnection; BOOL isConnecting; + BOOL isEditingConnection; + BOOL isTestingConnection; // Standard details NSInteger previousType; @@ -141,25 +144,31 @@ IBOutlet NSButton *socketSSLCertificateButton; IBOutlet NSButton *socketSSLCACertButton; - IBOutlet NSButton *addToFavoritesButton; IBOutlet NSButton *connectButton; + IBOutlet NSButton *testConnectButton; IBOutlet NSButton *helpButton; + IBOutlet NSButton *saveFavoriteButton; IBOutlet NSProgressIndicator *progressIndicator; IBOutlet NSTextField *progressIndicatorText; IBOutlet NSMenuItem *favoritesSortByMenuItem; IBOutlet NSView *exportPanelAccessoryView; - - BOOL isEditing; + IBOutlet NSView *editButtonsView; + + BOOL isEditingItemName; BOOL reverseFavoritesSort; BOOL initComplete; BOOL mySQLConnectionCancelled; - BOOL favoriteNameFieldWasTouched; + BOOL favoriteNameFieldWasAutogenerated; #ifndef SP_REFACTOR /* ivars */ NSArray *draggedNodes; NSImage *folderImage; SPTreeNode *favoritesRoot; + SPTreeNode *quickConnectItem; + + SPFavoriteTextFieldCell *quickConnectCell; + NSDictionary *currentFavorite; SPFavoritesController *favoritesController; SPFavoritesSortItem currentSortItem; @@ -198,6 +207,7 @@ #endif @property (readonly, assign) BOOL isConnecting; +@property (readonly, assign) BOOL isEditingConnection; // Connection processes - (IBAction)initiateConnection:(id)sender; @@ -211,18 +221,19 @@ - (IBAction)updateSSLInterface:(id)sender; - (IBAction)updateKeyLocationFileVisibility:(id)sender; - (void)updateSplitViewSize; + - (void)resizeTabViewToConnectionType:(NSUInteger)theType animating:(BOOL)animate; + - (IBAction)sortFavorites:(id)sender; - (IBAction)reverseSortFavorites:(NSMenuItem *)sender; -- (void)resizeTabViewToConnectionType:(NSUInteger)theType animating:(BOOL)animate; - // Favorites interaction - (void)updateFavoriteSelection:(id)sender; - (NSMutableDictionary *)selectedFavorite; - (SPTreeNode *)selectedFavoriteNode; - (NSArray *)selectedFavoriteNodes; +- (IBAction)saveFavorite:(id)sender; - (IBAction)addFavorite:(id)sender; - (IBAction)addFavoriteUsingCurrentDetails:(id)sender; - (IBAction)addGroup:(id)sender; diff --git a/Source/SPConnectionController.m b/Source/SPConnectionController.m index d6078f1d..a6ca5f75 100644 --- a/Source/SPConnectionController.m +++ b/Source/SPConnectionController.m @@ -31,6 +31,7 @@ // More info at <http://code.google.com/p/sequel-pro/> #import "SPConnectionController.h" +#import "SPConnectionHandler.h" #import "SPDatabaseDocument.h" #ifndef SP_REFACTOR /* headers */ @@ -67,6 +68,10 @@ static NSString *SPExportFavoritesFilename = @"SequelProFavorites.plist"; @interface SPConnectionController () +// Privately redeclare as read/write to get the synthesized setter +@property (readwrite, assign) BOOL isEditingConnection; + +- (void)_saveCurrentDetailsCreatingNewFavorite:(BOOL)createNewFavorite; - (BOOL)_checkHost; #ifndef SP_REFACTOR - (void)_sortFavorites; @@ -83,13 +88,22 @@ static NSString *SPExportFavoritesFilename = @"SequelProFavorites.plist"; - (SPTreeNode *)_favoriteNodeForFavoriteID:(NSInteger)favoriteID; - (NSString *)_stripInvalidCharactersFromString:(NSString *)subject; -- (void)_updateFavoritePasswordsFromField:(NSControl *)control; +- (NSString *)_generateNameForConnection; + +- (void)_startEditingConnection; static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, void *key); #endif @end +@interface SPConnectionController (SPConnectionControllerDelegate) + +- (void)_stopEditingConnection; + +@end + + @implementation SPConnectionController @synthesize delegate; @@ -125,6 +139,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, @synthesize connectionSSHKeychainItemAccount; @synthesize isConnecting; +@synthesize isEditingConnection; #pragma mark - #pragma mark Connection processes @@ -143,6 +158,9 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, if (sender == favoritesOutlineView && [favoritesOutlineView clickedRow] <= 0) return; #endif + // If triggered via the "Test Connection" button, set the state - otherwise clear it + isTestingConnection = (sender == testConnectButton); + // Ensure that host is not empty if this is a TCP/IP or SSH connection if (([self type] == SPTCPIPConnection || [self type] == SPSSHTunnelConnection) && ![[self host] length]) { SPBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [dbDocument parentWindow], self, nil, nil, NSLocalizedString(@"Insufficient details provided to establish a connection. Please enter at least the hostname.", @"insufficient details informative message")); @@ -212,9 +230,9 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, // Disable the favorites outline view to prevent further connections attempts [favoritesOutlineView setEnabled:NO]; - [addToFavoritesButton setHidden:YES]; [helpButton setHidden:YES]; [connectButton setEnabled:NO]; + [testConnectButton setEnabled:NO]; [progressIndicator startAnimation:self]; [progressIndicatorText setHidden:NO]; #endif @@ -226,7 +244,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, // have been changed or not; if not, leave the mark in place and remove the password from the field // for increased security. #ifndef SP_REFACTOR - if (connectionKeychainItemName) { + if (connectionKeychainItemName && !isTestingConnection) { if ([[keychain getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount] isEqualToString:[self password]]) { [self setPassword:[[NSString string] stringByPaddingToLength:[[self password] length] withString:@"sp" startingAtIndex:0]]; @@ -240,7 +258,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, } } - if (connectionSSHKeychainItemName) { + if (connectionSSHKeychainItemName && !isTestingConnection) { if ([[keychain getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount] isEqualToString:[self sshPassword]]) { [self setSshPassword:[[NSString string] stringByPaddingToLength:[[self sshPassword] length] withString:@"sp" startingAtIndex:0]]; [[sshSSHPasswordField undoManager] removeAllActionsWithTarget:sshSSHPasswordField]; @@ -302,10 +320,15 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, SPTreeNode *node = [self selectedFavoriteNode]; if (node) { + if (node == quickConnectItem) { + return; + } + // Only proceed to initiate a connection if a leaf node (i.e. a favorite and not a group) was double clicked. if (![node isGroup]) { [self initiateConnection:self]; } + // Otherwise start editing the group node's name else { [favoritesOutlineView editColumn:0 row:[favoritesOutlineView selectedRow] withEvent:nil select:YES]; @@ -325,6 +348,11 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, keySelectionPanel = [NSOpenPanel openPanel]; [keySelectionPanel setShowsHiddenFiles:[prefs boolForKey:SPHiddenKeyFileVisibilityKey]]; + // If the button was toggled off, ensure editing is ended + if ([sender state] == NSOffState) { + [self _startEditingConnection]; + } + // Switch details by sender. // First, SSH keys: if (sender == sshSSHKeyButton) { @@ -402,6 +430,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, - (IBAction)updateSSLInterface:(id)sender { [self resizeTabViewToConnectionType:[self type] animating:YES]; + [self _startEditingConnection]; } /** @@ -436,7 +465,10 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, - (void)resizeTabViewToConnectionType:(NSUInteger)theType animating:(BOOL)animate { NSRect frameRect, targetResizeRect; - NSInteger additionalFormHeight = 55; + + // Use a magic number which needs to be added to the form when calculating resizes - + // including the height of the button areas below. + NSInteger additionalFormHeight = 92; frameRect = [connectionResizeContainer frame]; @@ -529,6 +561,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, currentFavorite = [fav copy]; [connectionResizeContainer setHidden:NO]; + [self _stopEditingConnection]; // Set up the type, also storing it in the previous type store to prevent type "changes" triggering actions NSUInteger connectionType = ([fav objectForKey:SPFavoriteTypeKey] ? [[fav objectForKey:SPFavoriteTypeKey] integerValue] : SPTCPIPConnection); @@ -639,6 +672,14 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, } /** + * Saves the current connection favorite. + */ +- (IBAction)saveFavorite:(id)sender +{ + [self _saveCurrentDetailsCreatingNewFavorite:NO]; +} + +/** * Adds a new connection favorite. */ - (IBAction)addFavorite:(id)sender @@ -687,7 +728,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, [[[[NSApp delegate] preferenceController] generalPreferencePane] updateDefaultFavoritePopup]; - favoriteNameFieldWasTouched = NO; + favoriteNameFieldWasAutogenerated = YES; [favoritesOutlineView editColumn:0 row:[favoritesOutlineView selectedRow] withEvent:nil select:YES]; } @@ -698,98 +739,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, */ - (IBAction)addFavoriteUsingCurrentDetails:(id)sender { - NSString *thePassword, *theSSHPassword; - NSNumber *favoriteid = [self _createNewFavoriteID]; - NSString *favoriteName = [[self name] length] ? [self name] : [NSString stringWithFormat:@"%@@%@", ([self user] && [[self user] length])?[self user] : @"anonymous", (([self type] == SPSocketConnection) ? @"localhost" : [self host])]; - - if (![[self name] length] && [self database] && ![[self database] isEqualToString:@""]) { - favoriteName = [NSString stringWithFormat:@"%@ %@", [self database], favoriteName]; - } - - // Ensure that host is not empty if this is a TCP/IP or SSH connection - if (([self type] == SPTCPIPConnection || [self type] == SPSSHTunnelConnection) && ![[self host] length]) { - SPBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, [dbDocument parentWindow], nil, nil, nil, - NSLocalizedString(@"Insufficient details provided to establish a connection. Please provide at least a host.", @"insufficient details informative message")); - return; - } - - // If SSH is enabled, ensure that the SSH host is not nil - if ([self type] == SPSSHTunnelConnection && ![[self sshHost] length]) { - SPBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, [dbDocument parentWindow], nil, nil, nil, - NSLocalizedString(@"Please enter the hostname for the SSH Tunnel, or disable the SSH Tunnel.", @"message of panel when ssh details are incomplete")); - return; - } - - // Ensure that a socket connection is not inadvertently used - if (![self _checkHost]) return; - - // Construct the favorite details - cannot use only dictionaryWithObjectsAndKeys for possible nil values. - NSMutableDictionary *newFavorite = [NSMutableDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInteger:[self type]], SPFavoriteTypeKey, - favoriteName, SPFavoriteNameKey, - favoriteid, SPFavoriteIDKey, - nil]; - - // Standard details - if ([self host]) [newFavorite setObject:[self host] forKey:SPFavoriteHostKey]; - if ([self socket]) [newFavorite setObject:[self socket] forKey:SPFavoriteSocketKey]; - if ([self user]) [newFavorite setObject:[self user] forKey:SPFavoriteUserKey]; - if ([self port]) [newFavorite setObject:[self port] forKey:SPFavoritePortKey]; - if ([self database]) [newFavorite setObject:[self database] forKey:SPFavoriteDatabaseKey]; - - // SSL details - if ([self useSSL]) [newFavorite setObject:[NSNumber numberWithInteger:[self useSSL]] forKey:SPFavoriteUseSSLKey]; - [newFavorite setObject:[NSNumber numberWithInteger:[self sslKeyFileLocationEnabled]] forKey:SPFavoriteSSLKeyFileLocationEnabledKey]; - if ([self sslKeyFileLocation]) [newFavorite setObject:[self sslKeyFileLocation] forKey:SPFavoriteSSLKeyFileLocationKey]; - [newFavorite setObject:[NSNumber numberWithInteger:[self sslCertificateFileLocationEnabled]] forKey:SPFavoriteSSLCertificateFileLocationEnabledKey]; - if ([self sslCertificateFileLocation]) [newFavorite setObject:[self sslCertificateFileLocation] forKey:SPFavoriteSSLCertificateFileLocationKey]; - [newFavorite setObject:[NSNumber numberWithInteger:[self sslCACertFileLocationEnabled]] forKey:SPFavoriteSSLCACertFileLocationEnabledKey]; - if ([self sslCACertFileLocation]) [newFavorite setObject:[self sslCACertFileLocation] forKey:SPFavoriteSSLCACertFileLocationKey]; - - // SSH details - if ([self sshHost]) [newFavorite setObject:[self sshHost] forKey:SPFavoriteSSHHostKey]; - if ([self sshUser]) [newFavorite setObject:[self sshUser] forKey:SPFavoriteSSHUserKey]; - if ([self sshPort]) [newFavorite setObject:[self sshPort] forKey:SPFavoriteSSHPortKey]; - [newFavorite setObject:[NSNumber numberWithInteger:[self sshKeyLocationEnabled]] forKey:SPFavoriteSSHKeyLocationEnabledKey]; - if ([self sshKeyLocation]) [newFavorite setObject:[self sshKeyLocation] forKey:SPFavoriteSSHKeyLocationKey]; - - // Add the password to keychain as appropriate - thePassword = [self password]; - - if (mySQLConnection && connectionKeychainItemName) { - thePassword = [keychain getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount]; - } - - if (thePassword && (![thePassword isEqualToString:@""])) { - [keychain addPassword:thePassword - forName:[keychain nameForFavoriteName:favoriteName id:[NSString stringWithFormat:@"%lld", [favoriteid longLongValue]]] - account:[keychain accountForUser:[self user] host:(([self type] == SPSocketConnection) ? @"localhost" : [self host]) database:[self database]]]; - } - - // Add the SSH password to keychain as appropriate - theSSHPassword = [self sshPassword]; - - if (mySQLConnection && connectionSSHKeychainItemName) { - theSSHPassword = [keychain getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount]; - } - - if (theSSHPassword && (![theSSHPassword isEqualToString:@""])) { - [keychain addPassword:theSSHPassword - forName:[keychain nameForSSHForFavoriteName:favoriteName id:[NSString stringWithFormat:@"%lld", [favoriteid longLongValue]]] - account:[keychain accountForSSHUser:[self sshUser] sshHost:[self sshHost]]]; - } - - SPTreeNode *selectedNode = [self selectedFavoriteNode]; - - SPTreeNode *node = [favoritesController addFavoriteNodeWithData:newFavorite asChildOfNode:[selectedNode isGroup] ? selectedNode : nil]; - - [self _reloadFavoritesViewData]; - [self _selectNode:node]; - - // Update the favorites popup button in the preferences - [[[[NSApp delegate] preferenceController] generalPreferencePane] updateDefaultFavoritePopup]; + [self _saveCurrentDetailsCreatingNewFavorite:YES]; } /** @@ -805,9 +755,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, [self _reloadFavoritesViewData]; [self _selectNode:node]; - - isEditing = YES; - + [favoritesOutlineView editColumn:0 row:[favoritesOutlineView selectedRow] withEvent:nil select:YES]; } @@ -999,26 +947,9 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, */ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - NSMutableDictionary *selectedFavorite = [self selectedFavorite]; - if (!selectedFavorite) return; - - id oldObject = [change objectForKey:NSKeyValueChangeOldKey]; - id newObject = [change objectForKey:NSKeyValueChangeNewKey]; - - if (oldObject != newObject) { - [selectedFavorite setObject:![newObject isNSNull] ? newObject : @"" forKey:keyPath]; - - // Save the new data to disk - [favoritesController saveFavorites]; - - [self _reloadFavoritesViewData]; - - if ([keyPath isEqualToString:SPFavoriteNameKey]) { - [[NSNotificationCenter defaultCenter] postNotificationName:SPConnectionFavoritesChangedNotification object:self]; - } - } } + #pragma mark - #pragma mark Sheet methods @@ -1082,6 +1013,8 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, [self setSslCACertFileLocation:abbreviatedFileName]; } + + [self _startEditingConnection]; } /** @@ -1119,15 +1052,9 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, if (returnCode == NSAlertAlternateReturn) { [self setType:SPSocketConnection]; [self setHost:@""]; -#ifndef SP_REFACTOR - [self _updateFavoritePasswordsFromField:standardSQLHostField]; -#endif } else { [self setHost:@"127.0.0.1"]; -#ifndef SP_REFACTOR - [self _updateFavoritePasswordsFromField:standardSQLHostField]; -#endif } } @@ -1135,6 +1062,245 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, #pragma mark Private API /** + * Take the current details and either save them to the currently selected + * favourite, or create a new connection favourite using them. + * If creating a new favourite, also select it and ensure the selected + * favourite is visible. + */ +- (void)_saveCurrentDetailsCreatingNewFavorite:(BOOL)createNewFavorite +{ + + // Complete any active editing + if ([[connectionView window] firstResponder]) { + [[connectionView window] endEditingFor:[[connectionView window] firstResponder]]; + } + + + // Ensure that host is not empty if this is a TCP/IP or SSH connection + if (([self type] == SPTCPIPConnection || [self type] == SPSSHTunnelConnection) && ![[self host] length]) { + SPBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), + NSLocalizedString(@"OK", @"OK button"), nil, nil, [dbDocument parentWindow], nil, nil, nil, + NSLocalizedString(@"Insufficient details provided to establish a connection. Please provide at least a host.", @"insufficient details informative message")); + return; + } + + // If SSH is enabled, ensure that the SSH host is not nil + if ([self type] == SPSSHTunnelConnection && ![[self sshHost] length]) { + SPBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), + NSLocalizedString(@"OK", @"OK button"), nil, nil, [dbDocument parentWindow], nil, nil, nil, + NSLocalizedString(@"Please enter the hostname for the SSH Tunnel, or disable the SSH Tunnel.", @"message of panel when ssh details are incomplete")); + return; + } + + // Ensure that a socket connection is not inadvertently used + if (![self _checkHost]) return; + + + // Set up the favourite, or get the mutable dictionary for the current favourite. + NSMutableDictionary *theFavorite; + if (createNewFavorite) { + theFavorite = [NSMutableDictionary dictionary]; + [theFavorite setObject:[self _createNewFavoriteID] forKey:SPFavoriteIDKey]; + } else { + if (!currentFavorite) { + [NSException raise:NSInternalInconsistencyException format:@"Tried to save a current favourite with no currentFavorite"]; + } + theFavorite = [self selectedFavorite]; + } + + // Set the name - either taking the provided name, or generating one. + if ([[self name] length]) { + [theFavorite setObject:[self name] forKey:SPFavoriteNameKey]; + } else { + NSString *favoriteName = [self _generateNameForConnection]; + if (!favoriteName) { + favoriteName = NSLocalizedString(@"Untitled", @"Name for an untitled connection"); + } + [theFavorite setObject:favoriteName forKey:SPFavoriteNameKey]; + } + + // Set standard details for the connection + [theFavorite setObject:[NSNumber numberWithInteger:[self type]] forKey:SPFavoriteTypeKey]; + if ([self host]) { + [theFavorite setObject:[self host] forKey:SPFavoriteHostKey]; + } + if ([self socket]) { + [theFavorite setObject:[self socket] forKey:SPFavoriteSocketKey]; + } + if ([self user]) { + [theFavorite setObject:[self user] forKey:SPFavoriteUserKey]; + } + if ([self port]) { + [theFavorite setObject:[self port] forKey:SPFavoritePortKey]; + } + if ([self database]) { + [theFavorite setObject:[self database] forKey:SPFavoriteDatabaseKey]; + } + + // SSL details + if ([self useSSL]) { + [theFavorite setObject:[NSNumber numberWithInteger:[self useSSL]] forKey:SPFavoriteUseSSLKey]; + } + [theFavorite setObject:[NSNumber numberWithInteger:[self sslKeyFileLocationEnabled]] forKey:SPFavoriteSSLKeyFileLocationEnabledKey]; + if ([self sslKeyFileLocation]) { + [theFavorite setObject:[self sslKeyFileLocation] forKey:SPFavoriteSSLKeyFileLocationKey]; + } + [theFavorite setObject:[NSNumber numberWithInteger:[self sslCertificateFileLocationEnabled]] forKey:SPFavoriteSSLCertificateFileLocationEnabledKey]; + if ([self sslCertificateFileLocation]) { + [theFavorite setObject:[self sslCertificateFileLocation] forKey:SPFavoriteSSLCertificateFileLocationKey]; + } + [theFavorite setObject:[NSNumber numberWithInteger:[self sslCACertFileLocationEnabled]] forKey:SPFavoriteSSLCACertFileLocationEnabledKey]; + if ([self sslCACertFileLocation]) { + [theFavorite setObject:[self sslCACertFileLocation] forKey:SPFavoriteSSLCACertFileLocationKey]; + } + + // SSH details + if ([self sshHost]) { + [theFavorite setObject:[self sshHost] forKey:SPFavoriteSSHHostKey]; + } + if ([self sshUser]) { + [theFavorite setObject:[self sshUser] forKey:SPFavoriteSSHUserKey]; + } + if ([self sshPort]) { + [theFavorite setObject:[self sshPort] forKey:SPFavoriteSSHPortKey]; + } + [theFavorite setObject:[NSNumber numberWithInteger:[self sshKeyLocationEnabled]] forKey:SPFavoriteSSHKeyLocationEnabledKey]; + if ([self sshKeyLocation]) { + [theFavorite setObject:[self sshKeyLocation] forKey:SPFavoriteSSHKeyLocationKey]; + } + + + /** + * Password handling for the SQL connection + */ + NSString *oldKeychainName, *oldKeychainAccount, *newKeychainName, *newKeychainAccount;; + NSString *oldHostnameForPassword = ([[currentFavorite objectForKey:SPFavoriteTypeKey] integerValue] == SPSocketConnection) ? @"localhost" : [currentFavorite objectForKey:SPFavoriteHostKey]; + NSString *newHostnameForPassword = ([self type] == SPSocketConnection) ? @"localhost" : [self host]; + + // Grab the password for this connection + // Add the password to keychain as appropriate + NSString *sqlPassword = [self password]; + if (mySQLConnection && connectionKeychainItemName) { + sqlPassword = [keychain getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount]; + } + + // If creating a new favourite, always add the password to the keychain if it's set + if (createNewFavorite && [sqlPassword length]) { + [keychain addPassword:sqlPassword + forName:[keychain nameForFavoriteName:[theFavorite objectForKey:SPFavoriteNameKey] id:[theFavorite objectForKey:SPFavoriteIDKey]] + account:[keychain accountForUser:[self user] host:newHostnameForPassword database:[self database]]]; + } + + // If not creating a new favourite... + if (!createNewFavorite) { + + // Get the old keychain name and account strings + oldKeychainName = [keychain nameForFavoriteName:[currentFavorite objectForKey:SPFavoriteNameKey] id:[currentFavorite objectForKey:SPFavoriteIDKey]]; + oldKeychainAccount = [keychain accountForUser:[currentFavorite objectForKey:SPFavoriteUserKey] host:oldHostnameForPassword database:[currentFavorite objectForKey:SPFavoriteDatabaseKey]]; + + // If there's no new password, remove the old item from the keychain + if (![sqlPassword length]) { + [keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount]; + + // Otherwise, set up the new keychain name and account strings and create or edit the item + } else { + newKeychainName = [keychain nameForFavoriteName:[theFavorite objectForKey:SPFavoriteNameKey] id:[theFavorite objectForKey:SPFavoriteIDKey]]; + newKeychainAccount = [keychain accountForUser:[self user] host:newHostnameForPassword database:[self database]]; + if ([keychain passwordExistsForName:oldKeychainName account:oldKeychainAccount]) { + [keychain updateItemWithName:oldKeychainName account:oldKeychainAccount toName:newKeychainName account:newKeychainAccount password:sqlPassword]; + } else { + [keychain addPassword:sqlPassword forName:newKeychainName account:newKeychainAccount]; + } + } + } + sqlPassword = nil; + + + /** + * Password handling for the SSH connection + */ + NSString *theSSHPassword = [self sshPassword]; + if (mySQLConnection && connectionSSHKeychainItemName) { + theSSHPassword = [keychain getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount]; + } + + // If creating a new favourite, always add the password if it's set + if (createNewFavorite && [theSSHPassword length]) { + [keychain addPassword:theSSHPassword + forName:[keychain nameForSSHForFavoriteName:[theFavorite objectForKey:SPFavoriteNameKey] id:[theFavorite objectForKey:SPFavoriteIDKey]] + account:[keychain accountForSSHUser:[self sshUser] sshHost:[self sshHost]]]; + } + + // If not creating a new favourite... + if (!createNewFavorite) { + + // Get the old keychain name and account strings + oldKeychainName = [keychain nameForSSHForFavoriteName:[currentFavorite objectForKey:SPFavoriteNameKey] id:[currentFavorite objectForKey:SPFavoriteIDKey]]; + oldKeychainAccount = [keychain accountForSSHUser:[currentFavorite objectForKey:SPFavoriteSSHUserKey] sshHost:[currentFavorite objectForKey:SPFavoriteSSHHostKey]]; + + // If there's no new password, remove the old item from the keychain + if (![theSSHPassword length]) { + [keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount]; + + // Otherwise, set up the new keychain name and account strings and create or edit the item + } else { + newKeychainName = [keychain nameForSSHForFavoriteName:[theFavorite objectForKey:SPFavoriteNameKey] id:[theFavorite objectForKey:SPFavoriteIDKey]]; + newKeychainAccount = [keychain accountForSSHUser:[self sshUser] sshHost:[self sshHost]]; + if ([keychain passwordExistsForName:oldKeychainName account:oldKeychainAccount]) { + [keychain updateItemWithName:oldKeychainName account:oldKeychainAccount toName:newKeychainName account:newKeychainAccount password:theSSHPassword]; + } else { + [keychain addPassword:theSSHPassword forName:newKeychainName account:newKeychainAccount]; + } + } + } + theSSHPassword = nil; + + + /** + * Saving the connection + */ + + // If creating the connection, add to the favourites tree. + if (createNewFavorite) { + SPTreeNode *selectedNode = [self selectedFavoriteNode]; + SPTreeNode *parentNode = nil; + + // If the current node is a group node, create the favorite as a child of it + if ([selectedNode isGroup]) { + parentNode = selectedNode; + + // Otherwise, create the new node as a sibling of the selected node if possible + } else if ([selectedNode parentNode] && [selectedNode parentNode] != favoritesRoot) { + parentNode = (SPTreeNode *)[selectedNode parentNode]; + } + + // Add the new node and select it + SPTreeNode *newNode = [favoritesController addFavoriteNodeWithData:theFavorite asChildOfNode:parentNode]; + [self _reloadFavoritesViewData]; + [self _selectNode:newNode]; + + // Update the favorites popup button in the preferences + [[[[NSApp delegate] preferenceController] generalPreferencePane] updateDefaultFavoritePopup]; + + // Otherwise, if editing the favourite, update it + } else { + [[[self selectedFavoriteNode] representedObject] setNodeFavorite:theFavorite]; + + // Save the new data to disk + [favoritesController saveFavorites]; + + [self _stopEditingConnection]; + + if (currentFavorite) [currentFavorite release], currentFavorite = nil; + currentFavorite = [theFavorite copy]; + + [self _reloadFavoritesViewData]; + } + + [[NSNotificationCenter defaultCenter] postNotificationName:SPConnectionFavoritesChangedNotification object:self]; +} + +/** * Check the host field and ensure it isn't set to 'localhost' for non-socket connections. */ - (BOOL)_checkHost @@ -1281,16 +1447,12 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, } // Update the name for newly added favorites if not already touched by the user, by triggering a KVO update - if (![[self name] length]) { - [self setName:[NSString stringWithFormat:@"%@@%@", - ([favorite objectForKey:SPFavoriteUserKey]) ? [favorite objectForKey:SPFavoriteUserKey] : @"", - ((previousType == SPSocketConnection) ? @"localhost" : - (([favorite objectForKey:SPFavoriteHostKey]) ? [favorite valueForKeyPath:SPFavoriteHostKey] : @"")) - ]]; + if (![[self name] length] || favoriteNameFieldWasAutogenerated) { + NSString *favoriteName = [self _generateNameForConnection]; + if (favoriteName) { + [self setName:favoriteName]; + } } - - // Trigger a password change in response to host changes - [self _updateFavoritePasswordsFromField:nil]; } /** @@ -1350,13 +1512,12 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, [dbDocument setIsProcessing:NO]; // Reset the UI - [addToFavoritesButton setHidden:NO]; - [addToFavoritesButton display]; [helpButton setHidden:NO]; [helpButton display]; [connectButton setTitle:NSLocalizedString(@"Connect", @"connect button")]; [connectButton setEnabled:YES]; [connectButton display]; + [testConnectButton setEnabled:YES]; [progressIndicator stopAnimation:self]; [progressIndicator display]; [progressIndicatorText setHidden:YES]; @@ -1463,6 +1624,8 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, SPTreeNode *favoriteNode = nil; if (!favoritesRoot) return favoriteNode; + + if (!favoriteID) return quickConnectItem; for (SPTreeNode *node in [favoritesRoot allChildLeafs]) { @@ -1486,106 +1649,70 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, return [result stringByReplacingOccurrencesOfString:@"\n" withString:@""]; } -#ifndef SP_REFACTOR /** - * Check all fields used in the keychain names against the old values for that - * favorite, and update the keychain names to match if necessary. - * If an (optional) recognised password field is supplied, that field is assumed - * to have changed and is used to supply the new value. + * Generate a name for the current connection based on any other populated details. + * Currently uses the host and database fields. + * If a name cannot be generated because there are insufficient other details, returns nil. */ -- (void)_updateFavoritePasswordsFromField:(NSControl *)control +- (NSString *)_generateNameForConnection { - if (!currentFavorite) return; - - NSDictionary *oldFavorite = currentFavorite; - NSDictionary *newFavorite = [[[self selectedFavoriteNode] representedObject] nodeFavorite]; - - NSString *passwordValue; - NSString *oldKeychainName, *newKeychainName; - NSString *oldKeychainAccount, *newKeychainAccount; - NSString *oldHostnameForPassword = ([[oldFavorite objectForKey:SPFavoriteTypeKey] integerValue] == SPSocketConnection) ? @"localhost" : [oldFavorite objectForKey:SPFavoriteHostKey]; - NSString *newHostnameForPassword = ([[newFavorite objectForKey:SPFavoriteTypeKey] integerValue] == SPSocketConnection) ? @"localhost" : [newFavorite objectForKey:SPFavoriteHostKey]; - - // SQL passwords are indexed by name, host, user and database. If any of these - // have changed, or a standard password field has, alter the keychain item to match. - if (![[oldFavorite objectForKey:SPFavoriteNameKey] isEqualToString:[newFavorite objectForKey:SPFavoriteNameKey]] || - ![oldHostnameForPassword isEqualToString:newHostnameForPassword] || - ![[oldFavorite objectForKey:SPFavoriteUserKey] isEqualToString:[newFavorite objectForKey:SPFavoriteUserKey]] || - ![[oldFavorite objectForKey:SPFavoriteDatabaseKey] isEqualToString:[newFavorite objectForKey:SPFavoriteDatabaseKey]] || - control == standardPasswordField || control == socketPasswordField || control == sshPasswordField) - { - // Determine the correct password field to read the password from, defaulting to standard - if (control == socketPasswordField) { - passwordValue = [socketPasswordField stringValue]; - } - else if (control == sshPasswordField) { - passwordValue = [sshPasswordField stringValue]; - } - else { - passwordValue = [standardPasswordField stringValue]; - } - - // Get the old keychain name and account strings - oldKeychainName = [keychain nameForFavoriteName:[oldFavorite objectForKey:SPFavoriteNameKey] id:[newFavorite objectForKey:SPFavoriteIDKey]]; - oldKeychainAccount = [keychain accountForUser:[oldFavorite objectForKey:SPFavoriteUserKey] host:oldHostnameForPassword database:[oldFavorite objectForKey:SPFavoriteDatabaseKey]]; - - // If there's no new password, remove the old item from the keychain - if (![passwordValue length]) { - [keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount]; + NSString *aName; - // Otherwise, set up the new keychain name and account strings and create or edit the item - } else { - newKeychainName = [keychain nameForFavoriteName:[newFavorite objectForKey:SPFavoriteNameKey] id:[newFavorite objectForKey:SPFavoriteIDKey]]; - newKeychainAccount = [keychain accountForUser:[newFavorite objectForKey:SPFavoriteUserKey] host:newHostnameForPassword database:[newFavorite objectForKey:SPFavoriteDatabaseKey]]; - if ([keychain passwordExistsForName:oldKeychainName account:oldKeychainAccount]) { - [keychain updateItemWithName:oldKeychainName account:oldKeychainAccount toName:newKeychainName account:newKeychainAccount password:passwordValue]; - } else { - [keychain addPassword:passwordValue forName:newKeychainName account:newKeychainAccount]; - } - } - - // Synch password changes - [standardPasswordField setStringValue:passwordValue?passwordValue:@""]; - [socketPasswordField setStringValue:passwordValue?passwordValue:@""]; - [sshPasswordField setStringValue:passwordValue?passwordValue:@""]; - - passwordValue = @""; + if ([self type] != SPSocketConnection && ![[self host] length]) { + return nil; } - - // If SSH account/password details have changed, update the keychain to match - if (![[oldFavorite objectForKey:SPFavoriteNameKey] isEqualToString:[newFavorite objectForKey:SPFavoriteNameKey]] || - ![[oldFavorite objectForKey:SPFavoriteSSHHostKey] isEqualToString:[newFavorite objectForKey:SPFavoriteSSHHostKey]] || - ![[oldFavorite objectForKey:SPFavoriteSSHUserKey] isEqualToString:[newFavorite objectForKey:SPFavoriteSSHUserKey]] || - control == sshSSHPasswordField) - { - // Get the old keychain name and account strings - oldKeychainName = [keychain nameForSSHForFavoriteName:[oldFavorite objectForKey:SPFavoriteNameKey] id:[newFavorite objectForKey:SPFavoriteIDKey]]; - oldKeychainAccount = [keychain accountForSSHUser:[oldFavorite objectForKey:SPFavoriteSSHUserKey] sshHost:[oldFavorite objectForKey:SPFavoriteSSHHostKey]]; - // If there's no new password, delete the keychain item - if (![[sshSSHPasswordField stringValue] length]) { - [keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount]; + aName = ([self type] == SPSocketConnection) ? @"localhost" : [self host]; - // Otherwise, set up the new keychain name and account strings and create or update the keychain item - } else { - newKeychainName = [keychain nameForSSHForFavoriteName:[newFavorite objectForKey:SPFavoriteNameKey] id:[newFavorite objectForKey:SPFavoriteIDKey]]; - newKeychainAccount = [keychain accountForSSHUser:[newFavorite objectForKey:SPFavoriteSSHUserKey] sshHost:[newFavorite objectForKey:SPFavoriteSSHHostKey]]; - if ([keychain passwordExistsForName:oldKeychainName account:oldKeychainAccount]) { - [keychain updateItemWithName:oldKeychainName account:oldKeychainAccount toName:newKeychainName account:newKeychainAccount password:[sshSSHPasswordField stringValue]]; - } else { - [keychain addPassword:[sshSSHPasswordField stringValue] forName:newKeychainName account:newKeychainAccount]; - } - } + if ([[self database] length]) { + aName = [NSString stringWithFormat:@"%@/%@", aName, [self database]]; } - - // Update the current favorite - if (currentFavorite) [currentFavorite release], currentFavorite = nil; - - if ([[favoritesOutlineView selectedRowIndexes] count]) { - currentFavorite = [[[[self selectedFavoriteNode] representedObject] nodeFavorite] copy]; + + return aName; +} + + +/** + * If editing is not already active, mark editing as starting, triggering UI updates + * to match. + */ +- (void)_startEditingConnection +{ + + // If not connecting, hide the connection status text to reflect changes + if (!isConnecting) { + [progressIndicatorText setHidden:YES]; } + + if (isEditingConnection) return; + + // Fade and move the edit button area in + [editButtonsView setAlphaValue:0.0]; + [editButtonsView setHidden:NO]; + [editButtonsView setFrameOrigin:NSMakePoint([editButtonsView frame].origin.x, [editButtonsView frame].origin.y - 40)]; + [[editButtonsView animator] setFrameOrigin:NSMakePoint([editButtonsView frame].origin.x, [editButtonsView frame].origin.y + 40)]; + [[editButtonsView animator] setAlphaValue:1.0]; + + // Update the "Save" button state as appropriate + [saveFavoriteButton setEnabled:([self selectedFavorite] != nil)]; + + // Show the area to allow saving the changes + [self setIsEditingConnection:YES]; + [favoritesOutlineView setNeedsDisplayInRect:[favoritesOutlineView rectOfRow:[favoritesOutlineView selectedRow]]]; +} + +/** + * If editing is active, mark editing as complete, triggering UI updates to match. + */ +- (void)_stopEditingConnection +{ + if (!isEditingConnection) return; + + [editButtonsView setHidden:YES]; + [progressIndicatorText setHidden:YES]; + + [self setIsEditingConnection:NO]; } -#endif #pragma mark - @@ -1624,6 +1751,8 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, #ifndef SP_REFACTOR [folderImage release], folderImage = nil; + [quickConnectItem release], quickConnectItem = nil; + [quickConnectCell release], quickConnectCell = nil; #endif for (id retainedObject in nibObjectsToRelease) [retainedObject release]; diff --git a/Source/SPConnectionControllerDataSource.m b/Source/SPConnectionControllerDataSource.m index 86f6db49..d7f3d235 100644 --- a/Source/SPConnectionControllerDataSource.m +++ b/Source/SPConnectionControllerDataSource.m @@ -39,7 +39,6 @@ @interface SPConnectionController () - (void)_reloadFavoritesViewData; -- (void)_updateFavoritePasswordsFromField:(NSControl *)control; @end @@ -47,16 +46,42 @@ #ifndef SP_REFACTOR +/** + * Return the number of children for the specified item in the favourites tree. + * Note that to support the "Quick Connect" entry, the returned count is amended + * for the top level. + */ - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { SPTreeNode *node = (item == nil ? favoritesRoot : (SPTreeNode *)item); - + + // If at the root, return the count plus one for the "Quick Connect" entry + if (!item) { + return [[node childNodes] count] + 1; + } + return [[node childNodes] count]; } +/** + * Return the branch at the specified index of a supplied tree level. + * Note that to support the "Quick Connect" entry, children of the top level + * have their offsets amended. + */ - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)childIndex ofItem:(id)item { + + // For the top level of the tree, return the "Quick Connect" child for position zero; + // amend all other positions to compensate for the faked position. + if (!item) { + if (childIndex == 0) { + return quickConnectItem; + } + childIndex--; + } + SPTreeNode *node = (item == nil ? favoritesRoot : (SPTreeNode *)item); + return NSArrayObjectAtIndex([node childNodes], childIndex); } @@ -83,11 +108,10 @@ SPTreeNode *node = [self selectedFavoriteNode]; if (![node isGroup]) { + // Updating the name triggers a KVO update - [self setName:newName]; - - // Update associated Keychain items - [self _updateFavoritePasswordsFromField:nil]; + [self setName:newName]; + [self saveFavorite:self]; } else { [[node representedObject] setNodeName:newName]; diff --git a/Source/SPConnectionControllerDelegate.m b/Source/SPConnectionControllerDelegate.m index 30b602cd..e18c43ab 100644 --- a/Source/SPConnectionControllerDelegate.m +++ b/Source/SPConnectionControllerDelegate.m @@ -43,19 +43,27 @@ #endif static NSString *SPDatabaseImage = @"database-small"; +static NSString *SPQuickConnectImage = @"quick-connect-icon.pdf"; +static NSString *SPQuickConnectImageWhite = @"quick-connect-icon-white.pdf"; @interface SPConnectionController () +// Privately redeclare as read/write to get the synthesized setter +@property (readwrite, assign) BOOL isEditingConnection; + - (void)_checkHost; - (void)_sortFavorites; - (void)_favoriteTypeDidChange; - (void)_reloadFavoritesViewData; -- (void)_updateFavoritePasswordsFromField:(NSControl *)control; - (NSString *)_stripInvalidCharactersFromString:(NSString *)subject; +- (void)_startEditingConnection; +- (void)_stopEditingConnection; - (void)_setNodeIsExpanded:(BOOL)expanded fromNotification:(NSNotification *)notification; +- (NSString *)_generateNameForConnection; + @end @implementation SPConnectionController (SPConnectionControllerDelegate) @@ -88,25 +96,27 @@ static NSString *SPDatabaseImage = @"database-small"; return ([[(SPTreeNode *)item parentNode] parentNode] == nil); } +- (void)outlineViewSelectionIsChanging:(NSNotification *)notification +{ + if (isEditingConnection) { + [self _stopEditingConnection]; + [[notification object] setNeedsDisplay:YES]; + } +} + - (void)outlineViewSelectionDidChange:(NSNotification *)notification -{ +{ NSInteger selected = [favoritesOutlineView numberOfSelectedRows]; - - if (selected == 1) { - SPTreeNode *node = [self selectedFavoriteNode]; - - [self updateFavoriteSelection:self]; + if (isEditingConnection) { + [self _stopEditingConnection]; + [[notification object] setNeedsDisplay:YES]; + } - if (![node isGroup]) { - [addToFavoritesButton setEnabled:NO]; + if (selected == 1) { + [self updateFavoriteSelection:self]; - favoriteNameFieldWasTouched = YES; - } - else { - [addToFavoritesButton setEnabled:YES]; - } - + favoriteNameFieldWasAutogenerated = NO; [connectionResizeContainer setHidden:NO]; [connectionInstructionsTextField setStringValue:NSLocalizedString(@"Enter connection details below, or choose a favorite", @"enter connection details label")]; } @@ -116,17 +126,58 @@ static NSString *SPDatabaseImage = @"database-small"; } } +- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + if (item == quickConnectItem) { + return (NSCell *)quickConnectCell; + } + + return [tableColumn dataCellForRow:[outlineView rowForItem:item]]; +} + - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item { SPTreeNode *node = (SPTreeNode *)item; - + + // Draw entries with the small system font by default [(SPTableTextFieldCell *)cell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - [(SPTableTextFieldCell *)cell setImage:(![[node parentNode] parentNode]) ? nil : (![node isGroup]) ? [NSImage imageNamed:SPDatabaseImage] : folderImage]; + + // Set an image as appropriate; the quick connect image for that entry, no image for other + // top-level items, the folder image for group nodes, or the database image for other nodes. + if (![[node parentNode] parentNode]) { + if (node == quickConnectItem) { + if ([outlineView rowForItem:item] == [outlineView selectedRow]) { + [(SPTableTextFieldCell *)cell setImage:[NSImage imageNamed:SPQuickConnectImageWhite]]; + } else { + [(SPTableTextFieldCell *)cell setImage:[NSImage imageNamed:SPQuickConnectImage]]; + } + } else { + [(SPTableTextFieldCell *)cell setImage:nil]; + } + } else { + if ([node isGroup]) { + [(SPTableTextFieldCell *)cell setImage:folderImage]; + } else { + [(SPTableTextFieldCell *)cell setImage:[NSImage imageNamed:SPDatabaseImage]]; + } + } + + // If a favourite item is being edited, draw the text in bold to show state + if (isEditingConnection && ![node isGroup] && [outlineView rowForItem:item] == [outlineView selectedRow]) { + NSMutableAttributedString *editedCellString = [[cell attributedStringValue] mutableCopy]; + [editedCellString addAttribute:NSForegroundColorAttributeName value:[NSColor colorWithDeviceWhite:0.25f alpha:1.f] range:NSMakeRange(0, [editedCellString length])]; + [cell setAttributedStringValue:editedCellString]; + [editedCellString release]; + } } - (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item { - return ([[item parentNode] parentNode]) ? 17 : 22; + if (item == quickConnectItem) { + return 24.f; + } + + return ([[item parentNode] parentNode]) ? 17.f : 22.f; } - (NSString *)outlineView:(NSOutlineView *)outlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tableColumn item:(id)item mouseLocation:(NSPoint)mouseLocation @@ -170,8 +221,18 @@ static NSString *SPDatabaseImage = @"database-small"; } - (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item -{ - return ([[item parentNode] parentNode] != nil); +{ + + // If this is a top level item, only allow the "Quick Connect" item to be selectable + if (![[item parentNode] parentNode]) { + if (item == quickConnectItem) { + return YES; + } + return NO; + } + + // Otherwise allow all items to be selectable + return YES; } - (BOOL)outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item @@ -184,6 +245,11 @@ static NSString *SPDatabaseImage = @"database-small"; return ([[item parentNode] parentNode] != nil); } +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + return (item != quickConnectItem); +} + - (void)outlineViewItemDidCollapse:(NSNotification *)notification { [self _setNodeIsExpanded:NO fromNotification:notification]; @@ -210,12 +276,12 @@ static NSString *SPDatabaseImage = @"database-small"; } // If the user is in the process of changing a node's name, trigger a save and prevent dragging. - if (isEditing) { + if (isEditingItemName) { [favoritesController saveFavorites]; [self _reloadFavoritesViewData]; - isEditing = NO; + isEditingItemName = NO; return NO; } @@ -350,77 +416,60 @@ static NSString *SPDatabaseImage = @"database-small"; #ifndef SP_REFACTOR /** - * Trap and control the 'name' field of the selected favorite. If the user pressed - * 'Add Favorite' the 'name' field is set to 'New Favorite'. If the user did not - * change the 'name' field or delete that field it will be set to user@host automatically. + * React to control text changes in the connection interface */ - (void)controlTextDidChange:(NSNotification *)notification { id field = [notification object]; - + + // If a 'name' field was edited, and is now of zero length, trigger a replacement + // with a standard suggestion if (((field == standardNameField) || (field == socketNameField) || (field == sshNameField)) && [self selectedFavoriteNode]) { - - favoriteNameFieldWasTouched = YES; - - NSString *favoriteName = [self _stripInvalidCharactersFromString:[field stringValue]]; - - BOOL nameFieldIsEmpty = [favoriteName length] == 0; - - switch (previousType) - { - case SPTCPIPConnection: - if (nameFieldIsEmpty || (!favoriteNameFieldWasTouched && (field == standardUserField || field == standardSQLHostField))) { - [standardNameField setStringValue:[NSString stringWithFormat:@"%@@%@", [standardUserField stringValue], [standardSQLHostField stringValue]]]; - } - - break; - case SPSocketConnection: - if (nameFieldIsEmpty || (!favoriteNameFieldWasTouched && field == socketUserField)) { - [socketNameField setStringValue:[NSString stringWithFormat:@"%@@localhost", [socketUserField stringValue]]]; - } - - break; - case SPSSHTunnelConnection: - if (nameFieldIsEmpty || (!favoriteNameFieldWasTouched && (field == sshUserField || field == sshSQLHostField))) { - [sshNameField setStringValue:[NSString stringWithFormat:@"%@@%@", [sshUserField stringValue], [sshSQLHostField stringValue]]]; - } - - break; + if (![[self _stripInvalidCharactersFromString:[field stringValue]] length]) { + [self controlTextDidEndEditing:notification]; } - - // Trigger KVO update - [self setName:favoriteName]; - - // If name field is empty enable user@host update - if (nameFieldIsEmpty) favoriteNameFieldWasTouched = NO; + } + + [self _startEditingConnection]; + + if (favoriteNameFieldWasAutogenerated) { + [self setName:[self _generateNameForConnection]]; } } /** - * When a host field finishes editing, ensure that it hasn't been set to "localhost" - * to ensure that socket connections don't inadvertently occur. + * React to the end of control text changes in the connection interface. */ - (void)controlTextDidEndEditing:(NSNotification *)notification { - if ([notification object] == standardSQLHostField || [notification object] == sshSQLHostField) { - [self _checkHost]; + id field = [notification object]; + + // Handle updates to the 'name' field of the selected favourite. The favourite name should + // have leading or trailing spaces removed at the end of editing, and if it's left empty, + // should have a default name set. + if (((field == standardNameField) || (field == socketNameField) || (field == sshNameField)) && [self selectedFavoriteNode]) { + + NSString *favoriteName = [self _stripInvalidCharactersFromString:[field stringValue]]; + + if (![favoriteName length]) { + favoriteName = [self _generateNameForConnection]; + if (favoriteName) { + [self setName:favoriteName]; + } + + // Enable user@host update in reaction to other UI changes + favoriteNameFieldWasAutogenerated = YES; + } else if (![[field stringValue] isEqualToString:[self name]]) { + favoriteNameFieldWasAutogenerated = NO; + [self setName:favoriteName]; + } } -} -/** - * Trap editing end notifications and use them to update the keychain password - * appropriately when name, host, user, password or database changes. - */ -- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor -{ - // Request a password refresh to keep keychain references in sync with favorites, but only if a favorite - // is selected, meaning we're editing an existing one, not a new one. - if (((id)control != (id)favoritesOutlineView) && ([self selectedFavoriteNode])) { - [self _updateFavoritePasswordsFromField:control]; + // When a host field finishes editing, ensure that it hasn't been set to "localhost" to + // ensure that socket connections don't inadvertently occur. + if (field == standardSQLHostField || field == sshSQLHostField) { + [self _checkHost]; } - - // Proceed with editing - return YES; } #endif @@ -442,19 +491,18 @@ static NSString *SPDatabaseImage = @"database-small"; NSInteger selectedTabView = [tabView indexOfTabViewItem:tabViewItem]; if (selectedTabView == previousType) return; - + [self resizeTabViewToConnectionType:selectedTabView animating:YES]; // Update the host as appropriate if ((selectedTabView != SPSocketConnection) && [[self host] isEqualToString:@"localhost"]) { [self setHost:@""]; } - + previousType = selectedTabView; - - // Enable the add to favorites button - [addToFavoritesButton setEnabled:YES]; - + + [self _startEditingConnection]; + [self _favoriteTypeDidChange]; } @@ -510,7 +558,11 @@ static NSString *SPDatabaseImage = @"database-small"; SPTreeNode *node = [self selectedFavoriteNode]; NSInteger selectedRows = [favoritesOutlineView numberOfSelectedRows]; - + + if (node == quickConnectItem) { + return NO; + } + if ((action == @selector(sortFavorites:)) || (action == @selector(reverseSortFavorites:))) { if ([[favoritesRoot allChildLeafs] count] < 2) return NO; diff --git a/Source/SPConnectionControllerInitializer.m b/Source/SPConnectionControllerInitializer.m index cee0770a..73ebcad8 100644 --- a/Source/SPConnectionControllerInitializer.m +++ b/Source/SPConnectionControllerInitializer.m @@ -33,6 +33,7 @@ #import "SPConnectionControllerInitializer.h" #import "SPKeychain.h" #import "SPFavoritesController.h" +#import "SPFavoriteTextFieldCell.h" #import "SPTreeNode.h" #import "SPFavoriteNode.h" #import "SPGroupNode.h" @@ -78,13 +79,14 @@ static NSString *SPConnectionViewNibName = @"ConnectionView"; connectionSSHKeychainItemAccount = nil; initComplete = NO; - isEditing = NO; + isEditingItemName = NO; isConnecting = NO; + isTestingConnection = NO; sshTunnel = nil; mySQLConnection = nil; cancellingConnection = NO; mySQLConnectionCancelled = NO; - favoriteNameFieldWasTouched = YES; + favoriteNameFieldWasAutogenerated = NO; [self loadNib]; [self registerForNotifications]; @@ -101,9 +103,8 @@ static NSString *SPConnectionViewNibName = @"ConnectionView"; // Generic folder image for use in the outline view's groups folderImage = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)] retain]; - [folderImage setSize:NSMakeSize(16, 16)]; - + // Set up a keychain instance and preferences reference, and create the initial favorites list keychain = [[SPKeychain alloc] init]; prefs = [[NSUserDefaults standardUserDefaults] retain]; @@ -111,11 +112,20 @@ static NSString *SPConnectionViewNibName = @"ConnectionView"; // Create a reference to the favorites controller, forcing the data to be loaded from disk // and the tree to be constructed. favoritesController = [SPFavoritesController sharedFavoritesController]; - + // Tree references favoritesRoot = [favoritesController favoritesTree]; currentFavorite = nil; - + + // Create the "Quick Connect" placeholder group + quickConnectItem = [[SPTreeNode treeNodeWithRepresentedObject:[SPGroupNode groupNodeWithName:[NSLocalizedString(@"Quick Connect", @"Quick connect item label") uppercaseString]]] retain]; + [quickConnectItem setIsGroup:YES]; + + // Create a NSOutlineView cell for the Quick Connect group + quickConnectCell = [[SPFavoriteTextFieldCell alloc] init]; + [quickConnectCell setDrawsDividerUnderCell:YES]; + [quickConnectCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + // Update the UI [self _reloadFavoritesViewData]; [self setUpFavoritesOutlineView]; @@ -290,13 +300,16 @@ static NSString *SPConnectionViewNibName = @"ConnectionView"; SPTreeNode *favorite = [self _favoriteNodeForFavoriteID:[prefs integerForKey:[prefs boolForKey:SPSelectLastFavoriteUsed] ? SPLastFavoriteID : SPDefaultFavorite]]; if (favorite) { + + if (favorite == quickConnectItem) { + [self _selectNode:favorite]; + } else { + NSNumber *typeNumber = [[[favorite representedObject] nodeFavorite] objectForKey:SPFavoriteTypeKey]; + previousType = typeNumber ? [typeNumber integerValue] : SPTCPIPConnection; - NSNumber *typeNumber = [[[favorite representedObject] nodeFavorite] objectForKey:SPFavoriteTypeKey]; - - previousType = typeNumber ? [typeNumber integerValue] : SPTCPIPConnection; - - [self _selectNode:favorite]; - [self resizeTabViewToConnectionType:[[[[favorite representedObject] nodeFavorite] objectForKey:SPFavoriteTypeKey] integerValue] animating:NO]; + [self _selectNode:favorite]; + [self resizeTabViewToConnectionType:[[[[favorite representedObject] nodeFavorite] objectForKey:SPFavoriteTypeKey] integerValue] animating:NO]; + } [self _scrollToSelectedNode]; } diff --git a/Source/SPConnectionHandler.m b/Source/SPConnectionHandler.m index 5ae8d586..1a85bc50 100644 --- a/Source/SPConnectionHandler.m +++ b/Source/SPConnectionHandler.m @@ -45,8 +45,7 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; @interface SPConnectionController () - (void)_restoreConnectionInterface; - -- (void)_updateFavoritePasswordsFromField:(NSControl *)control; +- (void)_showConnectionTestResult:(NSString *)resultString; @end @@ -58,7 +57,17 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; - (void)initiateMySQLConnection { #ifndef SP_REFACTOR - [progressIndicatorText setStringValue:(sshTunnel) ? NSLocalizedString(@"MySQL connecting...", @"MySQL connecting very short status message") : NSLocalizedString(@"Connecting...", @"Generic connecting very short status message")]; + if (isTestingConnection) { + if (sshTunnel) { + [progressIndicatorText setStringValue:NSLocalizedString(@"Testing MySQL...", @"MySQL connection test very short status message")]; + } else { + [progressIndicatorText setStringValue:NSLocalizedString(@"Testing connection...", @"Connection test very short status message")]; + } + } else if (sshTunnel) { + [progressIndicatorText setStringValue:NSLocalizedString(@"MySQL connecting...", @"MySQL connecting very short status message")]; + } else { + [progressIndicatorText setStringValue:NSLocalizedString(@"Connecting...", @"Generic connecting very short status message")]; + } [progressIndicatorText display]; [connectButton setTitle:NSLocalizedString(@"Cancel", @"cancel button")]; @@ -200,7 +209,9 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; if ([self database] && ![[self database] isEqualToString:@""]) { if (![mySQLConnection selectDatabase:[self database]]) { - [[self onMainThread] failConnectionWithTitle:NSLocalizedString(@"Could not select database", @"message when database selection failed") errorMessage:[NSString stringWithFormat:NSLocalizedString(@"Connected to host, but unable to connect to database %@.\n\nBe sure that the database exists and that you have the necessary privileges.\n\nMySQL said: %@", @"message of panel when connection to db failed"), [self database], [mySQLConnection lastErrorMessage]] detail:nil rawErrorText:[mySQLConnection lastErrorMessage]]; + if (!isTestingConnection) { + [[self onMainThread] failConnectionWithTitle:NSLocalizedString(@"Could not select database", @"message when database selection failed") errorMessage:[NSString stringWithFormat:NSLocalizedString(@"Connected to host, but unable to connect to database %@.\n\nBe sure that the database exists and that you have the necessary privileges.\n\nMySQL said: %@", @"message of panel when connection to db failed"), [self database], [mySQLConnection lastErrorMessage]] detail:nil rawErrorText:[mySQLConnection lastErrorMessage]]; + } // Tidy up isConnecting = NO; @@ -209,6 +220,10 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; [mySQLConnection release], mySQLConnection = nil; [self _restoreConnectionInterface]; + if (isTestingConnection) { + [self _showConnectionTestResult:NSLocalizedString(@"Invalid database", @"Invalid database very short status message")]; + } + [pool release]; return; @@ -228,7 +243,11 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; */ - (void)initiateSSHTunnelConnection { - [progressIndicatorText setStringValue:NSLocalizedString(@"SSH connecting...", @"SSH connecting very short status message")]; + if (isTestingConnection) { + [progressIndicatorText setStringValue:NSLocalizedString(@"Testing SSH...", @"SSH testing very short status message")]; + } else { + [progressIndicatorText setStringValue:NSLocalizedString(@"SSH connecting...", @"SSH connecting very short status message")]; + } [progressIndicatorText display]; // Trim whitespace and newlines from the SSH host field before attempting to connect @@ -266,9 +285,9 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; { isConnecting = NO; - // If the user hit cancel during the connection attempt, kill the connection once - // established and reset the UI. - if (mySQLConnectionCancelled) { + // If the user hit cancel during the connection attempt, or a test connection is + // occurring, kill the connection once established and reset the UI. + if (mySQLConnectionCancelled || isTestingConnection) { if ([mySQLConnection isConnected]) { [mySQLConnection disconnect]; [mySQLConnection release], mySQLConnection = nil; @@ -278,7 +297,11 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; [self cancelConnection]; [self _restoreConnectionInterface]; - + + if (isTestingConnection) { + [self _showConnectionTestResult:NSLocalizedString(@"Connection succeeded", @"Connection success very short status message")]; + } + return; } @@ -296,7 +319,6 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; [connectButton display]; [progressIndicator stopAnimation:self]; [progressIndicatorText setHidden:YES]; - [addToFavoritesButton setHidden:NO]; #endif // If SSL was enabled, check it was established correctly @@ -418,9 +440,8 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; [progressIndicator display]; [progressIndicatorText setHidden:YES]; [progressIndicatorText display]; - [addToFavoritesButton setHidden:NO]; - [addToFavoritesButton display]; [connectButton setEnabled:YES]; + [testConnectButton setEnabled:YES]; [dbDocument clearStatusIcon]; #endif @@ -483,7 +504,6 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; // Change connection details [self setPort:tunnelPort]; [self setHost:SPLocalhostAddress]; - [self _updateFavoritePasswordsFromField:standardSQLHostField]; #ifndef SP_REFACTOR // Change to standard TCP/IP connection view @@ -495,4 +515,19 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; } } +/** + * Display a connection test error or success message + */ +- (void)_showConnectionTestResult:(NSString *)resultString +{ + if (![NSThread isMainThread]) { + [[self onMainThread] _showConnectionTestResult:resultString]; + } + + [helpButton setHidden:NO]; + [progressIndicator stopAnimation:self]; + [progressIndicatorText setStringValue:resultString]; + [progressIndicatorText setHidden:NO]; +} + @end diff --git a/Source/SPFavoriteTextFieldCell.h b/Source/SPFavoriteTextFieldCell.h index c006c9e7..84bee6ba 100644 --- a/Source/SPFavoriteTextFieldCell.h +++ b/Source/SPFavoriteTextFieldCell.h @@ -34,20 +34,10 @@ @interface SPFavoriteTextFieldCell : ImageAndTextCell { - NSString *favoriteName; - NSString *favoriteHost; - - NSColor *mainStringColor; - NSColor *subStringColor; + BOOL drawsDividerUnderCell; } -- (NSString *)favoriteName; -- (void)setFavoriteName:(NSString *)name; - -- (NSString *)favoriteHost; -- (void)setFavoriteHost:(NSString *)host; - -- (void)invertFontColors; -- (void)restoreFontColors; +- (BOOL)drawsDividerUnderCell; +- (void)setDrawsDividerUnderCell:(BOOL)drawsDivider; @end diff --git a/Source/SPFavoriteTextFieldCell.m b/Source/SPFavoriteTextFieldCell.m index e084d173..f9ebd280 100644 --- a/Source/SPFavoriteTextFieldCell.m +++ b/Source/SPFavoriteTextFieldCell.m @@ -32,17 +32,6 @@ #import "SPFavoriteTextFieldCell.h" -#define FAVORITE_NAME_FONT_SIZE 12.0f - -@interface SPFavoriteTextFieldCell (PrivateAPI) - -- (NSAttributedString *)constructSubStringAttributedString; -- (NSAttributedString *)attributedStringForFavoriteName; -- (NSDictionary *)mainStringAttributedStringAttributes; -- (NSDictionary *)subStringAttributedStringAttributes; - -@end - @implementation SPFavoriteTextFieldCell /** @@ -51,10 +40,7 @@ - (id)init { if ((self = [super init])) { - mainStringColor = [NSColor blackColor]; - subStringColor = [NSColor grayColor]; - favoriteName = nil; - favoriteHost = nil; + drawsDividerUnderCell = NO; } return self; @@ -63,186 +49,64 @@ - (id)copyWithZone:(NSZone *)zone { SPFavoriteTextFieldCell *cell = (SPFavoriteTextFieldCell *)[super copyWithZone:zone]; - - cell->favoriteName = nil; - if (favoriteName) cell->favoriteName = [favoriteName copyWithZone:zone]; - cell->favoriteHost = nil; - if (favoriteHost) cell->favoriteHost = [favoriteHost copyWithZone:zone]; + cell->drawsDividerUnderCell = drawsDividerUnderCell; return cell; } /** - * Get the cell's favorite name. - */ -- (NSString *)favoriteName -{ - return favoriteName; -} - -/** - * Set the cell's favorite name to the supplied name. + * Returns whether this cell is set to draw a divider in the space directly below + * the cell (whatever currently populates that space). */ -- (void)setFavoriteName:(NSString *)name +- (BOOL)drawsDividerUnderCell { - if (favoriteName != name) { - [favoriteName release]; - favoriteName = [name retain]; - } + return drawsDividerUnderCell; } /** - * Get the cell's favorite host. + * Set whether this cell should draw a divider in the space directly below + * the cell (whatever currently populates that space). */ -- (NSString *)favoriteHost +- (void)setDrawsDividerUnderCell:(BOOL)drawsDivider { - return favoriteHost; + drawsDividerUnderCell = drawsDivider; } -/** - * Set the cell's favorite host to the supplied name. - */ -- (void)setFavoriteHost:(NSString *)host -{ - if (favoriteHost != host) { - [favoriteHost release]; - favoriteHost = [host retain]; - } -} +#pragma mark - /** - * Draws the actual cell. + * Draws the actual cell, with a divider if appropriate. */ - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { - (([self isHighlighted]) && (![[self highlightColorWithFrame:cellFrame inView:controlView] isEqualTo:[NSColor secondarySelectedControlColor]])) ? [self invertFontColors] : [self restoreFontColors]; - - // Construct and get the sub text attributed string - NSAttributedString *mainString = [self attributedStringForFavoriteName]; - NSAttributedString *subString = [self constructSubStringAttributedString]; - - NSRect subFrame = NSMakeRect(0.0f, 0.0f, [subString size].width, [subString size].height); - - // Total height of both strings with a 2 pixel separation space - CGFloat totalHeight = [mainString size].height + [subString size].height + 1.0f; - - cellFrame.origin.y += (cellFrame.size.height - totalHeight) / 2.0f; - cellFrame.origin.x += 10.0f; // Indent main string from image - - // Position the sub text's frame rect - subFrame.origin.y = [mainString size].height + cellFrame.origin.y + 1.0f; - subFrame.origin.x = cellFrame.origin.x; - - cellFrame.size.height = totalHeight; - - NSUInteger i; - CGFloat maxWidth = cellFrame.size.width; - CGFloat mainStringWidth = [mainString size].width; - CGFloat subStringWidth = [subString size].width; - - // Set a right-padding - maxWidth -= 10; - - if (maxWidth < mainStringWidth) { - for (i = 0; i <= [mainString length]; i++) { - if ([[mainString attributedSubstringFromRange:NSMakeRange(0, i)] size].width >= maxWidth && i >= 3) { - mainString = [[[NSMutableAttributedString alloc] initWithString:[[[mainString attributedSubstringFromRange:NSMakeRange(0, i - 3)] string] stringByAppendingString:@"..."] attributes:[self mainStringAttributedStringAttributes]] autorelease]; - } - } - } - - if (maxWidth < subStringWidth) { - for (i = 0; i <= [subString length]; i++) { - if ([[subString attributedSubstringFromRange:NSMakeRange(0, i)] size].width >= maxWidth && i >= 3) { - subString = [[[NSMutableAttributedString alloc] initWithString:[[[subString attributedSubstringFromRange:NSMakeRange(0, i - 3)] string] stringByAppendingString:@"..."] attributes:[self subStringAttributedStringAttributes]] autorelease]; - } - } - } - - [mainString drawInRect:cellFrame]; - [subString drawInRect:subFrame]; -} + [super drawInteriorWithFrame:cellFrame inView:controlView]; -- (NSSize)cellSize -{ - NSSize cellSize = [super cellSize]; - NSAttributedString *mainString = [self attributedStringForFavoriteName]; - NSAttributedString *subString = [self constructSubStringAttributedString]; - - // 15 := indention 10 from image to string plus 5 px padding - CGFloat theWidth = MAX([mainString size].width, [subString size].width) + (([self image] != nil) ? [[self image] size].width : 0) + 15; + if (drawsDividerUnderCell) { + NSRect viewFrame = [controlView frame]; - CGFloat totalHeight = [mainString size].height + [subString size].height + 1.0f; - - cellSize.width = theWidth; - cellSize.height = totalHeight + 13.0f; - return cellSize; -} + NSPoint startPoint = NSMakePoint(viewFrame.origin.x + 7.f, viewFrame.origin.y); + NSPoint endPoint = NSMakePoint(viewFrame.origin.x + viewFrame.size.width - 7.f, viewFrame.origin.y); -/** - * Inverts the displayed font colors when the cell is selected. - */ -- (void)invertFontColors -{ - mainStringColor = [NSColor whiteColor]; - subStringColor = [NSColor whiteColor]; -} - -/** - * Restores the displayed font colors once the cell is no longer selected. - */ -- (void)restoreFontColors -{ - mainStringColor = [NSColor blackColor]; - subStringColor = [NSColor grayColor]; -} - -/** - * Dealloc. - */ -- (void)dealloc -{ - [favoriteName release], favoriteName = nil; - [favoriteHost release], favoriteHost = nil; - - [super dealloc]; -} - -@end - -@implementation SPFavoriteTextFieldCell (PrivateAPI) - -/** - * Constructs the attributed string to be used as the cell's substring. - */ -- (NSAttributedString *)constructSubStringAttributedString -{ - return [[[NSAttributedString alloc] initWithString:favoriteHost attributes:[self subStringAttributedStringAttributes]] autorelease]; -} - -/** - * Constructs the attributed string for the cell's favorite name. - */ -- (NSAttributedString *)attributedStringForFavoriteName -{ - return [[[NSAttributedString alloc] initWithString:favoriteName attributes:[self mainStringAttributedStringAttributes]] autorelease]; -} - -/** - * Returns the attributes of the cell's main string. - */ -- (NSDictionary *)mainStringAttributedStringAttributes -{ - return [NSDictionary dictionaryWithObjectsAndKeys:mainStringColor, NSForegroundColorAttributeName, [NSFont systemFontOfSize:FAVORITE_NAME_FONT_SIZE], NSFontAttributeName, nil]; -} + if ([controlView isFlipped]) { + startPoint.y += cellFrame.size.height + 8.5f; + endPoint.y += cellFrame.size.height + 8.5f; + } else { + startPoint.y -= cellFrame.size.height + 8.5f; + endPoint.y -= cellFrame.size.height + 8.5f; + } -/** - * Returns the attributes of the cell's sub string. - */ -- (NSDictionary *)subStringAttributedStringAttributes -{ - return [NSDictionary dictionaryWithObjectsAndKeys:subStringColor, NSForegroundColorAttributeName, [NSFont systemFontOfSize:[NSFont smallSystemFontSize]], NSFontAttributeName, nil]; + [NSGraphicsContext saveGraphicsState]; + [[NSColor gridColor] set]; + NSShadow *lineGlow = [[NSShadow alloc] init]; + [lineGlow setShadowBlurRadius:1]; + [lineGlow setShadowColor:[[NSColor controlLightHighlightColor] colorWithAlphaComponent:0.75f]]; + [lineGlow setShadowOffset:NSMakeSize(0, -1)]; + [lineGlow set]; + [NSBezierPath strokeLineFromPoint:startPoint toPoint:endPoint]; + [lineGlow release]; + [NSGraphicsContext restoreGraphicsState]; + } } -@end +@end
\ No newline at end of file diff --git a/Source/SPFavoritesOutlineView.m b/Source/SPFavoritesOutlineView.m index 883896ef..08880ec8 100644 --- a/Source/SPFavoritesOutlineView.m +++ b/Source/SPFavoritesOutlineView.m @@ -31,6 +31,9 @@ // More info at <http://code.google.com/p/sequel-pro/> #import "SPFavoritesOutlineView.h" +#import "SPConnectionControllerDelegate.h" + +static NSUInteger SPFavoritesOutlineViewUnindent = 14; @implementation SPFavoritesOutlineView @@ -88,4 +91,64 @@ } } +/** + * Don't reserve a gap for the disclosure triangles for top-level items. This involves reducing the + * padding - and increasing the width - of all rows to compensate. + */ +- (NSRect)frameOfCellAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex +{ + NSRect superFrame = [super frameOfCellAtColumn:columnIndex row:rowIndex]; + + return NSMakeRect(superFrame.origin.x - SPFavoritesOutlineViewUnindent, superFrame.origin.y, superFrame.size.width + SPFavoritesOutlineViewUnindent, superFrame.size.height); +} + +/** + * As no gap is reserved for the disclosure triangles at the top level, the frames for other + * disclosure items need to be similarly moved. + */ +- (NSRect)frameOfOutlineCellAtRow:(NSInteger)rowIndex +{ + NSRect superFrame = [super frameOfOutlineCellAtRow:rowIndex]; + + if (superFrame.origin.x > SPFavoritesOutlineViewUnindent) { + return NSMakeRect(superFrame.origin.x - SPFavoritesOutlineViewUnindent, superFrame.origin.y, superFrame.size.width, superFrame.size.height); + } + + return superFrame; +} + + +/** + * If the delegate is a SPConnectionControllerDelegate, and editing is currently in + * progress, draw a custom highlight. + */ +- (void)highlightSelectionInClipRect:(NSRect)clipRect +{ + + // Only proceed if a the delegate is a SPConnectionControllerDelegate and a favoruite being edited + if ([[self delegate] isKindOfClass:[SPConnectionController class]] + && [(SPConnectionController *)[self delegate] isEditingConnection] + && [(SPConnectionController *)[self delegate] selectedFavorite]) + { + + // Draw an editing dot instead of highlighting the whole row + NSRect rowRect = [self rectOfRow:[self selectedRow]]; + float dotSize = rowRect.size.height / 1.9; + NSRect dotRect = NSMakeRect(9.f, rowRect.origin.y + ((rowRect.size.height - dotSize) / 2), dotSize, dotSize); + [NSGraphicsContext saveGraphicsState]; + + NSBezierPath *clipPath = [NSBezierPath bezierPath]; + [clipPath appendBezierPathWithOvalInRect:dotRect]; + [clipPath addClip]; + + NSGradient *dotGradient = [[[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceRed:0.44f green:0.72f blue:0.92f alpha:1.f] endingColor:[NSColor colorWithDeviceRed:0.21f green:0.53f blue:0.82f alpha:1.f]] autorelease]; + [dotGradient drawInRect:dotRect angle:90.f]; + + [NSGraphicsContext restoreGraphicsState]; + return; + } + + [super highlightSelectionInClipRect:clipRect]; +} + @end diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj index 9126c6a7..a666fe74 100644 --- a/sequel-pro.xcodeproj/project.pbxproj +++ b/sequel-pro.xcodeproj/project.pbxproj @@ -208,6 +208,10 @@ 584095191107CB6600260CFD /* SPAlertSheets.m in Sources */ = {isa = PBXBuildFile; fileRef = 584095181107CB6600260CFD /* SPAlertSheets.m */; }; 5841423F0F97E11000A34B47 /* NoodleLineNumberView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5841423E0F97E11000A34B47 /* NoodleLineNumberView.m */; }; 584192A1101E57BB0089807F /* NSMutableArray-MultipleSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 584192A0101E57BB0089807F /* NSMutableArray-MultipleSort.m */; }; + 5843DA6C161FA35600EAA6D1 /* key-icon-alternate.png in Resources */ = {isa = PBXBuildFile; fileRef = 5843DA68161FA35600EAA6D1 /* key-icon-alternate.png */; }; + 5843DA6D161FA35600EAA6D1 /* key-icon-alternate@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5843DA69161FA35600EAA6D1 /* key-icon-alternate@2x.png */; }; + 5843DA6E161FA35600EAA6D1 /* key-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 5843DA6A161FA35600EAA6D1 /* key-icon.png */; }; + 5843DA6F161FA35600EAA6D1 /* key-icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5843DA6B161FA35600EAA6D1 /* key-icon@2x.png */; }; 584754D3120A05910057631F /* GeneratePreviewForURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 584754D0120A05910057631F /* GeneratePreviewForURL.m */; }; 584754D4120A05910057631F /* GenerateThumbnailForURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 584754D1120A05910057631F /* GenerateThumbnailForURL.m */; }; 584754D5120A05910057631F /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 584754D2120A05910057631F /* main.c */; }; @@ -344,6 +348,8 @@ 58DFC8E415C9F402003B4330 /* button_select_none.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 58DFC8E215C9F402003B4330 /* button_select_none.tiff */; }; 58DFC91615CB3501003B4330 /* BGHUDButtonCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 58DFC91515CB3501003B4330 /* BGHUDButtonCell.m */; }; 58E205FC1234FE4F00A97059 /* KeyTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 58E205FB1234FE4F00A97059 /* KeyTemplate.pdf */; }; + 58F48AA3161D03C6008536A1 /* quick-connect-icon.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 58F48AA2161D03C6008536A1 /* quick-connect-icon.pdf */; }; + 58F48B2E161D08C0008536A1 /* quick-connect-icon-white.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 58F48B2D161D08C0008536A1 /* quick-connect-icon-white.pdf */; }; 58FEF16D0F23D66600518E8E /* SPSQLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF16C0F23D66600518E8E /* SPSQLParser.m */; }; 58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF57D0F3B4E9700518E8E /* SPTableData.m */; }; 8D15AC340486D014006FF6A4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; }; @@ -923,6 +929,10 @@ 5841423E0F97E11000A34B47 /* NoodleLineNumberView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoodleLineNumberView.m; sourceTree = "<group>"; }; 5841929F101E57BB0089807F /* NSMutableArray-MultipleSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray-MultipleSort.h"; sourceTree = "<group>"; }; 584192A0101E57BB0089807F /* NSMutableArray-MultipleSort.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray-MultipleSort.m"; sourceTree = "<group>"; }; + 5843DA68161FA35600EAA6D1 /* key-icon-alternate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "key-icon-alternate.png"; sourceTree = "<group>"; }; + 5843DA69161FA35600EAA6D1 /* key-icon-alternate@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "key-icon-alternate@2x.png"; sourceTree = "<group>"; }; + 5843DA6A161FA35600EAA6D1 /* key-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "key-icon.png"; sourceTree = "<group>"; }; + 5843DA6B161FA35600EAA6D1 /* key-icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "key-icon@2x.png"; sourceTree = "<group>"; }; 584754C2120A04560057631F /* Sequel Pro.qlgenerator */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Sequel Pro.qlgenerator"; sourceTree = BUILT_PRODUCTS_DIR; }; 584754D0120A05910057631F /* GeneratePreviewForURL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratePreviewForURL.m; sourceTree = "<group>"; }; 584754D1120A05910057631F /* GenerateThumbnailForURL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GenerateThumbnailForURL.m; sourceTree = "<group>"; }; @@ -1132,6 +1142,8 @@ 58DFC91415CB3501003B4330 /* BGHUDButtonCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGHUDButtonCell.h; sourceTree = "<group>"; }; 58DFC91515CB3501003B4330 /* BGHUDButtonCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BGHUDButtonCell.m; sourceTree = "<group>"; }; 58E205FB1234FE4F00A97059 /* KeyTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = KeyTemplate.pdf; sourceTree = "<group>"; }; + 58F48AA2161D03C6008536A1 /* quick-connect-icon.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = "quick-connect-icon.pdf"; sourceTree = "<group>"; }; + 58F48B2D161D08C0008536A1 /* quick-connect-icon-white.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = "quick-connect-icon-white.pdf"; sourceTree = "<group>"; }; 58FEF16B0F23D66600518E8E /* SPSQLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSQLParser.h; sourceTree = "<group>"; }; 58FEF16C0F23D66600518E8E /* SPSQLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSQLParser.m; sourceTree = "<group>"; }; 58FEF57C0F3B4E9700518E8E /* SPTableData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTableData.h; sourceTree = "<group>"; }; @@ -2145,12 +2157,18 @@ 17E6419D0EF02036001BC333 /* grabber-horizontal.png */, 17E6419E0EF02036001BC333 /* grabber-vertical.png */, 17E6419F0EF02036001BC333 /* hideconsole.tiff */, + 5843DA68161FA35600EAA6D1 /* key-icon-alternate.png */, + 5843DA69161FA35600EAA6D1 /* key-icon-alternate@2x.png */, + 5843DA6A161FA35600EAA6D1 /* key-icon.png */, + 5843DA6B161FA35600EAA6D1 /* key-icon@2x.png */, 58E205FB1234FE4F00A97059 /* KeyTemplate.pdf */, 58D2E22D101222870063EF1D /* link-arrow.png */, 58D2E22B101222870063EF1D /* link-arrow-clicked.png */, 581068B51015411B0068C6E2 /* link-arrow-highlighted.png */, 58D2E22C101222870063EF1D /* link-arrow-highlighted-clicked.png */, 17E641A20EF02036001BC333 /* logo-48.png */, + 58F48AA2161D03C6008536A1 /* quick-connect-icon.pdf */, + 58F48B2D161D08C0008536A1 /* quick-connect-icon-white.pdf */, 17E641AE0EF02036001BC333 /* selectall.tiff */, 17E641AF0EF02036001BC333 /* selectnone.tiff */, 17E641B10EF02036001BC333 /* showconsole.tiff */, @@ -3014,6 +3032,12 @@ 58DFC0B215C728A5003B4330 /* button_add_folder.tiff in Resources */, 58DFC8E315C9F402003B4330 /* button_select_all.tiff in Resources */, 58DFC8E415C9F402003B4330 /* button_select_none.tiff in Resources */, + 58F48AA3161D03C6008536A1 /* quick-connect-icon.pdf in Resources */, + 58F48B2E161D08C0008536A1 /* quick-connect-icon-white.pdf in Resources */, + 5843DA6C161FA35600EAA6D1 /* key-icon-alternate.png in Resources */, + 5843DA6D161FA35600EAA6D1 /* key-icon-alternate@2x.png in Resources */, + 5843DA6E161FA35600EAA6D1 /* key-icon.png in Resources */, + 5843DA6F161FA35600EAA6D1 /* key-icon@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; |