/*
* Copyright (C) 2013-2017 たんらる
*/
package fourthline.mabiicco.ui;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import javax.swing.JTextField;
import javax.swing.JComboBox;
import fourthline.mabiicco.AppResource;
import fourthline.mabiicco.midi.InstClass;
import fourthline.mabiicco.midi.InstType;
import fourthline.mabiicco.midi.MabiDLS;
import fourthline.mmlTools.MMLTrack;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.HashMap;
public final class MMLTrackView extends JPanel implements ActionListener {
private static final long serialVersionUID = 4955513242349170508L;
public static final String MMLPART_NAME[] = {
AppResource.appText("melody"),
AppResource.appText("chord1"),
AppResource.appText("chord2"),
AppResource.appText("song")
};
private JToggleButton partButton[];
private JTextField mmlText[];
private JComboBox<InstClass> comboBox;
private JComboBox<InstClass> songComboBox;
private JLabel trackComposeLabel;
private JToolBar toolBar;
private JButton muteButton;
private JButton soloButton;
private JButton allButton;
private IMMLManager mmlManager;
private final InstClass noUseSongEx = new InstClass(AppResource.appText("instrument.nouse_chorus")+",0", -1, -1, null);
private int trackIndex;
private JPanel mmlTextPanel = new JPanel();
public void setVisibleMMLTextPanel(boolean b) {
if (b) {
add(mmlTextPanel, BorderLayout.CENTER);
} else {
remove(mmlTextPanel);
}
}
/**
* Create the panel.
*/
private MMLTrackView() {
setLayout(new BorderLayout());
JPanel northPanel = new JPanel();
northPanel.setLayout(new BorderLayout());
JPanel northLPanel = new JPanel();
northLPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
JPanel northRPanel = new JPanel();
northPanel.add(northLPanel, BorderLayout.WEST);
northPanel.add(northRPanel, BorderLayout.EAST);
add(northPanel, BorderLayout.NORTH);
setVisibleMMLTextPanel(true);
trackComposeLabel = new JLabel("");
northRPanel.add(trackComposeLabel);
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[]{20, 0, 0, 0, 0, 20};
gridBagLayout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE};
gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
mmlTextPanel.setLayout(gridBagLayout);
comboBox = new JComboBox<>( MabiDLS.getInstance().getAvailableInstByInstType(InstType.MAIN_INST_LIST) );
songComboBox = new JComboBox<>( MabiDLS.getInstance().getAvailableInstByInstType(InstType.SUB_INST_LIST) );
songComboBox.addItem(noUseSongEx);
songComboBox.setSelectedItem(noUseSongEx);
comboBox.setFocusable(false);
songComboBox.setFocusable(false);
northLPanel.add(comboBox);
comboBox.addActionListener(this);
comboBox.setMaximumRowCount(30);
comboBox.setPreferredSize(new Dimension(200, 20));
northLPanel.add(songComboBox);
songComboBox.addActionListener(this);
songComboBox.setMaximumRowCount(30);
songComboBox.setPreferredSize(new Dimension(140, 20));
toolBar = new JToolBar();
toolBar.setFloatable(false);
northPanel.add(toolBar);
// 各パートのボタンとテキストフィールドを作成します.
ButtonGroup bGroup = new ButtonGroup();
partButton = new JToggleButton[MMLPART_NAME.length];
mmlText = new JTextField[MMLPART_NAME.length];
for (int i = 0; i < MMLPART_NAME.length; i++) {
int gridy = 2*i + 1;
// パートタイトル
partButton[i] = new JToggleButton( MMLPART_NAME[i] );
partButton[i].setFocusable(false);
GridBagConstraints gbc_label = new GridBagConstraints();
gbc_label.insets = new Insets(0, 0, 5, 5);
gbc_label.gridx = 1;
gbc_label.gridy = gridy;
mmlTextPanel.add(partButton[i], gbc_label);
bGroup.add(partButton[i]);
// パートのテキストフィールド
mmlText[i] = new JTextField();
mmlText[i].setEditable(false);
mmlText[i].setFocusable(false);
mmlText[i].setFont(new Font("Monospaced", Font.PLAIN, 12));
mmlText[i].setColumns(10);
mmlText[i].addMouseListener(new ButtonCombAdapter(partButton[i]));
GridBagConstraints gbc_mmlText = new GridBagConstraints();
gbc_mmlText.insets = new Insets(0, 0, 5, 5);
gbc_mmlText.fill = GridBagConstraints.HORIZONTAL;
gbc_mmlText.gridx = 3;
gbc_mmlText.gridy = gridy;
mmlTextPanel.add(mmlText[i], gbc_mmlText);
}
// 一番上のパートが初期の選択パート.
partButton[0].setSelected(true);
}
/**
* マウスクリックで関連するボタンを選択状態にする.
*/
private class ButtonCombAdapter extends MouseAdapter {
private JToggleButton button;
private ButtonCombAdapter(JToggleButton b) {
button = b;
}
@Override
public void mousePressed(MouseEvent e) {
if (button.isEnabled()) {
button.setSelected(true);
Arrays.asList(button.getActionListeners()).forEach(t -> t.actionPerformed(null));
}
}
}
/**
* インデックス指定で MMLTrackViewを生成.
* @param trackIndex TrackTabIconを各ボタンに設定するためのIndex値.
* @param actionListener
*/
private MMLTrackView(int trackIndex, ActionListener actionListener) {
this();
this.trackIndex = trackIndex;
muteButton = new JButton("");
muteButton.setFocusable(false);
muteButton.setToolTipText(AppResource.appText("mmltrack.mute"));
toolBar.add(muteButton);
soloButton = new JButton("");
soloButton.setFocusable(false);
soloButton.setIcon(AppResource.getImageIcon(AppResource.appText("mmltrack.solo.icon")));
soloButton.setToolTipText(AppResource.appText("mmltrack.solo"));
toolBar.add(soloButton);
allButton = new JButton("");
allButton.setFocusable(false);
allButton.setIcon(AppResource.getImageIcon(AppResource.appText("mmltrack.all.icon")));
allButton.setToolTipText(AppResource.appText("mmltrack.all"));
toolBar.add(allButton);
for (int i = 0; i < partButton.length; i++) {
partButton[i].addActionListener(actionListener);
}
muteButton.addActionListener(this);
soloButton.addActionListener(this);
allButton.addActionListener(this);
}
private static HashMap<Integer, MMLTrackView> instanceList = new HashMap<>();
public static MMLTrackView getInstance(int trackIndex, ActionListener actionListener, IMMLManager mmlManager) {
MMLTrackView view;
if (instanceList.containsKey(trackIndex)) {
view = instanceList.get(trackIndex);
} else {
view = new MMLTrackView(trackIndex, actionListener);
instanceList.put(trackIndex, view);
}
view.mmlManager = mmlManager;
view.updateTrack();
view.updateMuteButton();
view.updatePartButtonStatus();
return view;
}
public void updateMuteButton() {
if (MabiDLS.getInstance().getMute(trackIndex)) {
muteButton.setIcon(AppResource.getImageIcon(AppResource.appText("mmltrack.mute.on.icon")));
} else {
muteButton.setIcon(AppResource.getImageIcon(AppResource.appText("mmltrack.mute.off.icon")));
}
}
/**
* 選択中のMMLパートのindex値を返します.
* @return 選択中のindex
*/
public int getSelectedMMLPartIndex() {
int index = 0;
for (int i = 0; i < MMLPART_NAME.length; i++) {
if (partButton[i].isSelected()) {
index = i;
break;
}
}
return index;
}
/**
* 指定されたIndexのMMLパートを選択します.
* @param index
*/
public void setSelectMMLPartOfIndex(int index) {
for (JToggleButton button : partButton) {
button.setSelected(false);
}
partButton[index].setSelected(true);
}
public void updateTrack() {
MMLTrack mmlTrack = mmlManager.getMMLScore().getTrack(trackIndex);
String mml[] = mmlTrack.getMabiMMLArray();
for (int i = 0; i < mmlText.length; i++) {
if (i < mml.length) {
mmlText[i].setText(mml[i]);
} else {
mmlText[i].setText("");
}
}
setInstProgram(mmlTrack);
trackComposeLabel.setText(mmlTrack.mmlRankFormat());
}
private void setInstProgram(MMLTrack track) {
int program = track.getProgram();
int songProgram = track.getSongProgram();
InstClass inst = MabiDLS.getInstance().getInstByProgram(program);
if (inst != null) {
comboBox.setSelectedItem(inst);
} else {
comboBox.setSelectedIndex(0);
program = ((InstClass) comboBox.getSelectedItem()).getProgram();
track.setProgram(program);
}
InstClass songInst = MabiDLS.getInstance().getInstByProgram(songProgram);
if ( (songInst != null) && (songInst.getType() == InstType.CHORUS) ) {
songComboBox.setSelectedItem(songInst);
} else {
songComboBox.setSelectedItem(noUseSongEx);
songProgram = ((InstClass) songComboBox.getSelectedItem()).getProgram();
track.setSongProgram(songProgram);
}
updateProgramChangeStatus();
}
/**
* 楽器選択
* N: melody, chord1, chord2
* D: melody
* V: songEX, songComboBoxは選択不可
* songEX
* V: songEX
*/
private void updateProgramChangeStatus() {
InstClass inst = (InstClass) comboBox.getSelectedItem();
if (inst.getType() == InstType.VOICE) {
songComboBox.setSelectedItem(noUseSongEx);
songComboBox.setVisible(false);
} else {
songComboBox.setVisible(true);
}
updatePartButtonStatus();
InstClass songInst = ((InstClass) songComboBox.getSelectedItem());
int program = inst.getProgram();
int songProgram = songInst.getProgram();
MMLTrack track = mmlManager.getMMLScore().getTrack(trackIndex);
if ( (track.getProgram() == program) && (track.getSongProgram() == songProgram) ) {
return;
}
mmlManager.updateActiveTrackProgram(trackIndex, program, songProgram);
}
private void updatePartButtonStatus() {
InstClass inst = (InstClass) comboBox.getSelectedItem();
InstClass songInst = ((InstClass) songComboBox.getSelectedItem());
// 選択された楽器にあわせて、MMLパートの有効/無効化を行う.
boolean instEnable[] = inst.getType().getEnablePart();
boolean songExEnable[] = songInst.getType().getEnablePart();
int iconIndex = 0;
for (int i = 0; i < partButton.length; i++) {
boolean b = (instEnable[i] || songExEnable[i]);
partButton[i].setEnabled(b);
mmlText[i].setEnabled(b);
partButton[i].setIcon(
(b)
? PartButtonIcon.getInstance(iconIndex++, trackIndex) :
PartButtonIcon.getDefautIcon() );
}
if (!partButton[getSelectedMMLPartIndex()].isEnabled()) {
partButton[getSelectedMMLPartIndex()].setSelected(false);
for (JToggleButton button : partButton) {
if (button.isEnabled()) {
button.setSelected(true);
}
}
}
}
public void switchMMLPart(boolean toNext) {
int partIndex = 0;
for (int i = 0; i < partButton.length; i++) {
if (partButton[i].isSelected()) {
partIndex = i;
break;
}
}
if (toNext) {
if ( (partIndex+1 < partButton.length) && (partButton[partIndex+1].isEnabled()) ) {
partIndex++;
}
} else {
if ( (partIndex-1 >= 0) && (partButton[partIndex-1].isEnabled()) ) {
partIndex--;
}
}
partButton[partIndex].setSelected(true);
}
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source instanceof JComboBox<?>) {
updateProgramChangeStatus();
} else if (source.equals(muteButton)) {
MabiDLS.getInstance().toggleMute(trackIndex);
updateMuteButton();
} else if (source.equals(soloButton)) {
MabiDLS.getInstance().solo(trackIndex);
updateMuteButton();
} else if (source.equals(allButton)) {
MabiDLS.getInstance().all();
updateMuteButton();
}
}
}