10

I'm trying to build an application that allows users to drag files from Finder to the menubar icon for processing. I've made progress in my journey, but I can't seem to summit this hill. I tried subclassing NSView and implementing the drag messages.

@interface CMDroppableView : NSView <NSMenuDelegate>

I wanted to not only accept drag operations, but to provide a NSMenu when the user clicks the icon. I've managed to get the NSMenu to display properly, but the drag functionality remains elusive.

It's my understanding that I needed to register the accepted drag types which I have done here:

-(void)awakeFromNib{
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
}

Drag messages:

-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
    NSLog(@"Drag Enter");
    return NSDragOperationCopy;
}

-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender{
    return NSDragOperationCopy;
}

-(void)draggingExited:(id <NSDraggingInfo>)sender{
    NSLog(@"Drag Exit");
}

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

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

Here is the code where the custom view is set:

statusItemView = [[CMDroppableView alloc] init];
[statusItemView retain];
[statusItemView setMenu: statusMenu];

[statusItem setView: statusItemView];   

Still nothing. So where have I gone wrong?

Thanks!

Jay
  • 6,572
  • 3
  • 37
  • 65
doomspork
  • 2,302
  • 1
  • 17
  • 24
  • 2
    Have you implemented the draggingEntered: and performDragOperation: methods? – omz May 15 '11 at 00:15
  • Yes, I have. I'll update my question to include them. – doomspork May 15 '11 at 00:29
  • 1
    I suspect that you just won't be able to do this. Dragging an object to a menu is not a normal UI behaviour, and I think that there are probably system-level restrictions on the interactions that an `NSStatusItem` views can have. I'd be surprised if the view responded to anything other than a mouseUp/Down. – Rob Keniger May 15 '11 at 01:31
  • @Rob, that's interesting. How then, do applications like CloudApp enable you to drop files on the statusbar icon? – doomspork May 15 '11 at 02:10
  • You can do it just fine; I do it in a few apps of my own. – Wevah May 15 '11 at 02:22
  • 1
    @Wevah, would you be kind enough to answer the question? – doomspork May 15 '11 at 02:55
  • I can't see anything that he's done wrong, providing that he's using his custom view for the status item's view. :/ – Wevah May 15 '11 at 03:40
  • @Wevah, I updated my post to include the code where the custom view is set. Would it be acceptable post all of the code to a pastiebin and link to it? – doomspork May 15 '11 at 04:04

2 Answers2

7

Through trial and error I stumbled onto the solution. I was registering my acceptable drag types within the awakeFromNib message (as seen in the original question). However, I was not using IB to set this view up so this method was never called. Moving the registration code to the initFromFrame message seemed to fix the problem.

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        ...
        [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
    }
    return self;
}
doomspork
  • 2,302
  • 1
  • 17
  • 24
  • Whoops, I didn't read this self-answer before I edited mine. You should accept yours as the right one! (Voted up.) – Wevah May 15 '11 at 18:23
6

Edit: D'oh, you're registering the drag types in -awakeFromNib, which won't be called if the view isn't being loaded from a nib. Try registering your drag types in -initWithFrame: instead!

Old answer:

IIRC you need to not set the menu on the status item. What I do is have my custom view manage a menu, and do something like this:

- (void)setMenu:(NSMenu *)menu {
    [menu setDelegate:self];
    [super setMenu:menu];
}

- (void)mouseDown:(NSEvent *)event {
    [statusItem popUpStatusItemMenu:[self menu]]; // or another method that returns a menu
}

- (void)menuWillOpen:(NSMenu *)menu {
    highlight = YES;
    [self setNeedsDisplay:YES];
}

- (void)menuDidClose:(NSMenu *)menu {
    highlight = NO;
    [self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)rect {
    NSImage *img = highlight ? [alternateImage copy] : [image copy];
    NSRect bounds = [self bounds];
    [statusItem drawStatusBarBackgroundInRect:bounds withHighlight:highlight];
    
    // rest of drawing code goes here, including drawing img where appropriate
}

in my custom view's implementation. This ensures the menu behavior is identical to the default.

Community
  • 1
  • 1
Wevah
  • 28,182
  • 7
  • 83
  • 72
  • I am setting the NSMenu to my custom view, not the NSStatusItem. More importantly, how do you handle your drag destinations? – doomspork May 15 '11 at 04:58
  • Oh, oops. I'll update my answer. Running low on sleep at the moment, though… – Wevah May 15 '11 at 18:18