Package de.sciss.eisenkraut.timeline

Source Code of de.sciss.eisenkraut.timeline.MarkerAxis

/*
*  MarkerAxis.java
*  Eisenkraut
*
*  Copyright (c) 2004-2014 Hanns Holger Rutz. All rights reserved.
*
*  This software is published under the GNU General Public License v3+
*
*
*  For further information, please contact Hanns Holger Rutz at
*  contact@sciss.de
*
*
*  Changelog:
*    24-Jul-05  created
*    19-Feb-06  doesn't use DynamicAncestorAdapter any more ; doc frame should
*          call startListening / stopListening !
*/

package de.sciss.eisenkraut.timeline;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.TexturePaint;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.event.AncestorEvent;

import de.sciss.eisenkraut.edit.BasicCompoundEdit;
import de.sciss.eisenkraut.session.Session;

import de.sciss.app.AbstractApplication;
import de.sciss.app.AncestorAdapter;
import de.sciss.app.DynamicListening;
import de.sciss.app.AbstractCompoundEdit;
import de.sciss.app.GraphicsHandler;
import de.sciss.common.BasicWindowHandler;
import de.sciss.gui.ComponentHost;
import de.sciss.gui.DoClickAction;
import de.sciss.gui.MenuAction;
import de.sciss.gui.ParamField;
import de.sciss.gui.SpringPanel;
import de.sciss.io.Span;
import de.sciss.timebased.MarkerStake;
import de.sciss.timebased.Trail;
import de.sciss.util.DefaultUnitTranslator;
import de.sciss.util.Disposable;
import de.sciss.util.Param;
import de.sciss.util.ParamSpace;

/**
@author    Hanns Holger Rutz
@version  0.70, 19-Nov-07
*
*  @todo    uses TimelineListener to
*        not miss document changes. should use
*        a document change listener!
*
*  @todo    marker sortierung sollte zentral von session o.ae. vorgenommen
*        werden sobald neues file geladen wird!
*
*  @todo    had to add 2 pixels to label y coordinate in java 1.5 ; have to check look back in 1.4
*
*  @todo    repaintMarkers : have to provide dirtySpan that accounts for flag width, esp. for dnd!
*
*  @todo    actionEditPrev/NextClass shortcuts funktionieren nicht
*/
public class MarkerAxis
extends JComponent
implements  TimelineListener, MouseListener, MouseMotionListener, KeyListener,
      DynamicListening, Trail.Listener, Disposable
{
  protected final Session    doc;

  private final Font      fntLabel; //    = new Font( "Helvetica", Font.ITALIC, 10 );

  private String[]      markLabels    = new String[0];
  private int[]        markFlagPos    = new int[0];
  private int          numMarkers    = 0;
//  private int          numRegions    = 0;  // XXX not yet used
  private final GeneralPath   shpFlags    = new GeneralPath();
  private int          recentWidth    = -1;
  private boolean        doRecalc    = true;
  private Span        visibleSpan    = new Span();
  private double        scale      = 1.0;

  private static final int[] pntBarGradientPixels = { 0xFFB8B8B8, 0xFFC0C0C0, 0xFFC8C8C8, 0xFFD3D3D3,
                            0xFFDBDBDB, 0xFFE4E4E4, 0xFFEBEBEB, 0xFFF1F1F1,
                            0xFFF6F6F6, 0xFFFAFAFA, 0xFFFBFBFB, 0xFFFCFCFC,
                            0xFFF9F9F9, 0xFFF4F4F4, 0xFFEFEFEF };
  private static final int barExtent = pntBarGradientPixels.length;

  private static final int[] pntMarkGradientPixels ={ 0xFF5B8581, 0xFF618A86, 0xFF5D8682, 0xFF59827E,
                            0xFF537D79, 0xFF4F7975, 0xFF4B7470, 0xFF47716D,
                            0xFF446E6A, 0xFF426B67, 0xFF406965, 0xFF3F6965,
                            0xFF3F6864 }// , 0xFF5B8581

  private static final int[] pntMarkDragPixels;
 
  private static final Color  colrLabel    = Color.white;
  private static final Color  colrLabelDrag  = new Color( 0xFF, 0xFF, 0xFF, 0xBF );

//  private static final Paint  pntMarkStick= new Color( 0x31, 0x50, 0x4D, 0xC0 );
  private static final Paint  pntMarkStick= new Color( 0x31, 0x50, 0x4D, 0x7F );
  private static final Paint  pntMarkStickDrag = new Color( 0x31, 0x50, 0x4D, 0x5F );
  private static final Stroke  strkStick  = new BasicStroke( 1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL,
    1.0f, new float[] { 4.0f, 4.0f }, 0.0f );

  private static final int  markExtent = pntMarkGradientPixels.length;
  private final Paint      pntBackground;
  private final Paint      pntMarkFlag, pntMarkFlagDrag;
  private final BufferedImage img1, img2, img3;

  private final ComponentHost  host;
  private boolean        isListening  = false;

  // ----- Edit-Marker Dialog -----
  private JPanel          editMarkerPane  = null;
  private Object[]        editOptions    = null;
  private ParamField        ggMarkPos;
  protected JTextField      ggMarkName;
  private JButton          ggEditPrev, ggEditNext;
  protected int          editIdx      = -1;
  private DefaultUnitTranslator  timeTrans;
 
  // ---- dnd ----
  private MarkerStake      dragMark    = null;
  private MarkerStake      dragLastMark  = null;
  private boolean        dragStarted    = false;
  private int          dragStartX    = 0;
 
  private boolean        adjustCatchBypass  = false;
   
  static {
    pntMarkDragPixels = new int[ pntMarkGradientPixels.length ];
    for( int i = 0; i < pntMarkGradientPixels.length; i++ ) {
      pntMarkDragPixels[ i ] = pntMarkGradientPixels[ i ] & 0xBFFFFFFF// = 50% alpha
    }
  }
 
  public MarkerAxis( Session doc )
  {
    this( doc, null );
  }


  /**
   *  Constructs a new object for
   *  displaying the timeline ruler
   *
   *  @param  root  application root
   *  @param  doc    session Session
   */
  public MarkerAxis( Session doc, ComponentHost host )
  {
    super();
       
        this.doc    = doc;
    this.host  = host;
   
    fntLabel  = AbstractApplication.getApplication().getGraphicsHandler().getFont( GraphicsHandler.FONT_LABEL | GraphicsHandler.FONT_MINI ).deriveFont( Font.ITALIC );
   
    setMaximumSize( new Dimension( getMaximumSize().width, barExtent ));
    setMinimumSize( new Dimension( getMinimumSize().width, barExtent ));
    setPreferredSize( new Dimension( getPreferredSize().width, barExtent ));

    img1    = new BufferedImage( 1, barExtent, BufferedImage.TYPE_INT_ARGB );
    img1.setRGB( 0, 0, 1, barExtent, pntBarGradientPixels, 0, 1 );
    pntBackground = new TexturePaint( img1, new Rectangle( 0, 0, 1, barExtent ));
    img2    = new BufferedImage( 1, markExtent, BufferedImage.TYPE_INT_ARGB );
    img2.setRGB( 0, 0, 1, markExtent, pntMarkGradientPixels, 0, 1 );
    pntMarkFlag  = new TexturePaint( img2, new Rectangle( 0, 0, 1, markExtent ));
    img3    = new BufferedImage( 1, markExtent, BufferedImage.TYPE_INT_ARGB );
    img3.setRGB( 0, 0, 1, markExtent, pntMarkDragPixels, 0, 1 );
    pntMarkFlagDrag = new TexturePaint( img3, new Rectangle( 0, 0, 1, markExtent ));

    setOpaque( true );
// not necessary; it also kills the VK_TAB response of DocumentFrame!
//    setFocusable( true );

    // --- Listener ---
//        new DynamicAncestorAdapter( this ).addTo( this );
//    new DynamicAncestorAdapter( new DynamicPrefChangeManager(
//      AbstractApplication.getApplication().getUserPrefs(), new String[] { PrefsUtil.KEY_TIMEUNITS }, this
//    )).addTo( this );
    this.addMouseListener( this );
    this.addMouseMotionListener( this );
    this.addKeyListener( this );

    // ------
//        HelpGlassPane.setHelp( this, "MarkerAxis" );
  }
 
  private String getResourceString( String key )
  {
    return( AbstractApplication.getApplication().getResourceString( key ));
  }
 
  // sync: attempts shared on timeline
  private void recalcDisplay( FontMetrics fm )
  {
    List      markers;
    long      start, stop;
    MarkerStake    mark;

//long t1 = System.currentTimeMillis();
   
    shpFlags.reset();
    numMarkers  = 0;
//    numRegions  = 0;
   
//    if( !doc.bird.attemptShared( Session.DOOR_TIME, 250 )) return;
//    try {
      visibleSpan = doc.timeline.getVisibleSpan()// so we don't have to do that after startListening
      start    = visibleSpan.start;
      stop    = visibleSpan.stop;
      scale    = (double) recentWidth / (stop - start);
     
      markers    = doc.markers.getRange( visibleSpan, true )// XXX plus a bit before
//long t3 = System.currentTimeMillis();
      numMarkers  = markers.size();
//System.err.println( "numMarkers = "+numMarkers );
      if( (numMarkers > markLabels.length) || (numMarkers < (markLabels.length >> 1)) ) {
        markLabels    = new String[ numMarkers * 3 / 2 ];    // 'decent growing and shrinking'
        markFlagPos    = new int[ markLabels.length ];
      }
     
      for( int i = 0; i < numMarkers; i++ ) {
        mark        = (MarkerStake) markers.get( i );
        markLabels[ i ]    = mark.name;
        markFlagPos[ i = (int) (((mark.pos - start) * scale) + 0.5);
        shpFlags.append( new Rectangle( markFlagPos[ i ], 1, fm.stringWidth( mark.name ) + 8, markExtent ), false );
      }

//      coll  = (java.util.List) afd.getProperty( AudioFileDescr.KEY_REGIONS );
//      if( coll != regions ) {
//        regions    = coll;
//        regionIdx  = 0;
//      }
//      if( (regions != null) && !regions.isEmpty() ) {
//     
//      }
      doRecalc  = false;
//    }
//    finally {
//      doc.bird.releaseShared( Session.DOOR_TIME );
//    }
//long t2 = System.currentTimeMillis();
//System.out.println( "recalcDisplay " + (t3-t1) + " / " + (t2-t3) );
  }

  public void paintComponent( Graphics g )
  {
    super.paintComponent( g );

//long t1 = System.currentTimeMillis();
   
    final Graphics2D  g2  = (Graphics2D) g;
   
    g2.setFont( fntLabel );
   
    final FontMetrics  fm  = g2.getFontMetrics();
    final int      y  = fm.getAscent() + 2;

    if( doRecalc || (recentWidth != getWidth()) ) {
      recentWidth = getWidth();
      recalcDisplay( fm );
    }

    g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );

    g2.setPaint( pntBackground );
    g2.fillRect( 0, 0, recentWidth, barExtent );

    g2.setPaint( pntMarkFlag );
    g2.fill( shpFlags );

    g2.setColor( colrLabel );
    g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );

    for( int i = 0; i < numMarkers; i++ ) {
      g2.drawString( markLabels[ i ], markFlagPos[ i ] + 4, y );
    }

    // handle dnd graphics
    if( dragLastMark != null ) {
      final int dragMarkFlagPos = (int) (((dragLastMark.pos - visibleSpan.start) * (double) recentWidth / visibleSpan.getLength()) + 0.5);
      g2.setPaint( pntMarkFlagDrag );
      g2.fillRect( dragMarkFlagPos, 1, fm.stringWidth( dragLastMark.name ) + 8, markExtent );
      g2.setColor( colrLabelDrag );
      g2.drawString( dragLastMark.name, dragMarkFlagPos + 4, y );
    }

//long t2 = System.currentTimeMillis();
//System.out.println( "paintComponent " + (t2 - t1) );
//System.out.println( numMarkers );
  }

  public void paintFlagSticks( Graphics2D g2, Rectangle bounds )
  {
    if( doRecalc ) {
      recalcDisplay( g2.getFontMetrics() )// XXX nicht ganz sauber (anderer graphics-context!)
    }
 
    final Stroke  strkOrig  = g2.getStroke();
//    int        x;
 
    g2.setPaint( pntMarkStick );
    g2.setStroke( strkStick );
    for( int i = 0; i < numMarkers; i++ ) {
      g2.drawLine( markFlagPos[i], bounds.y, markFlagPos[i], bounds.y + bounds.height );
    }
    if( dragLastMark != null ) {
      final int dragMarkFlagPos = (int) (((dragLastMark.pos - visibleSpan.start) * (double) recentWidth / visibleSpan.getLength()) + 0.5);
      g2.setPaint( pntMarkStickDrag );
      g2.drawLine( dragMarkFlagPos, bounds.y, dragMarkFlagPos, bounds.y + bounds.height );
    }
    g2.setStroke( strkOrig );
  }

  private void triggerRedisplay()
  {
    doRecalc  = true;
    if( host != null ) {
      host.update( this );
    } else if( isVisible() ) {
      repaint();
    }
  }
 
  public void addMarker( long pos )
  {
    final AbstractCompoundEdit  ce;
 
    pos    = Math.max( 0, Math.min( doc.timeline.getLength(), pos ));
    ce    = new BasicCompoundEdit( getResourceString( "editAddMarker" ));
    doc.markers.editBegin( ce );
    try {
      doc.markers.editAdd( this, new MarkerStake( pos, "Mark" ), ce );
    }
    catch( IOException e1 ) {  // should never happen
      System.err.println( e1 );
      ce.cancel();
      return;
    }
    finally {
      doc.markers.editEnd( ce );
    }
    ce.perform();
    ce.end();
    doc.getUndoManager().addEdit( ce );
  }
 
  private void removeMarkerLeftTo( long pos )
  {
    final AbstractCompoundEdit  ce;
    final MarkerStake    mark;
 
    mark  = getMarkerLeftTo( pos );
    pos    = Math.max( 0, Math.min( doc.timeline.getLength(), pos ));
    if( mark == null ) return;
   
    ce    = new BasicCompoundEdit( getResourceString( "editDeleteMarker" ));
    doc.markers.editBegin( ce );
    try {
      doc.markers.editRemove( this, mark, ce );
    }
    catch( IOException e1 ) {  // should never happen
      System.err.println( e1 );
      ce.cancel();
      return;
    }
    finally {
      doc.markers.editEnd( ce );
    }
    ce.perform();
    ce.end();
    doc.getUndoManager().addEdit( ce );
  }

  private void editMarkerLeftTo( long pos )
  {
    final int        result;
//    final MarkerStake    mark; //  = getMarkerLeftTo( pos );

//    if( mark == null ) return;

    editIdx    = doc.markers.indexOf( pos );
    if( editIdx < 0 ) {
      editIdx = -(editIdx + 2);
      if( editIdx == -1 ) return;
    }
 
    if( editMarkerPane == null ) {
      final SpringPanel    spring;
      final ActionMap      amap;
      final InputMap      imap;
      JLabel          lb;
      KeyStroke        ks;
      Action          a;

      spring      = new SpringPanel( 4, 2, 4, 2 );
      ggMarkName    = new JTextField( 24 );
//      GUIUtil.setInitialDialogFocus( ggMarkName );  // removes itself automatically
      ggMarkName.addAncestorListener( new AncestorAdapter() {
        public void ancestorAdded( AncestorEvent e ) {
          ggMarkName.requestFocusInWindow();
          ggMarkName.selectAll();
//          c.removeAncestorListener( this );
        }
      });

      // XXX sync
      timeTrans    = new DefaultUnitTranslator();
      ggMarkPos    = new ParamField( timeTrans );
      ggMarkPos.addSpace( ParamSpace.spcTimeHHMMSS );
      ggMarkPos.addSpace( ParamSpace.spcTimeSmps );
      ggMarkPos.addSpace( ParamSpace.spcTimeMillis );
      ggMarkPos.addSpace( ParamSpace.spcTimePercentF );

      lb        = new JLabel( getResourceString( "labelName" ));
//      lb.setLabelFor( ggMarkName );
      spring.gridAdd( lb, 0, 0 );
      spring.gridAdd( ggMarkName, 1, 0 );
      lb        = new JLabel( getResourceString( "labelPosition" ));
//      lb.setLabelFor( ggMarkPos );
      spring.gridAdd( lb, 0, 1 );
      spring.gridAdd( ggMarkPos, 1, 1, -1, 1 );
//      GUIUtil.setDeepFont( spring, null );
      spring.makeCompactGrid();
      editMarkerPane  = new JPanel( new BorderLayout() );
      editMarkerPane.add( spring, BorderLayout.NORTH );
     
      amap      = spring.getActionMap();
      imap      = spring.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
      ks        = KeyStroke.getKeyStroke( KeyEvent.VK_LEFT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() );
// XXX DOESN'T WORK ;-(
//      ggMarkName.getInputMap().remove( ks );
      imap.put( ks, "prev" );
      a        = new ActionEditPrev();
//      amap.put( "prev", a );
      ggEditPrev    = new JButton( a );
      amap.put( "prev", new DoClickAction( ggEditPrev ));
      ks        = KeyStroke.getKeyStroke( KeyEvent.VK_RIGHT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() );
// XXX DOESN'T WORK ;-(
//      ggMarkName.getInputMap().remove( ks );
      imap.put( ks, "next" );
      a        = new ActionEditNext();
//      amap.put( "next", a );
      ggEditNext    = new JButton( a );
      amap.put( "next", new DoClickAction( ggEditNext ));
     
      editOptions    = new Object[] { ggEditNext, ggEditPrev, getResourceString( "buttonOk" ), getResourceString( "buttonCancel" )};
//      editOptions    = new Object[] { ggEditNext, ggEditPrev, JOptionPane.OK_OPTION, JOptionPane.CANCEL_OPTION };
    }

    // XXX sync
    timeTrans.setLengthAndRate( doc.timeline.getLength(), doc.timeline.getRate() );
   
    updateEditMarker();
   
    final JOptionPane op = new JOptionPane( editMarkerPane, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION,
                                            null, editOptions, editOptions[ 2 ]);
//    result = JOptionPane.showOptionDialog( BasicWindowHandler.getWindowAncestor( this ),
//                         editMarkerPane, getResourceString( "inputDlgEditMarker" ),
//      JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, editOptions, editOptions[ 2 ]);
    result = BasicWindowHandler.showDialog( op, BasicWindowHandler.getWindowAncestor( this ), getResourceString( "inputDlgEditMarker" ));

//    if( result == JOptionPane.OK_OPTION ) {
    if( result == 2 ) {
      commitEditMarker();
    }
  }
 
  protected void updateEditMarker()
  {
    final MarkerStake mark = doc.markers.get( editIdx );
    if( mark == null ) return;

    ggMarkPos.setValue( new Param( mark.pos, ParamSpace.TIME | ParamSpace.SMPS ));
    ggMarkName.setText( mark.name );
   
    ggEditPrev.setEnabled( editIdx > 0 );
    ggEditNext.setEnabled( (editIdx + 1) < doc.markers.getNumStakes() );
   
    ggMarkName.requestFocusInWindow();
    ggMarkName.selectAll();
  }
 
  protected void commitEditMarker()
  {
    final MarkerStake mark = doc.markers.get( editIdx );
    if( mark == null ) return;

    final long        positionSmps;
    final AbstractCompoundEdit  ce;

    positionSmps  = (long) timeTrans.translate( ggMarkPos.getValue(), ParamSpace.spcTimeSmps ).val;
    if( (positionSmps == mark.pos) && (ggMarkName.getText().equals( mark.name ))) return; // no change
   
    ce    = new BasicCompoundEdit( getResourceString( "editEditMarker" ));
    doc.markers.editBegin( ce );
    try {
      doc.markers.editRemove( this, mark, ce );
      doc.markers.editAdd( this, new MarkerStake( positionSmps, ggMarkName.getText() ), ce );
    }
    catch( IOException e1 ) {  // should never happen
      System.err.println( e1 );
      ce.cancel();
      return;
    }
    finally {
      doc.markers.editEnd( ce );
    }
    ce.perform();
    ce.end();
    doc.getUndoManager().addEdit( ce );
  }

  private MarkerStake getMarkerLeftTo( long pos )
  {
    int idx;
 
//    if( !doc.bird.attemptShared( Session.DOOR_TIME, 250 )) return null;
//    try {
      idx    = doc.markers.indexOf( pos );
      if( idx < 0 ) {
        idx = -(idx + 2);
        if( idx == -1 ) return null;
      }
      return doc.markers.get( idx );
//    }
//    finally {
//      doc.bird.releaseShared( Session.DOOR_TIME );
//    }
  }

  // -------------- Disposable interface --------------

  public void dispose()
  {
    markLabels  = null;
    markFlagPos  = null;
    shpFlags.reset();
    img1.flush();
    img2.flush();
    img3.flush();
  }

// ---------------- LaterInvocationManager.Listener interface ----------------

    // called by DynamicPrefChangeManager ; o = PreferenceChangeEvent
//    public void laterInvocation( Object o )
//    {
//      final PreferenceChangeEvent  pce = (PreferenceChangeEvent) o;
//      final String        key = pce.getKey();
//     
//      if( key.equals( PrefsUtil.KEY_TIMEUNITS )) {
//        int timeUnits = pce.getNode().getInt( key, 0 );
//        setFlags( timeUnits == 0 ? 0 : TIMEFORMAT );
//        recalcDisplay();
//      }
//    }

// ---------------- DynamicListening interface ----------------

    public void startListening()
    {
    if( !isListening ) {
      doc.timeline.addTimelineListener( this );
  //    doc.tracks.addListener( this );
      doc.markers.addListener( this );
      triggerRedisplay();
      isListening = true;
    }
    }

    public void stopListening()
    {
    if( isListening ) {
      doc.markers.removeListener( this );
  //     doc.tracks.removeListener( this );
      doc.timeline.removeTimelineListener( this );
      isListening = false;
    }
    }

// ---------------- MarkerManager.Listener interface ----------------

  public void trailModified( Trail.Event e )
  {
    if( e.getAffectedSpan().touches( visibleSpan )) {
      triggerRedisplay();
    }
  }

// ---------------- MouseListener interface ----------------
// we're listening to the ourselves

  public void mouseEntered( MouseEvent e )
  {
//    if( isEnabled() ) dispatchMouseMove( e );
  }
 
  public void mouseExited( MouseEvent e ) { /* ignore */ }

  public void mousePressed( MouseEvent e )
    {
    final long pos = (long) (e.getX() / scale + visibleSpan.getStart() + 0.5);
 
    if( shpFlags.contains( e.getPoint() )) {
      if( e.isAltDown() ) {          // delete marker
        removeMarkerLeftTo( pos + 1 );
      } else if( e.getClickCount() == 2 ) {  // rename
        editMarkerLeftTo( pos + 1 );
      } else {                // start drag
        dragMark      = getMarkerLeftTo( pos + 1 );
//        dragLastMark    = dragMark;
        dragStarted      = false;
        dragStartX      = e.getX();
        adjustCatchBypass  = true;
        doc.getFrame().addCatchBypass();
        requestFocus();
      }
     
    } else if( !e.isAltDown() && (e.getClickCount() == 2) ) {    // insert marker
      addMarker( pos );
    }
  }

  public void mouseReleased( MouseEvent e )
  {
    AbstractCompoundEdit ce;
 
    if( adjustCatchBypass ) {
      adjustCatchBypass = false;
      doc.getFrame().removeCatchBypass();
    }
   
    try {
      if( dragLastMark != null ) {
//        if( !doc.bird.attemptExclusive( Session.DOOR_TIME, 250 )) return;
//        try {
          // ok this is tricky and totally stupid, have to replace it some day XXX
//          doc.markers.remove( this, dragLastMark );  // remove temporary marker
//          doc.markers.add( this, dragMark );      // restore original marker for undoable edit!
          ce  = new BasicCompoundEdit( getResourceString( "editMoveMarker" ));
          doc.markers.editBegin( ce );
          try {
            doc.markers.editRemove( this, dragMark, ce );
            doc.markers.editAdd( this, dragLastMark, ce );
          }
          catch( IOException e1 ) {  // should never happen
            System.err.println( e1 );
            ce.cancel();
            return;
          }
          doc.markers.editEnd( ce );
          ce.perform();
          ce.end();
          doc.getUndoManager().addEdit( ce );
//        }
//        catch( IOException e1 ) {  // should never happen
//          System.err.println( e1 );
//          return;
//        }
//        finally {
//          doc.bird.releaseExclusive( Session.DOOR_TIME );
//        }
      }
    }
    finally {
      dragStarted    = false;
      dragMark    = null;
      dragLastMark  = null;
    }
  }
 
  public void mouseClicked( MouseEvent e ) { /* ignored */ }

// ---------------- MouseMotionListener interface ----------------
// we're listening to ourselves

    public void mouseMoved( MouseEvent e ) { /* ignore */ }

  public void mouseDragged( MouseEvent e )
  {
    if( dragMark == null ) return;

    if( !dragStarted ) {
      if( Math.abs( e.getX() - dragStartX ) < 5 ) return;
      dragStarted = true;
    }

    final Span      dirtySpan;
    final long      oldPos  = dragLastMark != null ? dragLastMark.pos : dragMark.pos;
    final long      newPos  = Math.max( 0, Math.min( doc.timeline.getLength(), (long) ((e.getX() - dragStartX) / scale + dragMark.pos + 0.5) ));

    if( oldPos == newPos ) return;
   
    dirtySpan    = new Span( Math.min( oldPos, newPos ), Math.max( oldPos, newPos ));
    dragLastMark  = new MarkerStake( newPos, dragMark.name );
    doc.getFrame().repaintMarkers( dirtySpan );
  }

// ---------------- KeyListener interface ----------------
// we're listening to ourselves

    public void keyPressed( KeyEvent e )
  {
    if( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
      dragMark    = null;
      dragLastMark  = null;
      if( dragStarted ) {
        dragStarted  = false;
        doc.getFrame().repaintMarkers( visibleSpan );
      }
    }
  }
 
    public void keyReleased( KeyEvent e ) { /* ignored */ }
    public void keyTyped( KeyEvent e ) { /* ignored */ }
 
// ---------------- TimelineListener interface ----------------
 
     public void timelineSelected( TimelineEvent e ) { /* ignored */ }
  public void timelinePositioned( TimelineEvent e ) { /* ignored */ }

  public void timelineChanged( TimelineEvent e )
  {
    triggerRedisplay();
  }

     public void timelineScrolled( TimelineEvent e )
    {
//    if( !doc.bird.attemptShared( Session.DOOR_TIME, 250 )) return;
//    try {
      visibleSpan = doc.timeline.getVisibleSpan();
      scale    = (double) getWidth() / visibleSpan.getLength();
     
      triggerRedisplay();
//    }
//    finally {
//      doc.bird.releaseShared( Session.DOOR_TIME );
//    }
    }

// ---------------- internal classes ----------------

  private class ActionEditPrev
  extends MenuAction
  {
    protected ActionEditPrev()
    {
//      super( "\u21E0", KeyStroke.getKeyStroke( KeyEvent.VK_LEFT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ));
      super( "\u21E0" );
    }
   
    public void actionPerformed( ActionEvent e )
    {
      commitEditMarker();
      if( editIdx > 0 ) {
        editIdx--;
        updateEditMarker();
      }
    }
  }

  private class ActionEditNext
  extends MenuAction
  {
    protected ActionEditNext()
    {
      super( "\u21E2", KeyStroke.getKeyStroke( KeyEvent.VK_RIGHT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ));
    }
   
    public void actionPerformed( ActionEvent e )
    {
      commitEditMarker();
      if( (editIdx + 1) < doc.markers.getNumStakes() ) {
        editIdx++;
        updateEditMarker();
      }
    }
  }
}
TOP

Related Classes of de.sciss.eisenkraut.timeline.MarkerAxis

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.