7

I am trying to find a way to use drag and drop in my matlab GUI. The closest I've found is this.

However, I would like the result to look like this:

enter image description here

When a file has been dropped, all I need is the path of the file and a call to my load function.

All suggestions are much appreciated!

jkazan
  • 1,149
  • 12
  • 29
  • 1
    I am using Matlab 2012, so don't know if newer versions have dragdrop events implemented. Anyway, you can use the following method: (http://de.mathworks.com/matlabcentral/answers/94681-how-do-i-implement-drag-and-drop-functionality-in-matlab) – Lati Apr 25 '16 at 13:14
  • In the example in the link you provided, you can only drag items within a figure. I would like to drag files from explorer (or finder on mac) and into the GUI, then call a function to read the file. – jkazan May 10 '16 at 06:00
  • @JZKaz, you can still use the same method, only thing you need to add is to compare mouse position and the position of the element you want to drop (I guess it is a panel in your case). – Lati May 11 '16 at 16:41
  • @Lati, how does matlab know: a) When I am dragging something outside of the figure (e.g. from desktop into the figure) and b) the information of that object (e.g. filepath)? – jkazan May 12 '16 at 11:41
  • For information I modified Maarten van der Seijs with similar class that offers better control over DragEnter/DragExist/DragActionChanged/Drop events, for instance to refuse drag of some file types and change drag icon, etc ... see [here](https://fr.mathworks.com/matlabcentral/fileexchange/64776-droplistener) or [there](https://stackoverflow.com/a/46810619/684399) for more info – CitizenInsane Oct 26 '17 at 13:30

1 Answers1

1

This submission by Maarten van der Seijs on the file exchange seems to solve it.

It creates a callback function which can be coupled to java swing GUI components, as shown in the attached demo.

It uses a java class, which is a thin wrapper around java.awt.dnd.DropTarget:

import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.util.*;
import java.io.File;
import java.io.IOException;

public class MLDropTarget extends DropTarget
{
    /**
     * Modified DropTarget to be used for drag & drop in MATLAB UI control.
     */
    private static final long serialVersionUID = 1L;
    private int droptype;
    private Transferable t;
    private String[] transferData;

    public static final int DROPERROR = 0;
    public static final int DROPTEXTTYPE = 1;
    public static final int DROPFILETYPE = 2;

    @SuppressWarnings("unchecked")
    @Override
    public synchronized void drop(DropTargetDropEvent evt) {

        // Make sure drop is accepted
        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

        // Set droptype to zero
        droptype = DROPERROR;        

        // Get transferable and analyze
        t = evt.getTransferable();

        try {
            if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                // Interpret as list of files
                List<File> fileList = (ArrayList<File>) t.getTransferData(DataFlavor.javaFileListFlavor);
                transferData = new String[fileList.size()];
                for (int i = 0; i < fileList.size(); i++) 
                    transferData[i] = fileList.get(i).getAbsolutePath();
                droptype = DROPFILETYPE;
            } 
            else if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                // Interpret as string              
                transferData[0] = (String) t.getTransferData(DataFlavor.stringFlavor);
                droptype = DROPTEXTTYPE;
            }

        } catch (UnsupportedFlavorException e) {
            droptype = DROPERROR;
            super.drop(evt);            
            return;
        } catch (IOException e) {
            droptype = DROPERROR;
            super.drop(evt);
            return;
        }

        // Call built-in drop method (fire MATLAB Callback)       
        super.drop(evt);
    }

    public int getDropType() {
        return droptype;
    }   
    public Transferable getTransferable() {
        return t;
    }
    public String[] getTransferData() {
        return transferData;
    }
}

which is then initialized and called by a MATLAB class:

classdef (CaseInsensitiveProperties) dndcontrol < handle
%DNDCONTROL Class for Drag & Drop functionality.
%   obj = DNDCONTROL(javaobj) creates a dndcontrol object for the specified
%   Java object, such as 'javax.swing.JTextArea' or 'javax.swing.JList'. Two
%   callback functions are available: obj.DropFileFcn and obj.DropStringFcn, 
%   that listen to drop actions of respectively system files or plain text.
%
%   The Drag & Drop control class relies on a Java class that need to be
%   visible on the Java classpath. To initialize, call the static method
%   dndcontrol.initJava(). The Java class can be adjusted and recompiled if
%   desired.
%
%   DNDCONTROL Properties:
%       Parent            - The associated Java object.
%       DropFileFcn       - Callback function for system files.
%       DropStringFcn     - Callback function for plain text.
%
%   DNDCONTROL Methods:
%       dndcontrol        - Constructs the DNDCONTROL object.
%
%   DNDCONTROL Static Methods:
%       defaultDropFcn    - Default callback function for drop events.
%       demo              - Runs the demonstration script.
%       initJava          - Initializes the Java class.
%       isInitialized     - Checks if the Java class is visible.
%
%   A demonstration is available from the static method dndcontrol.demo().
%
%   Example:
%       dndcontrol.initJava();
%       dndcontrol.demo();
%
%   See also:
%       uicontrol, javaObjectEDT.    
%
%   Written by: Maarten van der Seijs, 2015.
%   Version: 1.0, 13 October 2015.


    properties (Hidden)
        dropTarget;                
    end

    properties (Dependent)
        %PARENT The associated Java object.
        Parent;
    end

    properties
        %DROPFILEFCN Callback function executed upon dropping of system files.
        DropFileFcn;        
        %DROPSTRINGFCN Callback function executed upon dropping of plain text.
        DropStringFcn;        
    end

    methods (Static)
        function initJava()
        %INITJAVA Initializes the required Java class.

            %Add java folder to javaclasspath if necessary
            if ~dndcontrol.isInitialized();
                classpath = fileparts(mfilename('fullpath'));                
                javaclasspath(classpath);                
            end 
        end

        function TF = isInitialized()            
        %ISINITIALIZED Returns true if the Java class is initialized.

            TF = (exist('MLDropTarget','class') == 8);
        end                           
    end

    methods
        function obj = dndcontrol(Parent,DropFileFcn,DropStringFcn)
        %DNDCONTROL Drag & Drop control constructor.
        %   obj = DNDCONTROL(javaobj) contstructs a DNDCONTROL object for 
        %   the given parent control javaobj. The parent control should be a 
        %   subclass of java.awt.Component, such as most Java Swing widgets.
        %
        %   obj = DNDCONTROL(javaobj,DropFileFcn,DropStringFcn) sets the
        %   callback functions for dropping of files and text.

            % Check for Java class
            assert(dndcontrol.isInitialized(),'Javaclass MLDropTarget not found. Call dndcontrol.initJava() for initialization.')

            % Construct DropTarget            
            obj.dropTarget = handle(javaObjectEDT('MLDropTarget'),'CallbackProperties');
            set(obj.dropTarget,'DropCallback',{@dndcontrol.DndCallback,obj});
            set(obj.dropTarget,'DragEnterCallback',{@dndcontrol.DndCallback,obj});

            % Set DropTarget to Parent
            if nargin >=1, Parent.setDropTarget(obj.dropTarget); end

            % Set callback functions
            if nargin >=2, obj.DropFileFcn = DropFileFcn; end 
            if nargin >=3, obj.DropStringFcn = DropStringFcn; end
        end

        function set.Parent(obj, Parent)
            if isempty(Parent)
                obj.dropTarget.setComponent([]);
                return
            end
            if isa(Parent,'handle') && ismethod(Parent,'java')
                Parent = Parent.java;
            end
            assert(isa(Parent,'java.awt.Component'),'Parent is not a subclass of java.awt.Component.')
            assert(ismethod(Parent,'setDropTarget'),'DropTarget cannot be set on this object.')

            obj.dropTarget.setComponent(Parent);
        end

        function Parent = get.Parent(obj)
            Parent = obj.dropTarget.getComponent();
        end
    end

    methods (Static, Hidden = true)
        %% Callback functions
        function DndCallback(jSource,jEvent,obj)

            if jEvent.isa('java.awt.dnd.DropTargetDropEvent')
                % Drop event     
                try
                    switch jSource.getDropType()
                        case 0
                            % No success.
                        case 1
                            % String dropped.
                            string = char(jSource.getTransferData());
                            if ~isempty(obj.DropStringFcn)
                                evt = struct();
                                evt.DropType = 'string';
                                evt.Data = string;                                
                                feval(obj.DropStringFcn,obj,evt);
                            end
                        case 2
                            % File dropped.
                            files = cell(jSource.getTransferData());                            
                            if ~isempty(obj.DropFileFcn)
                                evt = struct();
                                evt.DropType = 'file';
                                evt.Data = files;                                
                                feval(obj.DropFileFcn,obj,evt);
                            end
                    end

                    % Set dropComplete
                    jEvent.dropComplete(true);  
                catch ME
                    % Set dropComplete
                    jEvent.dropComplete(true);  
                    rethrow(ME)
                end                              

            elseif jEvent.isa('java.awt.dnd.DropTargetDragEvent')
                 % Drag event                               
                 action = java.awt.dnd.DnDConstants.ACTION_COPY;
                 jEvent.acceptDrag(action);
            end            
        end
    end

    methods (Static)
        function defaultDropFcn(src,evt)
        %DEFAULTDROPFCN Default drop callback.
        %   DEFAULTDROPFCN(src,evt) accepts the following arguments:
        %       src   - The dndcontrol object.
        %       evt   - A structure with fields 'DropType' and 'Data'.

            fprintf('Drop event from %s component:\n',char(src.Parent.class()));
            switch evt.DropType
                case 'file'
                    fprintf('Dropped files:\n');
                    for n = 1:numel(evt.Data)
                        fprintf('%d %s\n',n,evt.Data{n});
                    end
                case 'string'
                    fprintf('Dropped text:\n%s\n',evt.Data);
            end
        end            

        function [dndobj,hFig] = demo()
        %DEMO Demonstration of the dndcontrol class functionality.
        %   dndcontrol.demo() runs the demonstration. Make sure that the
        %   Java class is visible in the Java classpath.

            % Initialize Java class
            dndcontrol.initJava();

            % Create figure
            hFig = figure();

            % Create Java Swing JTextArea
            jTextArea = javaObjectEDT('javax.swing.JTextArea', ...
                sprintf('Drop some files or text content here.\n\n'));

            % Create Java Swing JScrollPane
            jScrollPane = javaObjectEDT('javax.swing.JScrollPane', jTextArea);
            jScrollPane.setVerticalScrollBarPolicy(jScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

            % Add Scrollpane to figure
            [~,hContainer] = javacomponent(jScrollPane,[],hFig);
            set(hContainer,'Units','normalized','Position',[0 0 1 1]);

            % Create dndcontrol for the JTextArea object
            dndobj = dndcontrol(jTextArea);

            % Set Drop callback functions
            dndobj.DropFileFcn = @demoDropFcn;
            dndobj.DropStringFcn = @demoDropFcn;

            % Callback function
            function demoDropFcn(~,evt)
                switch evt.DropType
                    case 'file'
                        jTextArea.append(sprintf('Dropped files:\n'));
                        for n = 1:numel(evt.Data)
                            jTextArea.append(sprintf('%d %s\n',n,evt.Data{n}));
                        end
                    case 'string'
                        jTextArea.append(sprintf('Dropped text:\n%s\n',evt.Data));
                end
                jTextArea.append(sprintf('\n'));
            end
        end
    end    
end
Rody Oldenhuis
  • 37,726
  • 7
  • 50
  • 96