view src/uk/ac/qmul/eecs/depic/daw/gui/ArrangeWindow.java @ 4:473da40f3d39 tip

added html formatting to Daw/package-info.java
author Fiore Martin <f.martin@qmul.ac.uk>
date Thu, 25 Feb 2016 17:50:09 +0000
parents 3074a84ef81e
children
line wrap: on
line source
/*  
 Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.

 Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
	
 This program 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.

 This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package uk.ac.qmul.eecs.depic.daw.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.border.Border;

import uk.ac.qmul.eecs.depic.daw.Selection;
import uk.ac.qmul.eecs.depic.daw.SoundWave;
import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
import uk.ac.qmul.eecs.depic.daw.SoundWaveListener;

/**
 * 
 * The panel containing the audio tracks.
 * 
 * Tracks are arranged in a scrollable BoxLayout. 
 *
 */
public class ArrangeWindow extends JPanel implements PropertyChangeListener, SoundWaveListener {
	private static final long serialVersionUID = 1L;
	public static final Color BACKGROUND_COLOR = new Color(226,226,226);
	public static final int SPACE_BETWEEN_TRACKS = 5;
	public static final Border BORDER_UNSELECTED = BorderFactory.createCompoundBorder(
			BorderFactory.createEmptyBorder(1, 0, 1, 0), BorderFactory.createMatteBorder(1, 0, 1, 0, Color.GRAY));
	public static final Border BORDER_SELECTED = BorderFactory.createCompoundBorder(
				BorderFactory.createMatteBorder(1, 0, 1, 0, new Color(214, 193, 99)), BorderFactory.createMatteBorder(1, 0, 1, 0, Color.GRAY));
			
	
	/* tracks panel contains the rule and audio tracks  */
	private JScrollPane tracksPanelScroll;
	/* view of the trackPanelScroll and panel where all the audio tracks are places */
	private JPanel tracksPanel;
	/* left panel with AudioTrackParameters */
	private JPanel parametersPanel;
	private Rule rule;
	private int currentTrackIndex;
	private List<AudioTrack> tracks;
	private List<AudioTrackParameters> audioTrackParameters;
	private List<Component> spacesBetweenTracks;
	private List<Component> spacesBetweenTrackParameters;
	private MouseInteraction mouseInteraction;
	
	
	public ArrangeWindow(){
		tracks = new ArrayList<>();
		audioTrackParameters = new ArrayList<>();
		spacesBetweenTracks = new ArrayList<>();
		spacesBetweenTrackParameters = new ArrayList<>();
		mouseInteraction = new MouseInteraction();
		
		setLayout(new BorderLayout());
		setBackground(BACKGROUND_COLOR);
		
		/* left panel: header (of the same height as rule) and parameters */
		parametersPanel = new JPanel();
		parametersPanel.setLayout(new BoxLayout(parametersPanel,BoxLayout.Y_AXIS));
		parametersPanel.add(Box.createRigidArea(new Dimension(247,Rule.HEIGHT+3)));
		parametersPanel.setBackground(BACKGROUND_COLOR);
		
		add(parametersPanel,BorderLayout.WEST);
		
		/* right panel: scrollable panel with rule and tracks as header*/
		tracksPanel = new JPanel();
		tracksPanel.setLayout(new BoxLayout(tracksPanel,BoxLayout.Y_AXIS));
		tracksPanel.setBackground(BACKGROUND_COLOR);
		rule = new Rule();
		rule.setBackground(BACKGROUND_COLOR);
		
		
		tracksPanelScroll = new JScrollPane(tracksPanel,
				JScrollPane.VERTICAL_SCROLLBAR_NEVER,
				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		
		/* disable the scrolling via the left-right keys to let the cursor scrub handler 
		 * take over when the track is focused */
		tracksPanelScroll.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "none");
		tracksPanelScroll.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "none");
		/* setPreferredSize important, otherwise the view port gets resized to the track size * 
		 * as soon as the window is resized and therefore the scroll bar disappear            */
		tracksPanelScroll.setPreferredSize(new Dimension(500,500));
		/* rule is set as the header of the scroll pane with audio tracks */
		tracksPanelScroll.setColumnHeaderView(rule);
		
		add(tracksPanelScroll, BorderLayout.CENTER);
		
		currentTrackIndex = -1;
	}
	
	/**
	 * Adds a track to this component. The added track is automatically set as the current track.
	 * 
	 * Adding a track that's already in the arrange window will have no effect.
	 *   
	 * @param track the new track to add. It must be a track that is not currently 
	 * contained in this arrange window
	 */
	public void addTrack(AudioTrack track){
		if(tracks.contains(track))
			return;
		
		AudioTrackParameters trackParameters = new AudioTrackParameters(track,1);
		
		/* install the mouse listener */
		track.addMouseListener(mouseInteraction);
		trackParameters.addMouseListener(mouseInteraction); // FIXME verify 
		
		
		/* add track and parameters in the respective lists */
		tracks.add(track);
		audioTrackParameters.add(trackParameters);
		Component box1 = Box.createVerticalStrut(SPACE_BETWEEN_TRACKS);
		Component box2 = Box.createVerticalStrut(SPACE_BETWEEN_TRACKS);
		spacesBetweenTracks.add(box1);
		spacesBetweenTrackParameters.add(box2);
		
		/* add track and parameters in the respective panels */
		parametersPanel.add(box1);
		parametersPanel.add(trackParameters);
		tracksPanel.add(box2);
		tracksPanel.add(track);
		/* set the new track as the current */
		setCurrentTrack(getTracksCount()-1);
		
		/* set the label. Users will count starting from 1 and not from 0 */
		trackParameters.setLabel("new Track "+(getCurrentTrackIndex()+1));
		repaint();
	}
	
	public AudioTrack getTrackAt(int index){
		return  tracks.get(index);
	}
	
	public AudioTrackParameters getTrackParametersAt(int index){
		return audioTrackParameters.get(index);
	}
	
	/**
	 * 
	 * @return the current track or {@code null} if there are no tracks
	 * in this arrange window
	 */
	public AudioTrack getCurrentTrack(){
		if(currentTrackIndex == -1)
			return null;
		return  tracks.get(currentTrackIndex);
	}
	
	/**
	 * Sets the track at the specified index as current. 
	 * 
	 * @param index the index of the new current track 
	 * @throws ArrayIndexOutOfBoundsException if {@code index} is lower than 0 or greater or equal to {@code getTracksCount()} 
	 * 
	 * 
	 */
	public void setCurrentTrack(int index){
		/* removes this from the listeners of the previous current track */
		if(currentTrackIndex != -1){
			AudioTrack previousTrack = getTrackAt(currentTrackIndex);
			previousTrack.removePropertyChangeListener(this);
			previousTrack.getSoundWave().removeSoundWaveListener(this);
			previousTrack.setBorder(BORDER_UNSELECTED); 
			audioTrackParameters.get(currentTrackIndex).setBorder(BORDER_UNSELECTED);
		}
		currentTrackIndex = index;
		
		AudioTrack currentTrack = getTrackAt(currentTrackIndex); 
		/* install listeners in the new audio track */
		currentTrack.addPropertyChangeListener(this);
		currentTrack.getSoundWave().addSoundWaveListener(this);
		/* set the borders */
		currentTrack.setBorder(BORDER_SELECTED); 
		audioTrackParameters.get(currentTrackIndex).setBorder(BORDER_SELECTED);

		rule.setAudioTrack(getTrackAt(currentTrackIndex));
	}
	
	/**
	 * 
	 * @return the index of the currently selected track or {@code -1} if no track 
	 * is open in the arrange window
	 */
	public int getCurrentTrackIndex(){
		return currentTrackIndex;
	}
	
	/**
	 * Returns the number of tracks in this arrange window.
	 * 
	 * @return the number of tracks in this arrange window
	 */
	public int getTracksCount(){
		return tracks.size();
	}
	
	public Rule getRule(){
		return rule;
	}
	
	/**
	 * 
	 * On {@code SELECTION_CHANGED} event from the current audio track, updates the other
	 * tracks accordingly. 
	 * 
	 * This enforces a unique selection and cursor position over the whole arrange window  
	 * 
	 * @param evt the sound wave event 
	 * 
	 */
	@Override
	public void update(SoundWaveEvent evt) {
		
		if(SoundWaveEvent.SCAN.equals(evt.getType())){
			for(int i=0; i< getTracksCount(); i++){
				SoundWave wave = getTrackAt(i).getSoundWave();
				/* only change selection if it's not the one that triggered the event */
				if(!wave.equals(evt.getSource())){  
					wave.scan((Integer)evt.getArgs());
				}
			}
		}
		
		if(SoundWaveEvent.POSITION_CHANGED.equals(evt.getType()) ){
			for(AudioTrack track : tracks){
				if(!track.getSoundWave().equals(evt.getSource())){
					track.getSoundWave().setPosition((Integer)evt.getArgs());
				}
			}
			
			/* this is called from the current audio track sound wave. The * 
			 * selection is changed in the other audio tracks accordingly  */
			//SoundWave currentWave = evt.getSource();
			
//			for(int i=0; i< getTracksCount(); i++){
//				SoundWave wave = getTrackAt(i).getSoundWave();
//				/* only change selection if it's not the one that triggered the event */
//				if(!wave.equals(currentWave)){  
//					wave.setSelection((Selection)evt.getArgs());
//				}
//			}
		}
	}
	
	private class MouseInteraction extends MouseAdapter {
		@Override
		public void mousePressed(MouseEvent e){
			Component c = e.getComponent();
			int index = tracks.lastIndexOf(c);
			
			if(index == getCurrentTrackIndex()){
				return;
			}else if(index != -1){
				setCurrentTrack(index);
				return;
			}
			
			index = audioTrackParameters.lastIndexOf(c);
			if(index == getCurrentTrackIndex()){
				return;
			}else if(index != -1){
				setCurrentTrack(index);
				return;
			}
			
		}
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		switch(evt.getPropertyName()){
			case "mouseDragSelection" : {
				Selection selection = (Selection)evt.getNewValue();
				for(AudioTrack track : tracks){
					if(!track.equals(evt.getSource())){
						track.trackInteraction.setMouseSelection(
								selection.getStart(), selection.getEnd());
					}
				}
			} break;
		}
	}
	
	
	
}