Mercurial > hg > cmdp
view src/uk/ac/qmul/eecs/depic/daw/gui/Rule.java @ 2:c0412c81d274
Added documentation
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Thu, 18 Feb 2016 18:35:26 +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.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.geom.Line2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.DecimalFormat; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleState; import javax.accessibility.AccessibleStateSet; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.event.MouseInputAdapter; import uk.ac.qmul.eecs.depic.daw.Selection; import uk.ac.qmul.eecs.depic.daw.SoundWave; /** * * The rule on top of the arrange window. * */ public class Rule extends JComponent implements PropertyChangeListener , Accessible { private static final long serialVersionUID = 1L; public static final int DEFAULT_TICK_INTERVAL = 50; public static final int TICK_HEIGHT = 10; public static final int HEIGHT = 40; public static final int CURSOR_HEIGHT = 10; public static final int CURSOR_WIDTH = 20; public static final Color LOOP_MARKERS_COLOR = new Color(10,100,30); public static final Color CURSOR_COLOR = AudioTrack.WAVE_COLOR; private static final DecimalFormat TICK_FORMAT = new DecimalFormat("###.#"); private AudioTrack track; private int scaleFactor; private Polygon cursor; private Polygon loopStartMark; private Polygon loopEndMark; public Rule(){ Dimension size = new Dimension(AudioTrack.MAX_TRACK_WIDTH,HEIGHT); setMaximumSize(size); setPreferredSize(new Dimension(0,HEIGHT)); setBorder(BorderFactory.createMatteBorder(0,0,1,0,Color.black)); /* a triangle initially centred on 0*/ cursor = new Polygon( new int[]{0,-CURSOR_WIDTH/2,CURSOR_WIDTH/2}, new int[] {HEIGHT,HEIGHT-CURSOR_HEIGHT,HEIGHT-CURSOR_HEIGHT}, 3); loopStartMark = new Polygon(); loopEndMark = new Polygon(); MouseInteraction mouseInteraction = new MouseInteraction(); addMouseListener(mouseInteraction); addMouseMotionListener(mouseInteraction); } @Override public void paintComponent(Graphics g){ Graphics2D g2 = (Graphics2D)g; Color oldColor = g2.getColor(); g2.setColor(getBackground()); g2.fillRect(0, 0, getWidth(), getHeight()); g2.setColor(getForeground()); Line2D line = new Line2D.Float(); int height = getHeight(); /* draw rule's ticks */ if(track.getSecondsPerPixel() == 0){ for(int i=DEFAULT_TICK_INTERVAL; i< getSize().width; i += DEFAULT_TICK_INTERVAL ){ line.setLine(i, height, i, height-TICK_HEIGHT); g2.draw(line); } }else{ int tickInterval = (int) ( (1/track.getSecondsPerPixel() / 2 )); for(int i=tickInterval, j=1; i< getSize().width; i += tickInterval, j++ ){ if(j % 2 == 1){ line.setLine(i, height, i, height-TICK_HEIGHT/3); g2.draw(line); }else{ line.setLine(i, height, i, height-TICK_HEIGHT); g2.draw(line); String tick = TICK_FORMAT.format(j/2);//track.getSecondsPerPixel()*i); int stringLen = (int)g2.getFontMetrics().getStringBounds(tick, g2).getWidth(); g2.drawString(tick, i-(stringLen/2), height-TICK_HEIGHT-3); } } } /* draw the cursor in WAVE_COLOR */ g2.setColor(CURSOR_COLOR); g2.fill(cursor); /* draw the loop markers, if any */ g2.setColor(LOOP_MARKERS_COLOR); g2.fill(loopStartMark); g2.fill(loopEndMark); if(loopEndMark.npoints != 0){ g2.drawLine(loopStartMark.xpoints[0], HEIGHT - (loopStartMark.getBounds().height/2), loopEndMark.xpoints[0]-1,// with -1 it's drawn it better HEIGHT - (loopEndMark.getBounds().height/2) ); } g2.setColor(oldColor); } public void setAudioTrack(AudioTrack t){ /* removes itself as a listener to the previous track, if any */ if(track != null){ track.removePropertyChangeListener(this); } /* adds itself as a listener to the new track */ if(t != null){ t.addPropertyChangeListener(this); scaleFactor = t.getScaleFactor(); }else { scaleFactor = 1; } track = t; } public int getCursorPos() { Rectangle bounds = cursor.getBounds(); return bounds.x + bounds.width/2; } public void setCursorPos(int position) { Rectangle bounds = cursor.getBounds(); int currentCenterX = bounds.x + bounds.width/2; cursor.translate(position-currentCenterX, 0); } @Override public void propertyChange(PropertyChangeEvent evt) { switch(evt.getPropertyName()){ case "cursorPos" : { setCursorPos((Integer)evt.getNewValue()); repaint(); break; } case "scaleFactor" : { scaleFactor = (Integer)evt.getNewValue(); repaint(); break; } case "mouseDragSelection" : { Selection selection = (Selection)evt.getNewValue(); if(selection.isOpen() ){ // no loop loopStartMark.reset(); loopEndMark.reset(); }else{ loopStartMark.reset(); /* first point is the actual marker */ loopStartMark.addPoint(selection.getStart(), HEIGHT-(CURSOR_HEIGHT)); loopStartMark.addPoint(selection.getStart()+(CURSOR_WIDTH/2), HEIGHT-(CURSOR_HEIGHT*2)); loopStartMark.addPoint(selection.getStart()+(CURSOR_WIDTH/2),HEIGHT); loopEndMark.reset(); /* first point is the actual marker */ loopEndMark.addPoint(selection.getEnd(),HEIGHT-(CURSOR_HEIGHT)); loopEndMark.addPoint(selection.getEnd()-(CURSOR_WIDTH/2),HEIGHT); loopEndMark.addPoint(selection.getEnd()-(CURSOR_WIDTH/2),HEIGHT-(CURSOR_HEIGHT*2)); } repaint(); break; } case "preferredSize" : { /* resize the width acording to the new value of the listened component */ Dimension d = (Dimension)evt.getNewValue(); setPreferredSize(new Dimension(d.width,HEIGHT)); break; } } } @Override public AccessibleContext getAccessibleContext(){ if(accessibleContext == null){ accessibleContext = new AccessibleRule(); } return accessibleContext; } private class MouseInteraction extends MouseInputAdapter { private Polygon clickedMark; // either loopStartMark or loopEndMark int clickOffset; @Override public void mousePressed(MouseEvent evt){ if(loopStartMark.contains(evt.getX(),evt.getY())){ // click on the loop start mark clickedMark = loopStartMark; clickOffset = evt.getX() - loopStartMark.xpoints[0]; }else if(loopEndMark.contains(evt.getX(),evt.getY())){ // click on the loop end mark clickedMark = loopEndMark; clickOffset = evt.getX() - loopEndMark.xpoints[0]; }else if(cursor.contains(evt.getX(),evt.getY())){ clickedMark = cursor; clickOffset = evt.getX() - cursor.xpoints[0]; } } @Override public void mouseDragged(MouseEvent evt){ /* check again that clickedMark is != null and not reset * * things might change in the middle of the mouse interaction * * if the property change listener is called from another source */ if(clickedMark == null) return; if(clickedMark != cursor && clickedMark.npoints > 0 && track != null){ // loop marks int mouseSelectionX = 0; int mouseSelectionY = 0; if(clickedMark == loopStartMark){ // dragging the start mark if(evt.getX() > loopEndMark.xpoints[0] - 1 || (evt.getX()-clickOffset < 0) ) return; // can't drag start mark too right, past the end mark or too left past the screen mouseSelectionX = evt.getX()-clickOffset; mouseSelectionY = loopEndMark.xpoints[0]; }else{ // dragging the end mark if(evt.getX() < loopStartMark.xpoints[0] + 1) return; // can't drag end mark too left, past the end mark mouseSelectionX = loopStartMark.xpoints[0]; mouseSelectionY = evt.getX()-clickOffset; } scrollRectToVisible(new Rectangle(evt.getX(), evt.getY(), 1, 1)); track.trackInteraction.setMouseSelection(mouseSelectionX, mouseSelectionY); } else if (clickedMark == cursor){ // cursor mark track.getSoundWave().scan(evt.getX()); } } @Override public void mouseReleased(MouseEvent evt){ if(clickedMark != null){ if(clickedMark == cursor){ track.getSoundWave().scan(SoundWave.STOP_SCANNING); // stop scrubbing }else track.getSoundWave().setSelection(track.trackInteraction.getMouseSelection()); } clickedMark = null; clickOffset = 0; } @Override public void mouseMoved(MouseEvent evt){ if(cursor.contains(evt.getPoint())){ setCursor(new Cursor(Cursor.E_RESIZE_CURSOR)); }else if(loopStartMark.contains(evt.getPoint())){ setCursor(new Cursor(Cursor.E_RESIZE_CURSOR)); }else if(loopEndMark.contains(evt.getPoint())){ setCursor(new Cursor(Cursor.W_RESIZE_CURSOR)); }else{ setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } } protected class AccessibleRule extends AccessibleJComponent { private static final long serialVersionUID = 1L; protected AccessibleRule(){ this.setAccessibleName("Time Bar"); this.setAccessibleDescription( "Measures he time of tracks"); } @Override public AccessibleRole getAccessibleRole(){ return AccessibleRole.RULER; } @Override public AccessibleStateSet getAccessibleStateSet(){ AccessibleStateSet states = super.getAccessibleStateSet(); states.add(AccessibleState.HORIZONTAL); states.add(AccessibleState.VISIBLE); return states; } } }