/* * Created on Feb 8, 2007 * * Copyright (c) 2007 Jens Gulden * * 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.model; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Rectangle; import java.io.PrintStream; import java.util.*; import com.frinika.sequencer.FrinikaTrackWrapper; import com.frinika.sequencer.model.Selectable; import com.frinika.sequencer.gui.partview.PartView; /** * Ghost ("reference-copy") of a MidiPart. * * Ghosts appear as Parts in the Tracker-view, but do not contain their own * events. Instead, Ghosts are internally linked to the origial Part from which * they have been created. They represent the original Part transparently, but * are not editable themselves. All changed applied to the original Part will * immediately take effect on Ghosts also. * * @author Jens Gulden */ public class MidiPartGhost extends MidiPart implements Ghost, CommitListener { private static final long serialVersionUID = 1L; MidiPart referredPart; /** * original-part's-multi-event -> cloned-multi-event */ transient Map<MultiEvent, MultiEvent> committedEvents = new HashMap<MultiEvent, MultiEvent>(); public MidiPartGhost(MidiPart referredPart, Lane lane) { this(); this.referredPart = referredPart; this.name = "Ghost of " + referredPart.name; this.lane = lane; referredPart.addCommitListener(this); } public MidiPartGhost(MidiPart referredPart, long deltaTicks) { this(referredPart, referredPart.getLane()); this.setStartTick(referredPart.getStartTick() + deltaTicks); this.setEndTick(referredPart.getEndTick() + deltaTicks); } /** * for cloning */ private MidiPartGhost() { super(); } public MidiPart getReferredPart() { return referredPart; } public void setReferredPart(MidiPart referredPart) { this.referredPart = referredPart; } public long getDistance() { // distance to referred original return this.getStartTick() - getReferredPart().getStartTick(); } @Override public void add(MultiEvent ev) { // nop - disable } // @Override // public void attach() { // super.attach(); // ??? // } @Override public Object clone() throws CloneNotSupportedException { MidiPartGhost clone=new MidiPartGhost(); clone.referredPart = referredPart; clone.name = name; clone.lane = lane; clone.setStartTick(getStartTick()); clone.setEndTick(getEndTick()); clone.selected = false; return clone; } @Override public void commitEventsAdd() { if (multiEvents == null ) return; long distance = getDistance(); for(MultiEvent multiEvent : getReferredPart().getMultiEvents()) { commitAddClone(multiEvent); } } @Override public synchronized void commitEventsRemove() { for (MultiEvent event : committedEvents.values()) { event.commitRemove(); } committedEvents.clear(); } /** * Called when the original MidiPart has undergone changes. */ public void commitAddPerformed(MultiEvent event) { commitAddClone(event); // mimic the same in ghost } /** * Called when the original MidiPart has undergone changes. */ public synchronized void commitRemovePerformed(MultiEvent event) { commitRemoveClone(event); // mimic the same in ghost } protected MultiEvent commitAddClone(MultiEvent multiEvent) { try { MultiEvent newEvent = (MultiEvent)multiEvent.clone(); newEvent.part = this; newEvent.startTick += getDistance(); newEvent.commitAdd(); if (committedEvents == null) { committedEvents = new HashMap<MultiEvent, MultiEvent>(); } committedEvents.put(multiEvent, newEvent); return newEvent; } catch (CloneNotSupportedException cnse) { cnse.printStackTrace(); return null; } } protected void commitRemoveClone(MultiEvent multiEvent) { MultiEvent clone = committedEvents.remove(multiEvent); if (clone != null) { clone.commitRemove(); } } @Override public void copyBy(double deltaTick, Lane dst) { MidiPartGhost clone = new MidiPartGhost(this.getReferredPart(), dst); clone.setStartTick(getStartTick()+deltaTick); clone.setEndTick(getEndTick()+deltaTick); } @Override public Selectable deepCopy(Selectable parent) { try { MidiPartGhost clone = (MidiPartGhost)this.clone(); if (parent == null) { clone.lane=lane; } clone.name="Copy of "+name; clone.color=color; return clone; } catch (CloneNotSupportedException cnse) { cnse.printStackTrace(); return null; } } @Override public void deepMove(long tick) { setStartTick(getStartTick() + tick); setEndTick(getEndTick() + tick); } // @Override // public void detach() { // super.detach(); // ??? // } @Override public void drawThumbNail(Graphics2D g, Rectangle rect, PartView panel) { int xShift = (int)panel.userToScreen(this.getDistance()); g.translate(xShift, 0); getReferredPart().drawThumbNail(g, rect, panel); // delegate g.translate(-xShift, 0); } @Override public EditHistoryContainer getEditHistoryContainer() { return getReferredPart().getEditHistoryContainer(); // delegate } @Override public int getMidiChannel() { return getReferredPart().getMidiChannel(); // delegate } @Override public SortedSet<MultiEvent> getMultiEvents() { return super.getMultiEvents(); // assert .isEmpty() } @Override public SortedSet<MultiEvent> getMultiEventSubset(long startTick, long endTick) { return super.getMultiEventSubset(startTick, endTick); // assert .isEmpty() } @Override public String getName() { return super.getName(); } @Override public int[] getPitchRange() { return getReferredPart().getPitchRange(); // delegate } @Override public FrinikaTrackWrapper getTrack() { return super.getTrack(); } @Override public void importFromMidiTrack(long startTickArg, long endTickArg) { // nop } @Override public void moveContentsBy(double dTick, Lane dstLane) { super.moveContentsBy(dTick, dstLane); } @Override protected void moveItemsBy(long deltaTick) { commitEventsRemove(); commitEventsAdd(); } @Override public void onLoad() { super.onLoad(); } @Override public void rebuildMultiEventEndTickComparables() { super.rebuildMultiEventEndTickComparables(); } @Override public void remove(MultiEvent multiEvent) { // nop - disabled } @Override public void restoreFromClone(EditHistoryRecordable o) { MidiPartGhost clone=(MidiPartGhost)o; this.referredPart = clone.referredPart; this.lane = clone.lane; this.name = clone.name; setStartTick(clone.getStartTick()); setEndTick(clone.getEndTick()); // selection selected=false; // clone.selected; } @Override public void setBoundsFromEvents() { // nop } @Override public void setName(String name) { super.setName(name); } // @Override // public void update(MultiEvent multiEvent) { // // nop // } @Override public void addToModel() { super.addToModel(); } @Override public void displayStructure(String prefix, PrintStream out) { super.displayStructure(prefix, out); } // @Override // public int getColorID() { // return getReferredPart().getColorID(); // delegate // } public Color getColor() { return getReferredPart().getColor(); // delegate } @Override public long getDurationInTicks() { return getReferredPart().getDurationInTicks(); // delegate } @Override public long getEndTick() { return getStartTick() + getDurationInTicks(); } @Override public Rectangle getEventBounds() { return getReferredPart().getEventBounds(); // delegate ??? } @Override public Lane getLane() { return super.getLane(); } @Override public long getStartTick() { return super.getStartTick(); } @Override public boolean isSelected() { return super.isSelected(); } @Override public long leftTickForMove() { return super.leftTickForMove(); } @Override public void moveBy(long deltaTick) { super.moveBy(deltaTick); } @Override public void removeFromModel() { getReferredPart().removeCommitListener(this); commitEventsRemove(); super.removeFromModel(); } @Override public long rightTickForMove() { return super.rightTickForMove(); } @Override public void setEndTick(double tick) { super.setEndTick(tick); // but will have no effect, see getEndTick() } @Override public void setSelected(boolean b) { super.setSelected(b); } @Override public void setStartTick(double tick) { super.setStartTick(tick); } }