annotate src/uk/ac/qmul/eecs/depic/daw/gui/Rule.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 c0412c81d274
children
rev   line source
f@0 1 /*
f@0 2 Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
f@0 3
f@0 4 Copyright (C) 2015 Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
f@0 5
f@0 6 This program is free software: you can redistribute it and/or modify
f@0 7 it under the terms of the GNU General Public License as published by
f@0 8 the Free Software Foundation, either version 3 of the License, or
f@0 9 (at your option) any later version.
f@0 10
f@0 11 This program is distributed in the hope that it will be useful,
f@0 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
f@0 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@0 14 GNU General Public License for more details.
f@0 15
f@0 16 You should have received a copy of the GNU General Public License
f@0 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
f@0 18 */
f@0 19 package uk.ac.qmul.eecs.depic.daw.gui;
f@0 20
f@0 21 import java.awt.Color;
f@0 22 import java.awt.Cursor;
f@0 23 import java.awt.Dimension;
f@0 24 import java.awt.Graphics;
f@0 25 import java.awt.Graphics2D;
f@0 26 import java.awt.Polygon;
f@0 27 import java.awt.Rectangle;
f@0 28 import java.awt.event.MouseEvent;
f@0 29 import java.awt.geom.Line2D;
f@0 30 import java.beans.PropertyChangeEvent;
f@0 31 import java.beans.PropertyChangeListener;
f@0 32 import java.text.DecimalFormat;
f@0 33
f@0 34 import javax.accessibility.Accessible;
f@0 35 import javax.accessibility.AccessibleContext;
f@0 36 import javax.accessibility.AccessibleRole;
f@0 37 import javax.accessibility.AccessibleState;
f@0 38 import javax.accessibility.AccessibleStateSet;
f@0 39 import javax.swing.BorderFactory;
f@0 40 import javax.swing.JComponent;
f@0 41 import javax.swing.event.MouseInputAdapter;
f@0 42
f@0 43 import uk.ac.qmul.eecs.depic.daw.Selection;
f@0 44 import uk.ac.qmul.eecs.depic.daw.SoundWave;
f@0 45
f@2 46 /**
f@2 47 *
f@2 48 * The rule on top of the arrange window.
f@2 49 *
f@2 50 */
f@0 51 public class Rule extends JComponent implements PropertyChangeListener , Accessible {
f@0 52 private static final long serialVersionUID = 1L;
f@0 53 public static final int DEFAULT_TICK_INTERVAL = 50;
f@0 54 public static final int TICK_HEIGHT = 10;
f@0 55 public static final int HEIGHT = 40;
f@0 56 public static final int CURSOR_HEIGHT = 10;
f@0 57 public static final int CURSOR_WIDTH = 20;
f@0 58 public static final Color LOOP_MARKERS_COLOR = new Color(10,100,30);
f@0 59 public static final Color CURSOR_COLOR = AudioTrack.WAVE_COLOR;
f@0 60 private static final DecimalFormat TICK_FORMAT = new DecimalFormat("###.#");
f@0 61
f@0 62 private AudioTrack track;
f@0 63 private int scaleFactor;
f@0 64 private Polygon cursor;
f@0 65 private Polygon loopStartMark;
f@0 66 private Polygon loopEndMark;
f@0 67
f@0 68
f@0 69 public Rule(){
f@0 70 Dimension size = new Dimension(AudioTrack.MAX_TRACK_WIDTH,HEIGHT);
f@0 71 setMaximumSize(size);
f@0 72
f@0 73 setPreferredSize(new Dimension(0,HEIGHT));
f@0 74 setBorder(BorderFactory.createMatteBorder(0,0,1,0,Color.black));
f@0 75
f@0 76 /* a triangle initially centred on 0*/
f@0 77 cursor = new Polygon(
f@0 78 new int[]{0,-CURSOR_WIDTH/2,CURSOR_WIDTH/2},
f@0 79 new int[] {HEIGHT,HEIGHT-CURSOR_HEIGHT,HEIGHT-CURSOR_HEIGHT},
f@0 80 3);
f@0 81 loopStartMark = new Polygon();
f@0 82 loopEndMark = new Polygon();
f@0 83
f@0 84 MouseInteraction mouseInteraction = new MouseInteraction();
f@0 85 addMouseListener(mouseInteraction);
f@0 86 addMouseMotionListener(mouseInteraction);
f@0 87 }
f@0 88
f@0 89 @Override
f@0 90 public void paintComponent(Graphics g){
f@0 91 Graphics2D g2 = (Graphics2D)g;
f@0 92 Color oldColor = g2.getColor();
f@0 93
f@0 94 g2.setColor(getBackground());
f@0 95 g2.fillRect(0, 0, getWidth(), getHeight());
f@0 96 g2.setColor(getForeground());
f@0 97
f@0 98 Line2D line = new Line2D.Float();
f@0 99 int height = getHeight();
f@0 100 /* draw rule's ticks */
f@0 101
f@0 102 if(track.getSecondsPerPixel() == 0){
f@0 103 for(int i=DEFAULT_TICK_INTERVAL; i< getSize().width; i += DEFAULT_TICK_INTERVAL ){
f@0 104 line.setLine(i, height, i, height-TICK_HEIGHT);
f@0 105 g2.draw(line);
f@0 106 }
f@0 107 }else{
f@0 108 int tickInterval = (int) ( (1/track.getSecondsPerPixel() / 2 ));
f@0 109 for(int i=tickInterval, j=1; i< getSize().width; i += tickInterval, j++ ){
f@0 110 if(j % 2 == 1){
f@0 111 line.setLine(i, height, i, height-TICK_HEIGHT/3);
f@0 112 g2.draw(line);
f@0 113 }else{
f@0 114 line.setLine(i, height, i, height-TICK_HEIGHT);
f@0 115 g2.draw(line);
f@0 116 String tick = TICK_FORMAT.format(j/2);//track.getSecondsPerPixel()*i);
f@0 117 int stringLen = (int)g2.getFontMetrics().getStringBounds(tick, g2).getWidth();
f@0 118 g2.drawString(tick, i-(stringLen/2), height-TICK_HEIGHT-3);
f@0 119 }
f@0 120 }
f@0 121 }
f@0 122
f@0 123
f@0 124 /* draw the cursor in WAVE_COLOR */
f@0 125 g2.setColor(CURSOR_COLOR);
f@0 126 g2.fill(cursor);
f@0 127
f@0 128 /* draw the loop markers, if any */
f@0 129 g2.setColor(LOOP_MARKERS_COLOR);
f@0 130 g2.fill(loopStartMark);
f@0 131 g2.fill(loopEndMark);
f@0 132 if(loopEndMark.npoints != 0){
f@0 133 g2.drawLine(loopStartMark.xpoints[0],
f@0 134 HEIGHT - (loopStartMark.getBounds().height/2),
f@0 135 loopEndMark.xpoints[0]-1,// with -1 it's drawn it better
f@0 136 HEIGHT - (loopEndMark.getBounds().height/2)
f@0 137 );
f@0 138 }
f@0 139
f@0 140 g2.setColor(oldColor);
f@0 141 }
f@0 142
f@0 143 public void setAudioTrack(AudioTrack t){
f@0 144 /* removes itself as a listener to the previous track, if any */
f@0 145 if(track != null){
f@0 146 track.removePropertyChangeListener(this);
f@0 147 }
f@0 148 /* adds itself as a listener to the new track */
f@0 149 if(t != null){
f@0 150 t.addPropertyChangeListener(this);
f@0 151 scaleFactor = t.getScaleFactor();
f@0 152 }else {
f@0 153 scaleFactor = 1;
f@0 154 }
f@0 155 track = t;
f@0 156 }
f@0 157
f@0 158 public int getCursorPos() {
f@0 159 Rectangle bounds = cursor.getBounds();
f@0 160 return bounds.x + bounds.width/2;
f@0 161 }
f@0 162
f@0 163 public void setCursorPos(int position) {
f@0 164 Rectangle bounds = cursor.getBounds();
f@0 165 int currentCenterX = bounds.x + bounds.width/2;
f@0 166 cursor.translate(position-currentCenterX, 0);
f@0 167 }
f@0 168
f@0 169 @Override
f@0 170 public void propertyChange(PropertyChangeEvent evt) {
f@0 171 switch(evt.getPropertyName()){
f@0 172 case "cursorPos" : {
f@0 173 setCursorPos((Integer)evt.getNewValue());
f@0 174 repaint();
f@0 175 break;
f@0 176 }
f@0 177 case "scaleFactor" : {
f@0 178 scaleFactor = (Integer)evt.getNewValue();
f@0 179 repaint();
f@0 180 break;
f@0 181 }
f@0 182 case "mouseDragSelection" : {
f@0 183 Selection selection = (Selection)evt.getNewValue();
f@0 184 if(selection.isOpen() ){ // no loop
f@0 185 loopStartMark.reset();
f@0 186 loopEndMark.reset();
f@0 187 }else{
f@0 188 loopStartMark.reset();
f@0 189 /* first point is the actual marker */
f@0 190 loopStartMark.addPoint(selection.getStart(), HEIGHT-(CURSOR_HEIGHT));
f@0 191 loopStartMark.addPoint(selection.getStart()+(CURSOR_WIDTH/2), HEIGHT-(CURSOR_HEIGHT*2));
f@0 192 loopStartMark.addPoint(selection.getStart()+(CURSOR_WIDTH/2),HEIGHT);
f@0 193
f@0 194 loopEndMark.reset();
f@0 195 /* first point is the actual marker */
f@0 196 loopEndMark.addPoint(selection.getEnd(),HEIGHT-(CURSOR_HEIGHT));
f@0 197 loopEndMark.addPoint(selection.getEnd()-(CURSOR_WIDTH/2),HEIGHT);
f@0 198 loopEndMark.addPoint(selection.getEnd()-(CURSOR_WIDTH/2),HEIGHT-(CURSOR_HEIGHT*2));
f@0 199 }
f@0 200 repaint();
f@0 201 break;
f@0 202 }
f@0 203 case "preferredSize" : {
f@0 204 /* resize the width acording to the new value of the listened component */
f@0 205 Dimension d = (Dimension)evt.getNewValue();
f@0 206 setPreferredSize(new Dimension(d.width,HEIGHT));
f@0 207 break;
f@0 208 }
f@0 209 }
f@0 210 }
f@0 211
f@0 212 @Override
f@0 213 public AccessibleContext getAccessibleContext(){
f@0 214 if(accessibleContext == null){
f@0 215 accessibleContext = new AccessibleRule();
f@0 216 }
f@0 217 return accessibleContext;
f@0 218 }
f@0 219
f@0 220 private class MouseInteraction extends MouseInputAdapter {
f@0 221 private Polygon clickedMark; // either loopStartMark or loopEndMark
f@0 222 int clickOffset;
f@0 223
f@0 224 @Override
f@0 225 public void mousePressed(MouseEvent evt){
f@0 226 if(loopStartMark.contains(evt.getX(),evt.getY())){ // click on the loop start mark
f@0 227 clickedMark = loopStartMark;
f@0 228 clickOffset = evt.getX() - loopStartMark.xpoints[0];
f@0 229 }else if(loopEndMark.contains(evt.getX(),evt.getY())){ // click on the loop end mark
f@0 230 clickedMark = loopEndMark;
f@0 231 clickOffset = evt.getX() - loopEndMark.xpoints[0];
f@0 232 }else if(cursor.contains(evt.getX(),evt.getY())){
f@0 233 clickedMark = cursor;
f@0 234 clickOffset = evt.getX() - cursor.xpoints[0];
f@0 235 }
f@0 236 }
f@0 237
f@0 238 @Override
f@0 239 public void mouseDragged(MouseEvent evt){
f@0 240 /* check again that clickedMark is != null and not reset *
f@0 241 * things might change in the middle of the mouse interaction *
f@0 242 * if the property change listener is called from another source */
f@0 243 if(clickedMark == null)
f@0 244 return;
f@0 245
f@0 246 if(clickedMark != cursor && clickedMark.npoints > 0 && track != null){ // loop marks
f@0 247 int mouseSelectionX = 0;
f@0 248 int mouseSelectionY = 0;
f@0 249 if(clickedMark == loopStartMark){ // dragging the start mark
f@0 250 if(evt.getX() > loopEndMark.xpoints[0] - 1 || (evt.getX()-clickOffset < 0) )
f@0 251 return; // can't drag start mark too right, past the end mark or too left past the screen
f@0 252 mouseSelectionX = evt.getX()-clickOffset;
f@0 253 mouseSelectionY = loopEndMark.xpoints[0];
f@0 254 }else{ // dragging the end mark
f@0 255 if(evt.getX() < loopStartMark.xpoints[0] + 1)
f@0 256 return; // can't drag end mark too left, past the end mark
f@0 257 mouseSelectionX = loopStartMark.xpoints[0];
f@0 258 mouseSelectionY = evt.getX()-clickOffset;
f@0 259 }
f@0 260 scrollRectToVisible(new Rectangle(evt.getX(), evt.getY(), 1, 1));
f@0 261 track.trackInteraction.setMouseSelection(mouseSelectionX, mouseSelectionY);
f@0 262 } else if (clickedMark == cursor){ // cursor mark
f@0 263 track.getSoundWave().scan(evt.getX());
f@0 264 }
f@0 265 }
f@0 266
f@0 267 @Override
f@0 268 public void mouseReleased(MouseEvent evt){
f@0 269 if(clickedMark != null){
f@0 270 if(clickedMark == cursor){
f@0 271 track.getSoundWave().scan(SoundWave.STOP_SCANNING); // stop scrubbing
f@0 272 }else
f@0 273 track.getSoundWave().setSelection(track.trackInteraction.getMouseSelection());
f@0 274 }
f@0 275 clickedMark = null;
f@0 276 clickOffset = 0;
f@0 277 }
f@0 278
f@0 279 @Override
f@0 280 public void mouseMoved(MouseEvent evt){
f@0 281 if(cursor.contains(evt.getPoint())){
f@0 282 setCursor(new Cursor(Cursor.E_RESIZE_CURSOR));
f@0 283 }else if(loopStartMark.contains(evt.getPoint())){
f@0 284 setCursor(new Cursor(Cursor.E_RESIZE_CURSOR));
f@0 285 }else if(loopEndMark.contains(evt.getPoint())){
f@0 286 setCursor(new Cursor(Cursor.W_RESIZE_CURSOR));
f@0 287 }else{
f@0 288 setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
f@0 289 }
f@0 290 }
f@0 291 }
f@0 292
f@0 293 protected class AccessibleRule extends AccessibleJComponent {
f@0 294 private static final long serialVersionUID = 1L;
f@0 295
f@0 296 protected AccessibleRule(){
f@0 297 this.setAccessibleName("Time Bar");
f@0 298 this.setAccessibleDescription( "Measures he time of tracks");
f@0 299 }
f@0 300
f@0 301 @Override
f@0 302 public AccessibleRole getAccessibleRole(){
f@0 303 return AccessibleRole.RULER;
f@0 304 }
f@0 305
f@0 306 @Override
f@0 307 public AccessibleStateSet getAccessibleStateSet(){
f@0 308 AccessibleStateSet states = super.getAccessibleStateSet();
f@0 309
f@0 310 states.add(AccessibleState.HORIZONTAL);
f@0 311 states.add(AccessibleState.VISIBLE);
f@0 312
f@0 313 return states;
f@0 314 }
f@0 315 }
f@0 316
f@0 317 }