/*
* Created on Jan 14, 2006
*
* Copyright (c) 2005 Peter Johan Salomonsen (http://www.petersalomonsen.com)
*
* 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.tracker;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.ShortMessage;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import com.frinika.project.ProjectContainer;
import com.frinika.project.gui.ProjectFrame;
import com.frinika.sequencer.FrinikaSequence;
import com.frinika.sequencer.SongPositionListener;
import com.frinika.sequencer.SwingSongPositionListenerWrapper;
import com.frinika.sequencer.gui.selection.SelectionContainer;
import com.frinika.sequencer.gui.selection.SelectionListener;
import com.frinika.sequencer.midi.MidiMessageListener;
import com.frinika.sequencer.model.ControllerEvent;
import com.frinika.sequencer.model.Lane;
import com.frinika.sequencer.model.MidiLane;
import com.frinika.sequencer.model.MidiPart;
import com.frinika.sequencer.model.MultiEvent;
import com.frinika.sequencer.model.NoteEvent;
import com.frinika.sequencer.model.Part;
import com.frinika.sequencer.model.PitchBendEvent;
import com.frinika.sequencer.model.SysexEvent;
import com.frinika.tracker.DoubleCellRenderer;
import com.frinika.sequencer.gui.ItemRollToolBar;
import com.frinika.sequencer.gui.NoteLengthPopup;
import com.frinika.sequencer.gui.Snapable;
import com.frinika.sequencer.gui.virtualkeyboard.VirtualKeyboard;
import java.awt.Toolkit;
import java.awt.event.ItemListener;
import static com.frinika.localization.CurrentLocale.getMessage;
/**
* A midi editor for editing a midi part in an old-school Amiga Soundtracker/ProTracker fashion. For adapting to Midi there
* are differences to the traditional tracker regarding handling of rows. First the row division is not locked - but can be changed at
* any time - so that you can edit your notes using the optimum "rows per beat" resolution. In traditional trackers you were stuck to the
* originally chosen row resolution. Also if your notes is between two rows - you can either increase the resolution - or you can use the
* time column which shows how much your note is in between the rows (-0.5 to 0.5 - before or after).
*
* All note column-sets, consist of row-time (before or after the row), actual note, velocity, and length of the note (number of rows). Notes
* are inserted by pointing the cursor in the note column, and using the computer keyboard as a piano with two octaves. Lower octave starting
* at Z making the twelve tone octave like this: ZSXDCVGBHNJM - and the upper octave: Q2W3ER5T6Y7U.
*
* To insert controllers you also use a note column, but hold down CTRL while typing a controller number. The velocity field is then used for
* controller value. Pitch bend is CTRL-P in a note column and velocity of 0 is bend a note down, 64 is no bend and 127 one note up.
*
* @author Peter Johan Salomonsen
*/
public class TrackerPanel extends JPanel implements SelectionListener<Part>,SongPositionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
ProjectFrame frame;
ProjectContainer project;
MidiPart part;
int playingRow = 0;
JTable table;
TrackerTableModel tableModel;
SelectionContainer<MultiEvent> multiEventSelectionContainer;
private JScrollPane trackerScrollPane;
private boolean followSong = true;
private int automaticRowJump = 0;
/**
* Octave selection combobox
*/
JComboBox octaveCombobox = new JComboBox(new Integer[] {1,2,3,4,5,6,7,8,9});
{
octaveCombobox.setSelectedItem(VirtualKeyboard.Octave);
octaveCombobox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
VirtualKeyboard.Octave = (Integer)octaveCombobox.getSelectedItem();
}
});
}
public TrackerPanel(FrinikaSequence sequence,ProjectFrame frame) {
this.frame = frame;
this.project = frame.getProjectContainer();
this.multiEventSelectionContainer=project.getMultiEventSelection();
initComponents();
project.getSequencer().addSongPositionListener(new SwingSongPositionListenerWrapper(this));
}
private class MidiMessageRunnable implements Runnable
{
MidiMessage message;
public void run()
{
midiMessage(message);
}
public void midiMessage(MidiMessage message) {
if(listenlane.isRecording())
{
if(table.getSelectedRow() == -1 || table.getSelectedColumn() == -1) return;
if(((table.getSelectedColumn() - 1) % TrackerTableModel.COLUMNS) != TrackerTableModel.COLUMN_NOTEORCC) return;
if(message instanceof ShortMessage)
{
ShortMessage sms = (ShortMessage)message;
if(sms.getCommand() == ShortMessage.NOTE_ON || sms.getCommand() == ShortMessage.NOTE_OFF )
{
int note = sms.getData1();
int vel = sms.getData2();
if(sms.getCommand() == ShortMessage.NOTE_OFF) vel = 0;
if(vel > 0)
{
int row = table.getSelectedRow();
int col = tableModel.tableColumnToTrackerColumn(table.getSelectedColumn());
part.getEditHistoryContainer().mark("new note");
final MultiEvent me = tableModel.getCellEvent(row, col);
if(me != null)
part.remove(me);
NoteEvent event = new NoteEvent(part,tableModel.getTickForRow(row),note,vel,1,(long)(tableModel.ticksPerRow*tableModel.getEditDuration()));
event.setTrackerColumn(col);
part.add(event);
part.getEditHistoryContainer().notifyEditHistoryListeners();
if(getAutomaticRowJump()>0 )
table.changeSelection(table.getSelectedRow()+getAutomaticRowJump(),table.getSelectedColumn(),false,false);
}
}
}
}
}
};
MidiMessageListener listener = new MidiMessageListener()
{
public void midiMessage(MidiMessage message) {
MidiMessageRunnable r = new MidiMessageRunnable();
r.message = message;
try {
SwingUtilities.invokeAndWait(r);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
};
MidiLane listenlane = null;
void connectMidiListener()
{
if(listenlane != null) disconnectMidiListener();
Lane lane = part.getLane();
if(!(lane instanceof MidiLane)) return;
listenlane = ((MidiLane)lane);
project.getSequencer().addMidiMessageListener(listener);
}
void disconnectMidiListener()
{
if(listenlane == null) return;
project.getSequencer().removeMidiMessageListener(listener);
}
void initComponents() {
setLayout(new BorderLayout());
tableModel = new TrackerTableModel(frame);
table = new JTable(tableModel) {
private static final long serialVersionUID = 1L;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int firstcolsize = getColumnModel().getColumn(0).getWidth();
for (int i = 0; i < getRowCount(); i++) {
int y = getRowHeight()*i;
g.setColor(Color.LIGHT_GRAY);
g.drawLine(0,y-1,firstcolsize,y-1);
if(i % tableModel.getTicksPerRow() == 0)
{
g.setColor(Color.BLACK);
g.drawLine(0,y-1,firstcolsize,y-1);
if(i % (tableModel.getTicksPerRow()*4) == 0)
g.setColor(Color.BLACK);
else
g.setColor(Color.LIGHT_GRAY);
g.drawLine(0,y-1,getWidth(),y-1);
}
}
g.setColor(Color.BLACK);
int x = 0;
for (int i = 0; i < getColumnCount(); i++) {
if((i-1) % TrackerTableModel.COLUMNS == 0)
{
g.drawLine(x-1,0,x-1, getHeight());
}
x += getColumnModel().getColumn(i).getWidth();
}
}
@Override
public void repaint(long tm, int x, int y, int width, int height) {
Rectangle v = getVisibleRect();
x = v.x;
width = getVisibleRect().width;
super.repaint(tm, x, y, width, height);
}
@Override
public void repaint(Rectangle r) {
Rectangle v = getVisibleRect();
r.x = v.x;
r.width = v.width;
super.repaint(r);
}
public boolean isColumnSelected(int column) {
if(column == 0) return false;
int c = column - 1;
c = c - c % 4;
if(columnModel.getSelectionModel().isSelectedIndex(c+1)) return true;
if(columnModel.getSelectionModel().isSelectedIndex(c+2)) return true;
if(columnModel.getSelectionModel().isSelectedIndex(c+3)) return true;
if(columnModel.getSelectionModel().isSelectedIndex(c+4)) return true;
return false;
//return columnModel.getSelectionModel().isSelectedIndex(column);
}
JTable thistable = this;
final DecimalFormat dec_format = new DecimalFormat("0.00");
final DecimalFormat dec_format2 = new DecimalFormat("0.0");
class TrackerTableCellRender extends DefaultTableCellRenderer
{
private static final long serialVersionUID = 1L;
Color note_background = null;
Color row_background = null;
Color row_background2 = null;
Font def_font = null;
Font note_font = null;
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
boolean hasFocusOrg = hasFocus;
if(note_background == null)
{
float[] rgb = getGridColor().getRGBComponents(new float[4]);
rgb[0] = 1f - (1f - rgb[0])*0.4f;
rgb[1] = 1f - (1f - rgb[1])*0.4f;
rgb[2] = 1f - (1f - rgb[2])*0.4f;
note_background = new Color(rgb[0], rgb[1], rgb[2], rgb[3]);
def_font = getFont();
note_font = def_font.deriveFont(Font.BOLD);
}
if(row_background == null)
{
float[] rgb = table.getSelectionBackground().getRGBComponents(new float[4]);
row_background = new Color(rgb[0], rgb[1], rgb[2], 0.2f);
}
if(row_background2 == null)
{
float[] rgb = table.getSelectionBackground().getRGBComponents(new float[4]);
row_background2 = new Color(rgb[0], rgb[1], rgb[2], 0.35f);
}
if(column == 0)
{
isSelected = false;
hasFocus = false;
}
if(hasFocus)
{
isSelected = false;
}
super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
row, column);
if(hasFocus)
{
isSelected = false;
hasFocus = false;
}
if(!isSelected)
if(!hasFocus)
{
setBackground(thistable.getBackground());
if(column == 0) setBackground(getGridColor());
if((column - 1) % TrackerTableModel.COLUMNS == TrackerTableModel.COLUMN_NOTEORCC) setBackground(note_background);
}
if((column - 1) % TrackerTableModel.COLUMNS == TrackerTableModel.COLUMN_NOTEORCC)
{
setFont(note_font);
setHorizontalAlignment(SwingConstants.CENTER);
}
else
{
setFont(def_font);
setHorizontalAlignment(SwingConstants.RIGHT);
}
if(value instanceof Double)
{
setText(dec_format.format((Double)value));
}
if(column == 0)
{
if(value.toString().length() != 0)
setText(dec_format2.format(Double.parseDouble(value.toString())));
}
if(column != 0)
if(!isSelected)
if(!hasFocus)
if(row == getSelectionModel().getLeadSelectionIndex()) // getSelectedRow()
{
setOpaque(true);
if(hasFocusOrg)
setBackground(row_background2);
else
setBackground(row_background);
}
return this;
}
}
TrackerTableCellRender renderer = new TrackerTableCellRender();
public TableCellRenderer getCellRenderer(int row, int column) {
return renderer;
}
public Component prepareRenderer(TableCellRenderer renderer,
int row, int column) {
Component c = super.prepareRenderer(renderer, row, column);
if(column == 0 && row == playingRow)
c.setBackground(Color.GREEN);
else if(c.getBackground()!=null && c.getBackground().equals(Color.GREEN))
c.setBackground(null);
return c;
}
/**
* Intercept addColumn so that correct widths and renderers/editors
* are applied on initialization
*/
@Override
public void addColumn(TableColumn column) {
if(getColumnCount()==0)
{
// Set width of bar.beat column
column.setPreferredWidth(60);
}
else
{
switch (((getColumnCount() - 1) % TrackerTableModel.COLUMNS)) {
case TrackerTableModel.COLUMN_TIME:
column.setPreferredWidth(40);
column.setCellRenderer(new DoubleCellRenderer());
break;
case TrackerTableModel.COLUMN_CHANNEL:
column.setPreferredWidth(30);
break;
case TrackerTableModel.COLUMN_NOTEORCC:
// column.setPreferredWidth(36);
column.setPreferredWidth(40); // PJL increased to see CC93 etc
column.setCellEditor(new MultiEventCellEditor(
TrackerPanel.this));
break;
case TrackerTableModel.COLUMN_VELORVAL:
column.setPreferredWidth(30);
break;
case TrackerTableModel.COLUMN_LEN:
column.setPreferredWidth(40);
column.setCellRenderer(new DoubleCellRenderer());
break;
}
}
super.addColumn(column);
}
/*
* (non-Javadoc)
*
* @see javax.swing.JTable#processKeyBinding(javax.swing.KeyStroke,
* java.awt.event.KeyEvent, int, boolean)
*/
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
int condition, boolean pressed) {
// Virtual keyboard octave selection
if(e.getKeyCode()>=KeyEvent.VK_F1 && e.getKeyCode()<=KeyEvent.VK_F9)
{
VirtualKeyboard.Octave = e.getKeyCode()-KeyEvent.VK_F1 +1;
octaveCombobox.setSelectedItem(VirtualKeyboard.Octave);
return true;
}
else if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE && e.isShiftDown())
{
if(getSelectedRow() != -1)
if(getSelectedColumn() > 0)
{
if(!pressed) return false;
if(getSelectedRow() != 0)
setRowSelectionInterval(getSelectedRow()-1, getSelectedRow()-1);
ArrayList<Integer> cols = new ArrayList<Integer>();
for(int c : getSelectedColumns())
{
int col = tableModel.tableColumnToTrackerColumn(c);
if(!cols.contains(col)) cols.add(col);
}
part.getEditHistoryContainer().mark("move events");
//int col = tableModel.tableColumnToTrackerColumn(table.getSelectedColumn());
int min_row = getSelectedRow();
int max_row = getRowCount();
for (int col : cols)
for (int i = min_row; i < max_row; i++) {
MultiEvent me = tableModel.getCellEvent(i,col);
if(me != null)
{
part.remove(me);
if(i != min_row)
{
me.setTrackerColumn(col);
me.setStartTick(tableModel.getTickForRow(i-1));
part.add(me);
}
}
}
part.getEditHistoryContainer().notifyEditHistoryListeners();
return true;
}
}
if(e.getKeyCode() == KeyEvent.VK_INSERT)
{
if(getSelectedRow() != -1)
if(getSelectedColumn() > 0)
{
if(!pressed) return false;
ArrayList<Integer> cols = new ArrayList<Integer>();
for(int c : getSelectedColumns())
{
int col = tableModel.tableColumnToTrackerColumn(c);
if(!cols.contains(col)) cols.add(col);
}
part.getEditHistoryContainer().mark("move events");
int min_row = getSelectedRow();
for (int col : cols)
for (int i = getRowCount() - 1; i >= min_row; i--) {
MultiEvent me = tableModel.getCellEvent(i,col);
if(me != null)
{
part.remove(me);
me.setTrackerColumn(col);
me.setStartTick(tableModel.getTickForRow(i+1));
part.add(me);
}
}
part.getEditHistoryContainer().notifyEditHistoryListeners();
if(getSelectedRow() < (getRowCount() - 1))
setRowSelectionInterval(getSelectedRow()+1, getSelectedRow()+1);
return true;
}
}
if(e.getKeyCode() == KeyEvent.VK_DELETE)
{
if(getSelectedColumnCount() > 1) return false;
if(getSelectedRowCount() > 1) return false;
if(getSelectedRow() != -1)
if(table.getSelectedColumn() > 0)
//if(((table.getSelectedColumn() - 1) % TrackerTableModel.COLUMNS) == TrackerTableModel.COLUMN_NOTEORCC)
{
if(!pressed) return false;
int col = tableModel.tableColumnToTrackerColumn(table.getSelectedColumn());
final MultiEvent me = tableModel.getCellEvent(getSelectedRow(),col);
if(me!=null)
{
if(me instanceof NoteEvent)
part.getEditHistoryContainer().mark("delete note");
else if(me instanceof ControllerEvent)
part.getEditHistoryContainer().mark("delete control change");
else if(me instanceof PitchBendEvent)
part.getEditHistoryContainer().mark("delete pitch bend");
else if(me instanceof SysexEvent) // Jens
part.getEditHistoryContainer().mark(getMessage("sequencer.sysex.delete_sysex"));
else
part.getEditHistoryContainer().mark("delete event");
part.remove(me);
part.getEditHistoryContainer().notifyEditHistoryListeners();
}
if(getAutomaticRowJump()>0 )
table.changeSelection(table.getSelectedRow()+getAutomaticRowJump(),table.getSelectedColumn(),false,false);
return true;
}
}
if (isAccelerator(ks))
return false;
/**
* Next similar column hotkeys feature Ctrl + Right / Ctrl + Left hotkeys
* to move the cursor one entire time/note/vel/len column
* right/left (i.e. to move 4 cols right/left; saves a few button hits) in tracker.
*/
else if(e.isControlDown() && ks.getKeyCode()==KeyEvent.VK_LEFT && pressed && condition == 1)
{
if(getSelectedColumn()-tableModel.COLUMNS>0)
changeSelection(getSelectedRow(), getSelectedColumn()-tableModel.COLUMNS, false, false);
return false;
}
else if(e.isControlDown() && ks.getKeyCode()==KeyEvent.VK_RIGHT && pressed && condition == 1)
{
if(getSelectedColumn()+tableModel.COLUMNS<tableModel.getColumnCount())
changeSelection(getSelectedRow(), getSelectedColumn()+tableModel.COLUMNS, false, false);
return false;
}
// -----------------------------------------------------------------------------------
else
return super.processKeyBinding(ks, e, condition, pressed);
}
@Override
public void tableChanged(TableModelEvent e) {
if(e.getFirstRow()==TableModelEvent.HEADER_ROW)
{
int selectedCol = getSelectedColumn();
int selectedRow = getSelectedRow();
super.tableChanged(e);
changeSelection(selectedRow, selectedCol, false, false);
}
else
super.tableChanged(e);
}
};
table.addFocusListener(new FocusListener()
{
public void focusGained(FocusEvent e) {
connectMidiListener();
}
public void focusLost(FocusEvent e) {
disconnectMidiListener();
}
});
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
//table.setGridColor(Color.GRAY);
table.setCellSelectionEnabled(true);
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
updateSelection();
}
}
});
table.getColumnModel().getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
updateSelection();
}
}
});
trackerScrollPane = new JScrollPane(table);
add(trackerScrollPane, BorderLayout.CENTER);
JPanel controlPanel = new JPanel(new FlowLayout());
controlPanel.setOpaque(false);
controlPanel.add(new JLabel("Octave"));
// Virtual keyboard octave selection
controlPanel.add(octaveCombobox);
String[] automaticRowJumpTextField_values = {"0", "1", "2", "3", "4","8","16"};
final JComboBox automaticRowJumpTextField = new JComboBox(automaticRowJumpTextField_values);
automaticRowJumpTextField.setEditable(true);
automaticRowJumpTextField.setSelectedItem("" + automaticRowJump);
//automaticRowJumpTextField.setColumns(5);
automaticRowJumpTextField.setToolTipText(getMessage("sequencer.tracker.automaticrowjump.tooltip"));
automaticRowJumpTextField.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
try {
automaticRowJump = Integer
.parseInt(automaticRowJumpTextField.getSelectedItem().toString());
} catch (Exception ex) {
automaticRowJumpTextField.setSelectedItem(automaticRowJump
+ "");
}
}
});
/*
automaticRowJumpTextField.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
try {
automaticRowJump = Integer
.parseInt(automaticRowJumpTextField.getSelectedItem().toString());
} catch (Exception ex) {
automaticRowJumpTextField.setSelectedItem(automaticRowJump
+ "");
}
}
});*/
controlPanel.add(new JLabel(
getMessage("sequencer.tracker.automaticrowjump")));
controlPanel.add(automaticRowJumpTextField);
String[] editVelocityTextField_values = {"16", "32", "48", "64", "80", "100", "127"};
final JComboBox editVelocityTextField = new JComboBox(editVelocityTextField_values);
editVelocityTextField.setEditable(true);
editVelocityTextField.setSelectedItem(""
+ tableModel.getEditVelocity());
editVelocityTextField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
tableModel.setEditVelocity(Integer
.parseInt(editVelocityTextField.getSelectedItem().toString()));
} catch (Exception ex) {
editVelocityTextField.setSelectedItem(tableModel.getEditVelocity()
+ "");
}
}
});
controlPanel.add(new JLabel(
getMessage("sequencer.tracker.editvelocity")));
controlPanel.add(editVelocityTextField);
final DecimalFormat dec_format = new DecimalFormat("0.00");
String[] editLenTextField_values = {
dec_format.format(0.25),dec_format.format(0.5),
dec_format.format(1),dec_format.format(2),dec_format.format(3),dec_format.format(4),
dec_format.format(6),dec_format.format(8),
dec_format.format(12),dec_format.format(16),
dec_format.format(24),dec_format.format(32),
};
final JComboBox editLenTextField = new JComboBox(editLenTextField_values);
editLenTextField.setEditable(true);
editLenTextField.setSelectedItem(dec_format.format(tableModel.getEditDuration()));
editLenTextField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
tableModel.setEditDuration(dec_format
.parse(editLenTextField.getSelectedItem().toString()).doubleValue());
} catch (Exception ex) {
editLenTextField.setSelectedItem(dec_format.format(tableModel.getEditDuration()));
}
}
});
controlPanel.add(new JLabel("Edit Length"));
controlPanel.add(editLenTextField);
String[] rowsPerBeatTextField_values = {"1", "2", "3", "4", "6", "8"};
final JComboBox rowsPerBeatTextField = new JComboBox(rowsPerBeatTextField_values);
rowsPerBeatTextField.setSelectedItem("" + tableModel.getTicksPerRow());
rowsPerBeatTextField.setEditable(true);
rowsPerBeatTextField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
tableModel.setRowsPerBeat(Integer.parseInt(rowsPerBeatTextField
.getSelectedItem().toString()));
NoteLengthPopup.updateButton(quantizeSet, snapables, project.getSequence());
} catch (Exception ex) {
rowsPerBeatTextField.setSelectedItem(tableModel.getTicksPerRow() + "");
}
}
});
controlPanel.add(new JLabel(getMessage("sequencer.tracker.rowsperbeat")));
controlPanel.add(rowsPerBeatTextField);
JPanel settings = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
//settings.setBorder(BorderFactory.createEtchedBorder());
final JToggleButton follow = ItemRollToolBar.makeFollowSongButton(null,settings);
follow.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
followSong = follow.isSelected();
}});
follow.setSelected(followSong);
Insets insets = new Insets(0, 0, 0, 0);
follow.setMargin(insets);
snapables = new Vector<Snapable>();
snapables.add(new Snapable() {
public double getSnapQuantization() {
// System.out.println("TrackerPanel ticks per row=" + tableModel.getTicksPerRow());
return (double)project.getSequence().getResolution() / (double)tableModel.getTicksPerRow();
}
public void setSnapQuantization(double quant) {
int rowsPerBeat = (int)(project.getSequence().getResolution() / quant);
if(rowsPerBeat <= 0) rowsPerBeat = 1;
tableModel.setRowsPerBeat(rowsPerBeat);
rowsPerBeatTextField.setSelectedItem(""+rowsPerBeat);
}});
quantizeSet = ItemRollToolBar.makeSnapToButton(snapables,settings,project.getSequence());
quantizeSet.setMargin(insets);
controlPanel.add(settings);
JToolBar toolbar = new JToolBar();
toolbar.add(controlPanel);
add(toolbar, BorderLayout.NORTH);
}
Vector<Snapable> snapables = null;
JButton quantizeSet = null;
/**
* Update the selection container according to the selection of rows/columns in the tracker.
*
*/
private void updateSelection() {
multiEventSelectionContainer.clearSelection();
multiEventSelectionContainer.setSelectionStartTick(tableModel
.getTickForRow(table.getSelectedRow()));
multiEventSelectionContainer.setSelectionLeftColumn(tableModel.tableColumnToTrackerColumn(table.getSelectedColumn()));
for (int row : table.getSelectedRows())
{
int lasttrack = -1;
for (int col : table.getSelectedColumns()) {
if(col == 0) continue;
int track = (col - 1) / TrackerTableModel.COLUMNS;
if(lasttrack == track) continue;
lasttrack = track;
// if (((col - 1) % TrackerTableModel.COLUMNS) == TrackerTableModel.COLUMN_NOTEORCC) {
MultiEvent selectedEvt = tableModel.getMultiEventAt(row,
col);
if (selectedEvt != null)
multiEventSelectionContainer
.addSelected(selectedEvt);
//}
}
}
multiEventSelectionContainer.notifyListeners(); // PJL
}
public void dispose() {
tableModel.dispose();
project.getSequencer().removeSongPositionListener(this);
}
public MidiPart getPart() {
return part;
}
public void setPart(MidiPart part) {
if(part!=this.part)
{
tableModel.setMidiPart(part);
this.part = part;
}
}
// TODO repair following stuff PJL
public void partSelectionCleared() {
// TODO Auto-generated method stub
}
public void partsRemovedFromSelection(Collection<Part> parts) {
// TODO Auto-generated method stub
}
void setPartToFocus() {
Part focus=project.getPartSelection().getFocus();
if (focus instanceof MidiPart)
setPart((MidiPart) focus);
else
setPart(null);
}
public void selectionChanged(SelectionContainer<? extends Part> src) {
setPartToFocus();
}
public void notifyTickPosition(long tick) {
int lastPlayingRow = playingRow;
playingRow = tableModel.getPlayingRow();
if(playingRow!=lastPlayingRow)
{
if(lastPlayingRow>=0 && lastPlayingRow<table.getRowCount())
tableModel.fireTableCellUpdated(lastPlayingRow,0);
if(playingRow>=0 && playingRow<tableModel.getRowCount())
tableModel.fireTableCellUpdated(playingRow,0);
//TODO: Autoscroll - should be configurable from gui
if(followSong && playingRow>=0 && playingRow<tableModel.getRowCount())
{
Rectangle playRowRect = table.getCellRect(playingRow,0,true);
if(!trackerScrollPane.getViewport().getViewRect().intersects(table.getCellRect(playingRow,0,true)))
{
trackerScrollPane.getViewport().setViewPosition(new Point(playRowRect.x,playRowRect.y));
table.repaint();
}
}
}
}
public boolean requiresNotificationOnEachTick() {
return false;
}
/**
* @return the table
*/
public JTable getTable() {
return table;
}
/**
*
* @return the trackerTableModel
*/
public TrackerTableModel getTableModel() {
return tableModel;
}
/**
* Return how many rows to automatically jump when a note is hit (default is 0)
* @return
*/
public int getAutomaticRowJump() {
return automaticRowJump;
}
/**
* Keystrokes that should not be handled by this panel or the table
*
* cut, copy, paste, undo, redo, save
* @param ks
* @return
*/
private boolean isAccelerator(KeyStroke ks) {
return (ks.equals(KeyStroke.getKeyStroke(KeyEvent.VK_X,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()))
|| ks.equals(KeyStroke.getKeyStroke(KeyEvent.VK_C,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()))
|| ks.equals(KeyStroke.getKeyStroke(KeyEvent.VK_V,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()))
|| ks.equals(KeyStroke.getKeyStroke(KeyEvent.VK_Z,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()))
|| ks.equals(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()))
|| ks.equals(KeyStroke.getKeyStroke(KeyEvent.VK_S,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()))
);
}
}