Fix transcript in transfer window.
[cyberduck.git] / source / ch / cyberduck / ui / cocoa / BrowserTableDataSource.java
blob8f5e12bb7fe96e4abdef6865b31c4717e001b648
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 abstract void render(final NSTableView view, final List<Path> folders);
167 protected AttributedList<Path> get(final Path directory) {
168 return cache.get(directory).filter(controller.getComparator(), controller.getFilter());
171 public int indexOf(NSTableView view, Path file) {
172 return this.get(controller.workdir()).indexOf(file);
175 protected void setObjectValueForItem(final Path item, final NSObject value, final String identifier) {
176 if(log.isDebugEnabled()) {
177 log.debug(String.format("Set new value %s for item %s", value, item));
179 if(identifier.equals(Column.filename.name())) {
180 if(StringUtils.isNotBlank(value.toString()) && !item.getName().equals(value.toString())) {
181 final Path renamed = new Path(
182 item.getParent(), value.toString(), item.getType());
183 controller.renamePath(item, renamed);
188 protected NSImage iconForPath(final Path item) {
189 if(item.isVolume()) {
190 return icons.volumeIcon(controller.getSession().getHost().getProtocol(), 16);
192 return icons.fileIcon(item, 16);
195 protected NSObject objectValueForItem(final Path item, final String identifier) {
196 if(null == item) {
197 return null;
199 if(log.isTraceEnabled()) {
200 log.trace("objectValueForItem:" + item.getAbsolute());
202 if(identifier.equals(Column.icon.name())) {
203 return this.iconForPath(item);
205 final Item key = new Item(item, identifier);
206 NSAttributedString value = attributed.get(key);
207 if(null != value) {
208 return value;
210 if(log.isDebugEnabled()) {
211 log.debug(String.format("Lookup failed for %s in cache", key));
213 if(identifier.equals(Column.filename.name())) {
214 value = NSAttributedString.attributedStringWithAttributes(
215 item.getName(),
216 TableCellAttributes.browserFontLeftAlignment());
218 else if(identifier.equals(Column.size.name())) {
219 value = NSAttributedString.attributedStringWithAttributes(
220 sizeFormatter.format(item.attributes().getSize()),
221 TableCellAttributes.browserFontRightAlignment());
223 else if(identifier.equals(Column.modified.name())) {
224 value = NSAttributedString.attributedStringWithAttributes(
225 dateFormatter.getShortFormat(item.attributes().getModificationDate(),
226 preferences.getBoolean("browser.date.natural")),
227 TableCellAttributes.browserFontLeftAlignment()
230 else if(identifier.equals(Column.owner.name())) {
231 value = NSAttributedString.attributedStringWithAttributes(
232 StringUtils.isBlank(item.attributes().getOwner()) ?
233 LocaleFactory.localizedString("Unknown") : item.attributes().getOwner(),
234 TableCellAttributes.browserFontLeftAlignment());
236 else if(identifier.equals(Column.group.name())) {
237 value = NSAttributedString.attributedStringWithAttributes(
238 StringUtils.isBlank(item.attributes().getGroup()) ?
239 LocaleFactory.localizedString("Unknown") : item.attributes().getGroup(),
240 TableCellAttributes.browserFontLeftAlignment());
242 else if(identifier.equals(Column.permission.name())) {
243 final Acl acl = item.attributes().getAcl();
244 if(!Acl.EMPTY.equals(acl)) {
245 final StringBuilder s = new StringBuilder();
246 for(Map.Entry<Acl.User, Set<Acl.Role>> entry : acl.entrySet()) {
247 s.append(String.format("%s%s:%s", s.length() == 0 ? StringUtils.EMPTY : ", ",
248 entry.getKey().getDisplayName(), entry.getValue()));
250 value = NSAttributedString.attributedStringWithAttributes(s.toString(),
251 TableCellAttributes.browserFontLeftAlignment());
253 else {
254 final Permission permission = item.attributes().getPermission();
255 value = NSAttributedString.attributedStringWithAttributes(
256 permission.toString(),
257 TableCellAttributes.browserFontLeftAlignment());
260 else if(identifier.equals(Column.kind.name())) {
261 value = NSAttributedString.attributedStringWithAttributes(
262 descriptor.getKind(item),
263 TableCellAttributes.browserFontLeftAlignment());
265 else if(identifier.equals(Column.extension.name())) {
266 value = NSAttributedString.attributedStringWithAttributes(
267 item.isFile() ? StringUtils.isNotBlank(item.getExtension()) ? item.getExtension() :
268 LocaleFactory.localizedString("None") : LocaleFactory.localizedString("None"),
269 TableCellAttributes.browserFontLeftAlignment());
271 else if(identifier.equals(Column.region.name())) {
272 value = NSAttributedString.attributedStringWithAttributes(
273 StringUtils.isNotBlank(item.attributes().getRegion()) ? item.attributes().getRegion() :
274 LocaleFactory.localizedString("Unknown"),
275 TableCellAttributes.browserFontLeftAlignment());
277 else if(identifier.equals(Column.version.name())) {
278 value = NSAttributedString.attributedStringWithAttributes(
279 StringUtils.isNotBlank(item.attributes().getVersionId()) ? item.attributes().getVersionId() :
280 LocaleFactory.localizedString("None"),
281 TableCellAttributes.browserFontLeftAlignment());
283 else {
284 throw new IllegalArgumentException(String.format("Unknown identifier %s", identifier));
286 attributed.put(key, value);
287 return value;
291 * Sets whether the use of modifier keys should have an effect on the type of operation performed.
293 * @return Always false
294 * @see NSDraggingSource
296 @Override
297 public boolean ignoreModifierKeysWhileDragging() {
298 // If this method is not implemented or returns false, the user can tailor the drag operation by
299 // holding down a modifier key during the drag.
300 return false;
304 * @param local indicates that the candidate destination object (the window or view over which the dragged
305 * image is currently poised) is in the same application as the source, while a NO value indicates that
306 * the destination object is in a different application
307 * @return A mask, created by combining the dragging operations listed in the NSDragOperation section of
308 * NSDraggingInfo protocol reference using the C bitwise OR operator.If the source does not permit
309 * any dragging operations, it should return NSDragOperationNone.
310 * @see NSDraggingSource
312 @Override
313 public NSUInteger draggingSourceOperationMaskForLocal(final boolean local) {
314 if(log.isDebugEnabled()) {
315 log.debug(String.format("Request dragging source operation mask for %s", local));
317 if(local) {
318 // Move or copy within the browser
319 return new NSUInteger(NSDraggingInfo.NSDragOperationMove.intValue() | NSDraggingInfo.NSDragOperationCopy.intValue());
321 // Copy to a thirdparty application or drag to trash to delete
322 return new NSUInteger(NSDraggingInfo.NSDragOperationCopy.intValue() | NSDraggingInfo.NSDragOperationDelete.intValue());
326 * @param view Table
327 * @param destination A directory or null to mount an URL
328 * @param info Dragging pasteboard
329 * @return True if accepted
331 public boolean acceptDrop(final NSTableView view, final Path destination, final NSDraggingInfo info) {
332 if(log.isDebugEnabled()) {
333 log.debug(String.format("Accept drop for destination %s", destination));
335 if(info.draggingPasteboard().availableTypeFromArray(NSArray.arrayWithObject(NSPasteboard.URLPboardType)) != null) {
336 final NSObject o = info.draggingPasteboard().propertyListForType(NSPasteboard.URLPboardType);
337 // Mount .webloc URLs dragged to browser window
338 if(o != null) {
339 if(o.isKindOfClass(Rococoa.createClass("NSArray", NSArray._Class.class))) {
340 final NSArray elements = Rococoa.cast(o, NSArray.class);
341 for(int i = 0; i < elements.count().intValue(); i++) {
342 if(ProtocolFactory.isURL(elements.objectAtIndex(new NSUInteger(i)).toString())) {
343 controller.mount(HostParser.parse(elements.objectAtIndex(new NSUInteger(i)).toString()));
344 return true;
350 if(controller.isMounted()) {
351 if(info.draggingPasteboard().availableTypeFromArray(NSArray.arrayWithObject(NSPasteboard.FilenamesPboardType)) != null) {
352 final NSObject o = info.draggingPasteboard().propertyListForType(NSPasteboard.FilenamesPboardType);
353 // A file drag has been received by another application; upload to the dragged directory
354 if(o != null) {
355 if(o.isKindOfClass(Rococoa.createClass("NSArray", NSArray._Class.class))) {
356 final NSArray elements = Rococoa.cast(o, NSArray.class);
357 final List<TransferItem> roots = new ArrayList<TransferItem>();
358 for(int i = 0; i < elements.count().intValue(); i++) {
359 final Local local = LocalFactory.get(elements.objectAtIndex(new NSUInteger(i)).toString());
360 roots.add(new TransferItem(new Path(destination, local.getName(),
361 local.isDirectory() ? EnumSet.of(Path.Type.directory) : EnumSet.of(Path.Type.file)), local));
363 controller.transfer(new UploadTransfer(controller.getSession().getHost(), roots));
364 return true;
367 return false;
369 final List<PathPasteboard> pasteboards = PathPasteboardFactory.allPasteboards();
370 for(PathPasteboard pasteboard : pasteboards) {
371 // A file dragged within the browser has been received
372 if(pasteboard.isEmpty()) {
373 continue;
375 if(info.draggingSourceOperationMask().intValue() == NSDraggingInfo.NSDragOperationCopy.intValue()
376 || pasteboard.getSession().getHost().compareTo(controller.getSession().getHost()) != 0) {
377 // Drag to browser windows with different session or explicit copy requested by user.
378 final Map<Path, Path> files = new HashMap<Path, Path>();
379 for(Path file : pasteboard) {
380 files.put(file, new Path(destination, file.getName(), file.getType()));
382 controller.transfer(new CopyTransfer(pasteboard.getSession().getHost(),
383 controller.getSession().getHost(),
384 files), new ArrayList<Path>(files.values()), false);
386 else {
387 // The file should be renamed
388 final Map<Path, Path> files = new HashMap<Path, Path>();
389 for(Path next : pasteboard) {
390 Path renamed = new Path(
391 destination, next.getName(), next.getType());
392 files.put(next, renamed);
394 controller.renamePaths(files);
396 pasteboard.clear();
398 return true;
400 return false;
404 * @param view Table
405 * @param destination A directory or null to mount an URL
406 * @param row Index
407 * @param info Dragging pasteboard
408 * @return Drag operation
410 public NSUInteger validateDrop(final NSTableView view, final Path destination, final NSInteger row, final NSDraggingInfo info) {
411 if(log.isDebugEnabled()) {
412 log.debug(String.format("Validate drop for destination %s", destination));
414 if(info.draggingPasteboard().availableTypeFromArray(NSArray.arrayWithObject(NSPasteboard.URLPboardType)) != null) {
415 // Dragging URLs to mount new session
416 final NSObject o = info.draggingPasteboard().propertyListForType(NSPasteboard.URLPboardType);
417 if(o != null) {
418 if(o.isKindOfClass(Rococoa.createClass("NSArray", NSArray._Class.class))) {
419 final NSArray elements = Rococoa.cast(o, NSArray.class);
420 for(int i = 0; i < elements.count().intValue(); i++) {
421 // Validate if .webloc URLs dragged to browser window have a known protocol
422 if(ProtocolFactory.isURL(elements.objectAtIndex(new NSUInteger(i)).toString())) {
423 // Passing a value of –1 for row, and NSTableViewDropOn as the operation causes the
424 // entire table view to be highlighted rather than a specific row.
425 view.setDropRow(new NSInteger(-1), NSTableView.NSTableViewDropOn);
426 return NSDraggingInfo.NSDragOperationCopy;
428 else {
429 log.warn(String.format("Protocol not supported for URL %s", elements.objectAtIndex(new NSUInteger(i)).toString()));
434 else {
435 log.warn("URL dragging pasteboard is empty.");
438 if(controller.isMounted()) {
439 if(null == destination) {
440 log.warn("Dragging destination is null.");
441 return NSDraggingInfo.NSDragOperationNone;
443 // Files dragged form other application
444 if(info.draggingPasteboard().availableTypeFromArray(NSArray.arrayWithObject(NSPasteboard.FilenamesPboardType)) != null) {
445 this.setDropRowAndDropOperation(view, destination, row);
446 final NSObject o = info.draggingPasteboard().propertyListForType(NSPasteboard.FilenamesPboardType);
447 if(o != null) {
448 if(o.isKindOfClass(Rococoa.createClass("NSArray", NSArray._Class.class))) {
449 final NSArray elements = Rococoa.cast(o, NSArray.class);
450 for(int i = 0; i < elements.count().intValue(); i++) {
451 final Local local = LocalFactory.get(elements.objectAtIndex(new NSUInteger(i)).toString());
452 if(local.isFile()) {
453 final Touch feature = controller.getSession().getFeature(Touch.class);
454 if(!feature.isSupported(destination)) {
455 // Target file system does not support creating files. Creating files is not supported
456 // for example in root of cloud storage accounts.
457 return NSDraggingInfo.NSDragOperationNone;
463 return NSDraggingInfo.NSDragOperationCopy;
465 // Files dragged from browser
466 for(Path next : controller.getPasteboard()) {
467 if(destination.equals(next)) {
468 // Do not allow dragging onto myself
469 return NSDraggingInfo.NSDragOperationNone;
471 if(next.isDirectory() && destination.isChild(next)) {
472 // Do not allow dragging a directory into its own containing items
473 return NSDraggingInfo.NSDragOperationNone;
475 if(next.isFile() && next.getParent().equals(destination)) {
476 // Moving a file to the same destination makes no sense
477 return NSDraggingInfo.NSDragOperationNone;
480 if(log.isDebugEnabled()) {
481 log.debug(String.format("Drag operation mask is %d", info.draggingSourceOperationMask().intValue()));
483 this.setDropRowAndDropOperation(view, destination, row);
484 final List<PathPasteboard> pasteboards = PathPasteboardFactory.allPasteboards();
485 for(PathPasteboard pasteboard : pasteboards) {
486 if(pasteboard.isEmpty()) {
487 continue;
489 if(pasteboard.getSession().getHost().compareTo(controller.getSession().getHost()) == 0) {
490 if(info.draggingSourceOperationMask().intValue() == NSDraggingInfo.NSDragOperationCopy.intValue()) {
491 // Explicit copy requested if drag operation is already NSDragOperationCopy. User is pressing the option key.
492 return NSDraggingInfo.NSDragOperationCopy;
494 // Defaulting to move for same session
495 return NSDraggingInfo.NSDragOperationMove;
497 else {
498 // If copying between sessions is supported
499 return NSDraggingInfo.NSDragOperationCopy;
503 return NSDraggingInfo.NSDragOperationNone;
506 private void setDropRowAndDropOperation(final NSTableView view, final Path destination, final NSInteger row) {
507 if(destination.equals(controller.workdir())) {
508 log.debug("setDropRowAndDropOperation:-1");
509 // Passing a value of –1 for row, and NSTableViewDropOn as the operation causes the
510 // entire table view to be highlighted rather than a specific row.
511 view.setDropRow(new NSInteger(-1), NSTableView.NSTableViewDropOn);
513 else if(destination.isDirectory()) {
514 log.debug("setDropRowAndDropOperation:" + row.intValue());
515 view.setDropRow(row, NSTableView.NSTableViewDropOn);
519 public boolean writeItemsToPasteBoard(final NSTableView view, final List<Path> selected, final NSPasteboard pboard) {
520 if(log.isDebugEnabled()) {
521 log.debug(String.format("Write items to pasteboard %s", pboard));
523 if(controller.isMounted()) {
524 if(selected.size() > 0) {
525 // The fileTypes argument is the list of fileTypes being promised.
526 // The array elements can consist of file extensions and HFS types encoded
527 // with the NSHFSFileTypes method fileTypeForHFSTypeCode. If promising a directory
528 // of files, only include the top directory in the array.
529 final NSMutableArray fileTypes = NSMutableArray.array();
530 final PathPasteboard pasteboard = controller.getPasteboard();
531 for(final Path f : selected) {
532 if(f.isFile()) {
533 if(StringUtils.isNotEmpty(f.getExtension())) {
534 fileTypes.addObject(NSString.stringWithString(f.getExtension()));
536 else {
537 fileTypes.addObject(NSString.stringWithString(NSFileManager.NSFileTypeRegular));
540 else if(f.isDirectory()) {
541 fileTypes.addObject(NSString.stringWithString("'fldr'")); //NSFileTypeForHFSTypeCode('fldr')
543 else {
544 fileTypes.addObject(NSString.stringWithString(NSFileManager.NSFileTypeUnknown));
546 // Writing data for private use when the item gets dragged to the transfer queue.
547 pasteboard.add(f);
549 NSEvent event = NSApplication.sharedApplication().currentEvent();
550 if(event != null) {
551 NSPoint dragPosition = view.convertPoint_fromView(event.locationInWindow(), null);
552 NSRect imageRect = new NSRect(new NSPoint(dragPosition.x.doubleValue() - 16, dragPosition.y.doubleValue() - 16), new NSSize(32, 32));
553 view.dragPromisedFilesOfTypes(fileTypes, imageRect, this.id(), true, event);
554 // @see http://www.cocoabuilder.com/archive/message/cocoa/2003/5/15/81424
555 return true;
559 return false;
562 @Override
563 public void draggedImage_beganAt(final NSImage image, final NSPoint point) {
564 if(log.isTraceEnabled()) {
565 log.trace("draggedImage_beganAt:" + point);
570 * See http://www.cocoabuilder.com/archive/message/2005/10/5/118857
572 @Override
573 public void draggedImage_endedAt_operation(final NSImage image, final NSPoint point, final NSUInteger operation) {
574 if(log.isTraceEnabled()) {
575 log.trace("draggedImage_endedAt_operation:" + operation);
577 final PathPasteboard pasteboard = controller.getPasteboard();
578 if(NSDraggingInfo.NSDragOperationDelete.intValue() == operation.intValue()) {
579 controller.deletePaths(pasteboard);
581 pasteboard.clear();
584 @Override
585 public void draggedImage_movedTo(final NSImage image, final NSPoint point) {
586 if(log.isTraceEnabled()) {
587 log.trace("draggedImage_movedTo:" + point);
592 * @return the names (not full paths) of the files that the receiver promises to create at dropDestination.
593 * This method is invoked when the drop has been accepted by the destination and the destination, in the case of another
594 * Cocoa application, invokes the NSDraggingInfo method namesOfPromisedFilesDroppedAtDestination. For long operations,
595 * you can cache dropDestination and defer the creation of the files until the finishedDraggingImage method to avoid
596 * blocking the destination application.
598 @Override
599 public NSArray namesOfPromisedFilesDroppedAtDestination(final NSURL url) {
600 if(log.isDebugEnabled()) {
601 log.debug(String.format("Return names of promised files dropped at %s", url));
603 NSMutableArray promisedDragNames = NSMutableArray.array();
604 if(null != url) {
605 final Local destination = LocalFactory.get(url.path());
606 final PathPasteboard pasteboard = controller.getPasteboard();
607 final List<TransferItem> downloads = new ArrayList<TransferItem>();
608 for(Path p : pasteboard) {
609 downloads.add(new TransferItem(p, LocalFactory.get(destination, p.getName())));
610 // Add to returned path names
611 promisedDragNames.addObject(NSString.stringWithString(p.getName()));
613 if(downloads.size() == 1) {
614 if(downloads.iterator().next().remote.isFile()) {
615 final Local file = downloads.iterator().next().local;
616 if(!file.exists()) {
617 try {
618 LocalTouchFactory.get().touch(file);
619 IconServiceFactory.get().set(file, new TransferStatus());
621 catch(AccessDeniedException e) {
622 log.warn(String.format("Failure creating file %s %s", file, e.getMessage()));
626 if(downloads.iterator().next().remote.isDirectory()) {
627 final Local file = downloads.iterator().next().local;
628 if(!file.exists()) {
629 try {
630 file.mkdir();
632 catch(AccessDeniedException e) {
633 log.warn(e.getMessage());
638 // kTemporaryFolderType
639 final boolean dock = destination.equals(LocalFactory.get("~/Library/Caches/TemporaryItems"));
640 if(dock) {
641 for(Path p : pasteboard) {
642 // Drag to application icon in dock.
643 controller.edit(p);
646 else {
647 final DownloadTransfer transfer = new DownloadTransfer(controller.getSession().getHost(), downloads);
648 controller.transfer(transfer, Collections.<Path>emptyList());
650 pasteboard.clear();
652 // Filenames
653 return promisedDragNames;