iTunes drag'n'drop


#1

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?


#2

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


#3

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


#4

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.


#5

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?


#6

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


#7

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]

#8

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.


#9
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.


#10

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


#11

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


#12

Any news on this ?

Thanks,


#13

Did it ages ago, didn’t I?


#14

Humm yes indeed.

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