1 package ch
.cyberduck
.ui
.cocoa
;
4 * Copyright (c) 2005 David Kocher. All rights reserved.
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
;
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
{
113 private String column
;
115 public Item(final Path file
, final String column
) {
117 this.column
= column
;
121 public boolean equals(final Object o
) {
125 if(o
== null || getClass() != o
.getClass()) {
128 final Item item
= (Item
) o
;
129 if(column
!= null ?
!column
.equals(item
.column
) : item
.column
!= null) {
132 if(file
!= null ?
!file
.equals(item
.file
) : item
.file
!= null) {
139 public int hashCode() {
140 int result
= file
!= null ? file
.hashCode() : 0;
141 result
= 31 * result
+ (column
!= null ? column
.hashCode() : 0);
146 public String
toString() {
147 final StringBuilder sb
= new StringBuilder("Item{");
148 sb
.append("file=").append(file
);
149 sb
.append(", column='").append(column
).append('\'');
151 return sb
.toString();
155 protected BrowserTableDataSource(final BrowserController controller
, final Cache
<Path
> cache
) {
156 this.controller
= controller
;
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
) {
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
) {
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
);
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(
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());
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());
287 throw new IllegalArgumentException(String
.format("Unknown identifier %s", identifier
));
289 attributed
.put(key
, 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
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.
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
316 public NSUInteger
draggingSourceOperationMaskForLocal(final boolean local
) {
317 if(log
.isDebugEnabled()) {
318 log
.debug(String
.format("Request dragging source operation mask for %s", 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());
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
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()));
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
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
));
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()) {
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);
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
);
408 * @param destination A directory or null to mount an URL
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
);
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
;
432 log
.warn(String
.format("Protocol not supported for URL %s", elements
.objectAtIndex(new NSUInteger(i
)).toString()));
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
);
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());
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()) {
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
;
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
) {
536 if(StringUtils
.isNotEmpty(f
.getExtension())) {
537 fileTypes
.addObject(NSString
.stringWithString(f
.getExtension()));
540 fileTypes
.addObject(NSString
.stringWithString(NSFileManager
.NSFileTypeRegular
));
543 else if(f
.isDirectory()) {
544 fileTypes
.addObject(NSString
.stringWithString("'fldr'")); //NSFileTypeForHFSTypeCode('fldr')
547 fileTypes
.addObject(NSString
.stringWithString(NSFileManager
.NSFileTypeUnknown
));
549 // Writing data for private use when the item gets dragged to the transfer queue.
552 NSEvent event
= NSApplication
.sharedApplication().currentEvent();
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
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
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
);
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.
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();
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
;
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
;
635 catch(AccessDeniedException e
) {
636 log
.warn(e
.getMessage());
641 // kTemporaryFolderType
642 final boolean dock
= destination
.equals(LocalFactory
.get("~/Library/Caches/TemporaryItems"));
644 for(Path p
: pasteboard
) {
645 // Drag to application icon in dock.
650 final DownloadTransfer transfer
= new DownloadTransfer(controller
.getSession().getHost(), downloads
);
651 controller
.transfer(transfer
, Collections
.<Path
>emptyList());
656 return promisedDragNames
;