/* * Copyright (C) 2013-2017 たんらる */ package fourthline.mabiicco.ui; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import java.util.List; import java.util.OptionalInt; import javax.sound.midi.Sequencer; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import fourthline.mabiicco.MabiIccoProperties; import fourthline.mabiicco.midi.MabiDLS; import fourthline.mabiicco.ui.color.ColorManager; import fourthline.mabiicco.ui.editor.IEditAlign; import fourthline.mabiicco.ui.editor.IMarkerEditor; import fourthline.mabiicco.ui.editor.MarkerEditor; import fourthline.mabiicco.ui.editor.MMLTempoEditor; import fourthline.mmlTools.MMLEventList; import fourthline.mmlTools.MMLNoteEvent; import fourthline.mmlTools.MMLScore; import fourthline.mmlTools.MMLTempoEvent; import fourthline.mmlTools.Marker; public final class ColumnPanel extends JPanel implements MouseListener, MouseMotionListener, IViewTargetMarker { private static final long serialVersionUID = -6609938350741425221L; private static final Color BEAT_BORDER_COLOR = new Color(0.4f, 0.4f, 0.4f); private static final Color TEMPO_MAKER_FILL_COLOR = new Color(0.4f, 0.8f, 0.8f); private static final Color MAKER_FILL_COLOR = new Color(0.2f, 0.8f, 0.2f); private static final Color TARGET_MAKER_FILL_COLOR = new Color(0.9f, 0.7f, 0.0f, 0.6f); private static final int DRAW_HEIGHT = 32; private final PianoRollView pianoRollView; private final IMMLManager mmlManager; private final IEditAlign editAlign; private final JPopupMenu popupMenu = new JPopupMenu(); private final ArrayList<IMarkerEditor> markerEditor = new ArrayList<>(); private OptionalInt targetMarker = OptionalInt.empty(); public ColumnPanel(Frame parentFrame, PianoRollView pianoRollView, IMMLManager mmlManager, IEditAlign editAlign) { super(); this.pianoRollView = pianoRollView; this.mmlManager = mmlManager; this.editAlign = editAlign; addMouseListener(this); addMouseMotionListener(this); markerEditor.add( new MMLTempoEditor(parentFrame, mmlManager, editAlign, this) ); markerEditor.add( new MarkerEditor(parentFrame, mmlManager, editAlign, this) ); // popupMenu に各MenuItemを登録する. markerEditor.forEach(t -> t.getMenuItems().forEach(popupMenu::add)); } @Override public Dimension getPreferredSize() { return new Dimension(getWidth(), DRAW_HEIGHT); } @Override public int getWidth() { int width = pianoRollView.getWidth(); return width; } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D)g.create(); paintRuler(g2); paintMarker(g2); paintTempoEvents(g2); pianoRollView.paintSequenceLine(g2, getHeight()); paintTargetMarker(g2); if (MabiIccoProperties.getInstance().viewVelocityLine.get()) { paintVelocityLine(g2); } g2.dispose(); } /** * ルーラを表示します。 */ private void paintRuler(Graphics2D g) { long measure = mmlManager.getMMLScore().getMeasureTick(); long length = pianoRollView.convertXtoTick( getWidth() ); g.setColor(BEAT_BORDER_COLOR); int count = 0; for (long i = 0; i < length; i += measure) { int x = pianoRollView.convertTicktoX(i); int y1 = 0; int y2 = getHeight(); g.drawLine(x, y1, x, y2); String s = "" + (count++); g.drawString(s, x+2, y1+10); } } /** * テンポを表示します. */ private void paintTempoEvents(Graphics2D g) { MMLScore score = mmlManager.getMMLScore(); for (MMLTempoEvent tempoEvent : score.getTempoEventList()) { int tick = tempoEvent.getTickOffset(); int x = pianoRollView.convertTicktoX(tick); String s = "t" + tempoEvent.getTempo(); drawMarker(g, s, x, TEMPO_MAKER_FILL_COLOR, 0); } } private void paintMarker(Graphics2D g) { if (MabiIccoProperties.getInstance().enableViewMarker.get()) { MMLScore score = mmlManager.getMMLScore(); for (Marker marker : score.getMarkerList()) { int tick = marker.getTickOffset(); int x = pianoRollView.convertTicktoX(tick); drawMarker(g, marker.getName(), x, MAKER_FILL_COLOR, -11); } } } private void drawMarker(Graphics2D g, String s, int x, Color color, int dy) { int xPoints[] = { x-3, x+3, x+3, x, x-3 }; int yPoints[] = { -10, -10, -4, -1, -4 }; for (int i = 0; i < yPoints.length; i++) { yPoints[i] += DRAW_HEIGHT + dy; } // label g.setColor(Color.DARK_GRAY); g.drawString(s, x+6, DRAW_HEIGHT-2+dy); // icon g.setColor(color); g.fillPolygon(xPoints, yPoints, xPoints.length); g.setColor(BEAT_BORDER_COLOR); g.drawPolygon(xPoints, yPoints, xPoints.length); } private void paintTargetMarker(Graphics2D g) { if (!targetMarker.isPresent()) { return; } int x = pianoRollView.convertTicktoX( targetMarker.getAsInt() ); int xPoints[] = { x-5, x+5, x+5, x, x-5 }; int yPoints[] = { 8, 8, DRAW_HEIGHT-5, DRAW_HEIGHT, DRAW_HEIGHT-5 }; // icon g.setColor(TARGET_MAKER_FILL_COLOR); g.fillPolygon(xPoints, yPoints, xPoints.length); g.setColor(BEAT_BORDER_COLOR); g.drawPolygon(xPoints, yPoints, xPoints.length); } private void paintVelocityLine(Graphics2D g) { MMLEventList activePart = mmlManager.getActiveMMLPart(); if (activePart == null) { return; } int trackIndex = mmlManager.getActiveTrackIndex(); Color rectColor = ColorManager.defaultColor().getActiveRectColor(trackIndex); g.setColor(rectColor); for (MMLNoteEvent noteEvent : activePart.getMMLNoteEventList()) { int x = pianoRollView.convertTicktoX( noteEvent.getTickOffset() ); int width = pianoRollView.convertTicktoX( noteEvent.getTick() ); int velocity = noteEvent.getVelocity(); if (velocity < 0) velocity = 0; if (velocity > 15) velocity = 15; int y = DRAW_HEIGHT - velocity - 2; g.drawLine(x, y, x+width-1, y); } } private void setSequenceBar(int x) { Sequencer sequencer = MabiDLS.getInstance().getSequencer(); if (!sequencer.isRunning()) { long tick = pianoRollView.convertXtoTick(x); tick -= tick % editAlign.getEditAlign(); pianoRollView.setSequenceTick(tick); repaint(); pianoRollView.repaint(); } else { long tick = pianoRollView.convertXtoTick(x); // 移動先のテンポに設定する. int tempo = mmlManager.getMMLScore().getTempoOnTick(tick); sequencer.setTickPosition(tick); sequencer.setTempoInBPM(tempo); System.out.printf("Sequence update: tick(%d), tempo(%d)\n", tick, tempo); } } private void playAllNoteOnTick(int x) { Sequencer sequencer = MabiDLS.getInstance().getSequencer(); if (!MabiIccoProperties.getInstance().enableClickPlay.get()) { return; } if (!sequencer.isRunning()) { MMLScore score = mmlManager.getMMLScore(); long tick = pianoRollView.convertXtoTick(x); int trackIndex = 0; List<MMLNoteEvent[]> noteListArray = score.getNoteListOnTickOffset(tick); for (MMLNoteEvent[] noteList : noteListArray) { int program = score.getTrack(trackIndex).getProgram(); if (x < 0) { MabiDLS.getInstance().playNotes(null, program, trackIndex); } else { MabiDLS.getInstance().playNotes(noteList, program, trackIndex); } trackIndex++; } } } private void popupAction(Component component, int x, int y) { int targetTick = (int)pianoRollView.convertXtoTick(x); int delta = (int)pianoRollView.convertXtoTick(6); // クリックした位置に、テンポ/マーカー イベントがあれば削除モードになります. markerEditor.forEach(t -> t.activateEditMenuItem(targetTick, delta)); popupMenu.show(component, x, y); } @Override public void mouseClicked(MouseEvent e) { int x = e.getX(); int y = e.getY(); if (SwingUtilities.isLeftMouseButton(e)) { } else if (SwingUtilities.isRightMouseButton(e)) { popupAction(e.getComponent(), x, y); } } @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} @Override public void mousePressed(MouseEvent e) { int x = e.getX(); if (SwingUtilities.isLeftMouseButton(e)) { setSequenceBar(x); playAllNoteOnTick(x); } } @Override public void mouseReleased(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { playAllNoteOnTick(-1); } } @Override public void mouseDragged(MouseEvent e) { int x = e.getX(); if (SwingUtilities.isLeftMouseButton(e)) { playAllNoteOnTick(x); } } @Override public void mouseMoved(MouseEvent e) {} @Override public void PaintOnTarget(int tickOffset) { this.targetMarker = OptionalInt.of( tickOffset ); repaint(); } @Override public void PaintOff() { this.targetMarker = OptionalInt.empty(); repaint(); } }