Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom NSView Drag Destination

I'm trying to create a simple NSView that will allow a folder from Finder to be dragged onto it. A folder path is the only thing I want the view to accept as a draggable item. I've been trying to follow the Apple documentation, but so far nothing's working. So far, I've just tried to get the view to work with any file type, but I can't even seem to do that. Here's what I have so far:

-(id) initWithFrame:(NSRect)frameRect
{
    if (self = [super initWithFrame:frameRect])
    {
        NSLog(@"getting called");
        [self registerForDraggedTypes:[NSArray arrayWithObjects:NSPasteboardTypeString,
                                       NSPasteboardTypePDF,
                                       NSPasteboardTypeTIFF,
                                       NSPasteboardTypePNG,
                                       NSPasteboardTypeRTF,
                                       NSPasteboardTypeRTFD,
                                       NSPasteboardTypeHTML,
                                       NSPasteboardTypeTabularText,
                                       NSPasteboardTypeFont,
                                       NSPasteboardTypeRuler,
                                       NSPasteboardTypeColor,
                                       NSPasteboardTypeSound,
                                       NSPasteboardTypeMultipleTextSelection,
                                       NSPasteboardTypeFindPanelSearchOptions, nil]];
    }
    return self;
}

-(BOOL) prepareForDragOperation: (id<NSDraggingInfo>) sender
{
    NSLog(@"preparing for drag");

    return YES;
}

The initWithFrame: method is getting called, but when I try to drag into the view the prepareForDragOperation: method doesn't ever seem to get called. My questions:

  1. What am I doing wrong? Why isn't prepareForDragOperation: ever getting called?
  2. What do I need to do to get the drag operation to only support dragging folders?

Update

I updated my registerForDraggedTypes: method with every type I could find. It now looks like this:

[self registerForDraggedTypes:[NSArray arrayWithObjects:NSPasteboardTypeString,
                               NSPasteboardTypePDF,
                               NSPasteboardTypeTIFF,
                               NSPasteboardTypePNG,
                               NSPasteboardTypeRTF,
                               NSPasteboardTypeRTFD,
                               NSPasteboardTypeHTML,
                               NSPasteboardTypeTabularText,
                               NSPasteboardTypeFont,
                               NSPasteboardTypeRuler,
                               NSPasteboardTypeColor,
                               NSPasteboardTypeSound,
                               NSPasteboardTypeMultipleTextSelection,
                               NSPasteboardTypeFindPanelSearchOptions,
                               NSStringPboardType,
                               NSFilenamesPboardType,
                               NSPostScriptPboardType,
                               NSTIFFPboardType,
                               NSRTFPboardType,
                               NSTabularTextPboardType,
                               NSFontPboardType,
                               NSRulerPboardType,
                               NSFileContentsPboardType,
                               NSColorPboardType,
                               NSRTFDPboardType,
                               NSHTMLPboardType,
                               NSURLPboardType,
                               NSPDFPboardType,
                               NSVCardPboardType,
                               NSFilesPromisePboardType,
                               NSMultipleTextSelectionPboardType, nil]];

I've noticed that the prepareForDragOperation: method isn't getting called when I drag a folder into the view. Did I miss a step?

like image 209
LandonSchropp Avatar asked Sep 12 '11 08:09

LandonSchropp


1 Answers

Here's a simple little drag & drop view meeting those criteria:

MDDragDropView.h:

@interface MDDragDropView : NSView {
    BOOL        isHighlighted;
}

@property (assign, setter=setHighlighted:) BOOL isHighlighted;

@end

MDDragDropView.m:

@implementation MDDragDropView

@dynamic isHighlighted;

- (void)awakeFromNib {
    NSLog(@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
}

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
    NSLog(@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));

    NSPasteboard *pboard = [sender draggingPasteboard];

    if ([[pboard types] containsObject:NSFilenamesPboardType]) {

        NSArray *paths = [pboard propertyListForType:NSFilenamesPboardType];
        for (NSString *path in paths) {
            NSError *error = nil;
            NSString *utiType = [[NSWorkspace sharedWorkspace]
                                       typeOfFile:path error:&error];
            if (![[NSWorkspace sharedWorkspace] 
                      type:utiType conformsToType:(id)kUTTypeFolder]) {

                [self setHighlighted:NO];
                return NSDragOperationNone;
            }
        }
    }
    [self setHighlighted:YES];
    return NSDragOperationEvery;
}

And the rest of the methods:

- (void)draggingExited:(id <NSDraggingInfo>)sender {
    [self setHighlighted:NO];
}


- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender  {
    return YES;
}

- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
    [self setHighlighted:NO];
    return YES;
}
- (BOOL)isHighlighted {
    return isHighlighted;
}

- (void)setHighlighted:(BOOL)value {
    isHighlighted = value;
    [self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
    if (isHighlighted) {
        [NSBezierPath setDefaultLineWidth:6.0];
        [[NSColor keyboardFocusIndicatorColor] set];
        [NSBezierPath strokeRect:self.frame];
    }
}

@end

The reason prepareForDragOperation: isn't being called is that the dragging destination sequence follows a precise set of steps, and if the earlier steps aren't implemented, or are implemented but return a "stop the drag operation" type of answer, the later methods are never reached. (In your case, it doesn't appear that you've implemented the draggingEntered: method, which would need to return something other than NSDragOperationNone to continue on in the sequence).

Before prepareForDragOperation: is sent, the view is first sent a series of dragging destination messages:

A single - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender.

Depending on the NSDragOperation mask returned from that method, the following will be called if it's implemented in your class:

Multiple - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender.

Depending on the NSDragOperation mask returned from that method, then prepareForDragOperation: will be called.

like image 91
NSGod Avatar answered Oct 15 '22 13:10

NSGod