]> ruin.nu Git - moosique.git/blob - MooTrackView.java
font settings..
[moosique.git] / MooTrackView.java
1 import javax.swing.*;
2 import java.awt.*;
3 import java.awt.event.*;
4 import javax.sound.midi.*;
5 import java.util.*;
6
7 /**
8  * Graphical representation of a MIDI track.
9  * 
10  * @author  Andersson , Andreen, Lanneskog, Pehrson
11  * @version 1
12  */
13
14 public class MooTrackView extends JPanel {
15
16         private Track track;
17         private MooTrackTitle title;
18         private Rectangle box;
19
20         private JPopupMenu popup;
21         private JMenuItem popupAdd;
22         private ArrayList rects;
23         private ArrayList selected;
24         private Insets insets;
25         private int ticksPerSixteenth, popupY = 0;
26         protected static int viewLength = 0;
27         protected static int extraHeight = 0;
28         public static final int NOTE_HEIGHT = 10, NOTE_WIDTH = 40, VIEW_WIDTH = 200;
29
30         /**
31          * Creates the trackview.
32          * @param track The track it represents graphically and operates on.
33          * @param title The object that is used to manipulate instrument, channel, solo, mute.
34          */
35         public MooTrackView (Track track, MooTrackTitle title) {
36                 super(true);
37                 this.track = track;
38                 this.title = title;
39                 insets = getInsets();
40                 selected = new ArrayList();
41
42                 // Configures panel
43                 setBackground(Color.white);
44                 setBorder(BorderFactory.createLineBorder(Color.black));
45                 setLayout(null);
46                 setPreferredSize(new Dimension(VIEW_WIDTH, 140 * NOTE_HEIGHT));
47
48                 // Creates temporary variables
49                 MidiEvent note;
50                 MooNoteElement elem;
51                 rects = new ArrayList(track.size() / 2);
52                 extraHeight = Toolkit.getDefaultToolkit().getScreenSize().height - 150;
53
54                 // Places note elements
55                 for (int i = 0; i < track.size(); i++) {
56                         note = track.get(i);
57                         if (note instanceof MooNote) {
58                                 // Adds the note element to the note area and moves it to the appropriate place.
59                                 MooNote mn = (MooNote)note;
60                                 elem = new MooNoteElement(this, mn);
61                                 add(elem);
62                                 layoutElement(elem, false);
63                         }
64                         setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
65
66                 }
67
68                 // Creates pop-up menu.
69                 popup = new JPopupMenu();
70                 PopupListener pList = new PopupListener();
71                 popupAdd = new JMenuItem("Add note...");
72                 popupAdd.addActionListener(pList);
73                 popup.add(popupAdd);
74
75                 // Adds listeners for popup menu and keyboard synthesizer.
76                 addMouseListener(new MAdapter());
77                 addKeyListener(new MooKeyboard());
78         }
79
80         /**
81          * Layouts the element to the right place.
82          * @param elem  the element that will be layouted.
83          * @param old   If true, this method will remove the old layout and set the new preferredSize for the trackview.
84          */
85         public void layoutElement(MooNoteElement elem, boolean old){
86                 // If the element is currently in the view, removes its coordinates from the list.
87                 Rectangle r = new Rectangle();
88                 if (old){
89                         r = elem.getBounds(r);
90                         for (Iterator i = rects.iterator(); i.hasNext();){
91                                 Object ob = i.next();
92                                 if (r.equals(ob)){
93                                         rects.remove(ob);
94                                         break;
95                                 }
96                         }
97                 }
98
99                 // Creates temporary variables.
100                 ticksPerSixteenth = Moosique.getSequence().getResolution() / 4;
101                 MooNote mn = elem.getNote();
102                 int x, y, height;
103
104                 // Calculates coordinates.
105                 x = insets.left;
106                 y = insets.top + (int)(mn.getTick() / ticksPerSixteenth) * NOTE_HEIGHT;
107                 height = (mn.getDuration() / ticksPerSixteenth) * NOTE_HEIGHT;
108                 if (height == 0) height = NOTE_HEIGHT;
109                 r = new Rectangle(x, y, NOTE_WIDTH, height);
110
111                 // Places the element in the appropriate place.
112                 while(isOccupied(r)) r.translate(NOTE_WIDTH, 0);
113                 elem.setBounds(r);
114                 rects.add(r);
115                 if (viewLength < (y + height)){
116                         viewLength = y + height;
117                         if(old)setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
118                 }
119         }
120
121         /** 
122          * Returns the track of this view.
123          * @return the track of this view
124          */
125         public Track getTrack() {
126                 return track;
127         }
128
129         /** 
130          * Returns the title of this view.
131          * @return the title of this view
132          */
133         public MooTrackTitle getTitle() {
134                 return title;
135         }
136
137         /**
138          * Checks if the element can be fully drawn as this position without inteferring with other elements.
139          * @return true if the position is occupied.
140          */
141         private boolean isOccupied(Rectangle r) {
142                 Iterator it = rects.iterator();
143                 while (it.hasNext()) {
144                         if(r.intersects((Rectangle)it.next())) return true;
145                 }
146                 return false;
147         }
148         
149         /** 
150          * Adds the given note to the current track, and visualises it.
151          * @param mn    the note to add
152          */
153         public void addNote(MooNote mn) {
154                 mn.addTo(track);
155                 MooNoteElement elem = new MooNoteElement(this, mn);
156                 add(elem);
157                 layoutElement(elem, false);
158                 setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
159                 repaint();
160         }
161
162         /** 
163          * Removes the given note element from the view and its note from the current track.
164          * @param elem  the note element to remove
165          */
166         public void removeNote(MooNoteElement elem) {
167                 elem.getNote().removeFrom(track);
168                 remove(elem);
169                 elem.getNote().removeFrom(track);
170                 repaint();
171         }
172
173         /**
174          * Adds a standard note to this track.
175          */
176         private void addStandardNote() {
177                 int row =  (popupY - insets.top) / NOTE_HEIGHT;
178                 long timestamp = (long)(ticksPerSixteenth * row);
179                 addNote(new MooNote(title.getChannel(), 60, 100, timestamp, Moosique.getSequence().getResolution() / 4));
180         }
181
182         /**
183          * Deselects all notes.
184          */
185         public void addSelected(MooNoteElement elem) {
186                 selected.add(elem);
187         }
188
189         /**
190          * Deselects all notes.
191          */
192         public void removeSelected(MooNoteElement elem) {
193                 selected.remove(selected.indexOf(elem));
194         }
195
196         /**
197          * Deselects all notes.
198          */
199         public void deselectAll() {
200                 Iterator it = selected.iterator();
201                 while(it.hasNext()) {
202                         ((MooNoteElement)it.next()).deselect();
203                 }
204                 selected.clear();
205         }
206
207         /**
208          * Draws the grid that is on the background.
209          * @param g The Graphics object used to draw the grid.
210          */
211         public void paintComponent(Graphics g) {
212                 super.paintComponent(g);
213                 Graphics2D g2 = (Graphics2D)g;
214                 for (int c = 0; c < viewLength || c < getHeight(); c += NOTE_HEIGHT) {
215                         for (int r = 0; r < (10 * NOTE_WIDTH); r += NOTE_WIDTH) {
216                                 box = new Rectangle(r, c, NOTE_WIDTH, NOTE_HEIGHT);
217                                 g2.setColor(Color.gray);
218                                 g2.draw(box);
219                         }
220                 }
221         }
222         
223         /**
224          * The adapter used to listen on mouse actions
225          */
226         class MAdapter extends MouseAdapter {
227
228                 /**
229                  * Deselects all note on click, adds a standard note on double click.
230                  */
231                 public void mouseClicked(MouseEvent e) {
232                         deselectAll();
233                         if (e.getClickCount() == 2) {
234                                 popupY = e.getY();
235                                 addStandardNote();
236                         }
237                 }
238         
239                 public void mousePressed(MouseEvent e) {
240                         maybeShowPopup(e);
241                 }
242
243                 public void mouseReleased(MouseEvent e) {
244                         maybeShowPopup(e);
245                 }
246
247                 /**
248                  * Shows the menu if an OS-specific popup-trigger was activated.
249                  */
250                 private void maybeShowPopup(MouseEvent e) {
251                         if (e.isPopupTrigger()) {
252                                 popupY = e.getY();
253                                 popup.show(e.getComponent(), e.getX(), e.getY());
254                         }
255                 }
256
257                 /**
258                  * Grabs the focus when the mouse has entered.
259                  */
260                 public void mouseEntered(MouseEvent e) {
261                         // Moosique.setActiveChannel(track.getChannel());
262                         grabFocus();
263                 }
264         }
265
266         /**
267          * Listens on actions on the popupmenu and executes the appropriate action.
268          */
269         class PopupListener implements ActionListener {
270                 public void actionPerformed(ActionEvent e) {
271                         Object source = e.getSource();
272                         if  (source == popupAdd) {
273                                 addStandardNote();
274                         }
275                         // new MooNote(int channel, int pitch, int velocity, long timestamp, int duration)
276                 }
277         }
278 }