com.maccasoft.composer.InstrumentEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.maccasoft.composer.InstrumentEditor.java

Source

/*
 * Copyright (c) 2016 Marco Maccaferri and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Marco Maccaferri - initial API and implementation
 */

package com.maccasoft.composer;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.observable.list.IListChangeListener;
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;

import com.maccasoft.composer.internal.ImageRegistry;
import com.maccasoft.composer.model.Command;
import com.maccasoft.composer.model.Instrument;

import jssc.SerialPort;

public class InstrumentEditor extends Window {

    Text name;
    ScrolledComposite sc;
    Composite instrumentContainer;
    InstrumentChart chart;
    PianoKeyboard keyBoard;

    final List<Command> list = new ArrayList<Command>();
    final WritableList observableList = new WritableList(list, Command.class);

    Instrument instrument;
    DataBindingContext context;
    private FontMetrics fontMetrics;
    private SerialPort serialPort;

    static int lastVoice = 1;

    final IListChangeListener listChangedListener = new IListChangeListener() {

        @Override
        public void handleListChange(ListChangeEvent event) {
            event.diff.accept(new ListDiffVisitor() {

                @Override
                public void handleAdd(int index, Object element) {
                    Control[] childs = instrumentContainer.getChildren();
                    Control control = createInstrumentControls((Command) element);
                    if (index < childs.length) {
                        control.moveAbove(childs[index]);
                    }
                    ((Command) element).addPropertyChangeListener(propertyChangeListener);
                }

                @Override
                public void handleRemove(int index, Object element) {
                    Control[] childs = instrumentContainer.getChildren();
                    childs[index].dispose();
                    ((Command) element).removePropertyChangeListener(propertyChangeListener);
                }
            });
            Display.getDefault().asyncExec(new Runnable() {

                @Override
                public void run() {
                    if (sc.isDisposed()) {
                        return;
                    }
                    Rectangle r = sc.getClientArea();
                    sc.setMinSize(sc.getContent().computeSize(r.width, SWT.DEFAULT));
                    sc.layout(true, true);
                    Display.getDefault().timerExec(250, updateRunnable);
                }
            });
        }
    };

    final PropertyChangeListener propertyChangeListener = new PropertyChangeListener() {

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            Display.getDefault().asyncExec(new Runnable() {

                @Override
                public void run() {
                    Display.getDefault().timerExec(250, updateRunnable);
                }
            });
        }
    };

    final Runnable updateRunnable = new Runnable() {

        @Override
        public void run() {
            if (chart.getControl().isDisposed()) {
                return;
            }

            int count = 0;
            for (Command cmd : list) {
                if (!cmd.isDisabled()) {
                    count++;
                }
            }

            int[][] waveTable = new int[count][2];

            count = 0;
            for (Command cmd : list) {
                if (!cmd.isDisabled()) {
                    waveTable[count][0] = (int) cmd.getCommand();
                    waveTable[count][1] = (int) cmd.getArgument();
                    count++;
                }
            }

            chart.setWaveTable(waveTable);
            keyBoard.setWaveTable(waveTable);
        }
    };

    public InstrumentEditor(Shell parentShell, Instrument instrument) {
        super(parentShell);
        this.instrument = instrument;
        setBlockOnOpen(true);
        setReturnCode(CANCEL);
    }

    @Override
    protected void configureShell(Shell newShell) {
        super.configureShell(newShell);

        GC gc = new GC(newShell);
        fontMetrics = gc.getFontMetrics();
        gc.dispose();

        Rectangle screen = newShell.getDisplay().getClientArea();

        Rectangle rect = new Rectangle(0, 0, Dialog.convertHorizontalDLUsToPixels(fontMetrics, 760),
                Dialog.convertVerticalDLUsToPixels(fontMetrics, 320));
        rect.x = (screen.width - rect.width) / 2;
        rect.y = (screen.height - rect.height) / 2;
        if (rect.y < 0) {
            rect.height += rect.y * 2;
            rect.y = 0;
        }

        newShell.setLocation(rect.x, rect.y);
        newShell.setSize(rect.width, rect.height);

        FillLayout layout = new FillLayout();
        layout.marginWidth = layout.marginHeight = 5;
        newShell.setLayout(layout);
        newShell.setText("Instrument Editor");
    }

    @Override
    protected Control createContents(Composite parent) {
        Composite container = new Composite(parent, SWT.NONE);
        container.setLayout(new GridLayout(2, false));

        context = new DataBindingContext();

        createToolBar(container);
        createControls(container);
        createButtonBar(container);

        try {
            for (Command o : instrument.getCommands()) {
                observableList.add(o.clone());
            }
        } catch (Exception e) {
            // Do nothing, clone is supported
        }
        observableList.addListChangeListener(listChangedListener);

        for (Command element : list) {
            createInstrumentControls(element);
            element.addPropertyChangeListener(propertyChangeListener);
        }

        container.addDisposeListener(new DisposeListener() {

            @Override
            public void widgetDisposed(DisposeEvent e) {
                context.dispose();
                observableList.removeListChangeListener(listChangedListener);
                for (Command element : list) {
                    element.removePropertyChangeListener(propertyChangeListener);
                }
            }
        });

        Display.getDefault().asyncExec(new Runnable() {

            @Override
            public void run() {
                Rectangle r = sc.getClientArea();
                sc.setMinSize(sc.getContent().computeSize(r.width, SWT.DEFAULT));
                sc.layout(true, true);
                Display.getDefault().timerExec(250, updateRunnable);
            }
        });

        return container;
    }

    void createToolBar(Composite parent) {
        Composite container = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout(2, false);
        layout.marginWidth = layout.marginHeight = 0;
        container.setLayout(layout);
        container.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));

        name = new Text(container, SWT.BORDER);
        name.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
        ((GridData) name.getLayoutData()).widthHint = Dialog.convertWidthInCharsToPixels(fontMetrics, 30);
        name.setText(instrument.getName());

        ToolBar toolBar = new ToolBar(container, SWT.FLAT);
        toolBar.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));

        ToolItem toolItem = new ToolItem(toolBar, SWT.PUSH);
        toolItem.setImage(ImageRegistry.getImageFromResources("document-import.png"));
        toolItem.setToolTipText("Import from file");
        toolItem.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                try {
                    handleImportFromFile();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        });

        toolItem = new ToolItem(toolBar, SWT.PUSH);
        toolItem.setImage(ImageRegistry.getImageFromResources("document-export.png"));
        toolItem.setToolTipText("Export to file");
        toolItem.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                try {
                    handleExportToFile();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    void createControls(Composite parent) {
        Group group = new Group(parent, SWT.NONE);
        group.setText("Commands");
        FillLayout fillLayout = new FillLayout();
        fillLayout.marginWidth = fillLayout.marginHeight = 0;
        group.setLayout(fillLayout);
        group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));

        sc = new ScrolledComposite(group, SWT.V_SCROLL);
        sc.setExpandHorizontal(true);
        sc.setExpandVertical(true);

        instrumentContainer = new Composite(sc, SWT.NONE);
        instrumentContainer.setLayout(new RowLayout(SWT.VERTICAL));

        sc.setContent(instrumentContainer);
        sc.addControlListener(new ControlAdapter() {

            @Override
            public void controlResized(ControlEvent e) {
                Rectangle r = sc.getClientArea();
                sc.setMinSize(sc.getContent().computeSize(r.width, SWT.DEFAULT));
            }
        });

        chart = new InstrumentChart(parent);
        chart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        Composite composite = new Composite(parent, SWT.NONE);
        GridLayout gridLayout = new GridLayout(4, false);
        gridLayout.marginWidth = gridLayout.marginHeight = 0;
        composite.setLayout(gridLayout);

        final Button square = new Button(composite, SWT.RADIO);
        square.setText("Square");
        square.setSelection(lastVoice == 1);
        square.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent event) {
                try {
                    serialPort.writeString("V1");
                    lastVoice = 1;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        final Button saw = new Button(composite, SWT.RADIO);
        saw.setText("Saw");
        saw.setSelection(lastVoice == 4);
        saw.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent event) {
                try {
                    serialPort.writeString("V4");
                    lastVoice = 4;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        final Button triangle = new Button(composite, SWT.RADIO);
        triangle.setText("Triangle");
        triangle.setSelection(lastVoice == 7);
        triangle.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent event) {
                try {
                    serialPort.writeString("V7");
                    lastVoice = 7;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        final Button noise = new Button(composite, SWT.RADIO);
        noise.setText("Noise");
        noise.setSelection(lastVoice == 8);
        noise.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent event) {
                try {
                    serialPort.writeString("V8");
                    lastVoice = 8;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        keyBoard = new PianoKeyboard(parent);
        GridData gridData = new GridData(SWT.CENTER, SWT.FILL, false, false, 2, 1);
        gridData.heightHint = 80;
        gridData.widthHint = (keyBoard.getKeyWidth() * 7) * 8 + keyBoard.getKeyWidth();
        keyBoard.setLayoutData(gridData);
        keyBoard.setSerialPort(serialPort);

        gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false, 2, 1);
        gridData.widthHint = (keyBoard.getKeyWidth() * 7) * 8 + keyBoard.getKeyWidth();
        composite.setLayoutData(gridData);
    }

    protected Control createButtonBar(Composite parent) {
        Composite composite = new Composite(parent, SWT.NONE);

        GridLayout layout = new GridLayout();
        layout.numColumns = 0;
        layout.makeColumnsEqualWidth = false;
        layout.marginWidth = 0;
        layout.marginTop = Dialog.convertVerticalDLUsToPixels(fontMetrics, IDialogConstants.VERTICAL_MARGIN);
        layout.horizontalSpacing = Dialog.convertHorizontalDLUsToPixels(fontMetrics,
                IDialogConstants.HORIZONTAL_SPACING);
        layout.verticalSpacing = Dialog.convertVerticalDLUsToPixels(fontMetrics, IDialogConstants.VERTICAL_SPACING);
        composite.setLayout(layout);
        composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));

        Button button = createButton(composite, -1, "Get Spin Data");
        button.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                StringBuilder sb = new StringBuilder();
                sb.append("DAT\n\n");

                sb.append(name.getText().replace(' ', '_'));
                sb.append("\n");

                for (Command cmd : list) {
                    if (!cmd.isDisabled()) {
                        sb.append("    " + cmd.toSpinString() + "\n");
                    }
                }

                TextDialog dlg = new TextDialog(getShell());
                dlg.setString(sb.toString());
                dlg.open();
            }
        });

        button = createButton(composite, -1, "Get C Data");
        button.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                StringBuilder sb = new StringBuilder();
                sb.append(String.format("uint32_t %s[] = {\n", name.getText().replace(' ', '_')));

                for (Command cmd : list) {
                    if (!cmd.isDisabled()) {
                        sb.append("    " + cmd.toCString() + ",\n");
                    }
                }

                sb.append("};\n");

                TextDialog dlg = new TextDialog(getShell());
                dlg.setString(sb.toString());
                dlg.open();
            }
        });

        Label label = new Label(composite, SWT.NONE);
        label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        layout.numColumns++;

        button = createButton(composite, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL);
        button.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                okPressed();
                setReturnCode(OK);
                close();
            }
        });
        getShell().setDefaultButton(button);

        button = createButton(composite, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL);
        button.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                close();
            }
        });

        return composite;
    }

    protected Button createButton(Composite parent, int id, String label) {
        ((GridLayout) parent.getLayout()).numColumns++;

        Button button = new Button(parent, SWT.PUSH);
        button.setText(label);
        button.setFont(JFaceResources.getDialogFont());
        button.setData(new Integer(id));

        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
        int widthHint = Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.BUTTON_WIDTH);
        Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
        data.widthHint = Math.max(widthHint, minSize.x);
        button.setLayoutData(data);

        return button;
    }

    Control createInstrumentControls(final Command element) {
        Composite container = new Composite(instrumentContainer, SWT.NONE);
        GridLayout layout = new GridLayout(2, false);
        layout.marginWidth = layout.marginHeight = 0;
        container.setLayout(layout);

        CommandControl control = new CommandControl(container);
        control.bind(context, element);

        ToolBar toolBar = new ToolBar(container, SWT.FLAT);

        ToolItem button = new ToolItem(toolBar, SWT.PUSH);
        button.setImage(ImageRegistry.getImageFromResources("arrow-090.png"));
        button.setToolTipText("Move up");
        button.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                int index = observableList.indexOf(element);
                if (index > 0) {
                    observableList.remove(index);
                    observableList.add(index - 1, element);
                    instrumentContainer.layout(true);
                }
            }
        });

        button = new ToolItem(toolBar, SWT.PUSH);
        button.setImage(ImageRegistry.getImageFromResources("arrow-270.png"));
        button.setToolTipText("Move down");
        button.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                int index = observableList.indexOf(element);
                if (index < (observableList.size() - 1)) {
                    observableList.remove(index);
                    observableList.add(index + 1, element);
                    instrumentContainer.layout(true);
                }
            }
        });

        button = new ToolItem(toolBar, SWT.PUSH);
        button.setImage(ImageRegistry.getImageFromResources("plus.png"));
        button.setToolTipText("Add new command");
        button.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                if (observableList.size() == 1) {
                    observableList.add(0, new Command(Command.JUMP, 0));
                } else {
                    int index = observableList.indexOf(element);
                    observableList.add(index + 1, new Command(Command.JUMP, 0));
                }
                instrumentContainer.layout();
            }
        });

        button = new ToolItem(toolBar, SWT.PUSH);
        button.setImage(ImageRegistry.getImageFromResources("minus.png"));
        button.setToolTipText("Delete");
        button.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                observableList.remove(element);
                if (observableList.size() == 0) {
                    observableList.add(new Command(Command.JUMP, -1));
                }
                instrumentContainer.layout(true);
            }
        });

        return container;
    }

    protected void okPressed() {
        instrument.setName(name.getText());
        instrument.setCommands(list);
    }

    public void setSerialPort(SerialPort serialPort) {
        this.serialPort = serialPort;
    }

    private void handleExportToFile() throws Exception {
        String name = this.name.getText();
        if ("".equals(name)) {
            name = "*.xml";
        } else {
            name += ".xml";
        }

        FileDialog dlg = new FileDialog(getShell(), SWT.SAVE);
        dlg.setFileName(name);
        dlg.setText("Save File");

        String fileName = dlg.open();
        if (fileName != null) {
            Writer os = new OutputStreamWriter(new FileOutputStream(fileName));
            try {
                for (Command cmd : list) {
                    os.write(cmd.toXmlString());
                    os.write("\n");
                }
            } finally {
                os.close();
            }
        }
    }

    private void handleImportFromFile() {
        FileDialog dlg = new FileDialog(getShell(), SWT.OPEN);
        String[] filterNames = new String[] { "Instrument Files (*.xml)" };
        String[] filterExtensions = new String[] { "*.xml" };
        dlg.setFilterNames(filterNames);
        dlg.setFilterExtensions(filterExtensions);
        dlg.setText("Open File");

        final String fileName = dlg.open();
        if (fileName != null) {
            BusyIndicator.showWhile(Display.getDefault(), new Runnable() {

                @Override
                public void run() {
                    try {
                        String line;
                        File fileToOpen = new File(fileName);
                        List<Command> list = new ArrayList<Command>();

                        BufferedReader is = new BufferedReader(new FileReader(fileToOpen));
                        try {
                            while ((line = is.readLine()) != null) {
                                if (line.contains("</instrument")) {
                                    break;
                                } else if (line.contains("<jump ") || line.contains("<set-")
                                        || line.contains("<modify-")) {
                                    list.add(Command.fromXml(line));
                                }
                            }
                        } finally {
                            is.close();
                        }

                        if (list.size() != 0) {
                            observableList.clear();
                            observableList.addAll(list);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}