]> ruin.nu Git - moosique.git/blob - MooTrackView.java
Fixed exit if sequence unsaved prompt.
[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                 Moosique.setEdited();
160                 repaint();
161         }
162
163         /** 
164          * Removes the given note element from the view and its note from the current track.
165          * @param elem  the note element to remove
166          */
167         public void removeNote(MooNoteElement elem) {
168                 elem.getNote().removeFrom(track);
169                 remove(elem);
170                 elem.getNote().removeFrom(track);
171                 Moosique.setEdited();
172                 repaint();
173         }
174
175         /**
176          * Adds a standard note to this track.
177          */
178         private void addStandardNote() {
179                 int row =  (popupY - insets.top) / NOTE_HEIGHT;
180                 long timestamp = (long)(ticksPerSixteenth * row);
181                 addNote(new MooNote(title.getChannel(), 60, 100, timestamp, Moosique.getSequence().getResolution() / 4));
182         }
183
184         /**
185          * Deselects all notes.
186          */
187         public void addSelected(MooNoteElement elem) {
188                 selected.add(elem);
189         }
190
191         /**
192          * Deselects all notes.
193          */
194         public void removeSelected(MooNoteElement elem) {
195                 selected.remove(selected.indexOf(elem));
196         }
197
198         /**
199          * Deselects all notes.
200          */
201         public void deselectAll() {
202                 Iterator it = selected.iterator();
203                 while(it.hasNext()) {
204                         ((MooNoteElement)it.next()).deselect();
205                 }
206                 selected.clear();
207         }
208
209         /**
210          * Determines if the given MooNoteElement is the only one in the track view that is selected.
211          * @return if the given element is the only selected one
212          */
213         public boolean isTheOnlySelected(MooNoteElement elem) {
214                 Iterator it = selected.iterator();
215                 while(it.hasNext()) {
216                         if (!it.next().equals(elem)) return false;
217                 }
218                 return true;
219         }
220
221         /**
222          * Shows a popup-menu with options for the current selection of note elements.
223          * @param c     the component over which to display the menu
224          * @param x     the x-coordinate in which to display the menu
225          * @param y     the y-coordinate in which to display the menu
226          */
227         public void showSelectionPopup(Component c, int x, int y) {
228                 
229         }
230
231         /**
232          * Draws the grid that is on the background.
233          * @param g The Graphics object used to draw the grid.
234          */
235         public void paintComponent(Graphics g) {
236                 super.paintComponent(g);
237                 Graphics2D g2 = (Graphics2D)g;
238                 for (int c = 0; c < viewLength || c < getHeight(); c += NOTE_HEIGHT) {
239                         for (int r = 0; r < (10 * NOTE_WIDTH); r += NOTE_WIDTH) {
240                                 box = new Rectangle(r, c, NOTE_WIDTH, NOTE_HEIGHT);
241                                 g2.setColor(Color.gray);
242                                 g2.draw(box);
243                         }
244                 }
245         }
246         
247         /**
248          * The adapter used to listen on mouse actions
249          */
250         class MAdapter extends MouseAdapter {
251
252                 /**
253                  * Deselects all note on click, adds a standard note on double click.
254                  */
255                 public void mouseClicked(MouseEvent e) {
256                         deselectAll();
257                         if (e.getClickCount() == 2) {
258                                 popupY = e.getY();
259                                 addStandardNote();
260                         }
261                 }
262         
263                 public void mousePressed(MouseEvent e) {
264                         maybeShowPopup(e);
265                 }
266
267                 public void mouseReleased(MouseEvent e) {
268                         maybeShowPopup(e);
269                 }
270
271                 /**
272                  * Selects the notes within the area that was selected.
273                  */
274                 public void mouseDragged(MouseEvent e) {
275                         
276                 }
277
278                 /**
279                  * Shows the menu if an OS-specific popup-trigger was activated.
280                  */
281                 private void maybeShowPopup(MouseEvent e) {
282                         if (e.isPopupTrigger()) {
283                                 popupY = e.getY();
284                                 popup.show(e.getComponent(), e.getX(), e.getY());
285                         }
286                 }
287
288                 /**
289                  * Grabs the focus when the mouse has entered.
290                  */
291                 public void mouseEntered(MouseEvent e) {
292                         // Moosique.setActiveChannel(track.getChannel());
293                         grabFocus();
294                 }
295         }
296
297         /**
298          * Listens on actions on the popupmenu and executes the appropriate action.
299          */
300         class PopupListener implements ActionListener {
301                 public void actionPerformed(ActionEvent e) {
302                         Object source = e.getSource();
303                         if  (source == popupAdd) {
304                                 addStandardNote();
305                         }
306                         // new MooNote(int channel, int pitch, int velocity, long timestamp, int duration)
307                 }
308         }
309 }