/*
* TrackRowHeader.java
* Eisenkraut
*
* Copyright (c) 2004-2016 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
*/
package de.sciss.eisenkraut.timeline;
import de.sciss.app.AbstractApplication;
import de.sciss.app.DynamicAncestorAdapter;
import de.sciss.app.DynamicListening;
import de.sciss.app.GraphicsHandler;
import de.sciss.eisenkraut.edit.EditSetSessionObjects;
import de.sciss.eisenkraut.gui.GraphicsUtil;
import de.sciss.eisenkraut.session.SessionCollection;
import de.sciss.eisenkraut.session.SessionObject;
import de.sciss.eisenkraut.util.MapManager;
import de.sciss.gui.GradientPanel;
import de.sciss.util.Disposable;
import javax.swing.*;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A row header in Swing's table 'ideology'
* is a component left to the leftmost
* column of each row in a table. It serves
* as a kind of label for that specific row.
* This class shows a header left to each
* sound file's waveform display, with information
* about the channel index, possible selections
* and soloing/muting. In the future it could
* carry insert effects and the like.
*/
@SuppressWarnings("serial")
public class TrackRowHeader
extends JPanel
implements MouseListener, DynamicListening, Disposable {
private final JLabel lbTrackName;
private final Track t;
private final SessionCollection tracks;
private final SessionCollection selectedTracks;
private final UndoManager undo;
protected boolean selected = false;
private final Color colrSelected = GraphicsUtil.colrSelection();
private static final Color colrUnselected = GraphicsUtil.colrInactiveSelection();
private static final Color colrDarken = new Color(0x00, 0x00, 0x00, 0x18);
private final Paint pntSelected = new GradientPaint(0, 0, colrSelected,
36, 0, new Color(colrSelected.getRGB() & 0xFFFFFF, true));
private static final Paint pntUnselected = new GradientPaint(0, 0, colrUnselected,
36, 0, new Color(colrUnselected.getRGB() & 0xFFFFFF, true));
private static final Paint pntDarken = new GradientPaint(0, 0, colrDarken,
36, 0, new Color(colrDarken.getRGB() & 0xFFFFFF, true));
private final MapManager.Listener trackListener;
private final SessionCollection.Listener selectedTracksListener;
public TrackRowHeader(final Track t, final SessionCollection tracks, final SessionCollection selectedTracks,
UndoManager undo) {
super();
this.t = t;
this.tracks = tracks;
this.selectedTracks = selectedTracks;
this.undo = undo;
final SpringLayout lay = new SpringLayout();
SpringLayout.Constraints cons;
setLayout(lay);
lbTrackName = new JLabel();
lbTrackName.setFont(AbstractApplication.getApplication().getGraphicsHandler().getFont(GraphicsHandler.FONT_SMALL));
cons = lay.getConstraints(lbTrackName);
cons.setX(Spring.constant(7));
cons.setY(Spring.minus(Spring.max( // min( X, Y ) = -max( -X, -Y )
Spring.constant(-4),
Spring.minus(Spring.sum(Spring.sum(lay.getConstraint(SpringLayout.SOUTH, this),
Spring.minus(lay.getConstraint(SpringLayout.NORTH, this))), Spring.constant(-15))))));
add(lbTrackName);
// setBorder(BorderFactory.createMatteBorder(0, 0, 0, 2, Color.white)); // top left bottom right
setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); // top left bottom right
// --- Listener ---
new DynamicAncestorAdapter(this).addTo(this);
this.addMouseListener(this);
trackListener = new MapManager.Listener() {
public void mapChanged(MapManager.Event e) { /* ignore */ }
public void mapOwnerModified(MapManager.Event e) {
if (e.getOwnerModType() == SessionObject.OWNER_RENAMED) {
checkTrackName();
}
}
};
selectedTracksListener = new SessionCollection.Listener() {
public void sessionCollectionChanged(SessionCollection.Event e) {
if (e.collectionContains(t)) {
if (selected != selectedTracks.contains(t)) {
selected = !selected;
repaint();
}
}
}
public void sessionObjectChanged(SessionCollection.Event e) { /* ignore */ }
public void sessionObjectMapChanged(SessionCollection.Event e) { /* ignore */ }
};
}
public void dispose() {
}
/**
* Determines if this row is selected
* i.e. is part of the selected transmitters
*
* @return <code>true</code> if the row (and thus the transmitter) is selected
*/
public boolean isSelected() {
return selected;
}
public Track getTrack() {
return t;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
final int h = getHeight();
final int w = getWidth();
final int x = Math.min( w - 36, lbTrackName.getX() + lbTrackName.getWidth() );
g2.translate(x, 0);
g2.setPaint(pntDarken);
g2.fillRect(-x, 19, x + 36, 2);
g2.setPaint(selected ? pntSelected : pntUnselected);
g2.fillRect(-x, 0, x + 36, 20);
g2.translate(-x, 0);
g2.translate(0, h - 8);
g2.setPaint(GradientPanel.pntBottomBorder());
g2.fillRect(0, 0, w, 8);
g2.translate(0, 8 - h);
}
public void paintChildren(Graphics g) {
super.paintChildren(g);
final Graphics2D g2 = (Graphics2D) g;
final int w = getWidth();
g2.setPaint(GradientPanel.pntTopBorder());
g2.fillRect(0, 0, w, 8);
}
protected void checkTrackName() {
if (!t.getName().equals(lbTrackName.getText())) {
lbTrackName.setText(t.getName());
}
}
// ---------------- DynamicListening interface ----------------
public void startListening() {
t.getMap().addListener(trackListener);
selectedTracks.addListener(selectedTracksListener);
checkTrackName();
if (selected != selectedTracks.contains(t)) {
selected = !selected;
repaint();
}
}
public void stopListening() {
t.getMap().removeListener(trackListener);
selectedTracks.removeListener(selectedTracksListener);
}
// ---------------- MouseListener interface ----------------
// we're listening to the ourselves
public void mouseEntered(MouseEvent e) { /* ignore */ }
public void mouseExited(MouseEvent e) { /* ignore */ }
/**
* Handle mouse presses.
* <pre>
* Keyboard shortcuts as in ProTools:
* Alt+Click = Toggle item and set all others to same new state
* Meta+Click = Toggle item and set all others to opposite state
* </pre>
*/
public void mousePressed(MouseEvent e) {
UndoableEdit edit;
List<SessionObject> collTracks;
if( e.isAltDown() ) {
selected = !selected; // toggle item
if( selected ) { // select all
collTracks = tracks.getAll();
} else { // deselect all
collTracks = new ArrayList<SessionObject>( 1 );
}
} else if( e.isMetaDown() ) {
selected = !selected; // toggle item
if( selected ) { // deselect all except uns
collTracks = Collections.singletonList((SessionObject) t);
} else { // select all except us
collTracks = tracks.getAll();
collTracks.remove( t );
}
} else {
if( e.isShiftDown() ) {
collTracks = selectedTracks.getAll();
selected = !selected;
if (selected) {
collTracks.add(t); // add us to selection
} else {
collTracks.remove(t); // remove us from selection
}
} else {
if( selected ) return; // no action
selected = true;
collTracks = Collections.singletonList((SessionObject) t); // deselect all except uns
}
}
// XXX should use a lazy edit here
edit = new EditSetSessionObjects(this, selectedTracks, collTracks).perform();
undo.addEdit( edit );
repaint();
}
public void mouseReleased(MouseEvent e) { /* ignore */ }
public void mouseClicked(MouseEvent e) { /* ignore */ }
}