Merge pull request #64 in ITERATE/cyberduck from feature/windows/9074 to master
[cyberduck.git] / source / ch / cyberduck / ui / cocoa / BrowserTableDataSource.java
blob73461373feb055d5125eebbb4939b42f1d172865
1 package ch.cyberduck.ui.cocoa;
3 /*
4 * Copyright (c) 2005 David Kocher. All rights reserved.
5 * http://cyberduck.ch/
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * Bug fixes, suggestions and comments should be sent to:
18 * dkocher@cyberduck.ch
21 import ch.cyberduck.binding.ProxyController;
22 import ch.cyberduck.binding.application.NSApplication;
23 import ch.cyberduck.binding.application.NSDraggingInfo;
24 import ch.cyberduck.binding.application.NSDraggingSource;
25 import ch.cyberduck.binding.application.NSEvent;
26 import ch.cyberduck.binding.application.NSImage;
27 import ch.cyberduck.binding.application.NSPasteboard;
28 import ch.cyberduck.binding.application.NSTableView;
29 import ch.cyberduck.binding.foundation.NSArray;
30 import ch.cyberduck.binding.foundation.NSAttributedString;
31 import ch.cyberduck.binding.foundation.NSFileManager;
32 import ch.cyberduck.binding.foundation.NSMutableArray;
33 import ch.cyberduck.binding.foundation.NSObject;
34 import ch.cyberduck.binding.foundation.NSString;
35 import ch.cyberduck.binding.foundation.NSURL;
36 import ch.cyberduck.core.Acl;
37 import ch.cyberduck.core.AttributedList;
38 import ch.cyberduck.core.Cache;
39 import ch.cyberduck.core.HostParser;
40 import ch.cyberduck.core.Local;
41 import ch.cyberduck.core.LocalFactory;
42 import ch.cyberduck.core.LocaleFactory;
43 import ch.cyberduck.core.Path;
44 import ch.cyberduck.core.Permission;
45 import ch.cyberduck.core.ProtocolFactory;
46 import ch.cyberduck.core.UserDateFormatterFactory;
47 import ch.cyberduck.core.date.AbstractUserDateFormatter;
48 import ch.cyberduck.core.exception.AccessDeniedException;
49 import ch.cyberduck.core.features.Touch;
50 import ch.cyberduck.core.formatter.SizeFormatter;
51 import ch.cyberduck.core.formatter.SizeFormatterFactory;
52 import ch.cyberduck.core.local.FileDescriptor;
53 import ch.cyberduck.core.local.FileDescriptorFactory;
54 import ch.cyberduck.core.local.IconServiceFactory;
55 import ch.cyberduck.core.local.LocalTouchFactory;
56 import ch.cyberduck.core.pasteboard.PathPasteboard;
57 import ch.cyberduck.core.pasteboard.PathPasteboardFactory;
58 import ch.cyberduck.core.preferences.Preferences;
59 import ch.cyberduck.core.preferences.PreferencesFactory;
60 import ch.cyberduck.core.resources.IconCache;
61 import ch.cyberduck.core.resources.IconCacheFactory;
62 import ch.cyberduck.core.transfer.CopyTransfer;
63 import ch.cyberduck.core.transfer.DownloadTransfer;
64 import ch.cyberduck.core.transfer.TransferItem;
65 import ch.cyberduck.core.transfer.TransferStatus;
66 import ch.cyberduck.core.transfer.UploadTransfer;
67 import ch.cyberduck.ui.browser.Column;
69 import org.apache.commons.collections.map.LRUMap;
70 import org.apache.commons.lang3.StringUtils;
71 import org.apache.log4j.Logger;
72 import org.rococoa.Rococoa;
73 import org.rococoa.cocoa.foundation.NSInteger;
74 import org.rococoa.cocoa.foundation.NSPoint;
75 import org.rococoa.cocoa.foundation.NSRect;
76 import org.rococoa.cocoa.foundation.NSSize;
77 import org.rococoa.cocoa.foundation.NSUInteger;
79 import java.util.ArrayList;
80 import java.util.Collections;
81 import java.util.EnumSet;
82 import java.util.HashMap;
83 import java.util.List;
84 import java.util.Map;
85 import java.util.Set;
87 /**
88 * @version $Id$
90 public abstract class BrowserTableDataSource extends ProxyController implements NSDraggingSource {
91 private static final Logger log = Logger.getLogger(BrowserTableDataSource.class);
93 private SizeFormatter sizeFormatter = SizeFormatterFactory.get();
95 private AbstractUserDateFormatter dateFormatter = UserDateFormatterFactory.get();
97 private IconCache<NSImage> icons = IconCacheFactory.<NSImage>get();
99 private FileDescriptor descriptor = FileDescriptorFactory.get();
101 private final Preferences preferences = PreferencesFactory.get();
103 private final Map<Item, NSAttributedString> attributed = new LRUMap(
104 preferences.getInteger("browser.model.cache.size")
107 protected BrowserController controller;
109 protected Cache<Path> cache;
111 private static final class Item {
112 private Path file;
113 private String column;
115 public Item(final Path file, final String column) {
116 this.file = file;
117 this.column = column;
120 @Override
121 public boolean equals(final Object o) {
122 if(this == o) {
123 return true;
125 if(o == null || getClass() != o.getClass()) {
126 return false;
128 final Item item = (Item) o;
129 if(column != null ? !column.equals(item.column) : item.column != null) {
130 return false;
132 if(file != null ? !file.equals(item.file) : item.file != null) {
133 return false;
135 return true;
138 @Override
139 public int hashCode() {
140 int result = file != null ? file.hashCode() : 0;
141 result = 31 * result + (column != null ? column.hashCode() : 0);
142 return result;
145 @Override
146 public String toString() {
147 final StringBuilder sb = new StringBuilder("Item{");
148 sb.append("file=").append(file);
149 sb.append(", column='").append(column).append('\'');
150 sb.append('}');
151 return sb.toString();
155 protected BrowserTableDataSource(final BrowserController controller, final Cache<Path> cache) {
156 this.controller = controller;
157 this.cache = cache;
161 * Tell the browser view to reload the data
163 * @param folders Changed files
165 public void render(final NSTableView view, final List<Path> folders) {
166 attributed.clear();
169 protected AttributedList<Path> get(final Path directory) {
170 return cache.get(directory).filter(controller.getComparator(), controller.getFilter());
173 public int indexOf(NSTableView view, Path file) {
174 return this.get(controller.workdir()).indexOf(file);
177 protected void setObjectValueForItem(final Path item, final NSObject value, final String identifier) {
178 if(log.isDebugEnabled()) {
179 log.debug(String.format("Set new value %s for item %s", value, item));
181 if(identifier.equals(Column.filename.name())) {
182 if(StringUtils.isNotBlank(value.toString()) && !item.getName().equals(value.toString())) {
183 final Path renamed = new Path(
184 item.getParent(), value.toString(), item.getType());
185 new MoveController(controller).rename(item, renamed);
190 protected NSImage iconForPath(final Path item) {
191 if(item.isVolume()) {
192 return icons.volumeIcon(controller.getSession().getHost().getProtocol(), 16);
194 return icons.fileIcon(item, 16);
197 protected NSObject objectValueForItem(final Path item, final String identifier) {
198 if(null == item) {
199 return null;
201 if(log.isTraceEnabled()) {
202 log.trace("objectValueForItem:" + item.getAbsolute());
204 if(identifier.equals(Column.icon.name())) {
205 return this.iconForPath(item);
207 final Item key = new Item(item, identifier);
208 // Query second level cache with view items
209 NSAttributedString value = attributed.get(key);
210 if(null != value) {
211 return value;
213 if(log.isTraceEnabled()) {
214 log.trace(String.format("Lookup failed for %s in cache", key));
216 if(identifier.equals(Column.filename.name())) {
217 value = NSAttributedString.attributedStringWithAttributes(
218 item.getName(),
219 TableCellAttributes.browserFontLeftAlignment());
221 else if(identifier.equals(Column.size.name())) {
222 value = NSAttributedString.attributedStringWithAttributes(
223 sizeFormatter.format(item.attributes().getSize()),
224 TableCellAttributes.browserFontRightAlignment());
226 else if(identifier.equals(Column.modified.name())) {
227 value = NSAttributedString.attributedStringWithAttributes(
228 dateFormatter.getShortFormat(item.attributes().getModificationDate(),
229 preferences.getBoolean("browser.date.natural")),
230 TableCellAttributes.browserFontLeftAlignment()
233 else if(identifier.equals(Column.owner.name())) {
234 value = NSAttributedString.attributedStringWithAttributes(
235 StringUtils.isBlank(item.attributes().getOwner()) ?
236 LocaleFactory.localizedString("Unknown") : item.attributes().getOwner(),
237 TableCellAttributes.browserFontLeftAlignment());
239 else if(identifier.equals(Column.group.name())) {
240 value = NSAttributedString.attributedStringWithAttributes(
241 StringUtils.isBlank(item.attributes().getGroup()) ?
242 LocaleFactory.localizedString("Unknown") : item.attributes().getGroup(),
243 TableCellAttributes.browserFontLeftAlignment());
245 else if(identifier.equals(Column.permission.name())) {
246 final Acl acl = item.attributes().getAcl();
247 if(!Acl.EMPTY.equals(acl)) {
248 final StringBuilder s = new StringBuilder();
249 for(Map.Entry<Acl.User, Set<Acl.Role>> entry : acl.entrySet()) {
250 s.append(String.format("%s%s:%s", s.length() == 0 ? StringUtils.EMPTY : ", ",
251 entry.getKey().getDisplayName(), entry.getValue()));
253 value = NSAttributedString.attributedStringWithAttributes(s.toString(),
254 TableCellAttributes.browserFontLeftAlignment());
256 else {
257 final Permission permission = item.attributes().getPermission();
258 value = NSAttributedString.attributedStringWithAttributes(
259 permission.toString(),
260 TableCellAttributes.browserFontLeftAlignment());
263 else if(identifier.equals(Column.kind.name())) {
264 value = NSAttributedString.attributedStringWithAttributes(
265 descriptor.getKind(item),
266 TableCellAttributes.browserFontLeftAlignment());
268 else if(identifier.equals(Column.extension.name())) {
269 value = NSAttributedString.attributedStringWithAttributes(
270 item.isFile() ? StringUtils.isNotBlank(item.getExtension()) ? item.getExtension() :
271 LocaleFactory.localizedString("None") : LocaleFactory.localizedString("None"),
272 TableCellAttributes.browserFontLeftAlignment());
274 else if(identifier.equals(Column.region.name())) {
275 value = NSAttributedString.attributedStringWithAttributes(
276 StringUtils.isNotBlank(item.attributes().getRegion()) ? item.attributes().getRegion() :
277 LocaleFactory.localizedString("Unknown"),
278 TableCellAttributes.browserFontLeftAlignment());
280 else if(identifier.equals(Column.version.name())) {
281 value = NSAttributedString.attributedStringWithAttributes(
282 StringUtils.isNotBlank(item.attributes().getVersionId()) ? item.attributes().getVersionId() :
283 LocaleFactory.localizedString("None"),
284 TableCellAttributes.browserFontLeftAlignment());
286 else {
287 throw new IllegalArgumentException(String.format("Unknown identifier %s", identifier));
289 attributed.put(key, value);
290 return value;
294 * Sets whether the use of modifier keys should have an effect on the type of operation performed.
296 * @return Always false
297 * @see NSDraggingSource
299 @Override
300 public boolean ignoreModifierKeysWhileDragging() {
301 // If this method is not implemented or returns false, the user can tailor the drag operation by
302 // holding down a modifier key during the drag.
303 return false;
307 * @param local indicates that the candidate destination object (the window or view over which the dragged
308 * image is currently poised) is in the same application as the source, while a NO value indicates that
309 * the destination object is in a different application
310 * @return A mask, created by combining the dragging operations listed in the NSDragOperation section of
311 * NSDraggingInfo protocol reference using the C bitwise OR operator.If the source does not permit
312 * any dragging operations, it should return NSDragOperationNone.
313 * @see NSDraggingSource
315 @Override
316 public NSUInteger draggingSourceOperationMaskForLocal(final boolean local) {
317 if(log.isDebugEnabled()) {
318 log.debug(String.format("Request dragging source operation mask for %s", local));
320 if(local) {
321 // Move or copy within the browser
322 return new NSUInteger(NSDraggingInfo.NSDragOperationMove.intValue() | NSDraggingInfo.NSDragOperationCopy.intValue());
324 // Copy to a thirdparty application or drag to trash to delete
325 return new NSUInteger(NSDraggingInfo.NSDragOperationCopy.intValue() | NSDraggingInfo.NSDragOperationDelete.intValue());
329 * @param view Table
330 * @param destination A directory or null to mount an URL
331 * @param info Dragging pasteboard
332 * @return True if accepted
334 public boolean acceptDrop(final NSTableView view, final Path destination, final NSDraggingInfo info) {
335 if(log.isDebugEnabled()) {
336 log.debug(String.format("Accept drop for destination %s", destination));
338 if(info.draggingPasteboard().availableTypeFromArray(NSArray.arrayWithObject(NSPasteboard.URLPboardType)) != null) {
339 final NSObject o = info.draggingPasteboard().propertyListForType(NSPasteboard.URLPboardType);
340 // Mount .webloc URLs dragged to browser window
341 if(o != null) {
342 if(o.isKindOfClass(Rococoa.createClass("NSArray", NSArray._Class.class))) {
343 final NSArray elements = Rococoa.cast(o, NSArray.class);
344 for(int i = 0; i < elements.count().intValue(); i++) {
345 if(ProtocolFactory.isURL(elements.objectAtIndex(new NSUInteger(i)).toString())) {
346 controller.mount(HostParser.parse(elements.objectAtIndex(new NSUInteger(i)).toString()));
347 return true;
353 if(controller.isMounted()) {
354 if(info.draggingPasteboard().availableTypeFromArray(NSArray.arrayWithObject(NSPasteboard.FilenamesPboardType)) != null) {
355 final NSObject o = info.draggingPasteboard().propertyListForType(NSPasteboard.FilenamesPboardType);
356 // A file drag has been received by another application; upload to the dragged directory
357 if(o != null) {
358 if(o.isKindOfClass(Rococoa.createClass("NSArray", NSArray._Class.class))) {
359 final NSArray elements = Rococoa.cast(o, NSArray.class);
360 final List<TransferItem> roots = new ArrayList<TransferItem>();
361 for(int i = 0; i < elements.count().intValue(); i++) {
362 final Local local = LocalFactory.get(elements.objectAtIndex(new NSUInteger(i)).toString());
363 roots.add(new TransferItem(new Path(destination, local.getName(),
364 local.isDirectory() ? EnumSet.of(Path.Type.directory) : EnumSet.of(Path.Type.file)), local));
366 controller.transfer(new UploadTransfer(controller.getSession().getHost(), roots));
367 return true;
370 return false;
372 final List<PathPasteboard> pasteboards = PathPasteboardFactory.allPasteboards();
373 for(PathPasteboard pasteboard : pasteboards) {
374 // A file dragged within the browser has been received
375 if(pasteboard.isEmpty()) {
376 continue;
378 if(info.draggingSourceOperationMask().intValue() == NSDraggingInfo.NSDragOperationCopy.intValue()
379 || pasteboard.getSession().getHost().compareTo(controller.getSession().getHost()) != 0) {
380 // Drag to browser windows with different session or explicit copy requested by user.
381 final Map<Path, Path> files = new HashMap<Path, Path>();
382 for(Path file : pasteboard) {
383 files.put(file, new Path(destination, file.getName(), file.getType()));
385 controller.transfer(new CopyTransfer(pasteboard.getSession().getHost(),
386 controller.getSession().getHost(),
387 files), new ArrayList<Path>(files.values()), false);
389 else {
390 // The file should be renamed
391 final Map<Path, Path> files = new HashMap<Path, Path>();
392 for(Path next : pasteboard) {
393 Path renamed = new Path(
394 destination, next.getName(), next.getType());
395 files.put(next, renamed);
397 new MoveController(controller).rename(files);
399 pasteboard.clear();
401 return true;
403 return false;
407 * @param view Table
408 * @param destination A directory or null to mount an URL
409 * @param row Index
410 * @param info Dragging pasteboard
411 * @return Drag operation
413 public NSUInteger validateDrop(final NSTableView view, final Path destination, final NSInteger row, final NSDraggingInfo info) {
414 if(log.isDebugEnabled()) {
415 log.debug(String.format("Validate drop for destination %s", destination));
417 if(info.draggingPasteboard().availableTypeFromArray(NSArray.arrayWithObject(NSPasteboard.URLPboardType)) != null) {
418 // Dragging URLs to mount new session
419 final NSObject o = info.draggingPasteboard().propertyListForType(NSPasteboard.URLPboardType);
420 if(o != null) {
421 if(o.isKindOfClass(Rococoa.createClass("NSArray", NSArray._Class.class))) {
422 final NSArray elements = Rococoa.cast(o, NSArray.class);
423 for(int i = 0; i < elements.count().intValue(); i++) {
424 // Validate if .webloc URLs dragged to browser window have a known protocol
425 if(ProtocolFactory.isURL(elements.objectAtIndex(new NSUInteger(i)).toString())) {
426 // Passing a value of –1 for row, and NSTableViewDropOn as the operation causes the
427 // entire table view to be highlighted rather than a specific row.
428 view.setDropRow(new NSInteger(-1), NSTableView.NSTableViewDropOn);
429 return NSDraggingInfo.NSDragOperationCopy;
431 else {
432 log.warn(String.format("Protocol not supported for URL %s", elements.objectAtIndex(new NSUInteger(i)).toString()));
437 else {
438 log.warn("URL dragging pasteboard is empty.");
441 if(controller.isMounted()) {
442 if(null == destination) {
443 log.warn("Dragging destination is null.");
444 return NSDraggingInfo.NSDragOperationNone;
446 // Files dragged form other application
447 if(info.draggingPasteboard().availableTypeFromArray(NSArray.arrayWithObject(NSPasteboard.FilenamesPboardType)) != null) {
448 this.setDropRowAndDropOperation(view, destination, row);
449 final NSObject o = info.draggingPasteboard().propertyListForType(NSPasteboard.FilenamesPboardType);
450 if(o != null) {
451 if(o.isKindOfClass(Rococoa.createClass("NSArray", NSArray._Class.class))) {
452 final NSArray elements = Rococoa.cast(o, NSArray.class);
453 for(int i = 0; i < elements.count().intValue(); i++) {
454 final Local local = LocalFactory.get(elements.objectAtIndex(new NSUInteger(i)).toString());
455 if(local.isFile()) {
456 final Touch feature = controller.getSession().getFeature(Touch.class);
457 if(!feature.isSupported(destination)) {
458 // Target file system does not support creating files. Creating files is not supported
459 // for example in root of cloud storage accounts.
460 return NSDraggingInfo.NSDragOperationNone;
466 return NSDraggingInfo.NSDragOperationCopy;
468 // Files dragged from browser
469 for(Path next : controller.getPasteboard()) {
470 if(destination.equals(next)) {
471 // Do not allow dragging onto myself
472 return NSDraggingInfo.NSDragOperationNone;
474 if(next.isDirectory() && destination.isChild(next)) {
475 // Do not allow dragging a directory into its own containing items
476 return NSDraggingInfo.NSDragOperationNone;
478 if(next.isFile() && next.getParent().equals(destination)) {
479 // Moving a file to the same destination makes no sense
480 return NSDraggingInfo.NSDragOperationNone;
483 if(log.isDebugEnabled()) {
484 log.debug(String.format("Drag operation mask is %d", info.draggingSourceOperationMask().intValue()));
486 this.setDropRowAndDropOperation(view, destination, row);
487 final List<PathPasteboard> pasteboards = PathPasteboardFactory.allPasteboards();
488 for(PathPasteboard pasteboard : pasteboards) {
489 if(pasteboard.isEmpty()) {
490 continue;
492 if(pasteboard.getSession().getHost().compareTo(controller.getSession().getHost()) == 0) {
493 if(info.draggingSourceOperationMask().intValue() == NSDraggingInfo.NSDragOperationCopy.intValue()) {
494 // Explicit copy requested if drag operation is already NSDragOperationCopy. User is pressing the option key.
495 return NSDraggingInfo.NSDragOperationCopy;
497 // Defaulting to move for same session
498 return NSDraggingInfo.NSDragOperationMove;
500 else {
501 // If copying between sessions is supported
502 return NSDraggingInfo.NSDragOperationCopy;
506 return NSDraggingInfo.NSDragOperationNone;
509 private void setDropRowAndDropOperation(final NSTableView view, final Path destination, final NSInteger row) {
510 if(destination.equals(controller.workdir())) {
511 log.debug("setDropRowAndDropOperation:-1");
512 // Passing a value of –1 for row, and NSTableViewDropOn as the operation causes the
513 // entire table view to be highlighted rather than a specific row.
514 view.setDropRow(new NSInteger(-1), NSTableView.NSTableViewDropOn);
516 else if(destination.isDirectory()) {
517 log.debug("setDropRowAndDropOperation:" + row.intValue());
518 view.setDropRow(row, NSTableView.NSTableViewDropOn);
522 public boolean writeItemsToPasteBoard(final NSTableView view, final List<Path> selected, final NSPasteboard pboard) {
523 if(log.isDebugEnabled()) {
524 log.debug(String.format("Write items to pasteboard %s", pboard));
526 if(controller.isMounted()) {
527 if(selected.size() > 0) {
528 // The fileTypes argument is the list of fileTypes being promised.
529 // The array elements can consist of file extensions and HFS types encoded
530 // with the NSHFSFileTypes method fileTypeForHFSTypeCode. If promising a directory
531 // of files, only include the top directory in the array.
532 final NSMutableArray fileTypes = NSMutableArray.array();
533 final PathPasteboard pasteboard = controller.getPasteboard();
534 for(final Path f : selected) {
535 if(f.isFile()) {
536 if(StringUtils.isNotEmpty(f.getExtension())) {
537 fileTypes.addObject(NSString.stringWithString(f.getExtension()));
539 else {
540 fileTypes.addObject(NSString.stringWithString(NSFileManager.NSFileTypeRegular));
543 else if(f.isDirectory()) {
544 fileTypes.addObject(NSString.stringWithString("'fldr'")); //NSFileTypeForHFSTypeCode('fldr')
546 else {
547 fileTypes.addObject(NSString.stringWithString(NSFileManager.NSFileTypeUnknown));
549 // Writing data for private use when the item gets dragged to the transfer queue.
550 pasteboard.add(f);
552 NSEvent event = NSApplication.sharedApplication().currentEvent();
553 if(event != null) {
554 NSPoint dragPosition = view.convertPoint_fromView(event.locationInWindow(), null);
555 NSRect imageRect = new NSRect(new NSPoint(dragPosition.x.doubleValue() - 16, dragPosition.y.doubleValue() - 16), new NSSize(32, 32));
556 view.dragPromisedFilesOfTypes(fileTypes, imageRect, this.id(), true, event);
557 // @see http://www.cocoabuilder.com/archive/message/cocoa/2003/5/15/81424
558 return true;
562 return false;
565 @Override
566 public void draggedImage_beganAt(final NSImage image, final NSPoint point) {
567 if(log.isTraceEnabled()) {
568 log.trace("draggedImage_beganAt:" + point);
573 * See http://www.cocoabuilder.com/archive/message/2005/10/5/118857
575 @Override
576 public void draggedImage_endedAt_operation(final NSImage image, final NSPoint point, final NSUInteger operation) {
577 if(log.isTraceEnabled()) {
578 log.trace("draggedImage_endedAt_operation:" + operation);
580 final PathPasteboard pasteboard = controller.getPasteboard();
581 if(NSDraggingInfo.NSDragOperationDelete.intValue() == operation.intValue()) {
582 new DeleteController(controller).delete(pasteboard);
584 pasteboard.clear();
587 @Override
588 public void draggedImage_movedTo(final NSImage image, final NSPoint point) {
589 if(log.isTraceEnabled()) {
590 log.trace("draggedImage_movedTo:" + point);
595 * @return the names (not full paths) of the files that the receiver promises to create at dropDestination.
596 * This method is invoked when the drop has been accepted by the destination and the destination, in the case of another
597 * Cocoa application, invokes the NSDraggingInfo method namesOfPromisedFilesDroppedAtDestination. For long operations,
598 * you can cache dropDestination and defer the creation of the files until the finishedDraggingImage method to avoid
599 * blocking the destination application.
601 @Override
602 public NSArray namesOfPromisedFilesDroppedAtDestination(final NSURL url) {
603 if(log.isDebugEnabled()) {
604 log.debug(String.format("Return names of promised files dropped at %s", url));
606 NSMutableArray promisedDragNames = NSMutableArray.array();
607 if(null != url) {
608 final Local destination = LocalFactory.get(url.path());
609 final PathPasteboard pasteboard = controller.getPasteboard();
610 final List<TransferItem> downloads = new ArrayList<TransferItem>();
611 for(Path p : pasteboard) {
612 downloads.add(new TransferItem(p, LocalFactory.get(destination, p.getName())));
613 // Add to returned path names
614 promisedDragNames.addObject(NSString.stringWithString(p.getName()));
616 if(downloads.size() == 1) {
617 if(downloads.iterator().next().remote.isFile()) {
618 final Local file = downloads.iterator().next().local;
619 if(!file.exists()) {
620 try {
621 LocalTouchFactory.get().touch(file);
622 IconServiceFactory.get().set(file, new TransferStatus());
624 catch(AccessDeniedException e) {
625 log.warn(String.format("Failure creating file %s %s", file, e.getMessage()));
629 if(downloads.iterator().next().remote.isDirectory()) {
630 final Local file = downloads.iterator().next().local;
631 if(!file.exists()) {
632 try {
633 file.mkdir();
635 catch(AccessDeniedException e) {
636 log.warn(e.getMessage());
641 // kTemporaryFolderType
642 final boolean dock = destination.equals(LocalFactory.get("~/Library/Caches/TemporaryItems"));
643 if(dock) {
644 for(Path p : pasteboard) {
645 // Drag to application icon in dock.
646 controller.edit(p);
649 else {
650 final DownloadTransfer transfer = new DownloadTransfer(controller.getSession().getHost(), downloads);
651 controller.transfer(transfer, Collections.<Path>emptyList());
653 pasteboard.clear();
655 // Filenames
656 return promisedDragNames;