Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.

2015-03-13

File drag & drop for OS-X

In the last update of my software (still in Objective-C) I could not get drag & drop of files working. In the upcoming release (that will be 100% Swift) I wanted to have this functionality back again. And I got it working. Without much ado, here is the solution:

This works for a child object of NSWindow, probably also for child's of NSView, but I have not tested that.

In awakeFromNib:

    override func awakeFromNib() {
        
        
        // ... other stuff here
                
        
        // Add drag and drop support
        
        registerForDraggedTypes([NSFilenamesPboardType])        
    }


Then add the following operations to the NSWindow child:

    // MARK: -  NSDraggingDestination protocol
    
    func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
        
        let sourceDragMask = sender.draggingSourceOperationMask()
        let pboard = sender.draggingPasteboard()!
        
        if pboard.availableTypeFromArray([NSFilenamesPboardType]) == NSFilenamesPboardType {
            if sourceDragMask.rawValue & NSDragOperation.Generic.rawValue != 0 {
                return NSDragOperation.Generic
            }
        }
        
        return NSDragOperation.None
    }
    
    func performDragOperation(sender: NSDraggingInfo) -> Bool {
        
        
        // Get the pasteboard
        
        if let pboard = sender.draggingPasteboard() {
        
        
            // Get the Y coordinate for the drop
            
            let dropCoordinate = sender.draggingLocation()
            
            
            // Since our frame is bigger than the scrollview frame, check if the drop position is within any of our scrollviews. If it is not inside a scrollview, then exit now
            
            if !coordinateIsInAChildView(dropCoordinate) { return false }
            
            
            // Offset the Y coordinate for the scrollview origin
            
            let dropPoint = myView.convertPoint(dropCoordinate, toView: myScrollView.contentView)
            
            
            // Get the index for the drop
            
            let dropIndex = (myScrollView.documentView as! MyView).lineIndexFromYCoordinate(dropPoint.y)
            
            
            // Make sure that there are filenames in the pasteboard
            
            if pboard.availableTypeFromArray([NSFilenamesPboardType]) == NSFilenamesPboardType {
                
                
                // Get the filenames from the pasteboard
                
                let files = filesFromPboard(pboard)
                
                
                // Add the filenames to the datasource
                
                insertLines(files, atIndex: dropIndex)
                
                
                // Update the view
                
                myView.needsDisplay = true
                
                
                // The drop was accepted
                
                return true
            }
        }
        

        // Still here, then something went wrong. The drop is not accepted.
        
        return false

    }


Discussion:


NSWindows already implements the NSDraggingDestination protocol. So we only have to implement those operations that we want. I have not implemented the "draggingUpdated" and "prepareForDragOperation" operations as the default behaviour is exactly what is needed.

It is probably obvious, but you have to implement some stuff yourself. For example I have to check if the drop location is in a childview, and I have to find out what the Y coordinate for the drop is. You will have to remove/replace that.

Do note that the dragging operations may not be marked 'private'.

Happy coding

2015-06-17: Added more pboard and drag & drop stuff in this post

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

No comments:

Post a Comment