uk.co.modularaudio.service.gui.impl.racktable.back.RackLinksCompositeOverlay.java Source code

Java tutorial

Introduction

Here is the source code for uk.co.modularaudio.service.gui.impl.racktable.back.RackLinksCompositeOverlay.java

Source

/**
 *
 * Copyright (C) 2015 - Daniel Hams, Modular Audio Limited
 *                      daniel.hams@gmail.com
 *
 * Mad is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mad is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Mad.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package uk.co.modularaudio.service.gui.impl.racktable.back;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.CubicCurve2D;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.co.modularaudio.service.bufferedimageallocation.BufferedImageAllocationService;
import uk.co.modularaudio.service.gui.plugs.GuiChannelPlug;
import uk.co.modularaudio.service.guicompfactory.AbstractGuiAudioComponent;
import uk.co.modularaudio.util.audio.gui.mad.rack.RackComponent;
import uk.co.modularaudio.util.audio.gui.mad.rack.RackComponentProperties;
import uk.co.modularaudio.util.audio.gui.mad.rack.RackDataModel;
import uk.co.modularaudio.util.audio.gui.mad.rack.RackIOLink;
import uk.co.modularaudio.util.audio.gui.mad.rack.RackIOLinkEvent;
import uk.co.modularaudio.util.audio.gui.mad.rack.RackIOLinkListener;
import uk.co.modularaudio.util.audio.gui.mad.rack.RackLink;
import uk.co.modularaudio.util.audio.gui.mad.rack.RackLinkEvent;
import uk.co.modularaudio.util.audio.gui.mad.rack.RackLinkListener;
import uk.co.modularaudio.util.audio.mad.MadChannelDefinition.MadChannelDirection;
import uk.co.modularaudio.util.audio.mad.MadChannelInstance;
import uk.co.modularaudio.util.bufferedimage.AllocationBufferType;
import uk.co.modularaudio.util.bufferedimage.AllocationLifetime;
import uk.co.modularaudio.util.bufferedimage.AllocationMatch;
import uk.co.modularaudio.util.bufferedimage.TiledBufferedImage;
import uk.co.modularaudio.util.exception.DatastoreException;
import uk.co.modularaudio.util.table.Span;
import uk.co.modularaudio.util.table.TableModelEvent;
import uk.co.modularaudio.util.table.TableModelListener;

public class RackLinksCompositeOverlay extends JComponent {
    private static final long serialVersionUID = 3441350163975600834L;

    private static Log log = LogFactory.getLog(RackLinksCompositeOverlay.class.getName());

    private static final int WIRE_DIP_PIXELS = 30;

    private static final float WIRE_STROKE_WIDTH = 10.0f;

    private static final BasicStroke WIRE_BODY_STROKE = new BasicStroke(WIRE_STROKE_WIDTH, BasicStroke.CAP_ROUND,
            BasicStroke.JOIN_ROUND);
    private static final CompositeStroke WIRE_STROKE = new CompositeStroke(WIRE_BODY_STROKE, new BasicStroke(2.0f));
    //   private static final BasicStroke BASIC_STROKE_OF_ONE = new BasicStroke( 1.0f );

    private final static float GENERAL_WIRE_TRANSPARENCY = 0.7f;
    private final static float ONE_WIRE_TRANSPARENCY = 0.7f;

    private static final AlphaComposite GENERAL_WIRE_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
            GENERAL_WIRE_TRANSPARENCY);
    private static final AlphaComposite ONE_WIRE_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
            ONE_WIRE_TRANSPARENCY);

    // Instance stuff

    private final BufferedImageAllocationService bias;

    private final AllocationMatch allocationMatch = new AllocationMatch();

    private final RackTableWithLinks rackTable;

    private RackDataModel dataModel;

    private final OverlayModelListener overlayModelListener;

    private TiledBufferedImage tbi;
    private BufferedImage bi;
    private Graphics2D g2d;
    private int biWidth;
    private int biHeight;

    private class OverlayModelListener implements TableModelListener<RackComponent, RackComponentProperties>,
            RackLinkListener, RackIOLinkListener {
        private final RackLinksCompositeOverlay rlco;

        public OverlayModelListener(final RackLinksCompositeOverlay rlco) {
            this.rlco = rlco;
        }

        @Override
        public void tableChanged(final TableModelEvent<RackComponent, RackComponentProperties> event) {
            final int eventType = event.getType();
            final int startRow = event.getFirstRow();
            final int endRow = event.getLastRow();
            if (startRow == 0 && endRow == Integer.MAX_VALUE) {
                log.error("NOT IMPLEMENTED!");
            } else {
                // Individual rows
                switch (eventType) {
                case TableModelEvent.INSERT:
                    // New entries added into the table
                    // We don't care as they won't have any links to start with and we'll receive the
                    // "new link" event in the link event listener
                    break;
                case TableModelEvent.UPDATE:
                    rlco.redrawAllLinks();
                    repaint();
                    break;
                case TableModelEvent.DELETE:
                    // Entries removed from the table
                    // Again, we don't care as we'll receive the link delete event in the rack link listener.
                    break;
                }
            }
        }

        @Override
        public void ioLinksChanged(final RackIOLinkEvent outEvent) {
            rlco.redrawAllLinks();
            repaint();
        }

        @Override
        public void linksChanged(final RackLinkEvent outEvent) {
            rlco.redrawAllLinks();
            repaint();
        }
    };

    public RackLinksCompositeOverlay(final BufferedImageAllocationService bias, final RackDataModel dataModel,
            final RackTableWithLinks rackTable) throws DatastoreException {
        this.bias = bias;
        this.rackTable = rackTable;
        this.dataModel = dataModel;
        setOpaque(false);

        // Work out how big the buffered image needs to be and allocate one
        final Span span = dataModel.getSpan();
        final Dimension rackGridSize = rackTable.getGridSize();
        biWidth = span.x * rackGridSize.width;
        biHeight = span.y * rackGridSize.height;

        tbi = bias.allocateBufferedImage(this.getClass().getSimpleName(), allocationMatch, AllocationLifetime.SHORT,
                AllocationBufferType.TYPE_INT_ARGB, biWidth, biHeight);
        bi = tbi.getUnderlyingBufferedImage();
        g2d = bi.createGraphics();
        g2d.setComposite(GENERAL_WIRE_COMPOSITE);

        overlayModelListener = new OverlayModelListener(this);

        startListening();

        setBounds(0, 0, biWidth, biHeight);
    }

    private final void startListening() {
        dataModel.addListener(overlayModelListener);
        dataModel.addRackLinksListener(overlayModelListener);
        dataModel.addRackIOLinksListener(overlayModelListener);
    }

    private final void stopListening() {
        dataModel.removeRackIOLinksListener(overlayModelListener);
        dataModel.removeRackLinksListener(overlayModelListener);
        dataModel.removeListener(overlayModelListener);
    }

    public void setDataModel(final RackDataModel dataModel) throws DatastoreException {
        stopListening();

        this.dataModel = dataModel;

        final Span span = dataModel.getSpan();
        final Dimension rackGridSize = rackTable.getGridSize();
        final int newBiWidth = span.x * rackGridSize.width;
        final int newBiHeight = span.y * rackGridSize.height;

        if (biWidth != newBiWidth || biHeight != newBiHeight) {
            //         log.debug("Reallocating changed buffered image due to dimensions diff");
            //         log.debug("(" + biWidth + "," + biHeight + ")->(" + newBiWidth + "," + newBiHeight + ")");
            bias.freeBufferedImage(tbi);

            tbi = bias.allocateBufferedImage(this.getClass().getSimpleName(), allocationMatch,
                    AllocationLifetime.SHORT, AllocationBufferType.TYPE_INT_ARGB, newBiWidth, newBiHeight);
            bi = tbi.getUnderlyingBufferedImage();
            g2d = bi.createGraphics();

            setBounds(0, 0, newBiWidth, newBiHeight);
            biWidth = newBiWidth;
            biHeight = newBiHeight;
        }

        redrawAllLinks();

        startListening();
    }

    @Override
    public void paint(final Graphics g) {
        //      log.debug("Paint was called with rect " + g.getClipBounds());
        final Graphics2D pg2d = (Graphics2D) g;
        pg2d.setComposite(GENERAL_WIRE_COMPOSITE);
        pg2d.drawImage(bi, 0, 0, null);
    }

    public final void destroy() {
        stopListening();

        try {
            g2d = null;
            bi = null;
            bias.freeBufferedImage(tbi);
            tbi = null;
        } catch (final DatastoreException e) {
            final String msg = "DatastoreException caught returning composite links master buffer: " + e.toString();
            log.error(msg, e);
        }
    }

    void redrawAllLinks() {
        g2d.setComposite(AlphaComposite.Clear);
        g2d.fillRect(0, 0, biWidth, biHeight);

        g2d.setComposite(ONE_WIRE_COMPOSITE);

        final int numLinks = this.dataModel.getNumLinks();
        for (int i = 0; i < numLinks; ++i) {
            final RackLink rl = this.dataModel.getLinkAt(i);
            drawOneLink(rl);
        }

        final int numIOLinks = this.dataModel.getNumIOLinks();
        for (int i = 0; i < numIOLinks; ++i) {
            final RackIOLink ril = this.dataModel.getIOLinkAt(i);
            drawOneIOLink(ril);
        }
    }

    private void drawOneLink(final RackLink rl) {
        // For now lets assume some magic about how the end points of the cable are computed.
        final RackComponent sourceRackComponent = rl.getProducerRackComponent();
        final AbstractGuiAudioComponent sourceGuiComponent = rackTable
                .getGuiComponentFromTableModel(sourceRackComponent);
        final MadChannelInstance sourceRackComponentChannel = rl.getProducerChannelInstance();
        final GuiChannelPlug sourceGuiPlug = sourceGuiComponent
                .getPlugFromMadChannelInstance(sourceRackComponentChannel);
        final Point sourcePoint = RackWirePositionHelper.calculateCenterForComponentPlug(rackTable, dataModel,
                sourceGuiComponent, sourceRackComponent, sourceGuiPlug);

        final RackComponent sinkRackComponent = rl.getConsumerRackComponent();
        final AbstractGuiAudioComponent sinkGuiComponent = rackTable
                .getGuiComponentFromTableModel(sinkRackComponent);
        final MadChannelInstance sinkRackComponentChannel = rl.getConsumerChannelInstance();
        final GuiChannelPlug sinkGuiPlug = sinkGuiComponent.getPlugFromMadChannelInstance(sinkRackComponentChannel);
        final Point sinkPoint = RackWirePositionHelper.calculateCenterForComponentPlug(rackTable, dataModel,
                sinkGuiComponent, sinkRackComponent, sinkGuiPlug);
        drawLine(sourcePoint, sinkPoint);
    }

    private void drawOneIOLink(final RackIOLink ril) {
        // Bit of a hack....
        final RackComponent masterIOComponent = dataModel.getContentsAtPosition(0, 0);
        final MadChannelInstance ioChannelInstance = ril.getRackChannelInstance();
        final RackComponent rackComponent = ril.getRackComponent();
        final MadChannelInstance rackComponentChannelInstance = ril.getRackComponentChannelInstance();
        final MadChannelDirection direction = rackComponentChannelInstance.definition.direction;

        // For now lets assume some magic about how the end points of the cable are computed.
        final RackComponent sourceRackComponent = (direction == MadChannelDirection.PRODUCER ? rackComponent
                : masterIOComponent);
        final AbstractGuiAudioComponent sourceGuiComponent = rackTable
                .getGuiComponentFromTableModel(sourceRackComponent);
        final MadChannelInstance sourceRackComponentChannel = (direction == MadChannelDirection.PRODUCER
                ? rackComponentChannelInstance
                : ioChannelInstance);
        final GuiChannelPlug sourceGuiPlug = sourceGuiComponent
                .getPlugFromMadChannelInstance(sourceRackComponentChannel);

        final Point sourcePoint = RackWirePositionHelper.calculateCenterForComponentPlug(rackTable, dataModel,
                sourceGuiComponent, sourceRackComponent, sourceGuiPlug);

        final RackComponent sinkRackComponent = (direction == MadChannelDirection.PRODUCER ? masterIOComponent
                : rackComponent);
        final AbstractGuiAudioComponent sinkGuiComponent = rackTable
                .getGuiComponentFromTableModel(sinkRackComponent);
        final MadChannelInstance sinkRackComponentChannel = (direction == MadChannelDirection.PRODUCER
                ? ioChannelInstance
                : rackComponentChannelInstance);
        final GuiChannelPlug sinkGuiPlug = sinkGuiComponent.getPlugFromMadChannelInstance(sinkRackComponentChannel);
        final Point sinkPoint = RackWirePositionHelper.calculateCenterForComponentPlug(rackTable, dataModel,
                sinkGuiComponent, sinkRackComponent, sinkGuiPlug);

        drawLine(sourcePoint, sinkPoint);
    }

    private final void drawLine(final Point sourcePoint, final Point sinkPoint) {
        final int fromX = sourcePoint.x;
        final int fromY = sourcePoint.y;
        final int toX = sinkPoint.x;
        final int toY = sinkPoint.y;

        float f1, f2, f3, f4, f5, f6, f7, f8 = 0.0f;
        f1 = fromX;
        f2 = fromY;
        f3 = fromX;
        f4 = fromY + WIRE_DIP_PIXELS;
        f5 = toX;
        f6 = toY + WIRE_DIP_PIXELS;
        f7 = toX;
        f8 = toY;
        final CubicCurve2D cubicCurve = new CubicCurve2D.Float(f1, f2, f3, f4, f5, f6, f7, f8);

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.setColor(Color.BLACK);
        g2d.setStroke(WIRE_STROKE);
        g2d.draw(cubicCurve);

        g2d.setColor(Color.BLUE);
        //      g2d.setColor( Color.YELLOW );
        g2d.setStroke(WIRE_BODY_STROKE);
        g2d.draw(cubicCurve);

        //      g2d.drawLine( sourcePoint.x, sourcePoint.y, sinkPoint.x, sinkPoint.y );
    }

}