diff --git a/binding/src/main/java/ch/cyberduck/binding/AbstractTableDelegate.java b/binding/src/main/java/ch/cyberduck/binding/AbstractTableDelegate.java
index 28847aed4f6..4b1fc99f7b2 100644
--- a/binding/src/main/java/ch/cyberduck/binding/AbstractTableDelegate.java
+++ b/binding/src/main/java/ch/cyberduck/binding/AbstractTableDelegate.java
@@ -115,7 +115,7 @@ public boolean selectionShouldChangeInTableView(final NSTableView view) {
/**
* @see NSOutlineView.DataSource
*/
- public boolean selectionShouldChangeInOutlineView(final NSTableView view) {
+ public boolean selectionShouldChangeInOutlineView(final NSOutlineView view) {
return this.selectionShouldChange();
}
diff --git a/binding/src/main/java/ch/cyberduck/binding/application/NSToolbarItem.java b/binding/src/main/java/ch/cyberduck/binding/application/NSToolbarItem.java
index eea03caac2b..8f27ea121e8 100644
--- a/binding/src/main/java/ch/cyberduck/binding/application/NSToolbarItem.java
+++ b/binding/src/main/java/ch/cyberduck/binding/application/NSToolbarItem.java
@@ -52,6 +52,8 @@ public abstract class NSToolbarItem extends NSObject implements NSCopying, NSVal
public static final String NSToolbarPrintItemIdentifier = "NSToolbarPrintItem";
public static final String NSToolbarToggleSidebarItemIdentifier = "NSToolbarToggleSidebarItem";
public static final String NSToolbarCloudSharingItemIdentifier = "NSToolbarCloudSharingItem";
+ public static final String NSToolbarInspectorTrackingSeparatorItemIdentifier = "NSToolbarInspectorTrackingSeparatorItem";
+ public static final String NSToolbarToggleInspectorItemIdentifier = "NSToolbarToggleInspectorItem";
public static final int VisibilityPriorityStandard = 0;
public static final int VisibilityPriorityLow = -1000;
diff --git a/binding/src/main/java/ch/cyberduck/binding/application/NSWindow.java b/binding/src/main/java/ch/cyberduck/binding/application/NSWindow.java
index 1d3df61d507..6c507d22132 100644
--- a/binding/src/main/java/ch/cyberduck/binding/application/NSWindow.java
+++ b/binding/src/main/java/ch/cyberduck/binding/application/NSWindow.java
@@ -67,6 +67,13 @@ public abstract class NSWindow extends NSResponder {
* native declaration : line 48
*/
public static final int NSUnifiedTitleAndToolbarWindowMask = 1 << 12;
+ /**
+ * When set, the window’s contentView consumes the full size of the window. Although you can combine this
+ * constant with other window style masks, it is respected only for windows with a title bar. Note that using
+ * this mask opts in to layer-backing. Use the contentLayoutRect or the contentLayoutGuide to lay out views
+ * underneath the title bar–toolbar area.
+ */
+ public static final int NSWindowStyleMaskFullSizeContentView = 1 << 15;
/**
* used with NSRunLoop's performSelector:target:argument:order:modes:
* native declaration : line 55
@@ -275,7 +282,7 @@ public interface _Class extends ObjCClass {
public static void setAllowsAutomaticWindowTabbing(boolean automatic) {
if(Rococoa.cast(CLASS, NSObject.class).respondsToSelector(
- Foundation.selector("setAllowsAutomaticWindowTabbing:"))) {
+ Foundation.selector("setAllowsAutomaticWindowTabbing:"))) {
CLASS.setAllowsAutomaticWindowTabbing(automatic);
}
}
diff --git a/bonjour/src/main/java/ch/cyberduck/core/bonjour/RendezvousCollection.java b/bonjour/src/main/java/ch/cyberduck/core/bonjour/RendezvousCollection.java
index 6c28a6a4f6a..3c9eaa177ae 100644
--- a/bonjour/src/main/java/ch/cyberduck/core/bonjour/RendezvousCollection.java
+++ b/bonjour/src/main/java/ch/cyberduck/core/bonjour/RendezvousCollection.java
@@ -112,16 +112,6 @@ public Host lookup(final String uuid) {
return null;
}
- @Override
- public Spliterator spliterator() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Stream stream() {
- throw new UnsupportedOperationException();
- }
-
@Override
public boolean allowsAdd() {
return false;
diff --git a/core/src/main/java/ch/cyberduck/core/DeserializerFactory.java b/core/src/main/java/ch/cyberduck/core/DeserializerFactory.java
index 156bc022252..5190d18e6ba 100644
--- a/core/src/main/java/ch/cyberduck/core/DeserializerFactory.java
+++ b/core/src/main/java/ch/cyberduck/core/DeserializerFactory.java
@@ -34,6 +34,10 @@ public DeserializerFactory() {
super("factory.deserializer.class");
}
+ public DeserializerFactory(final Class extends Deserializer> clazz) {
+ super(clazz);
+ }
+
public Deserializer create(final T dict) {
try {
final Constructor extends Deserializer> constructor = ConstructorUtils.getMatchingAccessibleConstructor(clazz, dict.getClass());
diff --git a/core/src/main/java/ch/cyberduck/core/SerializerFactory.java b/core/src/main/java/ch/cyberduck/core/SerializerFactory.java
index 1d46e021113..01f7e4e9f26 100644
--- a/core/src/main/java/ch/cyberduck/core/SerializerFactory.java
+++ b/core/src/main/java/ch/cyberduck/core/SerializerFactory.java
@@ -29,6 +29,10 @@ public SerializerFactory() {
super("factory.serializer.class");
}
+ public SerializerFactory(final Class extends Serializer> clazz) {
+ super(clazz);
+ }
+
public static Serializer get() {
return new SerializerFactory().create();
}
diff --git a/i18n/src/main/resources/ar.lproj/Browser.xib b/i18n/src/main/resources/ar.lproj/Browser.xib
index 50c3feaadc7..36a498f8112 100644
--- a/i18n/src/main/resources/ar.lproj/Browser.xib
+++ b/i18n/src/main/resources/ar.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/bg.lproj/Browser.xib b/i18n/src/main/resources/bg.lproj/Browser.xib
index 4e30f97787d..e70e524676c 100644
--- a/i18n/src/main/resources/bg.lproj/Browser.xib
+++ b/i18n/src/main/resources/bg.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/ca.lproj/Browser.xib b/i18n/src/main/resources/ca.lproj/Browser.xib
index 33bac15f64c..414c8956b53 100644
--- a/i18n/src/main/resources/ca.lproj/Browser.xib
+++ b/i18n/src/main/resources/ca.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/cs.lproj/Browser.xib b/i18n/src/main/resources/cs.lproj/Browser.xib
index 3fa4c78d883..7470ce728b7 100644
--- a/i18n/src/main/resources/cs.lproj/Browser.xib
+++ b/i18n/src/main/resources/cs.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/cy.lproj/Browser.xib b/i18n/src/main/resources/cy.lproj/Browser.xib
index d65f7472add..9a7e3f89a0e 100644
--- a/i18n/src/main/resources/cy.lproj/Browser.xib
+++ b/i18n/src/main/resources/cy.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/da.lproj/Browser.xib b/i18n/src/main/resources/da.lproj/Browser.xib
index a571a7f7a5b..2859eb0a8dc 100644
--- a/i18n/src/main/resources/da.lproj/Browser.xib
+++ b/i18n/src/main/resources/da.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/de.lproj/Browser.xib b/i18n/src/main/resources/de.lproj/Browser.xib
index 516eff19fa8..dbc4a000e30 100644
--- a/i18n/src/main/resources/de.lproj/Browser.xib
+++ b/i18n/src/main/resources/de.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/el.lproj/Browser.xib b/i18n/src/main/resources/el.lproj/Browser.xib
index 0e4066c9e17..b2ec7fd82dc 100644
--- a/i18n/src/main/resources/el.lproj/Browser.xib
+++ b/i18n/src/main/resources/el.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/en.lproj/Browser.xib b/i18n/src/main/resources/en.lproj/Browser.xib
index e08c3da8b1c..9cc97723e7d 100644
--- a/i18n/src/main/resources/en.lproj/Browser.xib
+++ b/i18n/src/main/resources/en.lproj/Browser.xib
@@ -95,7 +95,7 @@
-
+
diff --git a/i18n/src/main/resources/es.lproj/Browser.xib b/i18n/src/main/resources/es.lproj/Browser.xib
index 774fbe8d51d..a771beb19fc 100644
--- a/i18n/src/main/resources/es.lproj/Browser.xib
+++ b/i18n/src/main/resources/es.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/et.lproj/Browser.xib b/i18n/src/main/resources/et.lproj/Browser.xib
index 8ec7ad30e6a..8d5c852a57c 100644
--- a/i18n/src/main/resources/et.lproj/Browser.xib
+++ b/i18n/src/main/resources/et.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/fi.lproj/Browser.xib b/i18n/src/main/resources/fi.lproj/Browser.xib
index 5fb65726511..0e66be4da59 100644
--- a/i18n/src/main/resources/fi.lproj/Browser.xib
+++ b/i18n/src/main/resources/fi.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/fr.lproj/Browser.xib b/i18n/src/main/resources/fr.lproj/Browser.xib
index 42b86431230..c3ffab99ab5 100644
--- a/i18n/src/main/resources/fr.lproj/Browser.xib
+++ b/i18n/src/main/resources/fr.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/he.lproj/Browser.xib b/i18n/src/main/resources/he.lproj/Browser.xib
index a8b79f6a56d..e23ff968590 100644
--- a/i18n/src/main/resources/he.lproj/Browser.xib
+++ b/i18n/src/main/resources/he.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/hr.lproj/Browser.xib b/i18n/src/main/resources/hr.lproj/Browser.xib
index 67fb2429ebc..62feb7dfce1 100644
--- a/i18n/src/main/resources/hr.lproj/Browser.xib
+++ b/i18n/src/main/resources/hr.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/hu.lproj/Browser.xib b/i18n/src/main/resources/hu.lproj/Browser.xib
index 371d9038bbe..5ba941570d0 100644
--- a/i18n/src/main/resources/hu.lproj/Browser.xib
+++ b/i18n/src/main/resources/hu.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/it.lproj/Browser.xib b/i18n/src/main/resources/it.lproj/Browser.xib
index 1ac4a047add..6001665b8a9 100644
--- a/i18n/src/main/resources/it.lproj/Browser.xib
+++ b/i18n/src/main/resources/it.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/ja.lproj/Browser.xib b/i18n/src/main/resources/ja.lproj/Browser.xib
index 573fe4b6b29..df79c463364 100644
--- a/i18n/src/main/resources/ja.lproj/Browser.xib
+++ b/i18n/src/main/resources/ja.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/ka.lproj/Browser.xib b/i18n/src/main/resources/ka.lproj/Browser.xib
index f75965040f0..7612d2235c1 100644
--- a/i18n/src/main/resources/ka.lproj/Browser.xib
+++ b/i18n/src/main/resources/ka.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/ko.lproj/Browser.xib b/i18n/src/main/resources/ko.lproj/Browser.xib
index 8bd31acd0d6..331d84ccddc 100644
--- a/i18n/src/main/resources/ko.lproj/Browser.xib
+++ b/i18n/src/main/resources/ko.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/lv.lproj/Browser.xib b/i18n/src/main/resources/lv.lproj/Browser.xib
index 4bf67da4ead..6897b433ba5 100644
--- a/i18n/src/main/resources/lv.lproj/Browser.xib
+++ b/i18n/src/main/resources/lv.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/nl.lproj/Browser.xib b/i18n/src/main/resources/nl.lproj/Browser.xib
index e5e1284b368..6f4be1850bd 100644
--- a/i18n/src/main/resources/nl.lproj/Browser.xib
+++ b/i18n/src/main/resources/nl.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/no.lproj/Browser.xib b/i18n/src/main/resources/no.lproj/Browser.xib
index 957c48535bc..7f9a873f73d 100644
--- a/i18n/src/main/resources/no.lproj/Browser.xib
+++ b/i18n/src/main/resources/no.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/pl.lproj/Browser.xib b/i18n/src/main/resources/pl.lproj/Browser.xib
index 84ca50b1340..b11661b36e9 100644
--- a/i18n/src/main/resources/pl.lproj/Browser.xib
+++ b/i18n/src/main/resources/pl.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/pt_BR.lproj/Browser.xib b/i18n/src/main/resources/pt_BR.lproj/Browser.xib
index 8bddf55ea55..f4801c4bcf6 100644
--- a/i18n/src/main/resources/pt_BR.lproj/Browser.xib
+++ b/i18n/src/main/resources/pt_BR.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/pt_PT.lproj/Browser.xib b/i18n/src/main/resources/pt_PT.lproj/Browser.xib
index 2d7c309f9e7..169382484dd 100644
--- a/i18n/src/main/resources/pt_PT.lproj/Browser.xib
+++ b/i18n/src/main/resources/pt_PT.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/ro.lproj/Browser.xib b/i18n/src/main/resources/ro.lproj/Browser.xib
index 09f78365681..0e300823457 100644
--- a/i18n/src/main/resources/ro.lproj/Browser.xib
+++ b/i18n/src/main/resources/ro.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/ru.lproj/Browser.xib b/i18n/src/main/resources/ru.lproj/Browser.xib
index c59a5fda06f..9b09631e921 100644
--- a/i18n/src/main/resources/ru.lproj/Browser.xib
+++ b/i18n/src/main/resources/ru.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/sk.lproj/Browser.xib b/i18n/src/main/resources/sk.lproj/Browser.xib
index c2afa55e870..afe6940a334 100644
--- a/i18n/src/main/resources/sk.lproj/Browser.xib
+++ b/i18n/src/main/resources/sk.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/sl.lproj/Browser.xib b/i18n/src/main/resources/sl.lproj/Browser.xib
index 61789779aaf..9bd72e9aa0d 100644
--- a/i18n/src/main/resources/sl.lproj/Browser.xib
+++ b/i18n/src/main/resources/sl.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/sr.lproj/Browser.xib b/i18n/src/main/resources/sr.lproj/Browser.xib
index 3f4b026ffc4..8f5641587e1 100644
--- a/i18n/src/main/resources/sr.lproj/Browser.xib
+++ b/i18n/src/main/resources/sr.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/sv.lproj/Browser.xib b/i18n/src/main/resources/sv.lproj/Browser.xib
index d72a111e976..18ed85e97dc 100644
--- a/i18n/src/main/resources/sv.lproj/Browser.xib
+++ b/i18n/src/main/resources/sv.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/th.lproj/Browser.xib b/i18n/src/main/resources/th.lproj/Browser.xib
index d65f7472add..9a7e3f89a0e 100644
--- a/i18n/src/main/resources/th.lproj/Browser.xib
+++ b/i18n/src/main/resources/th.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/tr.lproj/Browser.xib b/i18n/src/main/resources/tr.lproj/Browser.xib
index 63e0cb673ab..6d47c6b1fbd 100644
--- a/i18n/src/main/resources/tr.lproj/Browser.xib
+++ b/i18n/src/main/resources/tr.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/uk.lproj/Browser.xib b/i18n/src/main/resources/uk.lproj/Browser.xib
index ef7e156f1bb..2e34ca4c075 100644
--- a/i18n/src/main/resources/uk.lproj/Browser.xib
+++ b/i18n/src/main/resources/uk.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/zh_CN.lproj/Browser.xib b/i18n/src/main/resources/zh_CN.lproj/Browser.xib
index 4a297716afc..ab88a11bd3e 100644
--- a/i18n/src/main/resources/zh_CN.lproj/Browser.xib
+++ b/i18n/src/main/resources/zh_CN.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/i18n/src/main/resources/zh_TW.lproj/Browser.xib b/i18n/src/main/resources/zh_TW.lproj/Browser.xib
index 5b0c4b6b990..18a40a06cb2 100644
--- a/i18n/src/main/resources/zh_TW.lproj/Browser.xib
+++ b/i18n/src/main/resources/zh_TW.lproj/Browser.xib
@@ -1,7 +1,7 @@
-
+
-
+
@@ -58,7 +58,7 @@
-
+
-
-
-
+
+
+
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BrowserController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BrowserController.java
index 18a1956914a..6b5e107eb33 100644
--- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BrowserController.java
+++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BrowserController.java
@@ -70,6 +70,8 @@
import ch.cyberduck.core.preferences.PreferencesFactory;
import ch.cyberduck.core.resources.IconCacheFactory;
import ch.cyberduck.core.serializer.HostDictionary;
+import ch.cyberduck.core.serializer.impl.jna.PlistDeserializer;
+import ch.cyberduck.core.serializer.impl.jna.PlistSerializer;
import ch.cyberduck.core.ssl.X509TrustManager;
import ch.cyberduck.core.threading.BackgroundAction;
import ch.cyberduck.core.threading.BrowserTransferBackgroundAction;
@@ -268,9 +270,9 @@ public class BrowserController extends WindowController implements NSToolbar.Del
@Delegate
private BookmarkTableDataSource bookmarkModel;
@Outlet
- private NSTableView bookmarkTable;
+ private NSOutlineView bookmarkTable;
@Delegate
- private AbstractTableDelegate bookmarkTableDelegate;
+ private AbstractBookmarkTableDelegate bookmarkTableDelegate;
@Outlet
private NSSearchField searchField;
@Outlet
@@ -1024,10 +1026,10 @@ public void run() {
this.setBookmarkFilter(null);
this.reloadBookmarks();
if(this.isMounted()) {
- int row = this.bookmarkModel.getSource().indexOf(pool.getHost());
- if(row != -1) {
- this.bookmarkTable.selectRowIndexes(NSIndexSet.indexSetWithIndex(new NSInteger(row)), false);
- this.bookmarkTable.scrollRowToVisible(new NSInteger(row));
+ final NSInteger row = bookmarkTable.rowForItem(pool.getHost().serialize(new PlistSerializer()));
+ if(row.intValue() != -1) {
+ this.bookmarkTable.selectRowIndexes(NSIndexSet.indexSetWithIndex(row), false);
+ this.bookmarkTable.scrollRowToVisible(row);
}
else {
this.bookmarkTable.selectRowIndexes(NSIndexSet.indexSetWithIndex(new NSInteger(0)), false);
@@ -1035,8 +1037,8 @@ public void run() {
}
}
else {
- this.bookmarkTable.selectRowIndexes(NSIndexSet.indexSetWithIndex(new NSInteger(0)), false);
- this.bookmarkTable.scrollRowToVisible(new NSInteger(0));
+ this.bookmarkTable.selectRowIndexes(NSIndexSet.indexSetWithIndex(new NSInteger(1)), false);
+ this.bookmarkTable.scrollRowToVisible(new NSInteger(1));
}
this.getFocus();
}
@@ -1046,6 +1048,7 @@ public void run() {
*/
public void reloadBookmarks() {
bookmarkTable.reloadData();
+// bookmarkTable.reloadItem_reloadChildren(null, true);
this.setStatus();
}
@@ -1529,7 +1532,7 @@ private void _configureBrowserColumns(final NSTableView table, final AbstractBro
this.reload();
}
- public NSTableView getBookmarkTable() {
+ public NSOutlineView getBookmarkTable() {
return bookmarkTable;
}
@@ -1538,7 +1541,7 @@ public NSTableView getBookmarkTable() {
// ----------------------------------------------------------
@Action
- public void setBookmarkTable(NSTableView view) {
+ public void setBookmarkTable(final NSOutlineView view) {
bookmarkTable = view;
bookmarkTable.setSelectionHighlightStyle(NSTableView.NSTableViewSelectionHighlightStyleSourceList);
bookmarkTable.setDataSource((this.bookmarkModel = new BookmarkTableDataSource(this)).id());
@@ -1548,6 +1551,7 @@ public void setBookmarkTable(NSTableView view) {
c.setResizingMask(NSTableColumn.NSTableColumnNoResizing);
c.setDataCell(imageCellPrototype);
bookmarkTable.addTableColumn(c);
+ bookmarkTable.setOutlineTableColumn(c);
}
{
NSTableColumn c = bookmarkTableColumnFactory.create(BookmarkColumn.bookmark.name());
@@ -1568,7 +1572,7 @@ public void setBookmarkTable(NSTableView view) {
c.dataCell().setAlignment(TEXT_ALIGNMENT_CENTER);
bookmarkTable.addTableColumn(c);
}
- bookmarkTable.setDelegate((bookmarkTableDelegate = new AbstractTableDelegate(
+ bookmarkTable.setDelegate((bookmarkTableDelegate = new AbstractBookmarkTableDelegate(
bookmarkTable.tableColumnWithIdentifier(BookmarkColumn.bookmark.name())
) {
private static final double kSwipeGestureLeft = 1.000000;
@@ -1576,6 +1580,29 @@ public void setBookmarkTable(NSTableView view) {
private static final double kSwipeGestureUp = 1.000000;
private static final double kSwipeGestureDown = -1.000000;
+ public NSCell outlineView_dataCellForTableColumn_item(final NSOutlineView view, final NSTableColumn column, final NSObject item) {
+ if(null == item) {
+ return null;
+ }
+ if(null == column) {
+ if(outlineView_isGroupItem(view, item)) {
+ // When each row (identified by the item) is being drawn, this method is first called
+ // with a nil value for tableColumn. At this time, you can return a cell that is used to draw the entire row, acting like a group
+ return textCellPrototype;
+ }
+ return null;
+ }
+ return column.dataCell();
+ }
+
+ @Override
+ public boolean outlineView_shouldSelectItem(final NSOutlineView view, final NSObject item) {
+ if(this.outlineView_isGroupItem(view, item)) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public String tooltip(Host bookmark, final BookmarkColumn column) {
return new HostUrlProvider().get(bookmark);
@@ -1586,11 +1613,6 @@ public void tableRowDoubleClicked(final ID sender) {
BrowserController.this.connectBookmarkButtonClicked(sender);
}
- @Override
- public void enterKeyPressed(final ID sender) {
- this.tableRowDoubleClicked(sender);
- }
-
@Override
public void deleteKeyPressed(final ID sender) {
if(bookmarkModel.getSource().allowsDelete()) {
@@ -1598,11 +1620,6 @@ public void deleteKeyPressed(final ID sender) {
}
}
- @Override
- public void tableColumnClicked(NSTableView view, NSTableColumn tableColumn) {
-
- }
-
@Override
public void selectionDidChange(final NSNotification notification) {
addBookmarkButton.setEnabled(bookmarkModel.getSource().allowsAdd());
@@ -1612,7 +1629,10 @@ public void selectionDidChange(final NSNotification notification) {
}
@Action
- public CGFloat tableView_heightOfRow(NSTableView view, NSInteger row) {
+ public CGFloat outlineView_heightOfRowByItem(final NSOutlineView view, final NSObject item) {
+ if(outlineView_isGroupItem(view, item)) {
+ return new CGFloat(18);
+ }
final int size = preferences.getInteger("bookmark.icon.size");
if(BookmarkCell.SMALL_BOOKMARK_SIZE == size) {
return new CGFloat(18);
@@ -1628,15 +1648,19 @@ public boolean isTypeSelectSupported() {
return true;
}
- @Action
- public String tableView_typeSelectStringForTableColumn_row(NSTableView view,
- NSTableColumn tableColumn,
- NSInteger row) {
- return BookmarkNameProvider.toString(bookmarkModel.getSource().get(row.intValue()));
+ public String outlineView_typeSelectStringForTableColumn_item(final NSOutlineView view, final NSTableColumn column, final NSObject item) {
+ final NSDictionary dict = Rococoa.cast(item, NSDictionary.class);
+ return BookmarkNameProvider.toString(new HostDictionary(new DeserializerFactory(PlistDeserializer.class)).deserialize(dict));
}
- @Action
- public boolean tableView_isGroupRow(NSTableView view, NSInteger row) {
+ @Override
+ public boolean outlineView_isGroupItem(final NSOutlineView view, final NSObject item) {
+ if(null == item) {
+ return false;
+ }
+ if(item.isKindOfClass(Rococoa.createClass("NSString", NSString._Class.class))) {
+ return true;
+ }
return false;
}
@@ -1646,7 +1670,7 @@ public boolean tableView_isGroupRow(NSTableView view, NSInteger row) {
* @param event Swipe event
*/
@Action
- public void swipeWithEvent(NSEvent event) {
+ public void swipeWithEvent(final NSEvent event) {
if(event.deltaY().doubleValue() == kSwipeGestureUp) {
NSInteger row = bookmarkTable.selectedRow();
NSInteger next;
@@ -1697,8 +1721,9 @@ else if(BookmarkCell.MEDIUM_BOOKMARK_SIZE == size) {
bookmarkTable.setRowHeight(new CGFloat(70));
}
- // setting appearance attributes()
+ // setting appearance attributes
bookmarkTable.setUsesAlternatingRowBackgroundColors(preferences.getBoolean("browser.alternatingRows"));
+ // No grid lines until bookmarks are loaded
bookmarkTable.setGridStyleMask(NSTableView.NSTableViewGridNone);
// selection properties
@@ -1707,6 +1732,8 @@ else if(BookmarkCell.MEDIUM_BOOKMARK_SIZE == size) {
bookmarkTable.setAllowsColumnResizing(false);
bookmarkTable.setAllowsColumnSelection(false);
bookmarkTable.setAllowsColumnReordering(false);
+ bookmarkTable.setAutosaveName("Bookmarks");
+ bookmarkTable.setAutosaveExpandedItems(true);
bookmarkTable.sizeToFit();
}
@@ -1839,7 +1866,7 @@ public void cleanup(final AttributedList list) {
private void setBookmarkFilter(final String searchString) {
if(StringUtils.isBlank(searchString)) {
searchField.setStringValue(StringUtils.EMPTY);
- bookmarkModel.setFilter(null);
+ bookmarkModel.setFilter(HostFilter.NONE);
}
else {
bookmarkModel.setFilter(new BookmarkSearchFilter(searchString));
@@ -1850,7 +1877,14 @@ private void setBookmarkFilter(final String searchString) {
@Action
public void connectBookmarkButtonClicked(final ID sender) {
if(bookmarkTable.numberOfSelectedRows().intValue() == 1) {
- final Host selected = bookmarkModel.getSource().get(bookmarkTable.selectedRow().intValue());
+ final NSObject item = bookmarkTable.itemAtRow(bookmarkTable.selectedRow());
+ if(bookmarkTableDelegate.outlineView_isGroupItem(bookmarkTable, item)) {
+ return;
+ }
+ final NSDictionaryHostFilter filter = new NSDictionaryHostFilter(
+ Rococoa.cast(item, NSDictionary.class)
+ );
+ final Host selected = bookmarkModel.getSource().stream().filter(filter::accept).findFirst().orElse(null);
this.mount(selected);
}
}
@@ -1870,15 +1904,20 @@ public void setEditBookmarkButton(NSButton editBookmarkButton) {
@Action
public void editBookmarkButtonClicked(final ID sender) {
- final BookmarkController c = BookmarkControllerFactory.create(bookmarks,
- bookmarkModel.getSource().get(bookmarkTable.selectedRow().intValue())
+ final NSDictionaryHostFilter filter = new NSDictionaryHostFilter(
+ Rococoa.cast(bookmarkTable.itemAtRow(bookmarkTable.selectedRow()), NSDictionary.class)
);
+ final Host selected = bookmarkModel.getSource().stream().filter(filter::accept).findFirst().orElse(null);
+ final BookmarkController c = BookmarkControllerFactory.create(bookmarks, selected);
c.window().makeKeyAndOrderFront(null);
}
@Action
public void duplicateBookmarkButtonClicked(final ID sender) {
- final Host selected = bookmarkModel.getSource().get(bookmarkTable.selectedRow().intValue());
+ final NSDictionaryHostFilter filter = new NSDictionaryHostFilter(
+ Rococoa.cast(bookmarkTable.itemAtRow(bookmarkTable.selectedRow()), NSDictionary.class)
+ );
+ final Host selected = bookmarkModel.getSource().stream().filter(filter::accept).findFirst().orElse(null);
this.selectBookmarks(BookmarkSwitchSegement.bookmarks);
final Host duplicate = new HostDictionary<>().deserialize(selected.serialize(SerializerFactory.get()));
// Make sure a new UUID is asssigned for duplicate
@@ -1936,10 +1975,13 @@ public void setDeleteBookmarkButton(NSButton deleteBookmarkButton) {
@Action
public void deleteBookmarkButtonClicked(final ID sender) {
- NSIndexSet iterator = bookmarkTable.selectedRowIndexes();
+ final NSIndexSet iterator = bookmarkTable.selectedRowIndexes();
final List selected = new ArrayList<>();
for(NSUInteger index = iterator.firstIndex(); !index.equals(NSIndexSet.NSNotFound); index = iterator.indexGreaterThanIndex(index)) {
- selected.add(bookmarkModel.getSource().get(index.intValue()));
+ final NSDictionaryHostFilter filter = new NSDictionaryHostFilter(
+ Rococoa.cast(bookmarkTable.itemAtRow(new NSInteger(index.intValue())), NSDictionary.class)
+ );
+ selected.add(bookmarkModel.getSource().stream().filter(filter::accept).findFirst().orElse(null));
}
StringBuilder alertText = new StringBuilder(
LocaleFactory.localizedString("Do you want to delete the selected bookmark?"));
@@ -3790,6 +3832,47 @@ protected Path pathAtRow(int row) {
}
}
+ private abstract static class AbstractBookmarkTableDelegate extends AbstractTableDelegate implements NSOutlineView.Delegate {
+ protected AbstractBookmarkTableDelegate(final NSTableColumn selectedColumn) {
+ super(selectedColumn);
+ }
+
+ @Override
+ public void enterKeyPressed(final ID sender) {
+ this.tableRowDoubleClicked(sender);
+ }
+
+ @Override
+ public void tableColumnClicked(NSTableView view, NSTableColumn tableColumn) {
+
+ }
+
+ @Override
+ public boolean outlineView_shouldExpandItem(final NSOutlineView view, final NSObject item) {
+ return true;
+ }
+
+ @Override
+ public void outlineViewItemWillExpand(final NSNotification notification) {
+
+ }
+
+ @Override
+ public void outlineViewItemDidExpand(final NSNotification notification) {
+
+ }
+
+ @Override
+ public void outlineViewItemWillCollapse(final NSNotification notification) {
+
+ }
+
+ @Override
+ public void outlineViewItemDidCollapse(final NSNotification notification) {
+
+ }
+ }
+
private abstract class AbstractBrowserTableDelegate extends AbstractPathTableDelegate {
private static final double kSwipeGestureLeft = 1.000000;
diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/NSDictionaryHostFilter.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/NSDictionaryHostFilter.java
new file mode 100644
index 00000000000..dc638e5f17b
--- /dev/null
+++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/NSDictionaryHostFilter.java
@@ -0,0 +1,39 @@
+package ch.cyberduck.ui.cocoa.controller;
+
+/*
+ * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.binding.foundation.NSDictionary;
+import ch.cyberduck.binding.foundation.NSObject;
+import ch.cyberduck.core.Host;
+import ch.cyberduck.core.HostFilter;
+
+public final class NSDictionaryHostFilter implements HostFilter {
+
+ private final NSObject uuid;
+
+ public NSDictionaryHostFilter(final NSDictionary item) {
+ this(item.objectForKey("UUID"));
+ }
+
+ public NSDictionaryHostFilter(final NSObject uuid) {
+ this.uuid = uuid;
+ }
+
+ @Override
+ public boolean accept(final Host host) {
+ return host.getUuid().equals(uuid.toString());
+ }
+}
diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/BookmarkTableDataSource.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/BookmarkTableDataSource.java
index a53135f88cf..64c9ef9cab4 100644
--- a/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/BookmarkTableDataSource.java
+++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/BookmarkTableDataSource.java
@@ -15,29 +15,35 @@
* GNU General Public License for more details.
*/
-import ch.cyberduck.binding.ListDataSource;
+import ch.cyberduck.binding.OutlineDataSource;
import ch.cyberduck.binding.application.NSApplication;
import ch.cyberduck.binding.application.NSDraggingInfo;
import ch.cyberduck.binding.application.NSDraggingSource;
import ch.cyberduck.binding.application.NSEvent;
import ch.cyberduck.binding.application.NSImage;
+import ch.cyberduck.binding.application.NSOutlineView;
import ch.cyberduck.binding.application.NSPasteboard;
import ch.cyberduck.binding.application.NSTableColumn;
import ch.cyberduck.binding.application.NSTableView;
import ch.cyberduck.binding.foundation.NSArray;
+import ch.cyberduck.binding.foundation.NSDictionary;
import ch.cyberduck.binding.foundation.NSIndexSet;
import ch.cyberduck.binding.foundation.NSMutableArray;
import ch.cyberduck.binding.foundation.NSMutableDictionary;
import ch.cyberduck.binding.foundation.NSObject;
+import ch.cyberduck.binding.foundation.NSString;
import ch.cyberduck.binding.foundation.NSURL;
import ch.cyberduck.core.*;
import ch.cyberduck.core.exception.AccessDeniedException;
import ch.cyberduck.core.exception.HostParserException;
import ch.cyberduck.core.pasteboard.HostPasteboard;
import ch.cyberduck.core.pool.SessionPool;
+import ch.cyberduck.core.preferences.Preferences;
import ch.cyberduck.core.preferences.PreferencesFactory;
import ch.cyberduck.core.resources.IconCacheFactory;
import ch.cyberduck.core.serializer.HostDictionary;
+import ch.cyberduck.core.serializer.impl.jna.PlistDeserializer;
+import ch.cyberduck.core.serializer.impl.jna.PlistSerializer;
import ch.cyberduck.core.threading.ScheduledThreadPool;
import ch.cyberduck.core.threading.WindowMainAction;
import ch.cyberduck.core.transfer.Transfer;
@@ -46,6 +52,7 @@
import ch.cyberduck.core.transfer.UploadTransfer;
import ch.cyberduck.ui.browser.BookmarkColumn;
import ch.cyberduck.ui.cocoa.controller.BrowserController;
+import ch.cyberduck.ui.cocoa.controller.NSDictionaryHostFilter;
import ch.cyberduck.ui.cocoa.controller.TransferControllerFactory;
import org.apache.commons.lang3.StringUtils;
@@ -59,91 +66,35 @@
import org.rococoa.cocoa.foundation.NSUInteger;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
-public class BookmarkTableDataSource extends ListDataSource {
+public class BookmarkTableDataSource extends OutlineDataSource {
private static final Logger log = LogManager.getLogger(BookmarkTableDataSource.class);
- protected final BrowserController controller;
-
- private HostFilter filter;
-
- private AbstractHostCollection source = AbstractHostCollection.empty();
-
- /**
- * Subset of the original source
- */
- private AbstractHostCollection filtered;
-
- private CollectionListener listener;
+ private final Preferences preferences = PreferencesFactory.get();
+ private final HostPasteboard pasteboard = HostPasteboard.getPasteboard();
+ private final BrowserController controller;
+ private final CollectionListener listener = new BookmarkReloadListener();
private final ScheduledThreadPool timerPool = new ScheduledThreadPool();
- private final HostPasteboard pasteboard
- = HostPasteboard.getPasteboard();
+ private AbstractHostCollection source = AbstractHostCollection.empty();
+ private Map> groups = Collections.emptyMap();
public BookmarkTableDataSource(final BrowserController controller) {
this.controller = controller;
}
public void setSource(final AbstractHostCollection source) {
- this.source.removeListener(listener); //Remove previous listener
+ this.source.removeListener(listener);
this.source = source;
- this.source.addListener(listener = new CollectionListener() {
- private ScheduledFuture> delayed = null;
-
- @Override
- public void collectionLoaded() {
- controller.invoke(new WindowMainAction(controller) {
- @Override
- public void run() {
- controller.reloadBookmarks();
- }
- });
- }
-
- @Override
- public void collectionItemAdded(final Host item) {
- controller.invoke(new WindowMainAction(controller) {
- @Override
- public void run() {
- controller.reloadBookmarks();
- }
- });
- }
-
- @Override
- public void collectionItemRemoved(final Host item) {
- controller.invoke(new WindowMainAction(controller) {
- @Override
- public void run() {
- controller.reloadBookmarks();
- }
- });
- }
-
- @Override
- public void collectionItemChanged(final Host item) {
- if(null != delayed) {
- delayed.cancel(false);
- }
- // Delay to 1 second. When typing changes we don't have to save every iteration.
- delayed = timerPool.schedule(new Runnable() {
- public void run() {
- controller.invoke(new WindowMainAction(controller) {
- @Override
- public void run() {
- controller.reloadBookmarks();
- }
- });
- }
- }, 1L, TimeUnit.SECONDS);
- }
- });
- this.setFilter(null);
+ this.source.addListener(listener);
+ this.groups = source.groups(HostFilter.NONE);
}
@Override
@@ -158,9 +109,8 @@ public void invalidate() {
*
* @param filter Filter for bookmarks
*/
- public void setFilter(HostFilter filter) {
- this.filter = filter;
- this.filtered = null;
+ public void setFilter(final HostFilter filter) {
+ this.groups = source.groups(filter);
}
/**
@@ -169,44 +119,80 @@ public void setFilter(HostFilter filter) {
* @see HostFilter
*/
public AbstractHostCollection getSource() {
- if(null == filter) {
- return source;
+ return source;
+ }
+
+ /**
+ * Invoked by outlineView to return the item for the archived object.
+ */
+ public NSObject outlineView_itemForPersistentObject(NSOutlineView view, NSObject object) {
+ final NSString uuid = Rococoa.cast(object, NSString.class);
+ final NSDictionaryHostFilter filter = new NSDictionaryHostFilter(uuid);
+ final Host selected = source.stream().filter(filter::accept).findFirst().orElse(null);
+ if(null == selected) {
+ // Group row
+ return uuid;
+ }
+ return selected.serialize(new PlistSerializer());
+ }
+
+ /**
+ * Invoked by outlineView to return an archived object for item.
+ */
+ public NSObject outlineView_persistentObjectForItem(NSOutlineView view, NSObject item) {
+ if(item.isKindOfClass(NSString.CLASS)) {
+ return item;
}
- if(null == filtered) {
- filtered = new FilterHostCollection(source, filter);
+ final NSDictionary dict = Rococoa.cast(item, NSDictionary.class);
+ return dict.objectForKey("UUID");
+ }
+
+ @Override
+ public boolean outlineView_isItemExpandable(final NSOutlineView view, final NSObject item) {
+ return item.isKindOfClass(NSString.CLASS);
+ }
+
+ @Override
+ public NSInteger outlineView_numberOfChildrenOfItem(final NSOutlineView view, final NSObject item) {
+ if(null == item) {
+ return new NSInteger(groups.size());
}
- return filtered;
+ return new NSInteger(groups.get(item.toString()).size());
}
@Override
- public NSInteger numberOfRowsInTableView(NSTableView view) {
- return new NSInteger(this.getSource().size());
+ public NSObject outlineView_child_ofItem(final NSOutlineView outlineView, final NSInteger index, final NSObject item) {
+ if(null == item) {
+ final String label = new ArrayList<>(groups.keySet()).get(index.intValue());
+ return NSString.stringWithString(label);
+ }
+ return groups.get(item.toString()).get(index.intValue()).serialize(new PlistSerializer());
}
@Override
- public NSObject tableView_objectValueForTableColumn_row(final NSTableView view, final NSTableColumn tableColumn,
- final NSInteger row) {
- if(row.intValue() >= this.numberOfRowsInTableView(view).intValue()) {
+ public NSObject outlineView_objectValueForTableColumn_byItem(final NSOutlineView view, final NSTableColumn column, final NSObject item) {
+ if(null == item) {
return null;
}
- final String identifier = tableColumn.identifier();
- final Host host = this.getSource().get(row.intValue());
+ if(null == column) {
+ // Group row)
+ if(StringUtils.isBlank(item.toString())) {
+ // Default group of bookmarks with no label assigned
+ return NSString.stringWithString(LocaleFactory.localizedString("Default"));
+ }
+ return item;
+ }
+ final NSMutableDictionary dict = Rococoa.cast(item, NSMutableDictionary.class);
+ final Host host = new HostDictionary(new DeserializerFactory(PlistDeserializer.class)).deserialize(dict);
+ final String identifier = column.identifier();
if(identifier.equals(BookmarkColumn.icon.name())) {
- return IconCacheFactory.get().iconNamed(host.getProtocol().disk(),
- PreferencesFactory.get().getInteger("bookmark.icon.size"));
+ return IconCacheFactory.get().iconNamed(host.getProtocol().disk(), preferences.getInteger("bookmark.icon.size"));
}
if(identifier.equals(BookmarkColumn.bookmark.name())) {
- final NSMutableDictionary dict = NSMutableDictionary.dictionary();
- dict.setObjectForKey(BookmarkNameProvider.toString(host), "Nickname");
- dict.setObjectForKey(host.getHostname(), "Hostname");
- if(StringUtils.isNotBlank(host.getCredentials().getUsername())) {
- dict.setObjectForKey(host.getCredentials().getUsername(), "Username");
- }
- final String comment = this.getSource().getComment(host);
- if(StringUtils.isNotBlank(comment)) {
- dict.setObjectForKey(comment, "Comment");
+ if(null == dict.objectForKey("Nickname")) {
+ dict.setObjectForKey(BookmarkNameProvider.toString(host), "Nickname");
}
- return dict;
+ return item;
}
if(identifier.equals(BookmarkColumn.status.name())) {
final SessionPool session = controller.getSession();
@@ -238,10 +224,9 @@ public boolean ignoreModifierKeysWhileDragging() {
}
@Override
- public NSUInteger tableView_validateDrop_proposedRow_proposedDropOperation(final NSTableView view, final NSDraggingInfo info,
- final NSInteger row, final NSUInteger operation) {
+ public NSUInteger outlineView_validateDrop_proposedItem_proposedChildIndex(final NSOutlineView view, final NSDraggingInfo info, final NSObject destination, final NSInteger row) {
NSPasteboard draggingPasteboard = info.draggingPasteboard();
- if(!this.getSource().allowsEdit()) {
+ if(!source.allowsEdit()) {
// Do not allow drags for non writable collections
return NSDraggingInfo.NSDragOperationNone;
}
@@ -304,22 +289,13 @@ else if(!pasteboard.isEmpty()) {
return NSDraggingInfo.NSDragOperationNone;
}
- /**
- * @param info contains details on this dragging operation.
- * @param row The proposed location is row and action is operation. The data source should incorporate the data
- * from the dragging pasteboard at this time.
- * @see NSTableView.DataSource Invoked by view when the mouse button is released over a table view that previously
- * decided to allow a drop.
- */
@Override
- public boolean tableView_acceptDrop_row_dropOperation(final NSTableView view, final NSDraggingInfo info,
- final NSInteger row, final NSUInteger operation) {
- NSPasteboard draggingPasteboard = info.draggingPasteboard();
+ public boolean outlineView_acceptDrop_item_childIndex(final NSOutlineView view, final NSDraggingInfo info, final NSObject item, final NSInteger row) {
+ final NSPasteboard draggingPasteboard = info.draggingPasteboard();
if(log.isDebugEnabled()) {
log.debug(String.format("Accept drop at row %s", row));
}
view.deselectAll(null);
- final AbstractHostCollection source = this.getSource();
if(draggingPasteboard.availableTypeFromArray(NSArray.arrayWithObject(NSPasteboard.StringPboardType)) != null) {
String o = draggingPasteboard.stringForType(NSPasteboard.StringPboardType);
if(null == o) {
@@ -344,7 +320,7 @@ else if(draggingPasteboard.availableTypeFromArray(NSArray.arrayWithObject(NSPast
if(object.isKindOfClass(NSArray.CLASS)) {
final NSArray elements = Rococoa.cast(object, NSArray.class);
// If regular files are dropped, these will be uploaded to the dropped bookmark location
- final List uploads = new ArrayList();
+ final List uploads = new ArrayList<>();
Host host = null;
for(int i = 0; i < elements.count().intValue(); i++) {
final String filename = elements.objectAtIndex(new NSUInteger(i)).toString();
@@ -411,7 +387,7 @@ else if(draggingPasteboard.availableTypeFromArray(NSArray.arrayWithObject(NSPast
}
else if(!pasteboard.isEmpty()) {
if(info.draggingSourceOperationMask().intValue() == NSDraggingInfo.NSDragOperationCopy.intValue()) {
- List duplicates = new ArrayList();
+ List duplicates = new ArrayList<>();
for(Host bookmark : pasteboard) {
final Host duplicate = new HostDictionary<>().deserialize(bookmark.serialize(SerializerFactory.get()));
// Make sure a new UUID is assigned for duplicate
@@ -486,19 +462,13 @@ public NSUInteger draggingSourceOperationMaskForLocal(final boolean local) {
return new NSUInteger(NSDraggingInfo.NSDragOperationCopy.intValue() | NSDraggingInfo.NSDragOperationDelete.intValue());
}
- /**
- * @param rowIndexes is the list of row numbers that will be participating in the drag.
- * @return To refuse the drag, return false. To start a drag, return true and place the drag data onto pboard (data,
- * owner, and so on).
- * @see NSTableView.DataSource Invoked by view after it has been determined that a drag should begin, but before the
- * drag has been started. The drag image and other drag-related information will be set up and provided by the table
- * view once this call returns with true.
- */
@Override
- public boolean tableView_writeRowsWithIndexes_toPasteboard(final NSTableView view, final NSIndexSet rowIndexes,
- final NSPasteboard pboard) {
- for(NSUInteger index = rowIndexes.firstIndex(); !index.equals(NSIndexSet.NSNotFound); index = rowIndexes.indexGreaterThanIndex(index)) {
- pasteboard.add(this.getSource().get(index.intValue()));
+ public boolean outlineView_writeItems_toPasteboard(final NSOutlineView view, final NSArray items, final NSPasteboard pboard) {
+ for(int i = 0; i < items.count().intValue(); i++) {
+ if(items.objectAtIndex(new NSUInteger(i)).isKindOfClass(NSDictionary.CLASS)) {
+ final NSDictionary dict = Rococoa.cast(items.objectAtIndex(new NSUInteger(i)), NSDictionary.class);
+ pasteboard.add(new HostDictionary(new DeserializerFactory(PlistDeserializer.class)).deserialize(dict));
+ }
}
NSEvent event = NSApplication.sharedApplication().currentEvent();
if(event != null) {
@@ -546,4 +516,55 @@ public NSArray namesOfPromisedFilesDroppedAtDestination(final NSURL dropDestinat
return promisedDragNames;
}
+ private final class BookmarkReloadListener implements CollectionListener {
+ private ScheduledFuture> delayed = null;
+
+ @Override
+ public void collectionLoaded() {
+ controller.invoke(new WindowMainAction(controller) {
+ @Override
+ public void run() {
+ controller.reloadBookmarks();
+ }
+ });
+ }
+
+ @Override
+ public void collectionItemAdded(final Host item) {
+ controller.invoke(new WindowMainAction(controller) {
+ @Override
+ public void run() {
+ controller.reloadBookmarks();
+ }
+ });
+ }
+
+ @Override
+ public void collectionItemRemoved(final Host item) {
+ controller.invoke(new WindowMainAction(controller) {
+ @Override
+ public void run() {
+ controller.reloadBookmarks();
+ }
+ });
+ }
+
+ @Override
+ public void collectionItemChanged(final Host item) {
+ if(null != delayed) {
+ delayed.cancel(false);
+ }
+ // Delay to 1 second. When typing changes we don't have to save every iteration.
+ delayed = timerPool.schedule(new Runnable() {
+ public void run() {
+ controller.invoke(new WindowMainAction(controller) {
+ @Override
+ public void run() {
+ controller.reloadBookmarks();
+ }
+ });
+ }
+ }, 1L, TimeUnit.SECONDS);
+ }
+ }
}
diff --git a/osx/src/main/objc/CDOutlineView.m b/osx/src/main/objc/CDOutlineView.m
index 1f721340ffc..c2b13e8fc3a 100644
--- a/osx/src/main/objc/CDOutlineView.m
+++ b/osx/src/main/objc/CDOutlineView.m
@@ -161,9 +161,10 @@ - (NSMenu *) menuForEvent:(NSEvent *) event
{
NSPoint where = [self convertPoint:[event locationInWindow] fromView:nil];
NSInteger row = [self rowAtPoint:where];
+ NSObject *item = [self itemAtRow:row];
if(row >= 0) {
- if([[self delegate] respondsToSelector:@selector(tableView:shouldSelectRow:)]) {
- if([[self delegate] tableView:self shouldSelectRow:row])
+ if([[self delegate] respondsToSelector:@selector(outlineView:shouldSelectItem:)]) {
+ if([[self delegate] outlineView:self shouldSelectItem:item])
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:[self isRowSelected:row]];
}
else {