/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad 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. * * Nomad 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 Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package net.sf.nmedit.jtheme.clavia.nordmodular.plaf; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Component; import java.awt.Composite; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ComponentEvent; import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; import net.sf.nmedit.jtheme.JTContext; import net.sf.nmedit.jtheme.clavia.nordmodular.NMNoteSeqEditor; import net.sf.nmedit.jtheme.component.JTComponent; import net.sf.nmedit.jtheme.component.JTControl; import net.sf.nmedit.jtheme.component.plaf.JTBasicControlUI; import net.sf.nmedit.nmutils.Platform; import net.sf.nmedit.nmutils.swing.NMLazyActionMap; public class NoteSeqEditorUI extends JTBasicControlUI { protected static final String COL_LEFT = "col.right"; protected static final String COL_RIGHT = "col.left"; protected static final String INCREASE_EXT = "increase.ext"; protected static final String DECREASE_EXT = "decrease.ext"; protected static final String INCREASE_EXT_FAST = "increase.ext.fast"; protected static final String DECREASE_EXT_FAST = "decrease.ext.fast"; protected NMNoteSeqEditor control; public static final Color DEFAULT_BACKGROUND = new Color(0xDCDCDC); public static final Color DEFAULT_LINE_COLOR = new Color(0xC0C0C0); public static final String borderKey = "NoteSeqEditorUI.border"; private Color noteColor = new Color(0x2e43ad); private Color lineColor = DEFAULT_LINE_COLOR; private int editedNote = -1; // index of the note curently edited with the keyboard private int columnWidth = 12; private int noteHeight = 4; public NoteSeqEditorUI(NMNoteSeqEditor control) { this.control = control; } public static ComponentUI createUI(JComponent c) { return new NoteSeqEditorUI((NMNoteSeqEditor)c); } public void installUI(JComponent c) { super.installUI(c); JTContext context = ((JTComponent) c).getContext(); Border border = context.getUIDefaults().getBorder(borderKey); c.setBorder(border); if (border != null) c.setOpaque(border.isBorderOpaque()); else c.setOpaque(false); c.setBackground(DEFAULT_BACKGROUND); } public void uninstallUI(JComponent c) { super.uninstallUI(c); // remove border c.setBorder(null); } protected NoteSeqEditorListener createNoteSeqEditorListener() { // return NoteSeqEditorListener.createListener(this); return new NoteSeqEditorListener(this); } protected NoteSeqEditorListener getNoteSeqEditorListener(JComponent c) { return seqControlListenerInstance == null ? createNoteSeqEditorListener(): (NoteSeqEditorListener) seqControlListenerInstance; } public Dimension getPreferredSize(JComponent c) { return new Dimension(204,72); } private Insets cachedInsets = new Insets(0,0,0,0); public void paintStaticLayer(Graphics2D g, JTComponent c) { NMNoteSeqEditor ed = control; Insets insets = (cachedInsets=ed.getInsets(cachedInsets)); int x = insets.left; int y = insets.top; int w = ed.getWidth()-(insets.left+insets.right); int h = ed.getHeight()-(insets.top+insets.bottom); noteHeight = ed.getZoom();//h/(12*(ed.getMaxZoom()+1-ed.getZoom())); paintBackground(g, ed, x, y, w, h); } public void paintDynamicLayer(Graphics2D g, JTComponent c) { NMNoteSeqEditor ed = control; Insets insets = (cachedInsets=ed.getInsets(cachedInsets)); int x = insets.left; int y = insets.top; int w = ed.getWidth()-(insets.left+insets.right); int h = ed.getHeight()-(insets.top+insets.bottom); // at maximum zoom level we display one octave noteHeight = ed.getZoom() ;//h/(12*(ed.getMaxZoom()+1-ed.getZoom())); paintForeground(g, ed, x, y, w, h); } private static final boolean BLACK_KEY_12[] = { false, // C true, // C# false, // D true, // D# false, // E false, // F true, // F# false, // G true, // G# false, // A true, // B false, // H }; private boolean isBlackKey(int midiNote) { // midiNote == 0 <=> midiNote == C midiNote = midiNote%12; if (midiNote<0) // this should never be true midiNote = 12-midiNote; return BLACK_KEY_12[midiNote]; } private void paintBackground(Graphics2D g, NMNoteSeqEditor ed, int x, int y, int w, int h) { { Insets is = ed.getInsets(); g.setColor(ed.getBackground()); g.fillRect(is.left, is.top, ed.getWidth()-is.left-is.right, ed.getHeight()-is.top-is.bottom); } int mid = h/2; // 11 octave lines int nr = x+ed.getNoteCount()*columnWidth; for (int octave = -4; octave<=6; octave++) { int oy = mid - octave * noteHeight*12 + (ed.getTranslation()- ed.getMaxTranslation()/2)*noteHeight; // draw keyboard if (noteHeight>=3) { g.setColor(new Color(0x9a9a9a)); for (int i = 0; i < 12 ; i ++){ if (isBlackKey(i)) { g.fillRect(x, oy-i*noteHeight, nr, noteHeight); } } } g.setColor(lineColor); // draw line separating octaves //g.drawLine(x, oy, nr, oy); } for (int i=1;i<=ed.getNoteCount()-1;i++) { int nx = (i*(w+1))/16; g.drawLine(nx+x, y, nx+x, y+h); } // highlight edited note if (editedNote > -1 && editedNote < 16){ Composite oldComposite = g.getComposite(); g.setComposite(NOTE_HIGHLIGHT_COMPOSITE); g.setColor(noteColor); g.fillRect(x+ (editedNote * (w+1)/16)+(editedNote>0?1:0), y, (w+1)/16, h-y); g.setComposite(oldComposite); // restore previous composite } } private static final AlphaComposite NOTE_HIGHLIGHT_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f); private void paintForeground(Graphics2D g, NMNoteSeqEditor ed, int x, int y, int w, int h) { g.setColor(noteColor); //int row_height = ed.getRowHeight(); int mid = h/2; int cw = (w+1)/16; for (int i=0;i<=ed.getNoteCount()-1;i++) { int nx = ((i*(w+1))/16) + (i>0?1:0); int note = ed.getNote(i); int ny = mid-(note-60- ed.getTranslation()+ ed.getMaxTranslation()/2)*noteHeight ; if (0<=ny && ny<y+h) { g.setColor(noteColor); g.fillRect(nx+x, ny, cw, noteHeight); } else { g.setColor(lineColor); paintArrow(g, ny<0, nx+x, cw, h); } Composite oldComposite = g.getComposite(); g.setComposite(NOTE_HIGHLIGHT_COMPOSITE); g.setColor(getExtensionColor(ed.getControlAdapter(i).getParameter())); int extension = ed.getExtension(i); if (extension >=0) g.fillRect(x+ (i * (w+1)/16)+(i>0?1:0), ny-extension*noteHeight, (w+1)/16, extension*noteHeight); else g.fillRect(x+ (i * (w+1)/16)+(i>0?1:0), ny+noteHeight, (w+1)/16, -extension*noteHeight); g.setComposite(oldComposite); // restore previous composite } } private void paintArrow(Graphics g, boolean uparrow, int nx, int cw, int h) { int arrowHeight = 5; int dy1 = 0; int dy2 = 0; if (!uparrow) { dy1 = h-arrowHeight-2-arrowHeight; dy2 = h-2; } int x[]= {nx,nx+cw/2,nx+cw,nx}; int y[] = {arrowHeight+1+dy1,1+dy2,arrowHeight+1+dy1,arrowHeight+1+dy1}; g.setColor(noteColor); g.fillPolygon(x,y,x.length); } private transient BasicControlListener seqControlListenerInstance; protected BasicControlListener createControlListener(JTControl control) { if (seqControlListenerInstance == null) seqControlListenerInstance = new NoteSeqEditorListener(this); return seqControlListenerInstance; } public static class NoteSeqEditorListener extends BasicControlListener { NoteSeqEditorUI controlUI; public NoteSeqEditorListener(NoteSeqEditorUI controlUI) { this.controlUI = controlUI; } public static void loadActionMap(NMLazyActionMap map) { map.put(new NoteSeqActions(DEFAULTVALUE)); map.put(new NoteSeqActions(INCREASE)); map.put(new NoteSeqActions(DECREASE)); map.put(new NoteSeqActions(INCREASE_FAST)); map.put(new NoteSeqActions(DECREASE_FAST)); } public void installKeyboardActions( JTControl control ) { ActionMap map = new ActionMap(); map.put(DEFAULTVALUE,new NoteSeqActions(DEFAULTVALUE)); map.put(INCREASE,new NoteSeqActions(INCREASE)); map.put(DECREASE,new NoteSeqActions(DECREASE)); map.put(INCREASE_FAST,new NoteSeqActions(INCREASE_FAST)); map.put(DECREASE_FAST,new NoteSeqActions(DECREASE_FAST)); map.put(COL_LEFT,new NoteSeqActions(COL_LEFT)); map.put(COL_RIGHT,new NoteSeqActions(COL_RIGHT)); map.put(INCREASE_EXT,new NoteSeqActions(INCREASE_EXT)); map.put(DECREASE_EXT,new NoteSeqActions(DECREASE_EXT)); map.put(INCREASE_EXT_FAST,new NoteSeqActions(INCREASE_EXT_FAST)); map.put(DECREASE_EXT_FAST,new NoteSeqActions(DECREASE_EXT_FAST)); SwingUtilities.replaceUIActionMap(control, map); InputMap im = createInputMapWhenFocused(); addSeqEditorKS(im); SwingUtilities.replaceUIInputMap(control, JComponent.WHEN_FOCUSED, im); } protected void addSeqEditorKS(InputMap map) { KeyStroke left = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0); map.put(left, COL_LEFT); KeyStroke right= KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0); map.put(right, COL_RIGHT); KeyStroke upExt = KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK); map.put(upExt, INCREASE_EXT); KeyStroke downExt = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK); map.put(downExt, DECREASE_EXT); KeyStroke upExtFast = KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK+InputEvent.SHIFT_DOWN_MASK); map.put(upExtFast, INCREASE_EXT_FAST); KeyStroke downExtFast = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK+InputEvent.SHIFT_DOWN_MASK); map.put(downExtFast, DECREASE_EXT_FAST); } protected NMNoteSeqEditor getControl(ComponentEvent e) { Component c = e.getComponent(); if (c!= null && c instanceof NMNoteSeqEditor) return (NMNoteSeqEditor) c; return null; } private int oldNote = 0, oldExtension = 0; private int y; private void popupTriggered(MouseEvent e) { NMNoteSeqEditor ed = getControl(e); controlUI.editedNote = e.getX() / controlUI.columnWidth; if (!e.getComponent().hasFocus()) e.getComponent().requestFocus(); e.getComponent().repaint(); ed.showControlPopup(e, controlUI.editedNote); } public void mousePressed(MouseEvent e) { if (Platform.isPopupTrigger(e)) { popupTriggered(e); } else if (Platform.isLeftMouseButtonOnly(e)) { controlUI.editedNote = e.getX() / controlUI.columnWidth; y = e.getY(); oldNote = getControl(e).getNote(controlUI.editedNote); oldExtension = getControl(e).getExtension(controlUI.editedNote); if (!e.getComponent().hasFocus()) e.getComponent().requestFocus(); e.getComponent().repaint(); } } public void mouseReleased(MouseEvent e) { if (Platform.isPopupTrigger(e)) { popupTriggered(e); } } public void mouseDragged(MouseEvent e) { //System.out.print("dragged"); NMNoteSeqEditor ed = getControl(e); if (isExtensionSelected(e)) { ed.setExtension(controlUI.editedNote, oldExtension - (e.getY()-y)/controlUI.getNoteHeight()); } else { ed.setNote(controlUI.editedNote, oldNote - (e.getY()-y)/controlUI.getNoteHeight()); } } @Override public void focusLost(FocusEvent e) { controlUI.editedNote = -1; super.focusLost(e); } public static class NoteSeqActions extends AbstractAction { // private String action; /** * */ private static final long serialVersionUID = -2104045982638755995L; public NoteSeqActions(String name) { super(name); } public String getName() { return (String) super.getValue(NAME); } public void actionPerformed(ActionEvent e) { NMNoteSeqEditor ed = (NMNoteSeqEditor) e.getSource(); NoteSeqEditorUI ui = ((NoteSeqEditorUI)ed.getUI()); int editedNote = ui.getEditedNote(); String key = getName(); if (key == INCREASE){ ed.setNote(editedNote, ed.getNote(editedNote) + 1 ); } else if (key == DECREASE) { ed.setNote(editedNote, ed.getNote(editedNote) - 1 ); } else if (key == INCREASE_FAST) { ed.setNote(editedNote, ed.getNote(editedNote) + 12 ); } else if (key == DECREASE_FAST) { ed.setNote(editedNote, ed.getNote(editedNote) - 12 ); } else if (key == INCREASE_EXT) { ed.setExtension(editedNote, ed.getExtension(editedNote)+1); } else if (key == DECREASE_EXT) { ed.setExtension(editedNote, ed.getExtension(editedNote)-1); } else if (key == INCREASE_EXT_FAST) { ed.setExtension(editedNote, ed.getExtension(editedNote)+12); } else if (key == DECREASE_EXT_FAST) { ed.setExtension(editedNote, ed.getExtension(editedNote)-12); } else if (key == COL_LEFT){ ui.editedNote--; if (ui.editedNote < 0 ) ui.editedNote = 0; ed.repaint(); } else if (key == COL_RIGHT){ ui.editedNote++; if (ui.editedNote >15 ) ui.editedNote = 15; ed.repaint(); } else if (key == DEFAULTVALUE) { ed.setNote(editedNote, 64 ); } } } } // public void stateChanged(ChangeEvent e) // { // Object o = e.getSource(); // if (o instanceof Component) // ((Component)o).repaint(); // } // public int getNoteHeight() { return this.noteHeight; } public void setNoteHeight(int noteHeight) { this.noteHeight = noteHeight; } public int getEditedNote() { return editedNote; } public void setEditedNote(int editedNote) { this.editedNote = editedNote; } }