]> ruin.nu Git - moosique.git/blobdiff - MooTrackView.java
no message
[moosique.git] / MooTrackView.java
index f2afcaae2be4e18373b97a3250d4cfe5ce7b29e0..c284e7df5acc9e4ce2a2407f67f373e5fdf548aa 100644 (file)
@@ -15,19 +15,23 @@ public class MooTrackView extends JPanel {
 
        private Track track;
        private MooTrackTitle title;
-       private Rectangle box;
+       private MooKeyboard keyboard;
 
        private JPopupMenu popup, selPopup;
-       private JMenu selPopupTranspUp, selPopupTranspDown;
-       private JMenuItem popupAdd;
-       private JMenuItem selPopupRemove, selPopupTranspUpOct, selPopupTranspDownOct;
+       private JMenu popupAdd, selPopupTranspUp, selPopupTranspDown;
+       private JMenuItem popupAddItemsCustom, popupAddItemsLast, popupPaste;
+       private JMenuItem popupAddItemsWhole, popupAddItemsHalf, popupAddItemsQuarter, popupAddItemsEighth, popupAddItemsSixteenth;
+       private JMenuItem selPopupProps, selPopupCopy, selPopupCut, selPopupRemove;
+       private JMenuItem[] selPopupTranspUpItems, selPopupTranspDownItems;
 
-       private ArrayList rects;
-       private ArrayList selected;
+       private ArrayList coords; 
        private Insets insets;
-       private int ticksPerSixteenth, popupY = 0;
-       protected static int viewLength = 0;
-       protected static int extraHeight = 0;
+       private Rectangle box;
+       private int ticksPerSixteenth, popupY = 0, lastNoteLength = 2;
+       private boolean leftMouseButtonPressed = false;
+       private static boolean snapToSixteenths = true;
+       private static int viewLength = 0;
+       private static int extraHeight = 0;
        public static final int NOTE_HEIGHT = 10, NOTE_WIDTH = 40, VIEW_WIDTH = 200;
 
        /**
@@ -37,10 +41,13 @@ public class MooTrackView extends JPanel {
         */
        public MooTrackView (Track track, MooTrackTitle title) {
                super(true);
+
+               // Defines instance variables
                this.track = track;
                this.title = title;
+               ticksPerSixteenth = Moosique.getSequence().getResolution() / 4;
                insets = getInsets();
-               selected = new ArrayList();
+               coords = new ArrayList(track.size() / 2);
 
                // Configures panel
                setBackground(Color.white);
@@ -51,7 +58,6 @@ public class MooTrackView extends JPanel {
                // Creates temporary variables
                MidiEvent note;
                MooNoteElement elem;
-               rects = new ArrayList(track.size() / 2);
                extraHeight = Toolkit.getDefaultToolkit().getScreenSize().height - 150;
 
                // Places note elements
@@ -64,38 +70,63 @@ public class MooTrackView extends JPanel {
                                add(elem);
                                layoutElement(elem, false);
                        }
-                       setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
-
                }
+               setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
 
                // Creates panel pop-up menu.
                popup = new JPopupMenu();
-               PopupListener pList = new PopupListener();
-               popupAdd = new JMenuItem("Add note...");
-               popupAdd.addActionListener(pList);
+               popupAdd = new JMenu("Add note");
                popup.add(popupAdd);
+               popupAddItemsCustom = addMenuItem(popupAdd, "Custom...");
+               popupAddItemsLast = addMenuItem(popupAdd, "As last added");
+               popupAdd.addSeparator();
+               popupAddItemsWhole = addMenuItem(popupAdd, "Whole");
+               popupAddItemsHalf = addMenuItem(popupAdd, "Half");
+               popupAddItemsQuarter = addMenuItem(popupAdd, "Quarter");
+               popupAddItemsEighth = addMenuItem(popupAdd, "Eighth");
+               popupAddItemsSixteenth = addMenuItem(popupAdd, "Sixteenth");
+               popupPaste = addMenuItem(popup, "Paste");
 
                // Creates selection pop-up menu.
                selPopup = new JPopupMenu();
-               selPopupRemove = new JMenuItem("Remove selection");
-               selPopupRemove.addActionListener(pList);
-               selPopup.add(selPopupRemove);
-               selPopupTranspUp = new JMenu("Transpose selection up");
-               selPopup.add(selPopupTranspUp);
-               selPopupTranspUpOct = new JMenuItem("One octave");
-               selPopupTranspUpOct.addActionListener(pList);
-               selPopupTranspUp.add(selPopupTranspUpOct);
-               selPopupTranspDown = new JMenu("Transpose selection down");
-               selPopup.add(selPopupTranspDown);
-               selPopupTranspDownOct = new JMenuItem("One octave");
-               selPopupTranspDownOct.addActionListener(pList);
-               selPopupTranspDown.add(selPopupTranspDownOct);
+               selPopupProps = addMenuItem(selPopup, "Properties...");
+               selPopupCopy = addMenuItem(selPopup, "Copy selection");
+               selPopupCut = addMenuItem(selPopup, "Cut selection");
+               selPopupRemove = addMenuItem(selPopup, "Remove selection");
+               selPopupTranspUpItems = new JMenuItem[12];
+               selPopupTranspDownItems = new JMenuItem[12];
+               selPopupTranspUp = createTransposeMenu(selPopup, selPopupTranspUpItems, "selection up");
+               selPopupTranspDown = createTransposeMenu(selPopup, selPopupTranspDownItems, "selection down");
 
                // Adds listeners for popup menu and keyboard synthesizer.
                addMouseListener(new MAdapter());
-               addKeyListener(new MooKeyboard());
+               keyboard = new MooKeyboard(title);
+               addKeyListener(keyboard);
        }
 
+       /**
+        * Creates note elements for all MooNotes in the given list, and places them in the appropriate place.
+        */
+       public void placeNewNotes(java.util.List notes) {
+               // Creates temporary variables
+               MidiEvent note;
+               MooNoteElement elem;
+               extraHeight = Toolkit.getDefaultToolkit().getScreenSize().height - 150;
+
+               // Places note elements
+               for (int i = 0; i < notes.size(); i++) {
+                       note = (MidiEvent)notes.get(i);
+                       if (note instanceof MooNote) {
+                               // Adds the note element to the note area and moves it to the appropriate place.
+                               MooNote mn = (MooNote)note;
+                               elem = new MooNoteElement(this, mn);
+                               add(elem);
+                               layoutElement(elem, false);
+                       }
+               }
+               setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
+       }       
+
        /**
         * Layouts the element to the right place.
         * @param elem  the element that will be layouted.
@@ -106,36 +137,39 @@ public class MooTrackView extends JPanel {
                Rectangle r = new Rectangle();
                if (old){
                        r = elem.getBounds(r);
-                       for (Iterator i = rects.iterator(); i.hasNext();){
+                       for (Iterator i = coords.iterator(); i.hasNext();){
                                Object ob = i.next();
                                if (r.equals(ob)){
-                                       rects.remove(ob);
+                                       coords.remove(ob);
                                        break;
                                }
                        }
                }
 
                // Creates temporary variables.
-               ticksPerSixteenth = Moosique.getSequence().getResolution() / 4;
                MooNote mn = elem.getNote();
                int x, y, height;
 
                // Calculates coordinates.
                x = insets.left;
-               y = insets.top + (int)(mn.getTick() / ticksPerSixteenth) * NOTE_HEIGHT;
-               height = (mn.getDuration() / ticksPerSixteenth) * NOTE_HEIGHT;
+               y = insets.top + (int)((mn.getTick() * NOTE_HEIGHT) / ticksPerSixteenth);
+               height = (mn.getDuration() * NOTE_HEIGHT) / ticksPerSixteenth;
                if (height == 0) height = NOTE_HEIGHT;
+               if (snapToSixteenths && height < NOTE_HEIGHT) height = NOTE_HEIGHT;
                r = new Rectangle(x, y, NOTE_WIDTH, height);
 
                // Places the element in the appropriate place.
                while(isOccupied(r)) r.translate(NOTE_WIDTH, 0);
                elem.setBounds(r);
-               rects.add(r);
+               coords.add(r);
                if (viewLength < (y + height)){
                        viewLength = y + height;
                        if(old)setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
                }
-               if(old)repaint();
+               if (old) {
+                       validate();
+                       repaint();
+               }
        }
 
        /** 
@@ -159,7 +193,7 @@ public class MooTrackView extends JPanel {
         * @return true if the position is occupied.
         */
        private boolean isOccupied(Rectangle r) {
-               Iterator it = rects.iterator();
+               Iterator it = coords.iterator();
                while (it.hasNext()) {
                        if(r.intersects((Rectangle)it.next())) return true;
                }
@@ -177,16 +211,18 @@ public class MooTrackView extends JPanel {
                layoutElement(elem, false);
                setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
                Moosique.setEdited();
+               validate();
                repaint();
        }
 
        /**
         * Adds a standard note to this track.
         */
-       private void addStandardNote() {
-               int row =  (popupY - insets.top) / NOTE_HEIGHT;
+       private void addNoteAtClickY(int length) {
+               lastNoteLength = length;
+               int row = (popupY - insets.top) / NOTE_HEIGHT;
                long timestamp = (long)(ticksPerSixteenth * row);
-               addNote(new MooNote(title.getChannel(), 60, 100, timestamp, Moosique.getSequence().getResolution() / 4));
+               addNote(new MooNote(title.getChannel(), 60, 100, timestamp, ticksPerSixteenth * length));
        }
 
        /** 
@@ -198,46 +234,153 @@ public class MooTrackView extends JPanel {
                remove(elem);
                Rectangle r = new Rectangle();
                r = elem.getBounds(r);
-               rects.remove(r);
+               coords.remove(r);
                Moosique.setEdited();
+               validate();
                repaint();
        }
 
        /**
-        * Deselects all notes.
+        * Copies the current selection.
         */
-       public void selectNote(MooNoteElement elem) {
-               selected.add(elem);
+       public void copySelectedNotes() {
+               TreeSet selection = Moosique.getSelection();
+               ArrayList copyBuffer = new ArrayList(selection.size());
+               Iterator it = selection.iterator();
+               while(it.hasNext()) {
+                       copyBuffer.add(((MooNoteElement)it.next()).getNote().clone());
+               }
+               Collections.sort(copyBuffer);
+               Moosique.setCopyBuffer(copyBuffer);
        }
 
        /**
-        * Deselects all notes.
+        * Cuts the current selection.
         */
-       public void deselectNote(MooNoteElement elem) {
-               selected.remove(selected.indexOf(elem));
+       public void cutSelectedNotes() {
+               copySelectedNotes();
+               removeSelectedNotes();
+       }
+
+       /**
+        * Pastes the current copy buffer at the given timestamp.
+        */
+       public void pasteCopiedNotes() {
+               int row = (popupY - insets.top) / NOTE_HEIGHT;
+               long timestamp = (long)(ticksPerSixteenth * row);
+               ArrayList copyBuffer = Moosique.getCopyBuffer();
+               if (copyBuffer.size() > 0) {
+                       long startTime = ((MooNote)copyBuffer.get(0)).getTick();
+                       Iterator it = copyBuffer.iterator();
+                       while(it.hasNext()) {
+                               MooNote mn = (MooNote)((MooNote)it.next()).clone();
+                               mn.setTick(mn.getTick() - startTime + timestamp);
+                               mn.setChannel(title.getChannel());
+                               addNote(mn);
+                       }
+                       Moosique.setEdited();
+               }
        }
 
        /**
-        * Deselects all notes.
+        * Removes the current selection.
         */
-       public void deselectAllNotes() {
-               Iterator it = selected.iterator();
+       public void removeSelectedNotes() {
+               TreeSet selection = Moosique.getSelection();
+               Iterator it = selection.iterator();
                while(it.hasNext()) {
-                       ((MooNoteElement)it.next()).deselect();
+                       removeNote((MooNoteElement)it.next());
                }
-               selected.clear();
+               selection.clear();
+               Moosique.setEdited();
        }
 
        /**
-        * Determines if the given MooNoteElement is the only one in the track view that is selected.
-        * @return if the given element is the only selected one
+        * Transposes all selected notes the given number of halftones.
         */
-       public boolean isTheOnlySelected(MooNoteElement elem) {
-               Iterator it = selected.iterator();
+       private void transposeSelectedNotes(int halftones) {
+               TreeSet selection = Moosique.getSelection();
+               Iterator it = selection.iterator();
                while(it.hasNext()) {
-                       if (!it.next().equals(elem)) return false;
+                       MooNoteElement elem = (MooNoteElement)it.next();
+                       elem.transpose(halftones);
+               }
+               Moosique.setEdited();
+       }
+
+       /**
+        * Moves the current selection the given number of ticks.
+        * @param ticks         the number of ticks to move the selection.
+        */
+       public void moveSelectedNotes(int ticks) {
+               TreeSet selection = Moosique.getSelection();
+               if (ticks < 0) {
+                       // If the selection should be moved upwards, traverses the list in the natural order.
+                       Iterator it = selection.iterator();
+                       while(it.hasNext()) {
+                               MooNoteElement elem = (MooNoteElement)it.next();
+                               elem.getNote().setTick(elem.getNote().getTick() + ticks);
+                               layoutElement(elem, true);
+                       }
+               } else {
+                       // If the selection should be moved downwards, traverses the list in the opposite order.
+                       ArrayList selectedList = new ArrayList(selection);
+                       ListIterator it = selectedList.listIterator(selectedList.size());
+                       while(it.hasPrevious()) {
+                               MooNoteElement elem = (MooNoteElement)it.previous();
+                               elem.getNote().setTick(elem.getNote().getTick() + ticks);
+                               layoutElement(elem, true);
+                       }
+               }
+               Moosique.setEdited();
+       }
+
+       /**
+        * Enables keyboard recording.
+        */
+       public void enableKeyboardRecording() {
+               keyboard.recordEnable();
+       }
+
+       /**
+        * Disables keyboard recording.
+        */
+       public void disableKeyboardRecording() {
+               keyboard.recordDisable();
+       }
+
+       /**
+        * Adds a menu item with the given command to the given popup menu.
+        */
+       private JMenuItem addMenuItem(JPopupMenu menu, String command) {
+               JMenuItem item = new JMenuItem(command);
+               item.addActionListener(new PopupListener());
+               menu.add(item);
+               return item;
+       }
+
+       /**
+        * Adds a menu item with the given command to the given menu.
+        */
+       private JMenuItem addMenuItem(JMenu menu, String command) {
+               JMenuItem item = new JMenuItem(command);
+               item.addActionListener(new PopupListener());
+               menu.add(item);
+               return item;
+       }
+
+       /**
+        * Creates a transpose sub menu with the given title in the given popup menu,
+        * inserting the items into the given array.
+        */
+       private JMenu createTransposeMenu(JPopupMenu menu, JMenuItem[] items, String title) {
+               JMenu trans = new JMenu("Transpose " + title);
+               menu.add(trans);
+               items[0] = addMenuItem(trans, "One octave");
+               for (int i = 1; i < 12; i++) {
+                       items[i] = addMenuItem(trans, (i) + " halftones");
                }
-               return true;
+               return trans;
        }
 
        /**
@@ -247,21 +390,11 @@ public class MooTrackView extends JPanel {
         * @param y     the y-coordinate in which to display the menu
         */
        public void showSelectionPopup(Component c, int x, int y) {
+               // Determines whether the "Properties" item should be available.
+               selPopupProps.setEnabled(Moosique.getSelection().size() == 1);
                selPopup.show(c, x, y);
        }
 
-       /**
-        * Transposes all selected notes the given number of halftones.
-        */
-       private void transposeSelectedNotes(int halftones) {
-               Iterator it = selected.iterator();
-               while(it.hasNext()) {
-                       MooNoteElement elem = (MooNoteElement)it.next();
-                       elem.getNote().transpose(halftones);
-                       elem.update();
-               }
-       }
-
        /**
         * Draws the grid that is on the background.
         * @param g The Graphics object used to draw the grid.
@@ -278,6 +411,14 @@ public class MooTrackView extends JPanel {
                }
        }
        
+       /**
+        * Returns whether the left mouse button is currently pressed or not.
+        * @return true if the left mosue button is currently pressed
+        */
+       public boolean isLeftMouseButtonPressed() {
+               return leftMouseButtonPressed;
+       }
+
        /**
         * The adapter used to listen on mouse actions
         */
@@ -288,29 +429,24 @@ public class MooTrackView extends JPanel {
                 */
                public void mouseClicked(MouseEvent e) {
                        if (SwingUtilities.isLeftMouseButton(e)) {
-                               deselectAllNotes();
+                               Moosique.deselectAllNotes();
                                if (e.getClickCount() == 2) {
                                        popupY = e.getY();
-                                       addStandardNote();
+                                       addNoteAtClickY(lastNoteLength);
                                }
                        }
                }
        
                public void mousePressed(MouseEvent e) {
+                       if (SwingUtilities.isLeftMouseButton(e)) leftMouseButtonPressed = true;
                        maybeShowPopup(e);
                }
 
                public void mouseReleased(MouseEvent e) {
+                       if (SwingUtilities.isLeftMouseButton(e)) leftMouseButtonPressed = false;
                        maybeShowPopup(e);
                }
 
-               /**
-                * Selects the notes within the area that was selected.
-                */
-               public void mouseDragged(MouseEvent e) {
-                       
-               }
-
                /**
                 * Shows the menu if an OS-specific popup-trigger was activated.
                 */
@@ -325,29 +461,55 @@ public class MooTrackView extends JPanel {
                 * Grabs the focus when the mouse has entered.
                 */
                public void mouseEntered(MouseEvent e) {
-                       // Moosique.setActiveChannel(track.getChannel());
+                       Moosique.setActiveChannel(title.getChannel());
                        grabFocus();
                }
        }
 
        /**
-        * Listens on actions on the popup menu and executes the appropriate action.
+        * Takes the appropriate action when a user selects an item on the popup menu.
         */
        class PopupListener implements ActionListener {
                public void actionPerformed(ActionEvent e) {
                        Object source = e.getSource();
-                       if (source == popupAdd) {
-                               addStandardNote();
+                       // Handling panel popup actions.
+                       if (source == popupAddItemsLast) {
+                               addNoteAtClickY(lastNoteLength);
+                       } else if (source == popupAddItemsCustom) {
+                               /* Show the user a dialog (identical to note preferences...
+                               then call       addNote(new MooNote(
+                               title.getChannel(), pitch, velocity, timestamp, duration));
+                               */
+                       } else if (source == popupAddItemsWhole) {
+                               addNoteAtClickY(16);
+                       } else if (source == popupAddItemsHalf) {
+                               addNoteAtClickY(8);
+                       } else if (source == popupAddItemsQuarter) {
+                               addNoteAtClickY(4);
+                       } else if (source == popupAddItemsEighth) {
+                               addNoteAtClickY(2);
+                       } else if (source == popupAddItemsSixteenth) {
+                               addNoteAtClickY(1);
+                       } else if (source == popupPaste) {
+                               pasteCopiedNotes();
+                       // Handling selection popup actions.
+                       } else if (source == selPopupProps) {
+                               new MooDialog(((MooNoteElement)Moosique.getSelection().first()).getNote());
+                       } else if (source == selPopupCopy) {
+                               copySelectedNotes();
+                       } else if (source == selPopupCut) {
+                               cutSelectedNotes();
                        } else if (source == selPopupRemove) {
-                               Iterator it = selected.iterator();
-                               while(it.hasNext()) {
-                                       removeNote((MooNoteElement)it.next());
-                               }
-                               selected.clear();
-                       } else if (source == selPopupTranspUpOct) {
+                               removeSelectedNotes();
+                       } else if (source == selPopupTranspUpItems[0]) {
                                transposeSelectedNotes(12);
-                       } else if (source == selPopupTranspDownOct) {
+                       } else if (source == selPopupTranspDownItems[0]) {
                                transposeSelectedNotes(-12);
+                       } else {
+                               for (int i = 1; i < 12; i++) {
+                                       if (source == selPopupTranspUpItems[i]) transposeSelectedNotes(i);
+                                       else if (source == selPopupTranspDownItems[i]) transposeSelectedNotes(-i);
+                               }
                        }
                }
        }