Copying binary files to Mac OSX Clipboard with Java

717 Views Asked by At

I'm writing a java app that's trying to copy a midi file into the Mac OSX clipboard (and eventually Windows). I've tried using the DataFlavor.javaFileListFlavor with getSystemClipboard().setContents but the data that's copied to the clipboard is unable to be read by the Mac OSX system, which uses a UTI (https://alastairs-place.net/blog/2012/06/06/utis-are-better-than-you-think-and-heres-why/) similar to a MimeType to determine the data type. Is there some custom DataFlavor code for a binary file (specifically Midi file) that will allow me to put a file on the Mac OS clipboard, similar to if I did a cmd-c on a file from finder?

Here's a snippet of what i'm currently doing.

import javax.sound.midi.*;
import java.awt.Toolkit;
import java.util.List;
import java.awt.datatransfer.*;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

/**
 * Created by vincentcimo on 12/22/16.
 */
public class WriteMidi {

public static byte[] longToBytes(long x) {
    ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE);
    buffer.putLong(x);
    return buffer.array();
}

public static void writeFile(String path, ArrayList<LoopNote> notes, double bpm, long loop_length) {
    try {

        long midiTempo = (long) (60000000 / bpm);
        double tickMS = (60 / (bpm * 24)) * 1000;

        /*
        * 24 ticks / beat --- 100 beats / minute
        * tick length = 60 / 100 bpm * 24 pulses per beat
        * */

        //****  Create a new MIDI sequence with 24 ticks per beat  ****
        Sequence s = new Sequence(javax.sound.midi.Sequence.PPQ, 24);

        //****  Obtain a MIDI track from the sequence  ****
        Track t = s.createTrack();

        //****  General MIDI sysex -- turn on General MIDI sound set  ****
        byte[] b = {(byte) 0xF0, 0x7E, 0x7F, 0x09, 0x01, (byte) 0xF7};
        SysexMessage sm = new SysexMessage();
        sm.setMessage(b, 6);
        MidiEvent me = new MidiEvent(sm, (long) 0);
        t.add(me);

        //****  set tempo (meta event)  ****
        MetaMessage mt = new MetaMessage();


        byte[] bt = longToBytes(midiTempo);
        mt.setMessage(0x51, bt, 3);
        me = new MidiEvent(mt, (long) 0);
        t.add(me);

        //****  set track name (meta event)  ****
        mt = new MetaMessage();
        String TrackName = new String("midifile track");
        mt.setMessage(0x03, TrackName.getBytes(), TrackName.length());
        me = new MidiEvent(mt, (long) 0);
        t.add(me);

        //****  set omni on  ****
        ShortMessage mm = new ShortMessage();
        mm.setMessage(0xB0, 0x7D, 0x00);
        me = new MidiEvent(mm, (long) 0);
        t.add(me);

        //****  set poly on  ****
        mm = new ShortMessage();
        mm.setMessage(0xB0, 0x7F, 0x00);
        me = new MidiEvent(mm, (long) 0);
        t.add(me);

        //****  set instrument to Piano  ****
        mm = new ShortMessage();
        mm.setMessage(0xC0, 0x00, 0x00);
        me = new MidiEvent(mm, (long) 0);
        t.add(me);

        for (LoopNote note : notes) {
            mm = new ShortMessage();
            if (note.getVelocity() != 0) {
                mm.setMessage(ShortMessage.NOTE_ON, 1, note.getNote(), note.getVelocity());
            } else {
                mm.setMessage(ShortMessage.NOTE_OFF, 1, note.getNote(), note.getVelocity());
            }
            me = new MidiEvent(mm, (long) (note.getStartTime() / tickMS));
            t.add(me);
        }

        //****  set end of track (meta event) 19 ticks later  ****
        mt = new MetaMessage();
        byte[] bet = {}; // empty array
        mt.setMessage(0x2F, bet, 0);
        me = new MidiEvent(mt, (long) (loop_length / tickMS));
        t.add(me);

        //****  write the MIDI sequence to a MIDI file  ****
        File f = new File(path);
        if (!f.exists()) {
            f.createNewFile();
            MidiSystem.write(s, 1, f);

            List listOfFiles = new ArrayList();
            listOfFiles.add(f);

            FileTransferable ft = new FileTransferable(listOfFiles);

            Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ft, new ClipboardOwner() {
                @Override
                public void lostOwnership(Clipboard clipboard, Transferable contents) {
                    System.out.println("Lost ownership");
                }
            });
        }


    } //try
    catch (Exception e) {
        System.out.println("Exception caught " + e.toString());
    } //catch
    System.out.println("midifile end ");


}

public static class FileTransferable implements Transferable {

    private List listOfFiles;

    public FileTransferable(List listOfFiles) {
        this.listOfFiles = listOfFiles;
    }

    @Override
    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[]{DataFlavor.javaFileListFlavor};
    }

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return DataFlavor.javaFileListFlavor.equals(flavor);
    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        return listOfFiles;
    }
}

  }

Here's a similar post which was never answered:

What is the required DataFlavor to copy files in Mac OSX

1

There are 1 best solutions below

0
On

I had the same problem and after hours and hours of search I came up with the following simple workaround. In Java you call a Terminal Command, that runs an apple script, telling Finder to put the file to the clilboard:

public static void copyToClipboard(String filepath) {
      String[] cmd = {"osascript", "-e", "tell app \"Finder\" to set the clipboard to ( POSIX file \""+filepath+"\" )"};
    try {
        Runtime.getRuntime().exec(cmd);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

you can then paste the file by cmd + v just as if you copied it in finder.