iTunes drag'n'drop

A friend of mine wanted the possibility to drag’n’drop audio files from iTunes into my DJ application. I know that NI Traktor allows this. I thought FileDragAndDropTarget would do the job, but it only works for files dragged and dropped from the Finder. How can I make drag’n’drop from iTunes work?

It is possible with cocoa. But am not sure whether it can be done with juce.
You can refer to this link for more information
http://developer.apple.com/documentation/Cocoa/Conceptual/DragandDrop/Tasks/acceptingdrags.html#//apple_ref/doc/uid/20000993-BABHHIHC

Don’t know if I remember correctly, but I think iTunes DnD are promised hfs
and not HFS.(so not handled by Juce)

Promised HFS ask where the target should be saved.

HTH

Yes, I’d need to add some tweaks to the cocoa stuff to handle the promised file type. Don’t think it’s a big job, I just haven’t had chance to try it yet.

But my app is not Cocoa-JUCE, I still prefer to use the old stable JUCE and not make any “experiments” at the moment. So this will not work without Cocoa?

There are probably horrific Carbon bodges you can use to do it, but you’re on your own with that!

Nah, nothing horrible. Here’s the working code (juce_mac_Windowing.cpp):

I can only recommend the cool utility DragPeeker X, which sort of helped me.

[code]void updateDragAndDropFileList (EventRef theEvent)
{
dragAndDropFiles.clear();

    DragRef dragRef;
    if (GetEventParameter (theEvent, kEventParamDragRef, typeDragRef, 0, sizeof (dragRef), 0, &dragRef) == noErr)
    {
        UInt16 numItems = 0;
        if (CountDragItems (dragRef, &numItems) == noErr)
        {
            for (int i = 0; i < (int) numItems; ++i)
            {
                DragItemRef ref;

                if (GetDragItemReferenceNumber (dragRef, i + 1, &ref) == noErr)
                {
                    const FlavorType flavorType = kDragFlavorTypeHFS;

                    Size size = 0;
                    if (GetFlavorDataSize (dragRef, ref, flavorType, &size) == noErr)
                    {
                        void* data = juce_calloc (size);

                        if (GetFlavorData (dragRef, ref, flavorType, data, &size, 0) == noErr)
                        {
                            HFSFlavor* f = (HFSFlavor*) data;
                            FSRef fsref;

                            if (FSpMakeFSRef (&f->fileSpec, &fsref) == noErr)
                            {
                                const String path (PlatformUtilities::makePathFromFSRef (&fsref));

                                if (path.isNotEmpty())
                                    dragAndDropFiles.add (path);
                            }
                        }

                        juce_free (data);
                    }
                    // --- ADDED BY ME ----
                    else
                    {
                    	const FlavorType flavorType = kDragPromisedFlavorFindFile;
                    	
                    	if (GetFlavorDataSize (dragRef, ref, flavorType, &size) == noErr)
                    	{
                        		void* data = juce_calloc (size);

                        		if (GetFlavorData (dragRef, ref, flavorType, data, &size, 0) == noErr)
                        		{
                            
                            		FSRef fsref;

	                                if (FSpMakeFSRef ((FSSpec *)data, &fsref) == noErr)
	                                {
                                			const String path (PlatformUtilities::makePathFromFSRef (&fsref));

                                			if (path.isNotEmpty())
                                    		dragAndDropFiles.add (path);
                            		}
                        		}

                        		juce_free (data);
                    	}
                    	
                    }
                    // --- END OF ADDED BY ME ---
                }
            }

            dragAndDropFiles.trim();
            dragAndDropFiles.removeEmptyStrings();
        }
    }
}[/code]

For those interested, here’s an update to juce 1.5.1 that worked well for me. The “NSFilesPromisePboardType” drag type needs to be supported and this change to NSViewComponentPeer::sendDragCallback.

//==============================================================================
BOOL NSViewComponentPeer::sendDragCallback (int type, id <NSDraggingInfo> sender)
{
    NSString* bestType
        = [[sender draggingPasteboard] availableTypeFromArray: [view getSupportedDragTypes]];

    if (bestType == nil)
        return false;

    NSPoint p = [view convertPoint: [sender draggingLocation] fromView: nil];
    const Point<int> pos ((int) p.x, (int) ([view frame].size.height - p.y));

    StringArray files;
    
    NSPasteboard *pboard = [sender draggingPasteboard];
    
    NSString * const kiTunesPboardType = @"CorePasteboardFlavorType 0x6974756E"; /* itun */
    
    if( bestType == NSFilesPromisePboardType )
    {
        if( [[pboard types] containsObject:kiTunesPboardType] )
        {
            // Handle iTunes drops
            NSDictionary *iTunesDictionary = [pboard propertyListForType:kiTunesPboardType];
        
            if( !iTunesDictionary )
            {
                return false;
            }
        
            NSArray *tracks = [iTunesDictionary valueForKey:@"Tracks"];
            NSEnumerator *enumerator = [tracks objectEnumerator];
            NSDictionary *track = nil;
            NSURL *url = nil;
             
            while( ( track = [enumerator nextObject] ) ) 
            {
                url = [NSURL URLWithString: [track valueForKey:@"Location"]];

                if( [url isFileURL] )
                {
                    files.add (nsStringToJuce ([url path]));
                }
            }
        }
        else
        {
            return false;
        }
    }
    else
    {
        id list = [ pboard propertyListForType: bestType];
        if (list == nil)
            return false;   
        
        if ([list isKindOfClass: [NSArray class]])
        {
            NSArray* items = (NSArray*) list;

            for (unsigned int i = 0; i < [items count]; ++i)
                files.add (nsStringToJuce ((NSString*) [items objectAtIndex: i]));
        }
    }

    if (files.size() == 0)
        return false;

    if (type == 0)
        handleFileDragMove (files, pos);
    else if (type == 1)
        handleFileDragExit (files);
    else if (type == 2)
        handleFileDragDrop (files, pos);

    return true;
}

The itunes data needs to be accessed to get the location of the files in question.

if (type == 0)
        handleFileDragMove (files, pos);
    else if (type == 1)
        handleFileDragExit (files);
    else if (type == 2)
        handleFileDragDrop (files, pos);

I think a good case for switch statement. :slight_smile:
Anyway a useful piece of code.

Cheers Justin, I’ll take a look at that…

I would be happy to see this in Juce :slight_smile:

Any news on this ?

Thanks,

Did it ages ago, didn’t I?

Humm yes indeed.

I was thinking about a more general support of NSFilesPromisePboardType in fact