/*
* Created on 14-Feb-2006
*
* Copyright (c) 2006 P.J.Leonard
*
* http://www.frinika.com
*
* This file is part of Frinika.
*
* Frinika 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 2 of the License, or
* (at your option) any later version.
* Frinika 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 Frinika; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.frinika.sequencer.gui.partview;
import static com.frinika.localization.CurrentLocale.getMessage;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.BasicStroke;
import java.awt.event.MouseEvent;
import java.util.Vector;
import com.frinika.project.gui.ProjectFrame;
import com.frinika.sequencer.FrinikaSequence;
import com.frinika.sequencer.SwingSongPositionListenerWrapper;
import com.frinika.sequencer.gui.ColorScheme;
import com.frinika.sequencer.gui.DragViewTool;
import com.frinika.sequencer.gui.EraseTool;
import com.frinika.sequencer.gui.Item;
import com.frinika.sequencer.gui.ItemPanel;
import com.frinika.sequencer.gui.ItemRollToolBar;
import com.frinika.sequencer.gui.ItemScrollPane;
import com.frinika.sequencer.gui.PartGlueTool;
import com.frinika.sequencer.gui.PartSplitTool;
import com.frinika.sequencer.gui.ToolAdapter;
import com.frinika.sequencer.gui.Layout;
import com.frinika.sequencer.gui.MyCursors;
import com.frinika.sequencer.gui.RectZoomTool;
import com.frinika.sequencer.gui.SelectTool;
import com.frinika.sequencer.gui.WriteTool;
import com.frinika.sequencer.gui.selection.SelectionContainer;
import com.frinika.sequencer.gui.selection.SelectionListener;
import com.frinika.sequencer.model.AudioLane;
import com.frinika.sequencer.model.AudioPart;
import com.frinika.sequencer.model.EditHistoryAction;
import com.frinika.sequencer.model.EditHistoryContainer;
import com.frinika.sequencer.model.EditHistoryListener;
import com.frinika.sequencer.model.Ghost;
import com.frinika.sequencer.model.GluePartEditAction;
import com.frinika.sequencer.model.Lane;
import com.frinika.sequencer.model.MidiLane;
import com.frinika.sequencer.model.MidiPart;
import com.frinika.sequencer.model.MidiPlayOptions;
import com.frinika.sequencer.model.SynthLane;
import com.frinika.sequencer.model.TextLane;
import com.frinika.sequencer.model.MovePartEditAction;
import com.frinika.sequencer.model.Part;
import com.frinika.sequencer.model.ResizePartAction;
import com.frinika.sequencer.model.SplitPartAction;
import com.frinika.sequencer.model.tempo.TempoList;
import com.frinika.sequencer.model.tempo.TempoListListener;
import com.frinika.sequencer.model.timesignature.TimeSignatureList;
import com.frinika.sequencer.model.timesignature.TimeSignatureList.QStepIterator;
import com.frinika.sequencer.model.timesignature.TimeSignatureList.TimeSignatureEvent;
import com.frinika.sequencer.model.util.TimeUtils;
/**
*
* Panel displaying the parts.
*
*
* @author pjl
*
*/
public class PartView extends ItemPanel implements SelectionListener<Part>,
EditHistoryListener {
private static final long serialVersionUID = 1L; // TODO use the piano roll event pattern and put this into the project
Vector<PartImage> dragList; // used for dragging
int dxTotal = 0;
int dLaneTotal = 0; // used when creating and stretching
private MidiPart newPart;
EditHistoryContainer editHistory;
private LaneHeaderPanel laneHeader;
private PartSplitTool splitTool;
private PartGlueTool glueTool;
private int splitX;
private boolean splitting;
private long splitTick;
private Stroke dashedLineStroke; // Jens
private TempoList tempoList;
private ProjectFrame frame;
public PartView(ProjectFrame frame, ItemScrollPane scroller) {
super(frame.getProjectContainer(),scroller, true, true, 20.0, true);
project.getPartSelection().addSelectionListener(
this);
this.frame = frame;
this.tempoList = frame.getProjectContainer().getTempoList();
tempoList.addTempoListListener(new TempoListListener() {
public void notifyTempoListChange() {
// System.out.println(" PART VIEW TEMPO CHANGE");
setDirty();// true;
repaint();
}
});
this.editHistory = frame.getProjectContainer().getEditHistoryContainer();
this.editHistory.addEditHistoryListener(this);
this.sequencer = frame.getProjectContainer().getSequencer();
this.sequencer.addSongPositionListener(new SwingSongPositionListenerWrapper(
this));
FrinikaSequence seq = (FrinikaSequence) this.sequencer.getSequence();
this.ticksPerBeat = seq.getResolution();
if (frame.getProjectContainer().getPartViewSnapQuantization() == 0) {
frame.getProjectContainer().setPartViewSnapQuantization(-1);
// this.ticksPerBeat*4);
// HACK PJL * frame.getProjectContainer().beatsPerBar);
}
// this.ticksToScreen = .02;
dashedLineStroke = new BasicStroke(1, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL, 0, new float[]{5f, 5f}, 0); // Jens
setLayout(null);
setBackground(ColorScheme.partViewBackground);
addComponentListener(this);
makeTools();
setFocusable(true);
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
protected void processMouseEvent(MouseEvent e) {
if (e.getID() == MouseEvent.MOUSE_PRESSED) {
grabFocus();
}
super.processMouseEvent(e);
}
void setLaneHeader(LaneHeaderPanel head) {
this.laneHeader = head;
repaintItems();
}
void dispose() {
this.project.getPartSelection().removeSelectionListener(this);
this.editHistory.removeEditHistoryListener(this);
removeComponentListener(this);
}
@Override
public void dragTo(Point p) {
int dxDragged = p.x - this.xAnchor;
int dyDragged = p.y - this.yAnchor;
// System.out.println(dxDragged + " " + dyDragged);
double dt = dxDragged / userToScreen;
double t = this.xAnchor / userToScreen;
double tick = tempoList.getTickAtTime(t);
double tick2 = tempoList.getTickAtTime(t + dt);
// double bpm=tempoList.getTempoAt((long) tick);
double dtick = tick2 - tick; // dt*project.getTicksPerBeat()*bpm/60.0;
// System.out.println(dt + " " + t + " "+ dtick);
if (!altIsDown && isSnapQuantized()) {
double quant = this.getSnapQuantization();
if (quant > 0.0) {
dtick = Math.round(dtick / this.getSnapQuantization()) * this.getSnapQuantization();
} else {
double beat = tick / project.getTicksPerBeat();
TimeSignatureEvent ev = project.getTimeSignatureList().getEventAtBeat((int) beat);
quant = ev.beatsPerBar * project.getTicksPerBeat();
dtick = Math.round(dtick / quant) * quant;
}
}
// TODO will break when lanes are different hieghts
int dLane = dyDragged / Layout.getLaneHeightScale();
// Lane lane=laneAt(p.y);
// If no significant movement then return
if (Math.abs(dtick) < 1e-10 && dLane == 0 && !altIsDown && this.dragMode != OVER_ENVELOPE_GAIN && this.dragMode != OVER_ENVELOPE_RIGHT && this.dragMode != OVER_ENVELOPE_LEFT) {
return;
}
int lMax = Integer.MIN_VALUE;
int lMin = Integer.MAX_VALUE;
int nLane = this.laneHeader.visibleLanes.getVisibleLanes().size();
if (this.dragArmed) {
startDrag();
}
PartImage dddPart = null;
if (altIsDown && (this.dragMode == OVER_ITEM_MIDDLE || this.dragMode == OVER_ITEM_LEFT || this.dragMode == OVER_ITEM_RIGHT) && project.isPartViewSnapQuantized()) {
dddPart = (PartImage) (dragList.get(0));
if (this.dragMode == OVER_ITEM_RIGHT) {
// double refTick = tempoList.getTickAtTime(dddPart.endTimeSecs) + dtick;
// double sTick = project.partQuantize((long) refTick);
// dtick = sTick - tempoList.getTickAtTime(dddPart.endTimeSecs);
double refTick = tempoList.getTickAtTime(dddPart.endTimeSecs) + dtick;
double sTick = project.partQuantize((long) refTick);
dtick = sTick - tempoList.getTickAtTime(dddPart.endTimeSecs);
} else {
double refTick = tempoList.getTickAtTime(dddPart.startTimeSecs) + dtick;
double sTick = project.partQuantize((long) refTick);
dtick = sTick - tempoList.getTickAtTime(dddPart.startTimeSecs);
}
// System.out.println(dddNote +" " + dtick);
if (dtick == 0) {
return;
}
}
if (this.dragMode == OVER_ENVELOPE_GAIN) {
if (dyDragged == 0) {
return;
}
for (PartImage pi : this.dragList) {
if (pi.part instanceof AudioPart) {
AudioPart ap = (AudioPart) pi.part;
ap.getEvelope().setGain(
(pi.y + pi.height - p.y) / (double) pi.height);
ap.refreshEnvelope();
}
}
} else if (this.dragMode == OVER_ENVELOPE_LEFT || this.dragMode == OVER_ENVELOPE_RIGHT) {
if (dxDragged == 0) {
return;
}
for (PartImage pi : this.dragList) {
if (pi.part instanceof AudioPart) {
double fact = (p.x - pi.x) / (double) pi.width;
AudioPart ap = (AudioPart) pi.part;
if (this.dragMode == OVER_ENVELOPE_RIGHT) {
ap.getEvelope().setTOffRel(fact);
} else {
ap.getEvelope().setTOnRel(fact);
}
ap.refreshEnvelope();
}
}
} else {
for (PartImage pi : this.dragList) {
lMax = Math.max(lMax, pi.laneId);
lMin = Math.min(lMin, pi.laneId);
}
if (lMax + dLane >= nLane) {
dLane = nLane - 1 - lMax;
} else if (lMin + dLane < 0) {
dLane = -lMin;
}
this.dragArmed = false;
if (this.dragList == null) {
return;
}
for (PartImage pi : this.dragList) {
// MidiPart part = pi.part;
switch (this.dragMode) {
case OVER_ITEM_MIDDLE:
pi.setMoveByDeltaTicks(dtick);
pi.laneId += dLane;
break;
case OVER_ITEM_RIGHT:
pi.setEndMoveByDeltaTicks(dtick);
dLane = 0;
break;
case OVER_ITEM_LEFT:
pi.setStartMoveByDeltaTicks(dtick);
// pi.startTimeSecs += dtick;
dLane = 0;
break;
default:
System.err.println(" unknown dragmode PartView" + this.dragMode);
}
pi.positionPartImage();
}
}
// TODO will break when lanes are different heights
// this.xAnchor = this.xAnchor + (int) (dt * this.userToScreen);
this.yAnchor = this.yAnchor + dLane * Layout.getLaneHeightScale();
repaintItems();
}
@Override
public void clientClearSelection() {
this.project.getPartSelection().clearSelection();
}
/**
* Call this to start dragging with the reference point. See dragTo
*
* @param e
*/
public void startDrag() {
this.dragList = new Vector<PartImage>();
for (Part it : this.project.getPartSelection().getSelected()) {
PartImage pi = new PartImage(it, it.getLane().getDisplayID());
this.dragList.add(pi);
pi.positionPartImage();
}
}
/*
* int laneIdOf(Lane lane) { return laneList.indexOf(lane); }
*/
/**
* Set all selectables in the rect to yes.
*
* @param yes
* @param rect
*/
@Override
public synchronized void selectInRect(Rectangle rect, boolean shift) {
Vector<Part> addTmp = new Vector<Part>();
Vector<Part> delTmp = new Vector<Part>();
for (Lane lane : this.project.getLanes()) {
for (Part it : lane.getParts()) {
if (rect.intersects(getBounds(it))) {
if (shift) {
if (it.isSelected()) {
delTmp.add(it);
} else {
addTmp.add(it);
}
} else {
addTmp.add(it);
}
}
}
}
this.project.getPartSelection().removeSelected(delTmp);
this.project.getPartSelection().addSelected(addTmp);
this.project.getPartSelection().notifyListeners();
}
/**
* return rectange of part in virtaul screen (unaffected by scrolling)
*/
final Rectangle rectTmp = new Rectangle();
final Rectangle getBounds(Part part) {
double x1 = part.getStart(timeBased);
double w = part.getDuration(timeBased);
x1 = (int) userToScreen(x1);
w = (int) userToScreen(w);
Lane lane = part.getLane();
int y1 = lane.getDisplayY();
rectTmp.setBounds((int) x1, y1, (int) w, lane.getDisplayH());
return rectTmp;
// return new Rectangle((int) x1, y1, (int) w, lane.getDisplayH());
}
// Jens: (same as above, but returning a new instance of Rectangle)
public Rectangle getPartBounds(Part part) {
double x1 = part.getStart(timeBased);
double w = part.getDuration(timeBased);
x1 = (int) userToScreen(x1);
w = (int) userToScreen(w);
Lane lane = part.getLane();
int y1 = lane.getDisplayY();
return new Rectangle((int) x1, y1, (int) w, lane.getDisplayH());
}
/**
*
*/
@Override
public void rightButtonPressedOnItem(int x, int y) {
frame.showRightButtonPartPopup(this, x, y);
}
@Override
public void rectZoomFinished() {
((ItemRollToolBar) this.toolBar).rectZoomFinished();
}
@Override
public synchronized void writeDraggedAt(Point p) {
if (this.newPart == null) {
return;
}
long tick = screenToTickAbs(p.x, true);
double tick1 = this.newPart.getStart(timeBased);
double tick2 = this.newPart.getEnd(timeBased);
if (tick > tick2) {
this.newPart.setEndTick(tick);
} else if (tick < tick2 && (tick - tick1) > this.project.getPartViewSnapQuantization()) {
this.newPart.setEndTick(tick);
} else {
return;
}
repaintItems();
}
@Override
protected void writeReleasedAt(Point p) {
if (this.newPart == null) {
return;
}
this.project.getPartSelection().setSelected(this.newPart);
this.project.getEditHistoryContainer().notifyEditHistoryListeners();
this.project.getPartSelection().notifyListeners();
}
@Override
protected synchronized void writePressedAt(Point p) {
Lane lane;
this.newPart = null;
if (itemAt(p) != null) {
System.out.println(" Can only create a Part in an empty space ");
return;
} else {
lane = laneAt(p.y);
if (lane == null) {
System.out.println(" Please create a lane first");
return;
}
}
if (lane instanceof MidiLane) {
this.project.getEditHistoryContainer().mark(
getMessage("sequencer.lane.add_part"));
this.newPart = (MidiPart) lane.createPart(); // new
// MidiPart((MidiLane)
// lane);
long tick = screenToTickAbs(p.x, true);
this.newPart.setStartTick(tick);
long quant = (long) this.project.getPartViewSnapQuantization();
if (quant < 0) {
TimeSignatureEvent ev = project.getTimeSignatureList().getEventAtBeat(
(int) (tick / this.project.getTicksPerBeat()));
int beatPerBar = ev.beatsPerBar;
quant = beatPerBar * project.getTicksPerBeat();
}
this.newPart.setEndTick(tick + quant);
repaintItems();
} else if (lane instanceof TextLane) { // Jens
this.project.getEditHistoryContainer().mark(
getMessage("sequencer.lane.add_part"));
long tick = screenToTickAbs(p.x, true);
((TextLane) lane).createNewTextPart(tick); // replace with gerenic
// lane.createPart() ?
repaintItems();
}
}
int mapY(int y) {
return y; // TODO stop being so confused about all this ampping
}
int unmapY(int y) {
return y; // TODO stop being so confused about all this ampping
}
/**
* Find component the contains point x,y and set. first component found is
* set (is this what we want ?) TODO multitrack thinking.
*
* @param x
* @param y
* @return
*/
@Override
public Item itemAt(Point p) {
for (Lane lane : this.project.getLanes()) {
if (lane.getParts() != null) {
for (Part part : lane.getParts()) {
if (getBounds(part).contains(p)) {
return part;
}
}
}
}
return null;
}
/**
*
* @param virtualScreenRect
* area of screen that needs to be painted
*
*
*/
@Override
public synchronized void paintImageImpl(final Rectangle clipRect,
Graphics2D g) {
if (g == null) {
return;
}
int w = clipRect.width;
int x = clipRect.x;
int y = clipRect.y;
int h = clipRect.height;
// Draw vertical lines
g.setColor(ColorScheme.partViewBackground);
g.fill(clipRect);
double step = this.project.getPartViewSnapQuantization() / ticksPerBeat;
TimeSignatureList ts = project.getTimeSignatureList();
double beat1 = screenToTickAbs(x, true) / ticksPerBeat;
double beat2 = screenToTickAbs(x + w, true) / ticksPerBeat;
QStepIterator iter = project.getTimeSignatureList().createQStepIterator(beat1, beat2, step);
// System.out.println(beat1 + " " + beat2 + " " + step);
boolean drawSub = true; // (int)userToScreen((long) (step *
// ticksPerBeat)) > 5;
boolean drawBeat = true; // (int)userToScreen((long) (ticksPerBeat))
// > 5;
while (iter.hasNext()) {
iter.next();
double beat = iter.getBeat();
boolean isBar = iter.isBar();
// System.out.println(beat + " " + isBar);
if (isBar) {
g.setColor(ColorScheme.partViewLinesBar);
} else {
if (!drawBeat) {
continue;
}
if (Math.abs((beat + 1e-7) % 1) < 2e-7) {
g.setColor(ColorScheme.partViewLinesBeat);
} else {
if (!drawSub) {
continue;
}
g.setColor(ColorScheme.partViewLinesSubBeat);
}
}
long tick = (long) (beat * ticksPerBeat);
double tt = project.getTempoList().getTimeAtTick(tick);
// System.out.println(beat + " " + isBar + " "+ tick + " "+ tt);
int x1 = (int) userToScreen(tt);
g.drawLine(x1, y, x1, y + h);
}
// // Horizontal lines
// //*/
// g.setColor(ColorScheme.partViewLinesHoriz); int p1 = screenToLane(y);
//
// int y1 = laneToScreen(p1); if (y1 < y) { p1 -= 1; y1 =
// laneToScreen(p1); } // assert (y1 >= y);
//
// while (y1 < y + h) { g.drawLine(x, y1, x + w, y1); p1 += 1; y1 =
// laneToScreen(p1); }
// //* /
paintParts(g, clipRect);
}
/**
*
* Private method to paint the part images.
*
*
* @param g
* graphic
* @param pb
*/
private synchronized void paintParts(Graphics2D g, Rectangle pb) {
final int gap = 2;
// System.out.println("Paint parts ");
Part focus = this.project.getPartSelection().getFocus();
for (Lane lane : this.laneHeader.visibleLanes) {
int lastX = -1; // Jens
if (lane.getParts() == null) {
continue;
}
synchronized (lane) {
for (Part part : lane.getParts()) {
Rectangle rect = getBounds(part);
if (rect.isEmpty()) {
continue;
}
rect.y += gap;
rect.height -= 2 * gap;
if (pb != null && !pb.intersects(rect)) {
if (rect.x > pb.x + pb.width) {
lastX = -2; // last part is out of visible rect, mark as
// don't look further, Jens
}
continue;
}
// Jens:
if (lastX != -2) {
int xx = rect.x + rect.width;
if (xx > lastX) {
lastX = xx;
}
}
Color color = part.getTransparentColor(); // Jens
if (part instanceof Ghost) {
int a = color.getAlpha() / 4;
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), a);
}
g.setColor(color);
g.fill(rect);
if (part == focus) {
g.setColor(Color.BLUE);
} else {
g.setColor(Color.BLACK);
}
g.draw(rect);
part.drawThumbNail(g, rect, this);
if (part.isSelected() && !(part.getLane() instanceof SynthLane)) {
if (this.splitting) {
if (part.getStartTick() < this.splitTick && part.getEndTick() > this.splitTick) {
double time = tempoList.getTimeAtTick(this.splitTick);
int xSplit = (int) userToScreen(time);
g.setColor(Color.WHITE);
g.drawLine(xSplit, rect.y - 2, xSplit, rect.y + rect.height + 2);
}
}
}
}
}
// Jens:
MidiPlayOptions opt;
if ((lastX >= 0) && (lane instanceof MidiLane) && ((opt = ((MidiLane) lane).getPlayOptions()).looped)) {
g.setColor(Color.GRAY); // mark rest of lane gray if looped-mode
// (indicating that track still goes on
// playing)
java.awt.Composite compositeBackup = g.getComposite();
g.setComposite(java.awt.AlphaComposite.getInstance(
java.awt.AlphaComposite.SRC_OVER, (float) 0.666));
g.fillRect(lastX + 1, lane.getDisplayY() + 1, pb.width - lastX - 2, lane.getDisplayH() - 2);
long lastTick = ((MidiLane) lane).getTrack().lastTickUsed();
Rectangle r = new Rectangle();
r.width = (int) userToScreen(opt.loopedTicks);
r.height = lane.getDisplayH() - 2;
r.x = (int) userToScreen(lastTick) + 1;
r.y = lane.getDisplayY() + 1;
g.setColor(Color.BLACK);
java.awt.Stroke strokeBackup = g.getStroke();
g.setStroke(dashedLineStroke);
while (pb.intersects(r)) {
g.drawRect(r.x, r.y, r.width, r.height);
lastTick += opt.loopedTicks;
r.x = (int) userToScreen(lastTick);
}
g.setStroke(strokeBackup);
g.setComposite(compositeBackup);
}
}
if (this.dragList == null) {
for (Part part : this.project.getPartSelection().getSelected()) {
Rectangle rect = getBounds(part);
if (part instanceof AudioPart) {
rect.y += gap;
rect.height -= 2 * gap;
((AudioPart) part).drawEnvelope(g, rect, this);
} else {
rect.y += gap - 1;
rect.height -= 2 * (gap - 1);
// rect.x += 1;
// rect.width -= 2;
if (part == focus) {
g.setColor(Color.BLUE);
} else {
g.setColor(Color.BLACK);
}
g.draw(rect);
part.drawThumbNail(g, rect, this);
}
}
return;
}
for (PartImage partImage : this.dragList) {
if (pb != null && !pb.intersects(partImage)) {
continue;
}
Rectangle rect = new Rectangle(partImage);
Part part = partImage.part;
if ((this.dragMode == OVER_ENVELOPE_GAIN || this.dragMode == OVER_ENVELOPE_RIGHT || this.dragMode == OVER_ENVELOPE_LEFT) && part instanceof AudioPart) {
rect.y += gap;
rect.height -= 2 * gap;
((AudioPart) part).drawEnvelope(g, rect, this);
continue;
}
g.setColor(Color.RED);
g.draw(rect);
if (this.dragMode != OVER_ITEM_MIDDLE) {
if (!(partImage.part instanceof AudioPart)) {
partImage.part.drawThumbNail(g, rect, this);
}
}
}
}
@Override
public void endDrag() {
if (this.dragList == null) {
return;
}
switch (this.dragMode) {
case OVER_ITEM_MIDDLE:
if (!this.controlIsDown) {
this.editHistory.mark(getMessage("sequencer.partview.drag_move_part"));
} else {
this.editHistory.mark(getMessage("sequencer.partview.drag_copy_part"));
}
break;
case OVER_ITEM_RIGHT:
case OVER_ITEM_LEFT:
this.editHistory.mark(getMessage("sequencer.partview.resize"));
break;
case OVER_ENVELOPE_LEFT:
case OVER_ENVELOPE_RIGHT:
case OVER_ENVELOPE_GAIN:
System.out.println(" TODO history for envelope dragging " + this.dragMode);
break;
default:
System.err.println(" unknown dragmode " + this.dragMode);
}
// Collection<Part> selected = project.getPartSelection().getSelected();
for (PartImage pi : this.dragList) {
pi.drop();
}
this.dragList = null;
this.project.getEditHistoryContainer().notifyEditHistoryListeners();
// repaintItems();
}
@Override
public void erase(Item it) {
Part part = (Part) it;
this.editHistory.mark("sequencer.project.erase_part");
part.getLane().remove(part);
this.project.getPartSelection().removeSelected(part);
this.editHistory.notifyEditHistoryListeners();
this.project.getPartSelection().notifyListeners();
}
/*
* public void addTrackSelectionListener(PartSelectionListener l) { //
* trackSelectionListeners.add(l); }
*/
@Override
public boolean requiresNotificationOnEachTick() {
return false;
}
public class PartImage extends Rectangle {
private double endTimeSecs;
private double endTimeDelta;
private double startTimeSecs;
private double startTimeDelta;
public Color color;
/**
*
*/
private static final long serialVersionUID = 1L; // long length;
int laneId;
Part part;
PartImage(Part part, int laneId) {
this.color = part.getColor();
this.startTimeSecs = part.getStartInSecs();
this.endTimeSecs = part.getEndInSecs();
// length = part.getDuration();
this.laneId = laneId;
this.part = part;
// print("Create");
}
private void print(String txt) {
if (txt != null) {
System.out.println(txt);
}
TimeUtils tu = project.getTimeUtils();
System.out.println(" start=" + tu.tickToBarBeatTick((long) tempoList.getTickAtTime(startTimeSecs + startTimeDelta)));
System.out.println(" end=" + tu.tickToBarBeatTick((long) tempoList.getTickAtTime(endTimeSecs + endTimeDelta)));
}
public void setEndMoveByDeltaTicks(double dtick) {
double endTick = tempoList.getTickAtTime(endTimeSecs);
endTimeDelta = tempoList.getTimeAtTick(endTick + dtick) - endTimeSecs;
}
public void setMoveByDeltaTicks(double dtick) {
if (part instanceof MidiPart) {
setStartMoveByDeltaTicks(dtick);
setEndMoveByDeltaTicks(dtick);
} else {
setStartMoveByDeltaTicks(dtick);
endTimeDelta = startTimeDelta;
}
}
public void setStartMoveByDeltaTicks(double dtick) {
double startTick = tempoList.getTickAtTime(startTimeSecs);
startTimeDelta = tempoList.getTimeAtTick(startTick + dtick) - startTimeSecs;
}
void drop() {
switch (PartView.this.dragMode) {
case OVER_ITEM_MIDDLE:
Lane lane = this.part.getLane();
Lane destLane = PartView.this.laneHeader.visibleLanes.getVisibleLanes().get(this.laneId);
if (lane.getClass() != destLane.getClass()) {
if (!(destLane instanceof AudioLane && lane instanceof SynthLane && PartView.this.controlIsDown)) {
System.out.println(" Lanes must be the same type to copy a part");
return;
}
}
if (lane.getDisplayID() == this.laneId) {
assert (destLane == lane);
}
double dTick = tempoList.getTickAtTime(this.startTimeSecs + startTimeDelta) - tempoList.getTickAtTime(startTimeSecs);
if (!PartView.this.controlIsDown) {
// System.out.println("moveby " + dTick);
MovePartEditAction act = new MovePartEditAction(this.part,
dTick, destLane);
act.redo();
PartView.this.editHistory.push(act);
} else {
this.part.copyBy(dTick, destLane);
try {
if (MidiPart.class.isInstance(part)) {
((MidiPart) this.part).rebuildMultiEventEndTickComparables(); // PJS:
// Previously
// was
// .onLoad()
// -
// but
// caused
// that
// the
// original
// events
// where
// inserted
// into
// the
// ftw
// twice
} else {
this.part.onLoad();
}
} catch (Exception e) {
System.err.println(" SHOULD NEVER HAPPEN ");
e.printStackTrace();
}
}
break;
case OVER_ITEM_RIGHT:
case OVER_ITEM_LEFT:
ResizePartAction act = new ResizePartAction(this.part,
this.startTimeSecs + startTimeDelta, this.endTimeSecs + endTimeDelta);
act.redo();
PartView.this.editHistory.push(act);
break;
default:
System.err.println(" unknown dragmode In PARTVIEW PartImage drop" + PartView.this.dragMode);
}
}
/**
* Set the rectanlge for this PartImage
*
* @param pi
*/
private void positionPartImage() {
double x1 = startTimeSecs + startTimeDelta;
double w = endTimeSecs + endTimeDelta - startTimeSecs - startTimeDelta;
x1 = (int) userToScreen(x1);
w = (int) userToScreen(w);
Lane lane = laneHeader.visibleLanes.getVisibleLanes().get(laneId);
int y1 = lane.getDisplayY();
x = (int) x1;
y = y1;
width = (int) w;
height = lane.getDisplayH();
// System.out.println(pi);
}
}
/*
* @Override public void deleteSelected() { // TODO Auto-generated method
* stub }
*/
@Override
public void clientAddToSelection(Item item) {
this.project.getPartSelection().addSelected((Part) item);
this.project.getPartSelection().notifyListeners();
}
@Override
public void clientRemoveFromSelection(Item item) {
this.project.getPartSelection().removeSelected((Part) item);
this.project.getPartSelection().notifyListeners();
}
/*
* public void selectionCleared(SelectionContainer<Part> src) {
* repaintItems(); }
*
* public void addedToSelection(SelectionContainer<? extends Part> src,
* Collection<? extends Part> items) { repaintItems(); }
*
* public void removedFromSelection(SelectionContainer<? extends Part> src,
* Collection<? extends Part> items) { repaintItems(); }
*/
@Override
public int getHoverStateAt(Point p) {
final int endTol = 20;
// final int extraX = 20 ;
// if (true) return OVER_ITEM_MIDDLE;
int tol = endTol;
for (Lane lane : this.laneHeader.visibleLanes) {
if (lane.getParts() == null) {
continue;
}
for (Part part : lane.getParts()) {
Rectangle rect = getBounds(part);
if (rect.contains(p)) {
if (part instanceof AudioPart) {
int ret = ((AudioPart) part).getHoverState(p, rect);
if (ret != -1) {
return ret;
}
}
if (rect.width < endTol * 5) {
tol = rect.width / 3;
}
if ((p.x - rect.x) <= tol) {
return OVER_ITEM_LEFT;
}
if ((rect.x + rect.width - p.x) <= tol) {
return OVER_ITEM_RIGHT;
}
return OVER_ITEM_MIDDLE;
}
}
}
return OVER_NOTHING;
}
void makeTools() {
Cursor c = new Cursor(Cursor.DEFAULT_CURSOR);
this.selectTool = new SelectTool(c);
this.rectZoomTool = new RectZoomTool(c);
this.writeTool = new WriteTool(MyCursors.getCursor("pencil"));
this.eraseTool = new EraseTool(MyCursors.getCursor("eraser"));
this.dragViewTool = new DragViewTool(MyCursors.getCursor("move"));
this.splitTool = new PartSplitTool(Cursor.getDefaultCursor());
this.glueTool = new PartGlueTool(MyCursors.getCursor("glue"));
}
Lane laneAt(int yy) {
int y = unmapY(yy);
for (Lane lane : this.laneHeader.visibleLanes) {
if (lane.getDisplayY() <= y && (lane.getDisplayY() + lane.getDisplayH()) > y) {
return lane;
}
}
return null;
}
public void fireSequenceDataChanged(EditHistoryAction[] edithistoryActions) {
repaintItems();
}
public void selectionChanged(SelectionContainer<? extends Part> src) {
repaintItems();
}
@Override
protected void paintImageImplLabel(Graphics2D graphics) {
// no labels to draw do nothing.
}
@Override
public double getSnapQuantization() {
return this.project.getPartViewSnapQuantization();
}
@Override
public void setSnapQuantization(double quant) {
this.project.setPartViewSnapQuantization(quant);
repaintItems();
}
@Override
public boolean isSnapQuantized() {
return this.project.isPartViewSnapQuantized();
}
@Override
public void setSnapQuantized(boolean b) {
this.project.setPartViewSnapQuantized(b);
}
@Override
public void setFocus(Item item) {
this.project.getPartSelection().setFocus((Part) item);
}
@Override
public ToolAdapter getTool(String name) {
this.splitting = false;
if (name.equals("split")) {
this.splitting = true;
return this.splitTool;
} else if (name.equals("glue")) {
return this.glueTool;
}
return super.getTool(name);
}
public void splitIsOver(Point p) {
// System.out.println(" split is over " + p );
boolean quant=project.isPartViewSnapQuantized();
long tick = screenToTickAbs(p.x, quant);
if (this.splitTick == tick) {
return;
}
this.splitTick = tick;
// System.out.println(" split is over " + p + " "
// + project.getTimeUtils().tickToBarBeatTick(tick));
repaintItems();
// TODO Auto-generated method stub
}
public void splitAt(Point p) {
SplitPartAction act = new SplitPartAction(this.project,
screenToTickAbs(p.x, project.isPartViewSnapQuantized()));
this.project.getEditHistoryContainer().mark(
getMessage("sequencer.partview.split.part"));
act.redo();
this.project.getEditHistoryContainer().push(act);
this.project.getEditHistoryContainer().notifyEditHistoryListeners();
this.project.getPartSelection().notifyListeners();
}
public void gluePart(Item item) {
EditHistoryAction act = new GluePartEditAction((MidiPart) item);
this.project.getEditHistoryContainer().mark(
getMessage("sequencer.partview.glue.part"));
act.redo();
this.project.getEditHistoryContainer().push(act);
this.project.getEditHistoryContainer().notifyEditHistoryListeners();
this.project.getPartSelection().notifyListeners();
}
public void selectAll() {
Vector<Part> list = new Vector<Part>();
for (Lane lane : this.laneHeader.visibleLanes) {
for (Part part : lane.getParts()) {
list.add(part);
}
}
this.project.getPartSelection().setSelected(list);
this.project.getPartSelection().notifyListeners();
}
@Override
public void clientNotifySelectionChange() {
this.project.getPartSelection().notifyListeners();
}
@Override
/**
* Invoked by ItemPanel.setTimeLineEvent to set cursor based on a click on
* the timeline
*/
public void setTimeAtX(int x) {
boolean iQuant=project.isPartViewSnapQuantized();
long tick = screenToTickAbs(x, iQuant);
this.sequencer.setTickPosition(tick);
}
public ProjectFrame getProjectFrame() {
return frame;
}
}